Sephfox commited on
Commit
9aae907
1 Parent(s): 710400f

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +140 -219
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 = set()
 
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.add("nucleus")
40
  self.color = "green"
41
  self.size = 2
42
  elif self.cell_type == "advanced_eukaryote":
43
- self.organelles.update(["nucleus", "mitochondria"])
44
  self.color = "red"
45
  self.size = 3
46
  elif self.cell_type == "plant_like":
47
- self.organelles.update(["nucleus", "mitochondria", "chloroplast"])
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, dy = random.uniform(-1, 1), random.uniform(-1, 1)
 
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 can_merge(self, other):
80
  return (self.cell_type == other.cell_type and
81
- random.random() < 0.01) # 1% chance of merging
82
 
83
- def merge(self, other):
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((self.x + other.x) / 2, (self.y + other.y) / 2, new_cell_type)
 
 
 
 
95
  new_cell.energy = self.energy + other.energy
96
- new_cell.organelles = self.organelles.union(other.organelles)
 
97
  new_cell.update_properties()
98
  return new_cell
99
 
 
 
 
 
 
 
 
 
 
 
 
 
100
  class Environment:
101
- def __init__(self, width, height, effects):
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": [], "complete": []
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 merging
137
  for i, cell1 in enumerate(self.cells):
138
  for cell2 in self.cells[i+1:]:
139
- if cell1.can_merge(cell2):
140
- new_cell = cell1.merge(cell2)
141
  new_cells.append(new_cell)
142
  cells_to_remove.extend([cell1, cell2])
143
 
144
- # Apply effects
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
- # Record population counts
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.01: # 1% chance of mutation
164
- cell.energy *= 0.8
165
- if random.random() < 0.5:
166
- cell.organelles.add(random.choice(["nucleus", "mitochondria", "chloroplast", "endoplasmic_reticulum", "golgi_apparatus"]))
167
- else:
168
- if cell.organelles:
169
- cell.organelles.remove(random.choice(list(cell.organelles)))
170
  cell.update_properties()
 
 
 
171
 
172
- def apply_predation(self):
173
- for i, predator in enumerate(self.cells):
174
- if predator.cell_type in ["advanced_eukaryote", "plant_like", "complete"]:
175
- for prey in self.cells[i+1:]:
176
- if prey.cell_type in ["prokaryote", "early_eukaryote"] and random.random() < 0.05:
177
- predator.energy += prey.energy * 0.5
178
- self.cells.remove(prey)
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 = {cell_type: {"x": [], "y": [], "size": []} for cell_type in self.population_history.keys()}
190
- colors = {"prokaryote": "lightblue", "early_eukaryote": "green", "advanced_eukaryote": "red", "plant_like": "darkgreen", "complete": "purple"}
 
 
 
 
 
191
 
192
  for cell in self.cells:
193
- cell_data[cell.cell_type]["x"].append(cell.x)
194
- cell_data[cell.cell_type]["y"].append(cell.y)
195
- cell_data[cell.cell_type]["size"].append(cell.size * 3)
 
196
 
197
- return cell_data, self.population_history, colors
198
 
199
  def setup_figure(env):
200
- cell_types = list(env.population_history.keys())
201
- fig = make_subplots(rows=2, cols=2,
202
  subplot_titles=("Cell Distribution", "Total Population",
203
- "Population by Cell Type", "Organelle Distribution"),
 
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 cell_data.items():
211
  fig.add_trace(go.Scatter(
212
  x=data["x"], y=data["y"], mode='markers',
213
- marker=dict(color=colors[cell_type], size=data["size"]),
214
  name=cell_type
215
  ), row=1, col=1)
216
 
217
  # Total population over time
218
- total_population = [sum(counts) for counts in zip(*population_history.values())]
219
- fig.add_trace(go.Scatter(y=total_population, mode='lines', name="Total"), row=1, col=2)
220
-
221
- # Population by cell type
222
- for cell_type, counts in population_history.items():
223
- fig.add_trace(go.Scatter(y=counts, mode='lines', name=cell_type, line=dict(color=colors[cell_type])), row=2, col=1)
224
-
225
- # Organelle distribution
226
- organelle_counts = {"nucleus": 0, "mitochondria": 0, "chloroplast": 0, "endoplasmic_reticulum": 0, "golgi_apparatus": 0}
227
- for cell in env.cells:
228
- for organelle in cell.organelles:
229
- organelle_counts[organelle] += 1
230
-
231
- fig.add_trace(go.Bar(x=list(organelle_counts.keys()), y=list(organelle_counts.values()), name="Organelles"), row=2, col=2)
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
- fig.update_xaxes(title_text="Time", row=2, col=1)
238
- fig.update_yaxes(title_text="Population", row=2, col=1)
239
- fig.update_xaxes(title_text="Organelle", row=2, col=2)
240
- fig.update_yaxes(title_text="Count", row=2, col=2)
241
 
242
  fig.update_layout(height=800, width=1200, title_text="Advanced Cell Evolution Simulation")
243
 
244
  return fig
245
 
246
- def format_number(num):
247
- if num >= 1_000_000:
248
- return f"{num/1_000_000:.1f}M"
249
- elif num >= 1_000:
250
- return f"{num/1_000:.1f}K"
251
- else:
252
- return str(num)
253
-
254
- def update_chart():
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
- st.session_state.fig.add_trace(go.Bar(x=list(organelle_counts.keys()), y=list(organelle_counts.values()),
289
- name="Organelles"), row=2, col=2)
290
-
291
- # Update axis labels and layout
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
- # Update the chart placeholder
309
- chart_placeholder.plotly_chart(st.session_state.fig)
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
- if st.session_state.fig is None:
327
- st.session_state.fig = setup_figure(st.session_state.env)
328
-
329
- def stop_simulation():
330
- st.session_state.running = False
331
-
332
- # Create two columns for start and stop buttons
333
- col1, col2 = st.columns(2)
334
-
335
- with col1:
336
- start_button = st.button("Start Simulation", on_click=start_simulation)
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
- # Check for merges
352
- if final_cell_count < initial_cell_count:
353
- merges = initial_cell_count - final_cell_count
354
- st.session_state.total_merges += merges
355
- event_log.appendleft(f"Time {st.session_state.env.time}: {merges} cell merges occurred")
 
 
356
 
357
- update_chart()
358
- update_statistics()
359
-
360
- time.sleep(update_interval)
361
- st.experimental_rerun()
 
362
 
363
- if not st.session_state.running:
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!")