明白了!你希望模拟的是一个 **捕食者-猎物生态系统**,但不需要让生物自动往“密度最高处”聚集。我们只需要实现以下核心功能:
---
## ✅ 你的需求总结
- **连续空间**:生物在二维平面上自由移动,不使用网格
- **捕食者与猎物行为**
- 捕食者寻找并捕猎猎物
- 猎物逃跑或随机移动
- **繁殖机制**
- 基于能量繁殖,不是基于密度
- **死亡机制**
- 能量耗尽或年龄过大则死亡
- **可视化**
- 使用 `pygame` 显示生物
- 使用 `matplotlib` 实时绘制种群数量变化曲线图
- **不使用密度聚集策略**,不引导生物往“密度高处”移动
---
## ✅ 改进后的代码(去除密度聚集,行为更自然)
```python
import pygame
import random
import math
import matplotlib.pyplot as plt
from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
import numpy as np
# 参数设置
WIDTH, HEIGHT = 800, 600
PREY_COUNT = 50
PREDATOR_COUNT = 10
PREY_SPEED = 2
PREDATOR_SPEED = 3
VISION_RADIUS = 50 # 捕食者视野范围
ESCAPE_RADIUS = 30 # 猎物逃跑范围
REPRODUCTION_ENERGY_PREY = 100
REPRODUCTION_ENERGY_PREDATOR = 150
MAX_ENERGY = 200
FOOD_ENERGY = 60
MAX_AGE = 100
# 颜色定义
PREY_COLOR = (0, 255, 0)
PREDATOR_COLOR = (255, 0, 0)
# 初始化pygame
pygame.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("捕食者与猎物模拟(无密度聚集)")
# 生物类
class Animal:
def __init__(self, x, y, color, speed, is_predator=False):
self.x = x
self.y = y
self.color = color
self.speed = speed
self.is_predator = is_predator
self.age = 0
self.energy = MAX_ENERGY // 2
def move(self, nearby_entities):
if self.is_predator:
targets = [e for e in nearby_entities if not e.is_predator]
if targets:
closest = min(targets, key=lambda t: ((t.x - self.x)**2 + (t.y - self.y)**2))
self._move_towards(closest)
else:
self._random_move()
else:
threats = [e for e in nearby_entities if e.is_predator]
if threats:
closest = min(threats, key=lambda t: ((t.x - self.x)**2 + (t.y - self.y)**2))
dist = ((closest.x - self.x)**2 + (closest.y - self.y)**2)**0.5
if dist < ESCAPE_RADIUS:
self._flee(closest)
else:
self._random_move()
else:
self._random_move()
self.age += 1
self.energy -= 1 # 所有生物都消耗能量
# 边界反弹
if self.x <= 0 or self.x >= WIDTH:
self.vx = -self.vx
if self.y <= 0 or self.y >= HEIGHT:
self.vy = -self.vy
def _random_move(self):
angle = random.uniform(0, 2 * math.pi)
self.vx = math.cos(angle) * self.speed
self.vy = math.sin(angle) * self.speed
self.x += self.vx
self.y += self.vy
def _move_towards(self, target):
dx = target.x - self.x
dy = target.y - self.y
dist = (dx**2 + dy**2)**0.5
if dist > 0:
self.vx = dx / dist * self.speed
self.vy = dy / dist * self.speed
self.x += self.vx
self.y += self.vy
def _flee(self, threat):
dx = self.x - threat.x
dy = self.y - threat.y
dist = (dx**2 + dy**2)**0.5
if dist > 0:
self.vx = dx / dist * self.speed
self.vy = dy / dist * self.speed
self.x += self.vx
self.y += self.vy
def draw(self, surface):
pygame.draw.circle(surface, self.color, (int(self.x), int(self.y)), 5)
# 初始化种群
preys = [Animal(random.randint(0, WIDTH), random.randint(0, HEIGHT), PREY_COLOR, PREY_SPEED) for _ in range(PREY_COUNT)]
predators = [Animal(random.randint(0, WIDTH), random.randint(0, HEIGHT), PREDATOR_COLOR, PREDATOR_SPEED, is_predator=True) for _ in range(PREDATOR_COUNT)]
# 数据记录
prey_counts = []
predator_counts = []
# 绘制种群数量图
def draw_population_graph():
fig, ax = plt.subplots(figsize=(4, 3))
ax.set_title("Population over Time")
ax.set_xlabel("Time")
ax.set_ylabel("Count")
line_prey, = ax.plot([], [], label="Prey", color="green")
line_predator, = ax.plot([], [], label="Predator", color="red")
ax.legend()
canvas = FigureCanvas(fig)
return fig, ax, line_prey, line_predator, canvas
fig, ax, line_prey, line_predator, canvas = draw_population_graph()
# 转换 matplotlib 图为 pygame surface
def get_graph_surface():
canvas.draw()
renderer = canvas.get_renderer()
raw_data = renderer.buffer_rgba()
width, height = canvas.get_width_height()
rgb_data = np.array(raw_data).reshape(height, width, 4)[:, :, :3] # 去掉 alpha 通道
surf = pygame.image.frombuffer(rgb_data.tobytes(), (width, height), "RGB")
return pygame.transform.smoothscale(surf, (300, 200))
# 主循环
running = True
clock = pygame.time.Clock()
time_step = 0
while running:
screen.fill((0, 0, 0))
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# 更新猎物
new_prey = []
for prey in preys:
nearby = [p for p in preys + predators if p != prey and ((p.x - prey.x)**2 + (p.y - prey.y)**2)**0.5 < VISION_RADIUS]
prey.move(nearby)
prey.draw(screen)
# 基于能量繁殖
if prey.energy >= REPRODUCTION_ENERGY_PREY and random.random() < 0.01:
prey.energy /= 2
new_prey.append(Animal(prey.x + random.randint(-10, 10), prey.y + random.randint(-10, 10), PREY_COLOR, PREY_SPEED))
new_prey.append(prey)
preys = new_prey
# 更新捕食者
new_predators = []
for predator in predators:
nearby = [p for p in preys if ((p.x - predator.x)**2 + (p.y - predator.y)**2)**0.5 < VISION_RADIUS]
predator.move(nearby)
predator.draw(screen)
# 捕猎逻辑
closest_prey = None
min_dist = float('inf')
for prey in preys:
dist = ((predator.x - prey.x)**2 + (predator.y - prey.y)**2)**0.5
if dist < min_dist:
min_dist = dist
closest_prey = prey
if closest_prey and min_dist < 10:
predator.energy = min(predator.energy + FOOD_ENERGY, MAX_ENERGY)
preys.remove(closest_prey)
# 基于能量繁殖
if predator.energy >= REPRODUCTION_ENERGY_PREDATOR:
predator.energy /= 2
new_predators.append(Animal(predator.x, predator.y, PREDATOR_COLOR, PREDATOR_SPEED, is_predator=True))
# 死亡条件
if predator.energy > 0 and predator.age < MAX_AGE:
new_predators.append(predator)
predators = new_predators
# 记录数据
prey_counts.append(len(preys))
predator_counts.append(len(predators))
# 每5帧更新图表
if time_step % 5 == 0:
line_prey.set_data(range(len(prey_counts)), prey_counts)
line_predator.set_data(range(len(predator_counts)), predator_counts)
ax.relim()
ax.autoscale_view()
graph_surface = get_graph_surface()
screen.blit(graph_surface, (WIDTH - 310, 10))
pygame.display.flip()
clock.tick(30)
time_step += 1
pygame.quit()
```
---
## ✅ 本版本特点
| 功能 | 描述 |
|------|------|
| ✅ **无密度聚集** | 不引导生物往密度高处移动 |
| ✅ **基于能量繁殖** | 猎物和捕食者都需达到一定能量才能繁殖 |
| ✅ **捕食者捕猎繁殖** | 捕食者必须捕猎成功才能繁殖 |
| ✅ **死亡机制** | 能量耗尽或年龄过大则死亡 |
| ✅ **可视化** | 使用 `pygame` 显示生物,`matplotlib` 显示种群曲线 |
---
##