Sephfox commited on
Commit
22b66c4
·
verified ·
1 Parent(s): 3ea8155

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +202 -0
app.py ADDED
@@ -0,0 +1,202 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import random
2
+ import numpy as np
3
+ import plotly.graph_objects as go
4
+ from plotly.subplots import make_subplots
5
+ import streamlit as st
6
+
7
+ class Organelle:
8
+ def __init__(self, type):
9
+ self.type = type # e.g., "nucleus", "mitochondria", "chloroplast"
10
+
11
+ class Cell:
12
+ def __init__(self, x, y, cell_type="prokaryote"):
13
+ self.x = x
14
+ self.y = y
15
+ self.energy = 100
16
+ self.cell_type = cell_type
17
+ self.organelles = []
18
+ self.size = 1
19
+ self.color = "blue"
20
+ self.division_threshold = 150
21
+
22
+ if cell_type == "prokaryote":
23
+ self.color = "lightblue"
24
+ elif cell_type == "early_eukaryote":
25
+ self.organelles.append(Organelle("nucleus"))
26
+ self.color = "green"
27
+ self.size = 2
28
+ elif cell_type == "advanced_eukaryote":
29
+ self.organelles.extend([Organelle("nucleus"), Organelle("mitochondria")])
30
+ self.color = "red"
31
+ self.size = 3
32
+ elif cell_type == "plant_like":
33
+ self.organelles.extend([Organelle("nucleus"), Organelle("mitochondria"), Organelle("chloroplast")])
34
+ self.color = "darkgreen"
35
+ self.size = 4
36
+
37
+ def move(self, environment):
38
+ dx = random.uniform(-1, 1)
39
+ dy = random.uniform(-1, 1)
40
+ self.x = max(0, min(environment.width - 1, self.x + dx))
41
+ self.y = max(0, min(environment.height - 1, self.y + dy))
42
+ self.energy -= 0.5 * self.size
43
+
44
+ def feed(self, environment):
45
+ if "chloroplast" in [org.type for org in self.organelles]:
46
+ # Photosynthesis
47
+ self.energy += environment.light_level * 2
48
+ else:
49
+ # Consume environmental nutrients
50
+ self.energy += environment.grid[int(self.y)][int(self.x)] * 0.1
51
+ environment.grid[int(self.y)][int(self.x)] *= 0.9
52
+
53
+ def can_divide(self):
54
+ return self.energy > self.division_threshold
55
+
56
+ def divide(self):
57
+ if self.can_divide():
58
+ self.energy /= 2
59
+ return Cell(self.x, self.y, self.cell_type)
60
+ return None
61
+
62
+ def can_fuse(self, other):
63
+ return (self.cell_type == "prokaryote" and other.cell_type == "prokaryote" and
64
+ random.random() < 0.001) # 0.1% chance of fusion
65
+
66
+ def fuse(self, other):
67
+ new_cell = Cell(
68
+ (self.x + other.x) / 2,
69
+ (self.y + other.y) / 2,
70
+ "early_eukaryote"
71
+ )
72
+ new_cell.energy = self.energy + other.energy
73
+ return new_cell
74
+
75
+ class Environment:
76
+ def __init__(self, width, height):
77
+ self.width = width
78
+ self.height = height
79
+ self.grid = np.random.rand(height, width) * 10 # Nutrient distribution
80
+ self.light_level = 5 # Ambient light level
81
+ self.cells = []
82
+ self.time = 0
83
+
84
+ def add_cell(self, cell):
85
+ self.cells.append(cell)
86
+
87
+ def update(self):
88
+ self.time += 1
89
+
90
+ # Update environment
91
+ self.grid += np.random.rand(self.height, self.width) * 0.1
92
+ self.light_level = 5 + np.sin(self.time / 100) * 2 # Fluctuating light levels
93
+
94
+ new_cells = []
95
+ cells_to_remove = []
96
+
97
+ for cell in self.cells:
98
+ cell.move(self)
99
+ cell.feed(self)
100
+
101
+ if cell.energy <= 0:
102
+ cells_to_remove.append(cell)
103
+ elif cell.can_divide():
104
+ new_cell = cell.divide()
105
+ if new_cell:
106
+ new_cells.append(new_cell)
107
+
108
+ # Handle cell fusion
109
+ for i, cell1 in enumerate(self.cells):
110
+ for cell2 in self.cells[i+1:]:
111
+ if cell1.can_fuse(cell2):
112
+ new_cell = cell1.fuse(cell2)
113
+ new_cells.append(new_cell)
114
+ cells_to_remove.extend([cell1, cell2])
115
+
116
+ # Add new cells and remove dead/fused cells
117
+ self.cells.extend(new_cells)
118
+ self.cells = [cell for cell in self.cells if cell not in cells_to_remove]
119
+
120
+ # Introduce mutations
121
+ for cell in self.cells:
122
+ if random.random() < 0.0001: # 0.01% chance of mutation
123
+ if cell.cell_type == "early_eukaryote":
124
+ cell.cell_type = "advanced_eukaryote"
125
+ cell.organelles.append(Organelle("mitochondria"))
126
+ cell.color = "red"
127
+ cell.size = 3
128
+ elif cell.cell_type == "advanced_eukaryote" and random.random() < 0.5:
129
+ cell.cell_type = "plant_like"
130
+ cell.organelles.append(Organelle("chloroplast"))
131
+ cell.color = "darkgreen"
132
+ cell.size = 4
133
+
134
+ def visualize(self):
135
+ fig = make_subplots(rows=1, cols=2, subplot_titles=("Cell Distribution", "Population Over Time"))
136
+
137
+ # Cell distribution
138
+ cell_types = set(cell.cell_type for cell in self.cells)
139
+ for cell_type in cell_types:
140
+ x = [cell.x for cell in self.cells if cell.cell_type == cell_type]
141
+ y = [cell.y for cell in self.cells if cell.cell_type == cell_type]
142
+ color = next(cell.color for cell in self.cells if cell.cell_type == cell_type)
143
+ size = next(cell.size * 3 for cell in self.cells if cell.cell_type == cell_type)
144
+ fig.add_trace(go.Scatter(x=x, y=y, mode='markers', marker=dict(color=color, size=size),
145
+ name=cell_type), row=1, col=1)
146
+
147
+ fig.update_xaxes(title_text="X", row=1, col=1)
148
+ fig.update_yaxes(title_text="Y", row=1, col=1)
149
+
150
+ # Population over time
151
+ population_counts = {
152
+ "prokaryote": [],
153
+ "early_eukaryote": [],
154
+ "advanced_eukaryote": [],
155
+ "plant_like": []
156
+ }
157
+
158
+ for cell_type in population_counts:
159
+ count = len([cell for cell in self.cells if cell.cell_type == cell_type])
160
+ population_counts[cell_type].append(count)
161
+
162
+ for cell_type, counts in population_counts.items():
163
+ fig.add_trace(go.Scatter(y=counts, mode='lines', name=cell_type), row=1, col=2)
164
+
165
+ fig.update_xaxes(title_text="Time", row=1, col=2)
166
+ fig.update_yaxes(title_text="Population", row=1, col=2)
167
+
168
+ fig.update_layout(height=600, width=1200,
169
+ title_text=f"Cell Evolution Simulation (Time: {self.time})")
170
+ return fig
171
+
172
+ def run_simulation(num_steps, initial_cells):
173
+ env = Environment(100, 100)
174
+
175
+ # Add initial cells
176
+ for _ in range(initial_cells):
177
+ cell = Cell(random.uniform(0, env.width), random.uniform(0, env.height))
178
+ env.add_cell(cell)
179
+
180
+ # Run simulation
181
+ for step in range(num_steps):
182
+ env.update()
183
+ if step % 10 == 0: # Visualize every 10 steps
184
+ yield env.visualize()
185
+
186
+ # Streamlit app
187
+ st.title("Cell Evolution Simulation")
188
+
189
+ num_steps = st.slider("Number of simulation steps", 100, 1000, 500)
190
+ initial_cells = st.slider("Initial number of cells", 10, 100, 50)
191
+
192
+ if st.button("Run Simulation"):
193
+ simulation = run_simulation(num_steps, initial_cells)
194
+
195
+ # Create a placeholder for the chart
196
+ chart_placeholder = st.empty()
197
+
198
+ # Update the chart for each step
199
+ for chart in simulation:
200
+ chart_placeholder.plotly_chart(chart, use_container_width=True)
201
+
202
+ st.write("Simulation complete!")