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()

    作者:Atlas Shepherd

    物联沃分享整理
    物联沃-IOTWORD物联网 » Python生态模拟系统实现详解

    发表回复