
用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 = 3def 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()