X commited on
Commit
ed3646c
·
verified ·
1 Parent(s): 767a93b

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +167 -247
app.py CHANGED
@@ -1,89 +1,68 @@
1
  # app.py
2
  import gradio as gr
3
  import numpy as np
4
- import torch
5
- import torch.nn as nn
6
  import random
7
- from collections import deque
8
- import math
9
  from PIL import Image, ImageDraw
 
10
 
11
  # ============== НАСТРОЙКИ ==============
12
- WORLD_SIZE = 40
13
- CELL_SIZE = 10
14
- MAX_CREATURES = 40
15
- MUTATION_RATE = 0.2
16
- MUTATION_SCALE = 0.25
17
- RESPAWN_RATE = 5
18
-
19
- # ============== ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ ==============
20
- def is_grass(color):
21
- return color[0] == 100 and color[1] == 200 and color[2] == 100
22
-
23
- def is_tree(color):
24
- return color[0] == 34 and color[1] == 139 and color[2] == 34
25
-
26
- def is_food(color):
27
- return color[0] == 0 and color[1] == 150 and color[2] == 0
28
 
29
- def is_stone(color):
30
- return color[0] == 128 and color[1] == 128 and color[2] == 128
31
-
32
- def is_metal(color):
33
- return color[0] == 192 and color[1] == 192 and color[2] == 192
34
-
35
- def is_water(color):
36
- return color[0] == 0 and color[1] == 100 and color[2] == 255
37
-
38
- # ============== НЕЙРОСЕТЬ ==============
39
- class CreatureBrain(nn.Module):
40
- def __init__(self, input_size=12, hidden_size=16, output_size=6):
41
- super().__init__()
42
- self.fc1 = nn.Linear(input_size, hidden_size)
43
- self.fc2 = nn.Linear(hidden_size, hidden_size)
44
- self.fc3 = nn.Linear(hidden_size, output_size)
45
-
46
- def forward(self, x):
47
- x = torch.relu(self.fc1(x))
48
- x = torch.relu(self.fc2(x))
49
- x = torch.tanh(self.fc3(x))
50
- return x
 
 
51
 
52
  # ============== СУЩЕСТВО ==============
53
  class Creature:
54
- def __init__(self, x, y, color, team, brain=None, generation=0):
55
  self.x = x
56
  self.y = y
57
  self.color = color
58
  self.team = team
59
- self.generation = generation
60
 
61
- self.health = 100.0
62
- self.energy = 80.0
63
- self.food = 50.0
64
- self.water = 50.0
65
  self.resources = 0
66
- self.metal = 0
67
  self.age = 0
68
  self.alive = True
69
  self.fitness = 0
70
  self.children = 0
71
 
72
- self.memory = deque(maxlen=5)
73
-
74
- if brain is None:
75
- self.brain = CreatureBrain()
76
- else:
77
- self.brain = brain
78
 
79
- def get_inputs(self, world, creatures):
80
- # Состояние
81
  hunger = 1.0 - self.food / 100.0
82
  thirst = 1.0 - self.water / 100.0
83
  health = self.health / 100.0
84
  energy = self.energy / 100.0
85
 
86
- # Обзор вокруг
87
  nearby = []
88
  for dx in [-1, 0, 1]:
89
  for dy in [-1, 0, 1]:
@@ -92,83 +71,56 @@ class Creature:
92
  nx, ny = self.x + dx, self.y + dy
93
  if 0 <= nx < WORLD_SIZE and 0 <= ny < WORLD_SIZE:
94
  cell = world[nx, ny]
95
- if is_tree(cell):
96
  nearby.append(1.0)
97
- elif is_food(cell):
98
  nearby.append(2.0)
99
- elif is_stone(cell):
100
  nearby.append(0.5)
101
- elif is_metal(cell):
102
- nearby.append(3.0)
103
- elif is_water(cell):
104
- nearby.append(4.0)
105
  else:
106
  nearby.append(0.0)
107
  else:
108
  nearby.append(0.0)
109
 
110
- # Ближайшие существа
111
  partners = 0
112
  enemies = 0
113
  for other in creatures:
114
  if other is self or not other.alive:
115
  continue
116
- dist = math.sqrt((self.x - other.x)**2 + (self.y - other.y)**2)
117
- if dist < 4:
118
  if other.team == self.team:
119
- partners = max(partners, 1.0 - dist/4.0)
120
  else:
121
- enemies = max(enemies, 1.0 - dist/4.0)
122
 
123
- # Формируем вход
124
  inputs = [hunger, thirst, health, energy, partners, enemies]
125
- inputs.extend(nearby[:6])
126
-
127
- # Добавляем память
128
- memory_flat = []
129
- for mem in self.memory:
130
- memory_flat.extend(mem[:2])
131
- while len(memory_flat) < 4:
132
- memory_flat.append(0.0)
133
- inputs.extend(memory_flat[:4])
134
 
135
- # Обрезаем до нужного размера
136
- inputs = inputs[:12]
137
- while len(inputs) < 12:
138
- inputs.append(0.0)
139
-
140
- return torch.tensor(inputs, dtype=torch.float32)
141
-
142
- def think(self, world, creatures):
143
- inputs = self.get_inputs(world, creatures)
144
 
145
- with torch.no_grad():
146
- output = self.brain(inputs.unsqueeze(0)).squeeze().numpy()
 
 
 
 
147
 
148
- actions = np.clip(output, -1, 1)
149
- self.memory.append(actions.copy())
150
-
151
- # Движение
152
- dx = int(np.round(actions[0] * 2))
153
- dy = int(np.round(actions[1] * 2))
154
  self.move(dx, dy)
155
 
156
- # Строительство
157
- if actions[2] > 0.3 and self.resources >= 2:
158
  self.build(world)
159
 
160
- # Размножение
161
- if actions[3] > 0.5 and self.food > 50 and self.energy > 60:
162
  return self.reproduce(creatures)
163
 
164
- # Атака
165
- if actions[4] > 0.4:
166
  self.attack(creatures)
167
 
168
- # Добыча металла
169
- if actions[5] > 0.5:
170
- self.mine_metal(world)
171
-
172
  return None
173
 
174
  def move(self, dx, dy):
@@ -184,59 +136,27 @@ class Creature:
184
  for dx, dy in [(1,0), (-1,0), (0,1), (0,-1)]:
185
  nx, ny = self.x + dx, self.y + dy
186
  if 0 <= nx < WORLD_SIZE and 0 <= ny < WORLD_SIZE:
187
- if is_grass(world[nx, ny]):
 
188
  world[nx, ny] = [139, 69, 19]
189
  self.fitness += 15
190
  return True
191
  return False
192
 
193
- def mine_metal(self, world):
194
- if is_metal(world[self.x, self.y]):
195
- self.metal += 1
196
- world[self.x, self.y] = [100, 200, 100]
197
- self.fitness += 15
198
- self.energy -= 3
199
- return True
200
- return False
201
-
202
  def reproduce(self, creatures):
203
  if len(creatures) >= MAX_CREATURES:
204
  return None
205
 
206
- # Ищем партнера
207
- partner = None
208
- for other in creatures:
209
- if other is not self and other.alive and other.team == self.team:
210
- dist = math.sqrt((self.x - other.x)**2 + (self.y - other.y)**2)
211
- if dist < 4 and other.food > 40:
212
- partner = other
213
- break
214
-
215
- if partner:
216
- child_brain = CreatureBrain()
217
- for child_param, p1_param, p2_param in zip(
218
- child_brain.parameters(),
219
- self.brain.parameters(),
220
- partner.brain.parameters()
221
- ):
222
- mask = torch.rand_like(p1_param) > 0.5
223
- child_param.data = torch.where(mask, p1_param.data, p2_param.data)
224
- if random.random() < MUTATION_RATE:
225
- child_param.data += torch.randn_like(child_param) * MUTATION_SCALE
226
- else:
227
- child_brain = CreatureBrain()
228
- child_brain.load_state_dict(self.brain.state_dict())
229
- for param in child_brain.parameters():
230
- if random.random() < MUTATION_RATE * 1.5:
231
- param.data += torch.randn_like(param) * MUTATION_SCALE * 1.2
232
 
233
  child = Creature(
234
- self.x + random.randint(-3, 3),
235
- self.y + random.randint(-3, 3),
236
  self.color,
237
  self.team,
238
- child_brain,
239
- self.generation + 1
240
  )
241
  child.food = 30
242
  child.water = 30
@@ -255,22 +175,22 @@ class Creature:
255
  if other is not self and other.alive and other.team != self.team:
256
  dist = abs(self.x - other.x) + abs(self.y - other.y)
257
  if dist <= 1:
258
- other.health -= 10 + self.health / 10
259
  self.energy -= 5
260
  self.food -= 2
261
  self.fitness += 8
262
  return True
263
  return False
264
 
265
- def eat_food(self, world):
266
  cell = world[self.x, self.y]
267
- if is_food(cell):
268
  self.food = min(100, self.food + 30)
269
  self.health = min(100, self.health + 8)
270
  world[self.x, self.y] = [100, 200, 100]
271
  self.fitness += 12
272
  return True
273
- elif is_tree(cell):
274
  self.food = min(100, self.food + 15)
275
  self.health = min(100, self.health + 5)
276
  world[self.x, self.y] = [100, 200, 100]
@@ -278,8 +198,9 @@ class Creature:
278
  return True
279
  return False
280
 
281
- def drink_water(self, world):
282
- if is_water(world[self.x, self.y]):
 
283
  self.water = min(100, self.water + 40)
284
  self.health = min(100, self.health + 5)
285
  if random.random() < 0.3:
@@ -289,7 +210,8 @@ class Creature:
289
  return False
290
 
291
  def collect_stone(self, world):
292
- if is_stone(world[self.x, self.y]):
 
293
  self.resources += 1
294
  world[self.x, self.y] = [100, 200, 100]
295
  self.fitness += 10
@@ -306,82 +228,78 @@ class Creature:
306
  self.health -= 2
307
  if self.water <= 0:
308
  self.health -= 1.5
309
-
310
- if self.health <= 0 or self.energy <= 0 or self.food < -10 or self.water < -10:
311
  self.alive = False
312
  return False
313
-
314
- if self.age > 500:
315
  self.alive = False
316
  return False
317
-
318
  return True
319
 
320
- # ============== СИМУЛЯЦИЯ ==============
321
  def create_world():
322
  world = np.zeros((WORLD_SIZE, WORLD_SIZE, 3), dtype=np.uint8)
323
  world[:, :] = [100, 200, 100]
324
 
325
  # Ресурсы
326
- for _ in range(20):
327
- for _ in range(50):
328
  x, y = random.randint(0, WORLD_SIZE-1), random.randint(0, WORLD_SIZE-1)
329
- if is_grass(world[x, y]):
330
  world[x, y] = [34, 139, 34]
331
  break
332
 
333
- for _ in range(15):
334
- for _ in range(50):
335
  x, y = random.randint(0, WORLD_SIZE-1), random.randint(0, WORLD_SIZE-1)
336
- if is_grass(world[x, y]):
337
  world[x, y] = [0, 150, 0]
338
  break
339
 
340
- for _ in range(10):
341
- for _ in range(50):
342
  x, y = random.randint(0, WORLD_SIZE-1), random.randint(0, WORLD_SIZE-1)
343
- if is_grass(world[x, y]):
344
  world[x, y] = [128, 128, 128]
345
  break
346
 
347
- for _ in range(5):
348
- for _ in range(50):
349
- x, y = random.randint(0, WORLD_SIZE-1), random.randint(0, WORLD_SIZE-1)
350
- if is_grass(world[x, y]):
351
- world[x, y] = [192, 192, 192]
352
- break
353
-
354
  for _ in range(4):
355
- for _ in range(50):
356
  x, y = random.randint(0, WORLD_SIZE-1), random.randint(0, WORLD_SIZE-1)
357
- if is_grass(world[x, y]):
358
  world[x, y] = [0, 100, 255]
359
  break
360
 
361
  # Существа
362
  creatures = []
363
 
364
- c1 = Creature(10, 10, [255, 50, 50], "red")
 
365
  creatures.append(c1)
366
 
367
- c2 = Creature(30, 30, [50, 50, 255], "blue")
 
368
  creatures.append(c2)
369
 
370
- c3 = Creature(20, 20, [255, 215, 0], "gold")
 
371
  creatures.append(c3)
372
 
373
  return world, creatures
374
 
375
- # ============== ОСНОВНАЯ ФУНКЦИЯ ==============
376
- def simulate_step(world, creatures, step_count):
377
  new_creatures = []
378
 
379
  for creature in creatures[:]:
380
  if not creature.alive:
381
  continue
382
 
383
- creature.eat_food(world)
384
- creature.drink_water(world)
385
  creature.collect_stone(world)
386
 
387
  child = creature.think(world, creatures)
@@ -396,11 +314,11 @@ def simulate_step(world, creatures, step_count):
396
  creatures = [c for c in creatures if c.alive]
397
 
398
  # Восстановление ресурсов
399
- if step_count % RESPAWN_RATE == 0:
400
  for _ in range(2):
401
- for _ in range(30):
402
  x, y = random.randint(0, WORLD_SIZE-1), random.randint(0, WORLD_SIZE-1)
403
- if is_grass(world[x, y]):
404
  world[x, y] = [0, 150, 0]
405
  break
406
 
@@ -413,29 +331,31 @@ def simulate_step(world, creatures, step_count):
413
 
414
  return world, creatures
415
 
416
- # ============== РЕНДЕРИНГ ==============
417
- def render_world(world, creatures, step_count):
418
- img = Image.new('RGB', (WORLD_SIZE * CELL_SIZE, WORLD_SIZE * CELL_SIZE + 100), (30, 30, 30))
419
  draw = ImageDraw.Draw(img)
420
 
421
  # Мир
422
  for i in range(WORLD_SIZE):
423
  for j in range(WORLD_SIZE):
424
  x, y = i * CELL_SIZE, j * CELL_SIZE
425
- draw.rectangle([x, y, x + CELL_SIZE, y + CELL_SIZE], fill=tuple(world[i, j]))
 
426
 
427
  # Существа
428
  for creature in creatures:
429
  if not creature.alive:
430
  continue
431
  x, y = creature.x * CELL_SIZE, creature.y * CELL_SIZE
 
432
  draw.ellipse([x+1, y+1, x+CELL_SIZE-1, y+CELL_SIZE-1],
433
- fill=tuple(creature.color), outline=(255,255,255))
434
 
435
  # Индикаторы
436
- hw = (creature.health / 100) * CELL_SIZE
437
- fw = (creature.food / 100) * CELL_SIZE
438
- ww = (creature.water / 100) * CELL_SIZE
439
 
440
  draw.rectangle([x, y-6, x + hw, y-4], fill=(255, 0, 0))
441
  draw.rectangle([x, y-4, x + fw, y-2], fill=(255, 165, 0))
@@ -448,37 +368,37 @@ def render_world(world, creatures, step_count):
448
  blue = sum(1 for c in creatures if c.alive and c.team == "blue")
449
  gold = sum(1 for c in creatures if c.alive and c.team == "gold")
450
 
451
- trees = sum(1 for i in range(WORLD_SIZE) for j in range(WORLD_SIZE) if is_tree(world[i, j]))
452
- foods = sum(1 for i in range(WORLD_SIZE) for j in range(WORLD_SIZE) if is_food(world[i, j]))
453
- stones = sum(1 for i in range(WORLD_SIZE) for j in range(WORLD_SIZE) if is_stone(world[i, j]))
454
- metals = sum(1 for i in range(WORLD_SIZE) for j in range(WORLD_SIZE) if is_metal(world[i, j]))
455
- waters = sum(1 for i in range(WORLD_SIZE) for j in range(WORLD_SIZE) if is_water(world[i, j]))
 
456
 
457
  best_fitness = max([c.fitness for c in creatures if c.alive] + [0])
458
 
459
- draw.text([10, y_offset], f"Шаг: {step_count} | Существ: {len(creatures)}", fill=(255,255,255))
460
- draw.text([10, y_offset + 20], f"🔴: {red} | 🔵: {blue} | 🟡: {gold}", fill=(255,255,255))
461
- draw.text([10, y_offset + 40], f"🌳: {trees} | 🫐: {foods} | 🪨: {stones} | ⚙️: {metals} | 💧: {waters}", fill=(255,255,255))
462
- draw.text([10, y_offset + 60], f"⭐ Лучший фитнес: {best_fitness:.1f}", fill=(255,255,255))
463
 
464
  return img
465
 
466
- # ============== GRADIO ==============
467
  world, creatures = create_world()
468
- step_count = 0
469
 
470
  def run_simulation(steps):
471
- global world, creatures, step_count
472
 
473
  images = []
474
- stats_text = ""
475
 
476
  for i in range(steps):
477
- step_count += 1
478
- world, creatures = simulate_step(world, creatures, step_count)
479
 
480
  if i % 2 == 0:
481
- img = render_world(world, creatures, step_count)
482
  images.append(img)
483
 
484
  # Статистика
@@ -486,69 +406,69 @@ def run_simulation(steps):
486
  blue = sum(1 for c in creatures if c.alive and c.team == "blue")
487
  gold = sum(1 for c in creatures if c.alive and c.team == "gold")
488
 
489
- trees = sum(1 for i in range(WORLD_SIZE) for j in range(WORLD_SIZE) if is_tree(world[i, j]))
490
- foods = sum(1 for i in range(WORLD_SIZE) for j in range(WORLD_SIZE) if is_food(world[i, j]))
491
- stones = sum(1 for i in range(WORLD_SIZE) for j in range(WORLD_SIZE) if is_stone(world[i, j]))
492
- metals = sum(1 for i in range(WORLD_SIZE) for j in range(WORLD_SIZE) if is_metal(world[i, j]))
493
- waters = sum(1 for i in range(WORLD_SIZE) for j in range(WORLD_SIZE) if is_water(world[i, j]))
 
 
494
  best_fitness = max([c.fitness for c in creatures if c.alive] + [0])
495
 
496
- stats_text = f"""
497
- 📊 СТАТИСТИКА:
498
 
499
- Шаг: {step_count}
500
  Существ: {len(creatures)}
 
501
  🔴 Красных: {red}
502
  🔵 Синих: {blue}
503
  🟡 Золотых: {gold}
504
 
505
- 🌳 Ресурсы:
506
- Деревья: {trees}
507
- 🫐 Еда: {foods}
508
- 🪨 Камни: {stones}
509
- ⚙️ Металл: {metals}
510
- 💧 Вода: {waters}
511
 
512
- ⭐ Лучший фитнес: {best_fitness:.1f}
513
- """
514
 
515
  return images, stats_text
516
 
517
  def reset_simulation():
518
- global world, creatures, step_count
519
  world, creatures = create_world()
520
- step_count = 0
521
- img = render_world(world, creatures, step_count)
522
- return [img], "Мир сброшен!"
523
 
524
  # ============== ИНТЕРФЕЙС ==============
525
- with gr.Blocks(title="🧬 Эволюционная симуляция") as demo:
526
  gr.Markdown("""
527
- # 🧬 Эволюционная симуляция с нейросетями
528
 
529
- 🔴 Красные | 🔵 Синие | 🟡 Золотые
530
 
531
- 🍽️ Едят, 💧 пьют, 🏗️ строят, ⚔️ воюют, 👶 размножаются!
532
  """)
533
 
534
  with gr.Row():
535
- gallery = gr.Gallery(label="Мир", columns=1, rows=1, height=600)
536
- stats = gr.Textbox(label="Статистика", lines=18, interactive=False)
537
 
538
  with gr.Row():
539
- steps = gr.Slider(label="Шагов", minimum=10, maximum=100, value=30, step=5)
540
  run_btn = gr.Button("▶️ Запустить", variant="primary")
541
  reset_btn = gr.Button("🔄 Сбросить", variant="secondary")
542
 
543
- def run_action(steps_val):
544
- return run_simulation(steps_val)
545
-
546
- def reset_action():
547
- return reset_simulation()
548
 
549
- run_btn.click(run_action, inputs=[steps], outputs=[gallery, stats])
550
- reset_btn.click(reset_action, outputs=[gallery, stats])
 
 
551
 
552
  # ============== ЗАПУСК ==============
553
  if __name__ == "__main__":
554
- demo.launch(share=True)
 
1
  # app.py
2
  import gradio as gr
3
  import numpy as np
 
 
4
  import random
 
 
5
  from PIL import Image, ImageDraw
6
+ import math
7
 
8
  # ============== НАСТРОЙКИ ==============
9
+ WORLD_SIZE = 30
10
+ CELL_SIZE = 12
11
+ MAX_CREATURES = 30
 
 
 
 
 
 
 
 
 
 
 
 
 
12
 
13
+ # ============== ПРОСТАЯ НЕЙРОСЕТЬ (без PyTorch) ==============
14
+ class SimpleBrain:
15
+ def __init__(self):
16
+ # Простая матрица весов
17
+ self.weights = np.random.randn(8, 5) * 0.5
18
+ self.bias = np.random.randn(5) * 0.5
19
+
20
+ def forward(self, inputs):
21
+ # Простой перцептрон
22
+ x = np.array(inputs)
23
+ output = np.tanh(np.dot(x, self.weights) + self.bias)
24
+ return output
25
+
26
+ def copy(self):
27
+ new_brain = SimpleBrain()
28
+ new_brain.weights = self.weights.copy()
29
+ new_brain.bias = self.bias.copy()
30
+ return new_brain
31
+
32
+ def mutate(self, rate=0.2, scale=0.3):
33
+ if random.random() < rate:
34
+ self.weights += np.random.randn(*self.weights.shape) * scale
35
+ if random.random() < rate:
36
+ self.bias += np.random.randn(*self.bias.shape) * scale
37
 
38
  # ============== СУЩЕСТВО ==============
39
  class Creature:
40
+ def __init__(self, x, y, color, team, brain=None):
41
  self.x = x
42
  self.y = y
43
  self.color = color
44
  self.team = team
 
45
 
46
+ self.health = 100
47
+ self.energy = 80
48
+ self.food = 50
49
+ self.water = 50
50
  self.resources = 0
 
51
  self.age = 0
52
  self.alive = True
53
  self.fitness = 0
54
  self.children = 0
55
 
56
+ self.brain = brain if brain else SimpleBrain()
 
 
 
 
 
57
 
58
+ def think(self, world, creatures):
59
+ # Собираем входные данные
60
  hunger = 1.0 - self.food / 100.0
61
  thirst = 1.0 - self.water / 100.0
62
  health = self.health / 100.0
63
  energy = self.energy / 100.0
64
 
65
+ # Смотрим вокруг
66
  nearby = []
67
  for dx in [-1, 0, 1]:
68
  for dy in [-1, 0, 1]:
 
71
  nx, ny = self.x + dx, self.y + dy
72
  if 0 <= nx < WORLD_SIZE and 0 <= ny < WORLD_SIZE:
73
  cell = world[nx, ny]
74
+ if cell[0] == 34 and cell[1] == 139 and cell[2] == 34:
75
  nearby.append(1.0)
76
+ elif cell[0] == 0 and cell[1] == 150 and cell[2] == 0:
77
  nearby.append(2.0)
78
+ elif cell[0] == 128 and cell[1] == 128 and cell[2] == 128:
79
  nearby.append(0.5)
 
 
 
 
80
  else:
81
  nearby.append(0.0)
82
  else:
83
  nearby.append(0.0)
84
 
85
+ # Ищем врагов и друзей
86
  partners = 0
87
  enemies = 0
88
  for other in creatures:
89
  if other is self or not other.alive:
90
  continue
91
+ dist = abs(self.x - other.x) + abs(self.y - other.y)
92
+ if dist < 3:
93
  if other.team == self.team:
94
+ partners = max(partners, 1.0 - dist/3.0)
95
  else:
96
+ enemies = max(enemies, 1.0 - dist/3.0)
97
 
98
+ # Входные данные (8 признаков)
99
  inputs = [hunger, thirst, health, energy, partners, enemies]
100
+ inputs.extend(nearby[:4]) # берем первые 4 направления
 
 
 
 
 
 
 
 
101
 
102
+ # Прогоняем через нейросеть
103
+ output = self.brain.forward(inputs)
 
 
 
 
 
 
 
104
 
105
+ # Декодируем действия
106
+ dx = int(round(output[0] * 2))
107
+ dy = int(round(output[1] * 2))
108
+ build = output[2] > 0.3
109
+ reproduce = output[3] > 0.5
110
+ attack = output[4] > 0.4
111
 
112
+ # Выполняем действия
 
 
 
 
 
113
  self.move(dx, dy)
114
 
115
+ if build and self.resources >= 2:
 
116
  self.build(world)
117
 
118
+ if reproduce and self.food > 50 and self.energy > 60:
 
119
  return self.reproduce(creatures)
120
 
121
+ if attack:
 
122
  self.attack(creatures)
123
 
 
 
 
 
124
  return None
125
 
126
  def move(self, dx, dy):
 
136
  for dx, dy in [(1,0), (-1,0), (0,1), (0,-1)]:
137
  nx, ny = self.x + dx, self.y + dy
138
  if 0 <= nx < WORLD_SIZE and 0 <= ny < WORLD_SIZE:
139
+ cell = world[nx, ny]
140
+ if cell[0] == 100 and cell[1] == 200 and cell[2] == 100:
141
  world[nx, ny] = [139, 69, 19]
142
  self.fitness += 15
143
  return True
144
  return False
145
 
 
 
 
 
 
 
 
 
 
146
  def reproduce(self, creatures):
147
  if len(creatures) >= MAX_CREATURES:
148
  return None
149
 
150
+ # Создаем ребенка
151
+ child_brain = self.brain.copy()
152
+ child_brain.mutate()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
153
 
154
  child = Creature(
155
+ self.x + random.randint(-2, 2),
156
+ self.y + random.randint(-2, 2),
157
  self.color,
158
  self.team,
159
+ child_brain
 
160
  )
161
  child.food = 30
162
  child.water = 30
 
175
  if other is not self and other.alive and other.team != self.team:
176
  dist = abs(self.x - other.x) + abs(self.y - other.y)
177
  if dist <= 1:
178
+ other.health -= 15
179
  self.energy -= 5
180
  self.food -= 2
181
  self.fitness += 8
182
  return True
183
  return False
184
 
185
+ def eat(self, world):
186
  cell = world[self.x, self.y]
187
+ if cell[0] == 0 and cell[1] == 150 and cell[2] == 0:
188
  self.food = min(100, self.food + 30)
189
  self.health = min(100, self.health + 8)
190
  world[self.x, self.y] = [100, 200, 100]
191
  self.fitness += 12
192
  return True
193
+ elif cell[0] == 34 and cell[1] == 139 and cell[2] == 34:
194
  self.food = min(100, self.food + 15)
195
  self.health = min(100, self.health + 5)
196
  world[self.x, self.y] = [100, 200, 100]
 
198
  return True
199
  return False
200
 
201
+ def drink(self, world):
202
+ cell = world[self.x, self.y]
203
+ if cell[0] == 0 and cell[1] == 100 and cell[2] == 255:
204
  self.water = min(100, self.water + 40)
205
  self.health = min(100, self.health + 5)
206
  if random.random() < 0.3:
 
210
  return False
211
 
212
  def collect_stone(self, world):
213
+ cell = world[self.x, self.y]
214
+ if cell[0] == 128 and cell[1] == 128 and cell[2] == 128:
215
  self.resources += 1
216
  world[self.x, self.y] = [100, 200, 100]
217
  self.fitness += 10
 
228
  self.health -= 2
229
  if self.water <= 0:
230
  self.health -= 1.5
231
+
232
+ if self.health <= 0 or self.energy <= 0:
233
  self.alive = False
234
  return False
235
+
236
+ if self.age > 300:
237
  self.alive = False
238
  return False
239
+
240
  return True
241
 
242
+ # ============== СОЗДАНИЕ МИРА ==============
243
  def create_world():
244
  world = np.zeros((WORLD_SIZE, WORLD_SIZE, 3), dtype=np.uint8)
245
  world[:, :] = [100, 200, 100]
246
 
247
  # Ресурсы
248
+ for _ in range(15):
249
+ for _ in range(30):
250
  x, y = random.randint(0, WORLD_SIZE-1), random.randint(0, WORLD_SIZE-1)
251
+ if world[x, y][0] == 100 and world[x, y][1] == 200 and world[x, y][2] == 100:
252
  world[x, y] = [34, 139, 34]
253
  break
254
 
255
+ for _ in range(12):
256
+ for _ in range(30):
257
  x, y = random.randint(0, WORLD_SIZE-1), random.randint(0, WORLD_SIZE-1)
258
+ if world[x, y][0] == 100 and world[x, y][1] == 200 and world[x, y][2] == 100:
259
  world[x, y] = [0, 150, 0]
260
  break
261
 
262
+ for _ in range(8):
263
+ for _ in range(30):
264
  x, y = random.randint(0, WORLD_SIZE-1), random.randint(0, WORLD_SIZE-1)
265
+ if world[x, y][0] == 100 and world[x, y][1] == 200 and world[x, y][2] == 100:
266
  world[x, y] = [128, 128, 128]
267
  break
268
 
 
 
 
 
 
 
 
269
  for _ in range(4):
270
+ for _ in range(30):
271
  x, y = random.randint(0, WORLD_SIZE-1), random.randint(0, WORLD_SIZE-1)
272
+ if world[x, y][0] == 100 and world[x, y][1] == 200 and world[x, y][2] == 100:
273
  world[x, y] = [0, 100, 255]
274
  break
275
 
276
  # Существа
277
  creatures = []
278
 
279
+ # Красный вид
280
+ c1 = Creature(5, 5, [255, 50, 50], "red")
281
  creatures.append(c1)
282
 
283
+ # Синий вид
284
+ c2 = Creature(25, 25, [50, 50, 255], "blue")
285
  creatures.append(c2)
286
 
287
+ # Золотой вид
288
+ c3 = Creature(15, 15, [255, 215, 0], "gold")
289
  creatures.append(c3)
290
 
291
  return world, creatures
292
 
293
+ # ============== ШАГ СИМУЛЯЦИИ ==============
294
+ def simulate_step(world, creatures, step):
295
  new_creatures = []
296
 
297
  for creature in creatures[:]:
298
  if not creature.alive:
299
  continue
300
 
301
+ creature.eat(world)
302
+ creature.drink(world)
303
  creature.collect_stone(world)
304
 
305
  child = creature.think(world, creatures)
 
314
  creatures = [c for c in creatures if c.alive]
315
 
316
  # Восстановление ресурсов
317
+ if step % 5 == 0:
318
  for _ in range(2):
319
+ for _ in range(20):
320
  x, y = random.randint(0, WORLD_SIZE-1), random.randint(0, WORLD_SIZE-1)
321
+ if world[x, y][0] == 100 and world[x, y][1] == 200 and world[x, y][2] == 100:
322
  world[x, y] = [0, 150, 0]
323
  break
324
 
 
331
 
332
  return world, creatures
333
 
334
+ # ============== ОТРИСОВКА ==============
335
+ def render_world(world, creatures, step):
336
+ img = Image.new('RGB', (WORLD_SIZE * CELL_SIZE, WORLD_SIZE * CELL_SIZE + 80), (30, 30, 30))
337
  draw = ImageDraw.Draw(img)
338
 
339
  # Мир
340
  for i in range(WORLD_SIZE):
341
  for j in range(WORLD_SIZE):
342
  x, y = i * CELL_SIZE, j * CELL_SIZE
343
+ color = tuple(world[i, j].tolist())
344
+ draw.rectangle([x, y, x + CELL_SIZE, y + CELL_SIZE], fill=color)
345
 
346
  # Существа
347
  for creature in creatures:
348
  if not creature.alive:
349
  continue
350
  x, y = creature.x * CELL_SIZE, creature.y * CELL_SIZE
351
+ color = tuple(creature.color)
352
  draw.ellipse([x+1, y+1, x+CELL_SIZE-1, y+CELL_SIZE-1],
353
+ fill=color, outline=(255,255,255))
354
 
355
  # Индикаторы
356
+ hw = int((creature.health / 100) * CELL_SIZE)
357
+ fw = int((creature.food / 100) * CELL_SIZE)
358
+ ww = int((creature.water / 100) * CELL_SIZE)
359
 
360
  draw.rectangle([x, y-6, x + hw, y-4], fill=(255, 0, 0))
361
  draw.rectangle([x, y-4, x + fw, y-2], fill=(255, 165, 0))
 
368
  blue = sum(1 for c in creatures if c.alive and c.team == "blue")
369
  gold = sum(1 for c in creatures if c.alive and c.team == "gold")
370
 
371
+ trees = sum(1 for i in range(WORLD_SIZE) for j in range(WORLD_SIZE)
372
+ if world[i, j][0] == 34 and world[i, j][1] == 139 and world[i, j][2] == 34)
373
+ foods = sum(1 for i in range(WORLD_SIZE) for j in range(WORLD_SIZE)
374
+ if world[i, j][0] == 0 and world[i, j][1] == 150 and world[i, j][2] == 0)
375
+ stones = sum(1 for i in range(WORLD_SIZE) for j in range(WORLD_SIZE)
376
+ if world[i, j][0] == 128 and world[i, j][1] == 128 and world[i, j][2] == 128)
377
 
378
  best_fitness = max([c.fitness for c in creatures if c.alive] + [0])
379
 
380
+ draw.text([10, y_offset], f"Шаг: {step} | Существ: {len(creatures)}", fill=(255,255,255))
381
+ draw.text([10, y_offset + 20], f"🔴:{red} 🔵:{blue} 🟡:{gold}", fill=(255,255,255))
382
+ draw.text([10, y_offset + 40], f"🌳:{trees} 🫐:{foods} 🪨:{stones}", fill=(255,255,255))
383
+ draw.text([10, y_offset + 60], f"⭐ Лучший фитнес: {best_fitness:.0f}", fill=(255,255,255))
384
 
385
  return img
386
 
387
+ # ============== GRADIO ФУНКЦИИ ==============
388
  world, creatures = create_world()
389
+ step_counter = 0
390
 
391
  def run_simulation(steps):
392
+ global world, creatures, step_counter
393
 
394
  images = []
 
395
 
396
  for i in range(steps):
397
+ step_counter += 1
398
+ world, creatures = simulate_step(world, creatures, step_counter)
399
 
400
  if i % 2 == 0:
401
+ img = render_world(world, creatures, step_counter)
402
  images.append(img)
403
 
404
  # Статистика
 
406
  blue = sum(1 for c in creatures if c.alive and c.team == "blue")
407
  gold = sum(1 for c in creatures if c.alive and c.team == "gold")
408
 
409
+ trees = sum(1 for i in range(WORLD_SIZE) for j in range(WORLD_SIZE)
410
+ if world[i, j][0] == 34 and world[i, j][1] == 139 and world[i, j][2] == 34)
411
+ foods = sum(1 for i in range(WORLD_SIZE) for j in range(WORLD_SIZE)
412
+ if world[i, j][0] == 0 and world[i, j][1] == 150 and world[i, j][2] == 0)
413
+ stones = sum(1 for i in range(WORLD_SIZE) for j in range(WORLD_SIZE)
414
+ if world[i, j][0] == 128 and world[i, j][1] == 128 and world[i, j][2] == 128)
415
+
416
  best_fitness = max([c.fitness for c in creatures if c.alive] + [0])
417
 
418
+ stats_text = f"""📊 СТАТИСТИКА
 
419
 
420
+ Шаг: {step_counter}
421
  Существ: {len(creatures)}
422
+
423
  🔴 Красных: {red}
424
  🔵 Синих: {blue}
425
  🟡 Золотых: {gold}
426
 
427
+ 🌳 Деревьев: {trees}
428
+ 🫐 Ягод: {foods}
429
+ 🪨 Камней: {stones}
 
 
 
430
 
431
+ ⭐ Лучший фитнес: {best_fitness:.0f}"""
 
432
 
433
  return images, stats_text
434
 
435
  def reset_simulation():
436
+ global world, creatures, step_counter
437
  world, creatures = create_world()
438
+ step_counter = 0
439
+ img = render_world(world, creatures, 0)
440
+ return [img], "Мир сброшен! Нажмите 'Запустить'"
441
 
442
  # ============== ИНТЕРФЕЙС ==============
443
+ with gr.Blocks(title="🧬 Эволюция") as demo:
444
  gr.Markdown("""
445
+ # 🧬 Эволюционная симуляция
446
 
447
+ 🔴 Красные | 🔵 Синие | 🟡 Золотые
448
 
449
+ Существа **едят**, **пьют**, **строят**, **воюют** и **размножаются**!
450
  """)
451
 
452
  with gr.Row():
453
+ gallery = gr.Gallery(label="Мир", columns=1, rows=1, height=500)
454
+ stats = gr.Textbox(label="Статистика", lines=20, interactive=False)
455
 
456
  with gr.Row():
457
+ steps = gr.Slider(label="Шагов", minimum=5, maximum=50, value=20, step=5)
458
  run_btn = gr.Button("▶️ Запустить", variant="primary")
459
  reset_btn = gr.Button("🔄 Сбросить", variant="secondary")
460
 
461
+ run_btn.click(
462
+ run_simulation,
463
+ inputs=[steps],
464
+ outputs=[gallery, stats]
465
+ )
466
 
467
+ reset_btn.click(
468
+ reset_simulation,
469
+ outputs=[gallery, stats]
470
+ )
471
 
472
  # ============== ЗАПУСК ==============
473
  if __name__ == "__main__":
474
+ demo.launch(share=False)