Update app.py
Browse files
app.py
CHANGED
@@ -4,64 +4,59 @@ import streamlit as st
|
|
4 |
import plotly.graph_objects as go
|
5 |
from plotly.subplots import make_subplots
|
6 |
import time
|
7 |
-
from collections import deque # Add this line
|
8 |
-
import threading
|
9 |
-
|
10 |
-
|
11 |
-
# Initialize session state variables
|
12 |
-
if 'running' not in st.session_state:
|
13 |
-
st.session_state.running = False
|
14 |
-
|
15 |
-
if 'env' not in st.session_state:
|
16 |
-
st.session_state.env = None
|
17 |
-
|
18 |
-
if 'fig' not in st.session_state:
|
19 |
-
st.session_state.fig = None
|
20 |
|
21 |
class Organelle:
|
22 |
def __init__(self, type):
|
23 |
self.type = type
|
24 |
|
|
|
|
|
|
|
|
|
|
|
25 |
class Cell:
|
26 |
def __init__(self, x, y, cell_type="prokaryote"):
|
27 |
self.x = x
|
28 |
self.y = y
|
29 |
self.energy = 100
|
30 |
self.cell_type = cell_type
|
31 |
-
self.organelles =
|
|
|
32 |
self.size = 1
|
33 |
self.color = "lightblue"
|
34 |
self.division_threshold = 150
|
|
|
35 |
self.update_properties()
|
36 |
|
37 |
def update_properties(self):
|
38 |
if self.cell_type == "early_eukaryote":
|
39 |
-
self.organelles.
|
40 |
self.color = "green"
|
41 |
self.size = 2
|
42 |
elif self.cell_type == "advanced_eukaryote":
|
43 |
-
self.organelles.
|
44 |
self.color = "red"
|
45 |
self.size = 3
|
46 |
elif self.cell_type == "plant_like":
|
47 |
-
self.organelles.
|
48 |
self.color = "darkgreen"
|
49 |
self.size = 4
|
50 |
-
elif self.cell_type == "complete":
|
51 |
-
self.organelles.update(["nucleus", "mitochondria", "chloroplast", "endoplasmic_reticulum", "golgi_apparatus"])
|
52 |
-
self.color = "purple"
|
53 |
-
self.size = 5
|
54 |
|
55 |
def move(self, environment):
|
56 |
-
dx
|
|
|
57 |
self.x = max(0, min(environment.width - 1, self.x + dx))
|
58 |
self.y = max(0, min(environment.height - 1, self.y + dy))
|
59 |
self.energy -= 0.5 * self.size
|
60 |
|
61 |
def feed(self, environment):
|
62 |
base_energy = environment.grid[int(self.y)][int(self.x)] * 0.1
|
63 |
-
if "chloroplast" in self.organelles:
|
64 |
base_energy += environment.light_level * 2
|
|
|
|
|
|
|
|
|
65 |
self.energy += base_energy
|
66 |
environment.grid[int(self.y)][int(self.x)] *= 0.9
|
67 |
|
@@ -73,32 +68,46 @@ class Cell:
|
|
73 |
self.energy /= 2
|
74 |
new_cell = Cell(self.x, self.y, self.cell_type)
|
75 |
new_cell.organelles = self.organelles.copy()
|
|
|
76 |
return new_cell
|
77 |
return None
|
78 |
|
79 |
-
def
|
80 |
return (self.cell_type == other.cell_type and
|
81 |
-
random.random() < 0.
|
82 |
|
83 |
-
def
|
84 |
new_cell_type = self.cell_type
|
85 |
if self.cell_type == "prokaryote":
|
86 |
new_cell_type = "early_eukaryote"
|
87 |
elif self.cell_type == "early_eukaryote":
|
88 |
new_cell_type = "advanced_eukaryote"
|
89 |
-
elif self.cell_type == "advanced_eukaryote":
|
90 |
-
new_cell_type = "plant_like"
|
91 |
-
elif self.cell_type == "plant_like":
|
92 |
-
new_cell_type = "complete"
|
93 |
|
94 |
-
new_cell = Cell(
|
|
|
|
|
|
|
|
|
95 |
new_cell.energy = self.energy + other.energy
|
96 |
-
new_cell.organelles = self.organelles
|
|
|
97 |
new_cell.update_properties()
|
98 |
return new_cell
|
99 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
100 |
class Environment:
|
101 |
-
def __init__(self, width, height
|
102 |
self.width = width
|
103 |
self.height = height
|
104 |
self.grid = np.random.rand(height, width) * 10
|
@@ -107,9 +116,8 @@ class Environment:
|
|
107 |
self.time = 0
|
108 |
self.population_history = {
|
109 |
"prokaryote": [], "early_eukaryote": [],
|
110 |
-
"advanced_eukaryote": [], "plant_like": [], "
|
111 |
}
|
112 |
-
self.effects = effects
|
113 |
|
114 |
def add_cell(self, cell):
|
115 |
self.cells.append(cell)
|
@@ -133,232 +141,145 @@ class Environment:
|
|
133 |
if new_cell:
|
134 |
new_cells.append(new_cell)
|
135 |
|
136 |
-
# Handle cell
|
137 |
for i, cell1 in enumerate(self.cells):
|
138 |
for cell2 in self.cells[i+1:]:
|
139 |
-
if cell1.
|
140 |
-
new_cell = cell1.
|
141 |
new_cells.append(new_cell)
|
142 |
cells_to_remove.extend([cell1, cell2])
|
143 |
|
144 |
-
#
|
145 |
-
if self.effects['radiation']:
|
146 |
-
self.apply_radiation()
|
147 |
-
if self.effects['predation']:
|
148 |
-
self.apply_predation()
|
149 |
-
if self.effects['symbiosis']:
|
150 |
-
self.apply_symbiosis()
|
151 |
-
|
152 |
-
# Add new cells and remove dead/merged cells
|
153 |
self.cells.extend(new_cells)
|
154 |
self.cells = [cell for cell in self.cells if cell not in cells_to_remove]
|
155 |
|
156 |
-
#
|
157 |
-
for cell_type in self.population_history.keys():
|
158 |
-
count = len([cell for cell in self.cells if cell.cell_type == cell_type])
|
159 |
-
self.population_history[cell_type].append(count)
|
160 |
-
|
161 |
-
def apply_radiation(self):
|
162 |
for cell in self.cells:
|
163 |
-
if random.random() < 0.
|
164 |
-
cell.
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
if cell.organelles:
|
169 |
-
cell.organelles.remove(random.choice(list(cell.organelles)))
|
170 |
cell.update_properties()
|
|
|
|
|
|
|
171 |
|
172 |
-
|
173 |
-
for
|
174 |
-
if
|
175 |
-
for
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
def apply_symbiosis(self):
|
181 |
-
for i, cell1 in enumerate(self.cells):
|
182 |
-
for cell2 in self.cells[i+1:]:
|
183 |
-
if cell1.cell_type != cell2.cell_type and random.random() < 0.01:
|
184 |
-
shared_energy = (cell1.energy + cell2.energy) * 0.1
|
185 |
-
cell1.energy += shared_energy
|
186 |
-
cell2.energy += shared_energy
|
187 |
|
188 |
def get_visualization_data(self):
|
189 |
-
cell_data = {
|
190 |
-
|
|
|
|
|
|
|
|
|
|
|
191 |
|
192 |
for cell in self.cells:
|
193 |
-
|
194 |
-
cell_data[
|
195 |
-
cell_data[
|
|
|
196 |
|
197 |
-
return cell_data, self.population_history
|
198 |
|
199 |
def setup_figure(env):
|
200 |
-
cell_types =
|
201 |
-
fig = make_subplots(rows=2, cols=
|
202 |
subplot_titles=("Cell Distribution", "Total Population",
|
203 |
-
"
|
|
|
204 |
vertical_spacing=0.1,
|
205 |
horizontal_spacing=0.05)
|
206 |
|
207 |
-
cell_data, population_history, colors = env.get_visualization_data()
|
208 |
-
|
209 |
# Cell distribution
|
210 |
-
for cell_type, data in
|
211 |
fig.add_trace(go.Scatter(
|
212 |
x=data["x"], y=data["y"], mode='markers',
|
213 |
-
marker=dict(color=
|
214 |
name=cell_type
|
215 |
), row=1, col=1)
|
216 |
|
217 |
# Total population over time
|
218 |
-
|
219 |
-
|
220 |
-
|
221 |
-
#
|
222 |
-
for
|
223 |
-
|
224 |
-
|
225 |
-
|
226 |
-
|
227 |
-
|
228 |
-
|
229 |
-
|
230 |
-
|
231 |
-
|
232 |
|
233 |
fig.update_xaxes(title_text="X", row=1, col=1)
|
234 |
fig.update_yaxes(title_text="Y", row=1, col=1)
|
235 |
fig.update_xaxes(title_text="Time", row=1, col=2)
|
236 |
fig.update_yaxes(title_text="Population", row=1, col=2)
|
237 |
-
|
238 |
-
|
239 |
-
|
240 |
-
|
241 |
|
242 |
fig.update_layout(height=800, width=1200, title_text="Advanced Cell Evolution Simulation")
|
243 |
|
244 |
return fig
|
245 |
|
246 |
-
|
247 |
-
|
248 |
-
|
249 |
-
|
250 |
-
|
251 |
-
|
252 |
-
|
253 |
-
|
254 |
-
|
255 |
-
# Clear existing traces
|
256 |
-
st.session_state.fig.data = []
|
257 |
-
|
258 |
-
if st.session_state.running and st.session_state.env is not None:
|
259 |
-
st.session_state.env.update()
|
260 |
-
update_chart()
|
261 |
-
|
262 |
-
# Cell positions
|
263 |
-
cell_types = [cell.cell_type for cell in st.session_state.env.cells]
|
264 |
-
x_positions = [cell.x for cell in st.session_state.env.cells]
|
265 |
-
y_positions = [cell.y for cell in st.session_state.env.cells]
|
266 |
-
|
267 |
-
st.session_state.fig.add_trace(go.Scatter(x=x_positions, y=y_positions, mode='markers',
|
268 |
-
marker=dict(color=[colors[ct] for ct in cell_types]),
|
269 |
-
text=cell_types, hoverinfo='text'), row=1, col=1)
|
270 |
-
|
271 |
-
# Population history
|
272 |
-
for cell_type, counts in st.session_state.env.population_history.items():
|
273 |
-
st.session_state.fig.add_trace(go.Scatter(y=counts, mode='lines', name=cell_type,
|
274 |
-
line=dict(color=colors[cell_type])), row=1, col=2)
|
275 |
-
|
276 |
-
# Population by cell type
|
277 |
-
for cell_type, counts in st.session_state.env.population_history.items():
|
278 |
-
st.session_state.fig.add_trace(go.Scatter(y=counts, mode='lines', name=cell_type,
|
279 |
-
line=dict(color=colors[cell_type])), row=2, col=1)
|
280 |
-
|
281 |
-
# Organelle distribution
|
282 |
-
organelle_counts = {"nucleus": 0, "mitochondria": 0, "chloroplast": 0,
|
283 |
-
"endoplasmic_reticulum": 0, "golgi_apparatus": 0}
|
284 |
-
for cell in st.session_state.env.cells:
|
285 |
-
for organelle in cell.organelles:
|
286 |
-
organelle_counts[organelle] += 1
|
287 |
|
288 |
-
|
289 |
-
|
290 |
-
|
291 |
-
|
292 |
-
st.session_state.fig.update_xaxes(title_text="X", row=1, col=1)
|
293 |
-
st.session_state.fig.update_yaxes(title_text="Y", row=1, col=1)
|
294 |
-
st.session_state.fig.update_xaxes(title_text="Time", row=1, col=2)
|
295 |
-
st.session_state.fig.update_yaxes(title_text="Population", row=1, col=2)
|
296 |
-
st.session_state.fig.update_xaxes(title_text="Time", row=2, col=1)
|
297 |
-
st.session_state.fig.update_yaxes(title_text="Population", row=2, col=1)
|
298 |
-
st.session_state.fig.update_xaxes(title_text="Organelle", row=2, col=2)
|
299 |
-
st.session_state.fig.update_yaxes(title_text="Count", row=2, col=2)
|
300 |
-
|
301 |
-
st.session_state.fig.update_layout(height=800, width=1200, title_text="Advanced Cell Evolution Simulation")
|
302 |
-
|
303 |
-
st.sidebar.header("Simulation Settings")
|
304 |
-
st.session_state.radiation = st.sidebar.checkbox("Enable Radiation", value=False)
|
305 |
-
st.session_state.predation = st.sidebar.checkbox("Enable Predation", value=False)
|
306 |
-
st.session_state.symbiosis = st.sidebar.checkbox("Enable Symbiosis", value=False)
|
307 |
|
308 |
-
#
|
309 |
-
|
310 |
-
|
311 |
-
def start_simulation():
|
312 |
-
st.session_state.running = True
|
313 |
-
if st.session_state.env is None:
|
314 |
-
# Initialize your environment here
|
315 |
-
effects = {
|
316 |
-
'radiation': st.session_state.radiation,
|
317 |
-
'predation': st.session_state.predation,
|
318 |
-
'symbiosis': st.session_state.symbiosis
|
319 |
-
}
|
320 |
-
st.session_state.env = Environment(100, 100, effects)
|
321 |
-
# Add initial cells to the environment
|
322 |
-
for _ in range(50):
|
323 |
-
cell = Cell(random.uniform(0, 100), random.uniform(0, 100))
|
324 |
-
st.session_state.env.add_cell(cell)
|
325 |
|
326 |
-
|
327 |
-
|
328 |
-
|
329 |
-
|
330 |
-
|
331 |
-
|
332 |
-
|
333 |
-
|
334 |
-
|
335 |
-
|
336 |
-
|
337 |
-
|
338 |
-
with col2:
|
339 |
-
stop_button = st.button("Stop Simulation", on_click=stop_simulation)
|
340 |
-
|
341 |
-
# Main simulation loop
|
342 |
-
simulation_container = st.empty()
|
343 |
-
|
344 |
-
if st.session_state.running and st.session_state.env is not None:
|
345 |
-
with simulation_container.container():
|
346 |
-
for _ in range(4): # Update 4 times per frame to increase simulation speed
|
347 |
-
initial_cell_count = len(st.session_state.env.cells)
|
348 |
-
st.session_state.env.update()
|
349 |
-
final_cell_count = len(st.session_state.env.cells)
|
350 |
|
351 |
-
|
352 |
-
|
353 |
-
|
354 |
-
|
355 |
-
|
|
|
|
|
356 |
|
357 |
-
|
358 |
-
|
359 |
-
|
360 |
-
|
361 |
-
|
|
|
362 |
|
363 |
-
|
364 |
-
st.write("Simulation stopped. Click 'Start Simulation' to run again.")
|
|
|
4 |
import plotly.graph_objects as go
|
5 |
from plotly.subplots import make_subplots
|
6 |
import time
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
7 |
|
8 |
class Organelle:
|
9 |
def __init__(self, type):
|
10 |
self.type = type
|
11 |
|
12 |
+
class Modification:
|
13 |
+
def __init__(self, name, effect):
|
14 |
+
self.name = name
|
15 |
+
self.effect = effect
|
16 |
+
|
17 |
class Cell:
|
18 |
def __init__(self, x, y, cell_type="prokaryote"):
|
19 |
self.x = x
|
20 |
self.y = y
|
21 |
self.energy = 100
|
22 |
self.cell_type = cell_type
|
23 |
+
self.organelles = []
|
24 |
+
self.modifications = []
|
25 |
self.size = 1
|
26 |
self.color = "lightblue"
|
27 |
self.division_threshold = 150
|
28 |
+
|
29 |
self.update_properties()
|
30 |
|
31 |
def update_properties(self):
|
32 |
if self.cell_type == "early_eukaryote":
|
33 |
+
self.organelles.append(Organelle("nucleus"))
|
34 |
self.color = "green"
|
35 |
self.size = 2
|
36 |
elif self.cell_type == "advanced_eukaryote":
|
37 |
+
self.organelles.extend([Organelle("nucleus"), Organelle("mitochondria")])
|
38 |
self.color = "red"
|
39 |
self.size = 3
|
40 |
elif self.cell_type == "plant_like":
|
41 |
+
self.organelles.extend([Organelle("nucleus"), Organelle("mitochondria"), Organelle("chloroplast")])
|
42 |
self.color = "darkgreen"
|
43 |
self.size = 4
|
|
|
|
|
|
|
|
|
44 |
|
45 |
def move(self, environment):
|
46 |
+
dx = random.uniform(-1, 1)
|
47 |
+
dy = random.uniform(-1, 1)
|
48 |
self.x = max(0, min(environment.width - 1, self.x + dx))
|
49 |
self.y = max(0, min(environment.height - 1, self.y + dy))
|
50 |
self.energy -= 0.5 * self.size
|
51 |
|
52 |
def feed(self, environment):
|
53 |
base_energy = environment.grid[int(self.y)][int(self.x)] * 0.1
|
54 |
+
if "chloroplast" in [org.type for org in self.organelles]:
|
55 |
base_energy += environment.light_level * 2
|
56 |
+
|
57 |
+
for mod in self.modifications:
|
58 |
+
base_energy *= mod.effect
|
59 |
+
|
60 |
self.energy += base_energy
|
61 |
environment.grid[int(self.y)][int(self.x)] *= 0.9
|
62 |
|
|
|
68 |
self.energy /= 2
|
69 |
new_cell = Cell(self.x, self.y, self.cell_type)
|
70 |
new_cell.organelles = self.organelles.copy()
|
71 |
+
new_cell.modifications = self.modifications.copy()
|
72 |
return new_cell
|
73 |
return None
|
74 |
|
75 |
+
def can_fuse(self, other):
|
76 |
return (self.cell_type == other.cell_type and
|
77 |
+
random.random() < 0.005) # 0.5% chance of fusion
|
78 |
|
79 |
+
def fuse(self, other):
|
80 |
new_cell_type = self.cell_type
|
81 |
if self.cell_type == "prokaryote":
|
82 |
new_cell_type = "early_eukaryote"
|
83 |
elif self.cell_type == "early_eukaryote":
|
84 |
new_cell_type = "advanced_eukaryote"
|
|
|
|
|
|
|
|
|
85 |
|
86 |
+
new_cell = Cell(
|
87 |
+
(self.x + other.x) / 2,
|
88 |
+
(self.y + other.y) / 2,
|
89 |
+
new_cell_type
|
90 |
+
)
|
91 |
new_cell.energy = self.energy + other.energy
|
92 |
+
new_cell.organelles = list(set(self.organelles + other.organelles))
|
93 |
+
new_cell.modifications = list(set(self.modifications + other.modifications))
|
94 |
new_cell.update_properties()
|
95 |
return new_cell
|
96 |
|
97 |
+
def acquire_modification(self):
|
98 |
+
possible_mods = [
|
99 |
+
Modification("Enhanced metabolism", 1.2),
|
100 |
+
Modification("Thick cell wall", 0.8),
|
101 |
+
Modification("Efficient energy storage", 1.1),
|
102 |
+
Modification("Rapid division", 0.9)
|
103 |
+
]
|
104 |
+
new_mod = random.choice(possible_mods)
|
105 |
+
if new_mod not in self.modifications:
|
106 |
+
self.modifications.append(new_mod)
|
107 |
+
self.color = "purple" # Visual indicator of modification
|
108 |
+
|
109 |
class Environment:
|
110 |
+
def __init__(self, width, height):
|
111 |
self.width = width
|
112 |
self.height = height
|
113 |
self.grid = np.random.rand(height, width) * 10
|
|
|
116 |
self.time = 0
|
117 |
self.population_history = {
|
118 |
"prokaryote": [], "early_eukaryote": [],
|
119 |
+
"advanced_eukaryote": [], "plant_like": [], "modified": []
|
120 |
}
|
|
|
121 |
|
122 |
def add_cell(self, cell):
|
123 |
self.cells.append(cell)
|
|
|
141 |
if new_cell:
|
142 |
new_cells.append(new_cell)
|
143 |
|
144 |
+
# Handle cell fusion
|
145 |
for i, cell1 in enumerate(self.cells):
|
146 |
for cell2 in self.cells[i+1:]:
|
147 |
+
if cell1.can_fuse(cell2):
|
148 |
+
new_cell = cell1.fuse(cell2)
|
149 |
new_cells.append(new_cell)
|
150 |
cells_to_remove.extend([cell1, cell2])
|
151 |
|
152 |
+
# Add new cells and remove dead/fused cells
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
153 |
self.cells.extend(new_cells)
|
154 |
self.cells = [cell for cell in self.cells if cell not in cells_to_remove]
|
155 |
|
156 |
+
# Introduce mutations and modifications
|
|
|
|
|
|
|
|
|
|
|
157 |
for cell in self.cells:
|
158 |
+
if random.random() < 0.0001: # 0.01% chance of mutation
|
159 |
+
if cell.cell_type == "early_eukaryote":
|
160 |
+
cell.cell_type = "advanced_eukaryote"
|
161 |
+
elif cell.cell_type == "advanced_eukaryote" and random.random() < 0.5:
|
162 |
+
cell.cell_type = "plant_like"
|
|
|
|
|
163 |
cell.update_properties()
|
164 |
+
|
165 |
+
if random.random() < 0.0005: # 0.05% chance of acquiring a modification
|
166 |
+
cell.acquire_modification()
|
167 |
|
168 |
+
# Record population counts
|
169 |
+
for cell_type in self.population_history.keys():
|
170 |
+
if cell_type != "modified":
|
171 |
+
count = len([cell for cell in self.cells if cell.cell_type == cell_type and not cell.modifications])
|
172 |
+
else:
|
173 |
+
count = len([cell for cell in self.cells if cell.modifications])
|
174 |
+
self.population_history[cell_type].append(count)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
175 |
|
176 |
def get_visualization_data(self):
|
177 |
+
cell_data = {
|
178 |
+
"prokaryote": {"x": [], "y": [], "size": [], "color": "lightblue", "symbol": "circle"},
|
179 |
+
"early_eukaryote": {"x": [], "y": [], "size": [], "color": "green", "symbol": "square"},
|
180 |
+
"advanced_eukaryote": {"x": [], "y": [], "size": [], "color": "red", "symbol": "diamond"},
|
181 |
+
"plant_like": {"x": [], "y": [], "size": [], "color": "darkgreen", "symbol": "star"},
|
182 |
+
"modified": {"x": [], "y": [], "size": [], "color": "purple", "symbol": "cross"}
|
183 |
+
}
|
184 |
|
185 |
for cell in self.cells:
|
186 |
+
cell_type = "modified" if cell.modifications else cell.cell_type
|
187 |
+
cell_data[cell_type]["x"].append(cell.x)
|
188 |
+
cell_data[cell_type]["y"].append(cell.y)
|
189 |
+
cell_data[cell_type]["size"].append(cell.size * 3)
|
190 |
|
191 |
+
return cell_data, self.population_history
|
192 |
|
193 |
def setup_figure(env):
|
194 |
+
cell_types = ["prokaryote", "early_eukaryote", "advanced_eukaryote", "plant_like", "modified"]
|
195 |
+
fig = make_subplots(rows=2, cols=3,
|
196 |
subplot_titles=("Cell Distribution", "Total Population",
|
197 |
+
"Prokaryotes", "Early Eukaryotes",
|
198 |
+
"Advanced Eukaryotes", "Plant-like & Modified"),
|
199 |
vertical_spacing=0.1,
|
200 |
horizontal_spacing=0.05)
|
201 |
|
|
|
|
|
202 |
# Cell distribution
|
203 |
+
for cell_type, data in env.get_visualization_data()[0].items():
|
204 |
fig.add_trace(go.Scatter(
|
205 |
x=data["x"], y=data["y"], mode='markers',
|
206 |
+
marker=dict(color=data["color"], size=data["size"], symbol=data["symbol"]),
|
207 |
name=cell_type
|
208 |
), row=1, col=1)
|
209 |
|
210 |
# Total population over time
|
211 |
+
for cell_type, counts in env.population_history.items():
|
212 |
+
fig.add_trace(go.Scatter(y=counts, mode='lines', name=cell_type), row=1, col=2)
|
213 |
+
|
214 |
+
# Individual population charts
|
215 |
+
for i, cell_type in enumerate(cell_types):
|
216 |
+
if cell_type == "modified":
|
217 |
+
fig.add_trace(go.Scatter(y=env.population_history[cell_type], mode='lines',
|
218 |
+
name=cell_type, line=dict(color="purple")), row=2, col=3)
|
219 |
+
elif cell_type == "plant_like":
|
220 |
+
fig.add_trace(go.Scatter(y=env.population_history[cell_type], mode='lines',
|
221 |
+
name=cell_type, line=dict(color="darkgreen")), row=2, col=3)
|
222 |
+
else:
|
223 |
+
fig.add_trace(go.Scatter(y=env.population_history[cell_type], mode='lines',
|
224 |
+
name=cell_type), row=2, col=i+1)
|
225 |
|
226 |
fig.update_xaxes(title_text="X", row=1, col=1)
|
227 |
fig.update_yaxes(title_text="Y", row=1, col=1)
|
228 |
fig.update_xaxes(title_text="Time", row=1, col=2)
|
229 |
fig.update_yaxes(title_text="Population", row=1, col=2)
|
230 |
+
|
231 |
+
for i in range(1, 4):
|
232 |
+
fig.update_xaxes(title_text="Time", row=2, col=i)
|
233 |
+
fig.update_yaxes(title_text="Population", row=2, col=i)
|
234 |
|
235 |
fig.update_layout(height=800, width=1200, title_text="Advanced Cell Evolution Simulation")
|
236 |
|
237 |
return fig
|
238 |
|
239 |
+
# Streamlit app
|
240 |
+
st.title("Advanced Cell Evolution Simulation")
|
241 |
+
|
242 |
+
num_steps = st.slider("Number of simulation steps", 100, 2000, 1000)
|
243 |
+
initial_cells = st.slider("Initial number of cells", 10, 200, 100)
|
244 |
+
update_interval = st.slider("Update interval (milliseconds)", 50, 500, 100)
|
245 |
+
|
246 |
+
if st.button("Run Simulation"):
|
247 |
+
env = Environment(100, 100)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
248 |
|
249 |
+
# Add initial cells
|
250 |
+
for _ in range(initial_cells):
|
251 |
+
cell = Cell(random.uniform(0, env.width), random.uniform(0, env.height))
|
252 |
+
env.add_cell(cell)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
253 |
|
254 |
+
# Set up the figure
|
255 |
+
fig = setup_figure(env)
|
256 |
+
chart = st.plotly_chart(fig, use_container_width=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
257 |
|
258 |
+
# Run simulation
|
259 |
+
for step in range(num_steps):
|
260 |
+
env.update()
|
261 |
+
|
262 |
+
# Update the figure data
|
263 |
+
with fig.batch_update():
|
264 |
+
cell_data, population_history = env.get_visualization_data()
|
265 |
+
for i, (cell_type, data) in enumerate(cell_data.items()):
|
266 |
+
fig.data[i].x = data["x"]
|
267 |
+
fig.data[i].y = data["y"]
|
268 |
+
fig.data[i].marker.size = data["size"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
269 |
|
270 |
+
for i, (cell_type, counts) in enumerate(population_history.items()):
|
271 |
+
fig.data[i+5].y = counts # +5 because we have 5 cell types in the first subplot
|
272 |
+
if cell_type != "modified" and cell_type != "plant_like":
|
273 |
+
fig.data[i+10].y = counts # Update individual population charts
|
274 |
+
else:
|
275 |
+
fig.data[13].y = population_history["plant_like"]
|
276 |
+
fig.data[14].y = population_history["modified"]
|
277 |
|
278 |
+
fig.layout.title.text = f"Advanced Cell Evolution Simulation (Time: {env.time})"
|
279 |
+
|
280 |
+
# Update the chart
|
281 |
+
chart.plotly_chart(fig, use_container_width=True)
|
282 |
+
|
283 |
+
time.sleep(update_interval / 1000) # Convert milliseconds to seconds
|
284 |
|
285 |
+
st.write("Simulation complete!")
|
|