python实现的生态模拟系统
用python实现的一个简单的生态模拟系统
- 红色:顶层捕食者(攻击性越强颜色越红)
- 绿色:中层消费者
- 蓝色:底层生产者
- 动态行为:
- 底层生物主动寻找黄色食物
- 中层生物追捕底层生物
- 顶层生物猎杀中层生物
- 统计面板:
- 实时显示各层级生物数量
- 食物数量和帧率显示
import pygame
import math
import random
from enum import Enum
from collections import defaultdict
from dataclasses import dataclass
# 初始化Pygame
pygame.init()
# 常量定义
SCREEN_WIDTH = 1280
SCREEN_HEIGHT = 720
FPS = 45
GRID_SIZE = 60
MAX_FOOD = 150
FOOD_SPAWN_RATE = 0.02
# 基因参数结构体
@dataclass
class Genes:
size: float
speed: float
sense_range: float
metabolism: float
aggression: float
# 生态位类型枚举
class TrophicLevel(Enum):
TOP = 0 # 顶层捕食者(红色)
MIDDLE = 1 # 中层消费者(绿色)
BOTTOM = 2 # 底层生产者(蓝色)
# 食物类
class Food:
def __init__(self):
self.x = random.uniform(0, SCREEN_WIDTH)
self.y = random.uniform(0, SCREEN_HEIGHT)
self.energy = random.uniform(8, 15)
self.size = 3
def draw(self, surface):
pygame.draw.circle(surface, (255, 255, 100), (int(self.x), int(self.y)), self.size)
# 生物类
class Organism:
def __init__(self, x, y, level, genes=None):
self.x = x
self.y = y
self.level = level
self.age = 0
self.sick = False
self.energy = 150.0
# 基因系统
if genes:
self.genes = genes
else:
self.genes = self._generate_genes(level)
# 动态属性
self.color = self._get_color()
self.direction = random.uniform(0, 2 * math.pi)
self.target = None
def _generate_genes(self, level):
"""根据生态位生成初始基因"""
base_genes = {
TrophicLevel.TOP: (8, 2.8, 120, 0.06, 0.8),
TrophicLevel.MIDDLE: (6, 2.0, 90, 0.04, 0.5),
TrophicLevel.BOTTOM: (4, 1.2, 60, 0.02, 0.2)
}
size, speed, sense, meta, agg = base_genes[level]
return Genes(
size * random.uniform(0.9, 1.1),
speed * random.uniform(0.9, 1.1),
sense * random.uniform(0.8, 1.2),
meta * random.uniform(0.9, 1.1),
agg * random.uniform(0.8, 1.2)
)
def _get_color(self):
"""根据生态位和攻击性生成颜色"""
base_colors = {
TrophicLevel.TOP: (200, 50, 50),
TrophicLevel.MIDDLE: (50, 200, 50),
TrophicLevel.BOTTOM: (50, 50, 200)
}
r, g, b = base_colors[self.level]
return (
min(255, int(r * (1 + self.genes.aggression / 3))),
min(255, int(g * (1 - self.genes.aggression / 3))),
b
)
def move_towards(self, target_x, target_y):
"""向目标方向移动"""
dx = target_x - self.x
dy = target_y - self.y
target_angle = math.atan2(dy, dx)
angle_diff = (target_angle - self.direction) % (2 * math.pi)
# 平滑转向
if angle_diff > math.pi:
angle_diff -= 2 * math.pi
self.direction += angle_diff * 0.1
# 前进
self.x += self.genes.speed * math.cos(self.direction)
self.y += self.genes.speed * math.sin(self.direction)
def avoid(self, target_x, target_y):
"""躲避目标"""
dx = target_x - self.x
dy = target_y - self.y
target_angle = math.atan2(dy, dx)
self.direction = target_angle + math.pi # 反向
def update_target(self, foods, organisms):
"""根据感知选择目标"""
closest_food = None
closest_predator = None
closest_prey = None
min_food_dist = float('inf')
min_pred_dist = float('inf')
min_prey_dist = float('inf')
# 感知范围内检测
for obj in foods + organisms:
if obj is self:
continue
dx = obj.x - self.x
dy = obj.y - self.y
dist_sq = dx ** 2 + dy ** 2
# 食物检测(仅底层)
if (self.level == TrophicLevel.BOTTOM and
isinstance(obj, Food) and
dist_sq < self.genes.sense_range ** 2 and
dist_sq < min_food_dist):
closest_food = obj
min_food_dist = dist_sq
# 天敌检测
if (isinstance(obj, Organism) and
obj.level.value == self.level.value - 1 and
dist_sq < self.genes.sense_range ** 2 and
dist_sq < min_pred_dist):
closest_predator = obj
min_pred_dist = dist_sq
# 猎物检测
if (isinstance(obj, Organism) and
obj.level.value == self.level.value + 1 and
dist_sq < self.genes.sense_range ** 2 and
dist_sq < min_prey_dist):
closest_prey = obj
min_prey_dist = dist_sq
# 行为优先级:躲避天敌 > 寻找食物/猎物 > 随机游走
if closest_predator:
self.target = closest_predator
self.avoid(self.target.x, self.target.y)
elif closest_prey and self.level != TrophicLevel.BOTTOM:
self.target = closest_prey
self.move_towards(self.target.x, self.target.y)
elif closest_food and self.level == TrophicLevel.BOTTOM:
self.target = closest_food
self.move_towards(self.target.x, self.target.y)
else:
# 随机方向微调
self.direction += random.uniform(-0.3, 0.3)
def check_collision(self, other):
"""通用碰撞检测方法"""
if isinstance(other, Organism):
dx = self.x - other.x
dy = self.y - other.y
return dx ** 2 + dy ** 2 < (self.genes.size + other.genes.size) ** 2
elif isinstance(other, Food):
dx = self.x - other.x
dy = self.y - other.y
return dx ** 2 + dy ** 2 < (self.genes.size + other.size) ** 2
return False
def draw(self, surface):
"""绘制生物体"""
pygame.draw.circle(
surface,
self.color,
(int(self.x), int(self.y)),
int(self.genes.size)
)
def update(self, foods):
self.age += 1
self.energy -= self.genes.metabolism
if self.sick and random.random() < 0.03:
self.sick = False
if self.level == TrophicLevel.BOTTOM:
self.energy += 0.05
self.energy = min(self.energy, 200)
def reproduce(self):
child_genes = Genes(
size=max(2, self.genes.size * random.uniform(0.95, 1.05)),
speed=max(0.5, self.genes.speed * random.uniform(0.9, 1.1)),
sense_range=max(30, self.genes.sense_range * random.uniform(0.8, 1.2)),
metabolism=max(0.01, self.genes.metabolism * random.uniform(0.95, 1.05)),
aggression=min(1.0, self.genes.aggression * random.uniform(0.9, 1.1))
)
child = Organism(
self.x + random.uniform(-20, 20),
self.y + random.uniform(-20, 20),
self.level,
child_genes
)
self.energy -= 35
return child
# 模拟管理器
class EcosystemSimulation:
def __init__(self):
self.screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
self.clock = pygame.time.Clock()
self.font = pygame.font.Font(None, 28)
self.organisms = []
self.foods = []
self.init_ecosystem()
def init_ecosystem(self):
# 初始种群分布
for _ in range(80):
x = random.uniform(0, SCREEN_WIDTH)
y = random.uniform(0, SCREEN_HEIGHT)
level = random.choices(
list(TrophicLevel),
weights=[0.12, 0.25, 0.63]
)[0]
self.organisms.append(Organism(x, y, level))
# 初始食物
for _ in range(80):
self.foods.append(Food())
def spawn_food(self):
if random.random() < FOOD_SPAWN_RATE and len(self.foods) < MAX_FOOD:
self.foods.append(Food())
def handle_collisions(self):
to_add = []
to_remove = []
grid = defaultdict(list)
# 建立空间索引
for org in self.organisms:
grid_key = (int(org.x // GRID_SIZE), int(org.y // GRID_SIZE))
grid[grid_key].append(org)
# 处理生物行为
for org in self.organisms:
org.update_target(self.foods, self.organisms)
# 边界反弹
if org.x < 0 or org.x > SCREEN_WIDTH:
org.direction = math.pi - org.direction
if org.y < 0 or org.y > SCREEN_HEIGHT:
org.direction = -org.direction
org.x = max(0, min(SCREEN_WIDTH, org.x))
org.y = max(0, min(SCREEN_HEIGHT, org.y))
# 进食检测(底层吃食物)
if org.level == TrophicLevel.BOTTOM:
for food in self.foods[:]:
if org.check_collision(food):
org.energy += food.energy
self.foods.remove(food)
# 捕食检测
current_key = (int(org.x // GRID_SIZE), int(org.y // GRID_SIZE))
for dx in (-1, 0, 1):
for dy in (-1, 0, 1):
neighbor_key = (current_key[0] + dx, current_key[1] + dy)
for other in grid.get(neighbor_key, []):
if org != other and org.check_collision(other):
if org.level.value == other.level.value + 1:
org.energy += other.energy * 0.6
to_remove.append(other)
# 繁殖与死亡
for org in self.organisms[:]:
org.update(self.foods)
if org.energy > 130 and org.age > 18:
to_add.append(org.reproduce())
if org.energy <= 0 or org.age > 250:
to_remove.append(org)
# 更新列表
self.organisms = [o for o in self.organisms if o not in to_remove]
self.organisms.extend(to_add)
def draw_stats(self):
stats = [
f"Time: {pygame.time.get_ticks() // 1000}s",
f"Organisms: {len(self.organisms)}",
f" TOP: {sum(1 for o in self.organisms if o.level == TrophicLevel.TOP)}",
f" MID: {sum(1 for o in self.organisms if o.level == TrophicLevel.MIDDLE)}",
f" BOT: {sum(1 for o in self.organisms if o.level == TrophicLevel.BOTTOM)}",
f"Food: {len(self.foods)}",
f"FPS: {self.clock.get_fps():.1f}"
]
for i, text in enumerate(stats):
surface = self.font.render(text, True, (240, 240, 240))
self.screen.blit(surface, (10, 10 + i * 28))
def run(self):
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# 更新状态
self.spawn_food()
self.handle_collisions()
# 渲染
self.screen.fill((18, 18, 18))
for food in self.foods:
food.draw(self.screen)
for org in self.organisms:
org.draw(self.screen)
self.draw_stats()
pygame.display.flip()
self.clock.tick(FPS)
pygame.quit()
if __name__ == "__main__":
sim = EcosystemSimulation()
sim.run()