Update app.py
Browse files
app.py
CHANGED
@@ -7,7 +7,12 @@ import time
|
|
7 |
|
8 |
class Organelle:
|
9 |
def __init__(self, type):
|
10 |
-
self.type = type
|
|
|
|
|
|
|
|
|
|
|
11 |
|
12 |
class Cell:
|
13 |
def __init__(self, x, y, cell_type="prokaryote"):
|
@@ -16,21 +21,23 @@ class Cell:
|
|
16 |
self.energy = 100
|
17 |
self.cell_type = cell_type
|
18 |
self.organelles = []
|
|
|
19 |
self.size = 1
|
20 |
-
self.color = "
|
21 |
self.division_threshold = 150
|
22 |
|
23 |
-
|
24 |
-
|
25 |
-
|
|
|
26 |
self.organelles.append(Organelle("nucleus"))
|
27 |
self.color = "green"
|
28 |
self.size = 2
|
29 |
-
elif cell_type == "advanced_eukaryote":
|
30 |
self.organelles.extend([Organelle("nucleus"), Organelle("mitochondria")])
|
31 |
self.color = "red"
|
32 |
self.size = 3
|
33 |
-
elif cell_type == "plant_like":
|
34 |
self.organelles.extend([Organelle("nucleus"), Organelle("mitochondria"), Organelle("chloroplast")])
|
35 |
self.color = "darkgreen"
|
36 |
self.size = 4
|
@@ -43,13 +50,15 @@ class Cell:
|
|
43 |
self.energy -= 0.5 * self.size
|
44 |
|
45 |
def feed(self, environment):
|
|
|
46 |
if "chloroplast" in [org.type for org in self.organelles]:
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
|
|
53 |
|
54 |
def can_divide(self):
|
55 |
return self.energy > self.division_threshold
|
@@ -57,35 +66,57 @@ class Cell:
|
|
57 |
def divide(self):
|
58 |
if self.can_divide():
|
59 |
self.energy /= 2
|
60 |
-
|
|
|
|
|
|
|
61 |
return None
|
62 |
|
63 |
def can_fuse(self, other):
|
64 |
-
return (self.cell_type ==
|
65 |
-
random.random() < 0.
|
66 |
|
67 |
def fuse(self, other):
|
|
|
|
|
|
|
|
|
|
|
|
|
68 |
new_cell = Cell(
|
69 |
(self.x + other.x) / 2,
|
70 |
(self.y + other.y) / 2,
|
71 |
-
|
72 |
)
|
73 |
new_cell.energy = self.energy + other.energy
|
|
|
|
|
|
|
74 |
return new_cell
|
75 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
76 |
class Environment:
|
77 |
def __init__(self, width, height):
|
78 |
self.width = width
|
79 |
self.height = height
|
80 |
-
self.grid = np.random.rand(height, width) * 10
|
81 |
-
self.light_level = 5
|
82 |
self.cells = []
|
83 |
self.time = 0
|
84 |
self.population_history = {
|
85 |
-
"prokaryote": [],
|
86 |
-
"
|
87 |
-
"advanced_eukaryote": [],
|
88 |
-
"plant_like": []
|
89 |
}
|
90 |
|
91 |
def add_cell(self, cell):
|
@@ -93,10 +124,8 @@ class Environment:
|
|
93 |
|
94 |
def update(self):
|
95 |
self.time += 1
|
96 |
-
|
97 |
-
# Update environment
|
98 |
self.grid += np.random.rand(self.height, self.width) * 0.1
|
99 |
-
self.light_level = 5 + np.sin(self.time / 100) * 2
|
100 |
|
101 |
new_cells = []
|
102 |
cells_to_remove = []
|
@@ -124,23 +153,24 @@ class Environment:
|
|
124 |
self.cells.extend(new_cells)
|
125 |
self.cells = [cell for cell in self.cells if cell not in cells_to_remove]
|
126 |
|
127 |
-
# Introduce mutations
|
128 |
for cell in self.cells:
|
129 |
if random.random() < 0.0001: # 0.01% chance of mutation
|
130 |
if cell.cell_type == "early_eukaryote":
|
131 |
cell.cell_type = "advanced_eukaryote"
|
132 |
-
cell.organelles.append(Organelle("mitochondria"))
|
133 |
-
cell.color = "red"
|
134 |
-
cell.size = 3
|
135 |
elif cell.cell_type == "advanced_eukaryote" and random.random() < 0.5:
|
136 |
cell.cell_type = "plant_like"
|
137 |
-
|
138 |
-
|
139 |
-
|
|
|
140 |
|
141 |
# Record population counts
|
142 |
for cell_type in self.population_history.keys():
|
143 |
-
|
|
|
|
|
|
|
144 |
self.population_history[cell_type].append(count)
|
145 |
|
146 |
def get_visualization_data(self):
|
@@ -148,13 +178,15 @@ class Environment:
|
|
148 |
"prokaryote": {"x": [], "y": [], "size": [], "color": "lightblue"},
|
149 |
"early_eukaryote": {"x": [], "y": [], "size": [], "color": "green"},
|
150 |
"advanced_eukaryote": {"x": [], "y": [], "size": [], "color": "red"},
|
151 |
-
"plant_like": {"x": [], "y": [], "size": [], "color": "darkgreen"}
|
|
|
152 |
}
|
153 |
|
154 |
for cell in self.cells:
|
155 |
-
|
156 |
-
cell_data[
|
157 |
-
cell_data[
|
|
|
158 |
|
159 |
return cell_data, self.population_history
|
160 |
|
@@ -178,16 +210,16 @@ def setup_figure(env):
|
|
178 |
fig.update_xaxes(title_text="Time", row=1, col=2)
|
179 |
fig.update_yaxes(title_text="Population", row=1, col=2)
|
180 |
|
181 |
-
fig.update_layout(height=600, width=1200, title_text="Cell Evolution Simulation")
|
182 |
|
183 |
return fig
|
184 |
|
185 |
# Streamlit app
|
186 |
-
st.title("Cell Evolution Simulation")
|
187 |
|
188 |
-
num_steps = st.slider("Number of simulation steps", 100,
|
189 |
-
initial_cells = st.slider("Initial number of cells", 10,
|
190 |
-
update_interval = st.slider("Update interval (milliseconds)",
|
191 |
|
192 |
if st.button("Run Simulation"):
|
193 |
env = Environment(100, 100)
|
@@ -214,9 +246,9 @@ if st.button("Run Simulation"):
|
|
214 |
fig.data[i].marker.size = data["size"]
|
215 |
|
216 |
for i, (cell_type, counts) in enumerate(population_history.items()):
|
217 |
-
fig.data[i+
|
218 |
|
219 |
-
fig.layout.title.text = f"Cell Evolution Simulation (Time: {env.time})"
|
220 |
|
221 |
# Update the chart
|
222 |
chart.plotly_chart(fig, use_container_width=True)
|
|
|
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"):
|
|
|
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
|
|
|
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 |
|
63 |
def can_divide(self):
|
64 |
return self.energy > self.division_threshold
|
|
|
66 |
def divide(self):
|
67 |
if self.can_divide():
|
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
|
114 |
+
self.light_level = 5
|
115 |
self.cells = []
|
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):
|
|
|
124 |
|
125 |
def update(self):
|
126 |
self.time += 1
|
|
|
|
|
127 |
self.grid += np.random.rand(self.height, self.width) * 0.1
|
128 |
+
self.light_level = 5 + np.sin(self.time / 100) * 2
|
129 |
|
130 |
new_cells = []
|
131 |
cells_to_remove = []
|
|
|
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):
|
|
|
178 |
"prokaryote": {"x": [], "y": [], "size": [], "color": "lightblue"},
|
179 |
"early_eukaryote": {"x": [], "y": [], "size": [], "color": "green"},
|
180 |
"advanced_eukaryote": {"x": [], "y": [], "size": [], "color": "red"},
|
181 |
+
"plant_like": {"x": [], "y": [], "size": [], "color": "darkgreen"},
|
182 |
+
"modified": {"x": [], "y": [], "size": [], "color": "purple"}
|
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 |
|
|
|
210 |
fig.update_xaxes(title_text="Time", row=1, col=2)
|
211 |
fig.update_yaxes(title_text="Population", row=1, col=2)
|
212 |
|
213 |
+
fig.update_layout(height=600, width=1200, title_text="Advanced Cell Evolution Simulation")
|
214 |
|
215 |
return fig
|
216 |
|
217 |
# Streamlit app
|
218 |
+
st.title("Advanced Cell Evolution Simulation")
|
219 |
|
220 |
+
num_steps = st.slider("Number of simulation steps", 100, 2000, 1000)
|
221 |
+
initial_cells = st.slider("Initial number of cells", 10, 200, 100)
|
222 |
+
update_interval = st.slider("Update interval (milliseconds)", 50, 500, 100)
|
223 |
|
224 |
if st.button("Run Simulation"):
|
225 |
env = Environment(100, 100)
|
|
|
246 |
fig.data[i].marker.size = data["size"]
|
247 |
|
248 |
for i, (cell_type, counts) in enumerate(population_history.items()):
|
249 |
+
fig.data[i+5].y = counts # +5 because we have 5 cell types in the first subplot
|
250 |
|
251 |
+
fig.layout.title.text = f"Advanced Cell Evolution Simulation (Time: {env.time})"
|
252 |
|
253 |
# Update the chart
|
254 |
chart.plotly_chart(fig, use_container_width=True)
|