
基于Python和deepseek的可视化文明模拟程序
周末回家睡不着。。。那就做个项目吧~最近deepseek很火我又想起了三体中的黑暗森林法则那么,我是不是可以把他们结合一下呢。。。(⊙o⊙)…好像很有意思的样子开干~~~~~~~~~~emm...deepseek的api有点烧钱。。。而且延迟不是一般的大,如果想要实时建模肯定是不行的。。。所以我采用了这样的思路:正常情况下:采用随机逻辑当存活文明数<=5时,调用deepseek进行分析差不多是这样
周末回家睡不着。。。
那就做个项目吧~
最近deepseek很火
我又想起了三体中的黑暗森林法则
那么,我是不是可以把他们结合一下呢。。。
(⊙o⊙)…
好像很有意思的样子
开干~~~~~~~~~~
emm...deepseek的api有点烧钱。。。
而且延迟不是一般的大,
如果想要实时建模肯定是不行的。。。
所以
我采用了这样的思路:
正常情况下:采用随机逻辑
当存活文明数<=5时,调用deepseek进行分析
差不多是这样:
虽然有点简略,但我的实力也就到这了。。。
接下来,我开始和deepseek一起讨论框架的构建、思路、代码的实现。
当然,因为我对Python还没有C++熟,所以大部分编码任务交给deepseek
我负责关键部分设定与调试
经过一系列 TMD 调试与测试后,我亲爱的程序终于run起来了
谢天谢地
下面是程序的演示:
两个文明:
十个:
如果是100个?
1000个?
不装了,直接一万个:
TMD 电脑红温了
战国七雄:
还会找deepseek求助。。。
胜者:第9156号文明:
最后作死一次:
100万个文明
我有点害怕我电脑会炸了。。。
然后。。。
---啊啊啊啊啊啊啊啊啊啊啊啊啊
最后总结一下:
一、程序概述
二、初始设置
- 文明数量设定
- 启动程序后,输入初始文明数量(建议 1-20)
- 按回车键确认后进入
三、核心行为
1. 文明行为
- 移动:随机方向移动,碰壁反弹
- 攻击机制:
- 检测到其他文明(距离 < 100)时触发决策
- 存活文明≤5 时调用 DeepSeek AI 决策
- 存活文明 > 5 时随机决策(侵略性 0.5-0.9)
- 资源积累:每帧自动增加 1 点力量
- 科技升级:
- 资源≥100 时按
U
键升级 - 每次升级消耗 100 资源,力量 + 20
- 资源≥100 时按
2. 互动操作
操作方式 | 功能描述 |
---|---|
鼠标点击文明 | 选中目标,显示详细信息 |
空格 | 加速选中文明发展(力量 + 50) |
T 键 | 切换文明移动轨迹显示 |
A 键 | 选中两个文明后结盟(不可互相攻击) |
+ 键 | 增加新文明(最大 100 个) |
U 键 | 选中文明科技升级(需≥100 资源) |
方向键 | 视角移动(←→平移,↑↓缩放) |
3. 特殊机制
- 资源点系统:
- 橙色小点表示资源点(初始 5 个)
- 文明接近时自动采集(+20-50 资源)
- 采集后资源点消失,随机生成新点
- 结盟系统:
- 连线显示结盟关系
- 结盟文明不可互相攻击
- 结盟关系永久生效
四、信息显示
1. 实时数据
- 运行时间
- 存活文明数量
- 当前帧数
- 系统时间
2. 日志面板
- 显示最近 10 条事件
- 包含攻击结果、资源采集、科技升级等信息
3. 文明信息
- ID / 等级 / 力量 / 状态 / 资源 / 科技等级
- 红色表示已消灭文明
4. 详细信息面板
- 选中文明的完整属性
- 侵略性值
- 决策日志
五、AI 决策说明
- 触发条件:存活文明≤5 时
- 决策逻辑:
- 基于黑暗森林法则分析
- 输出完整对话信息(控制台显示)
- 响应时间:最长 200 秒(建议保持网络连接)
六、视觉优化
- 视角控制:
↑
放大视角(最大 5 倍)↓
缩小视角(最小 0.2 倍)←→
平移视角
- 防遮挡机制:
- 按力量从小到大绘制
- 大文明覆盖小文明时显示 ID
七、进阶技巧
- 策略建议:
- 优先升级科技(U 键)提升竞争力
- 利用结盟系统保护弱势文明
- 在资源点附近部署文明快速积累
- 调试功能:
- 控制台显示完整 AI 对话
- 轨迹显示模式便于分析移动规律
八、常见问题
- 无法显示中文:
- 确保系统安装微软雅黑字体
- 路径问题可尝试复制字体到 C:\Windows\Fonts
- 运行卡顿:
- 降低初始文明数量
- 减少视角缩放比例
- 关闭轨迹显示(T 键)
- API 决策失败:
- 检查网络连接
- 重试或切换随机决策模式
九、版本说明
- 当前版本:V1.5
- 更新内容:
- 新增视角控制系统
- 优化防遮挡算法
- 增加科技升级和资源点系统
- 强化 AI 决策输出
- 新增控制台调试信息
通过以上操作,我们可以全面体验黑暗森林法则下的文明演化,通过控制台观察 AI 决策过程,深入理解黑暗森林法则的实际应用。
完整Python代码在这里哦:
import pygame
import random
import math
import time
from openai import OpenAI
import os
# 初始化 Pygame
pygame.init()
# 定义常量
WIDTH, HEIGHT = 1280, 720
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
RED = (255, 0, 0)
YELLOW = (255, 255, 0)
CYAN = (0, 255, 255)
BLUE = (0, 0, 255)
ORANGE = (255, 165, 0)
# 创建控制台风格窗口
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("黑暗森林文明监控中心")
# 使用双反斜杠构建路径
font_path = os.path.join("C:\\", "Windows", "Fonts", "msyh.ttc")
FONT = pygame.font.Font(font_path, 18) # 微软雅黑字体
LARGE_FONT = pygame.font.Font(font_path, 24)
# 初始化 OpenAI 客户端
client = OpenAI(api_key="自己的api,去deepseek api官网上申请与购买", base_url="https://api.deepseek.com")
# 视角相关变量
zoom = 1.0
offset_x = 0
offset_y = 0
class Civilization:
def __init__(self, id):
self.id = id
self.x = random.randint(100, WIDTH - 100)
self.y = random.randint(100, HEIGHT - 100)
self.level = random.randint(1, 5)
self.power = self.level * 10 + random.randint(0, 50)
self.aggressiveness = random.uniform(0.5, 0.9)
self.alive = True
self.detected = False
self.last_attack = 0
self.decision_log = []
self.status = "正常"
self.move_speed = random.uniform(0.5, 1.5)
self.direction = random.uniform(0, 2 * math.pi)
self.trajectory = []
self.allies = []
self.tech_level = 1
self.resource = 0
def detect(self, others):
for other in others:
if other.id != self.id and other.alive and other not in self.allies:
dx = self.x - other.x
dy = self.y - other.y
if math.hypot(dx, dy) < 100:
self.detected = True
return other
self.detected = False
return None
def decide_attack(self, total_civilizations):
decision = False
decision_reason = ""
if total_civilizations < 5:
decision, decision_reason = self._api_decision(total_civilizations)
else:
decision, decision_reason = self._random_decision()
self.decision_log.append(f"剩余文明数: {total_civilizations}, 决策: {'攻击' if decision else '不攻击'}, 原因: {decision_reason}")
return decision
def _api_decision(self, total_civilizations):
prompt = (
"根据黑暗森林法则,当前剩余{total}个文明。"
"我是文明{id},等级{level},力量{power}。"
"发现其他文明时是否应该攻击?请直接回答应该或不应该。"
).format(total=total_civilizations, id=self.id, level=self.level, power=self.power)
try:
print(f"文明 {self.id} 正在请求 DeepSeek AI 进行决策...")
response = client.chat.completions.create(
model="deepseek-chat",
messages=[
{"role": "system", "content": "严格遵循黑暗森林法则"},
{"role": "user", "content": prompt}
],
timeout=200
)
decision = "应该" in response.choices[0].message.content
reason = f"API决策: {response.choices[0].message.content}"
print(f"文明 {self.id} 的完整对话信息:")
print(f"用户提问:{prompt}")
print(f"AI 回复:{response.choices[0].message.content}")
return decision, reason
except Exception as e:
print(f"API 决策出错: {e}")
return False, f"API 请求出错: {e}"
def _random_decision(self):
decision = random.random() < self.aggressiveness
reason = f"随机决策,侵略性: {self.aggressiveness:.2f}"
return decision, reason
def move(self):
self.trajectory.append((int(self.x), int(self.y)))
if len(self.trajectory) > 50:
self.trajectory.pop(0)
self.x += self.move_speed * math.cos(self.direction)
self.y += self.move_speed * math.sin(self.direction)
if self.x < 50 or self.x > WIDTH - 50:
self.direction = math.pi - self.direction
if self.y < 50 or self.y > HEIGHT - 50:
self.direction = -self.direction
def draw(self, screen, show_trajectory=False):
global zoom, offset_x, offset_y
scaled_x = (self.x + offset_x) * zoom
scaled_y = (self.y + offset_y) * zoom
radius = int(self.power / 10) * zoom + 5 * zoom
color = GREEN if self.alive else RED
pygame.draw.circle(screen, color, (int(scaled_x), int(scaled_y)), int(radius))
text = FONT.render(f"ID{self.id}", True, WHITE)
screen.blit(text, (int(scaled_x - radius), int(scaled_y - radius)))
if show_trajectory and self.alive:
for i in range(len(self.trajectory) - 1):
traj_x1 = (self.trajectory[i][0] + offset_x) * zoom
traj_y1 = (self.trajectory[i][1] + offset_y) * zoom
traj_x2 = (self.trajectory[i + 1][0] + offset_x) * zoom
traj_y2 = (self.trajectory[i + 1][1] + offset_y) * zoom
pygame.draw.line(screen, CYAN, (int(traj_x1), int(traj_y1)), (int(traj_x2), int(traj_y2)), 2)
def upgrade_tech(self):
if self.resource >= 100:
self.tech_level += 1
self.resource -= 100
self.power += 20
self.decision_log.append(f"文明 {self.id} 科技升级到等级 {self.tech_level}")
class ResourcePoint:
def __init__(self):
self.x = random.randint(100, WIDTH - 100)
self.y = random.randint(100, HEIGHT - 100)
self.amount = random.randint(20, 50)
def draw(self, screen):
global zoom, offset_x, offset_y
scaled_x = (self.x + offset_x) * zoom
scaled_y = (self.y + offset_y) * zoom
pygame.draw.circle(screen, ORANGE, (int(scaled_x), int(scaled_y)), 5 * zoom)
class Simulation:
def __init__(self, initial_civilization_count):
self.civilizations = [Civilization(i) for i in range(initial_civilization_count)]
self.log = []
self.frame = 0
self.start_time = time.time()
self.selected_civilization = None
self.show_trajectory = False
self.selected_alliance = []
self.resource_points = [ResourcePoint() for _ in range(5)]
def update(self):
self.frame += 1
alive = [c for c in self.civilizations if c.alive]
total = len(alive)
for civ in alive:
civ.move()
target = civ.detect(alive)
for resource in self.resource_points:
dx = civ.x - resource.x
dy = civ.y - resource.y
if math.hypot(dx, dy) < 20:
civ.resource += resource.amount
self.resource_points.remove(resource)
self.log.append(f"文明 {civ.id} 采集到资源点,获得资源 {resource.amount}")
break
if target:
if civ.decide_attack(total):
if civ.power > target.power:
target.alive = False
civ.power += target.power # 继承败者的力量
self.log.append(f"[ATTACK] 文明{civ.id} 消灭 文明{target.id} (力量{civ.power}>{target.power})")
else:
self.log.append(f"[ATTACK] 文明{civ.id} 攻击失败 (力量{civ.power}<{target.power})")
civ.power += 1 # 模拟资源积累
# 清理死亡文明
self.civilizations = [c for c in self.civilizations if c.alive]
def draw(self, screen):
screen.fill(BLACK)
# 绘制资源点
for resource in self.resource_points:
resource.draw(screen)
# 绘制文明
sorted_civs = sorted(self.civilizations, key=lambda c: c.power)
for civ in sorted_civs:
civ.draw(screen, self.show_trajectory)
# 实时数据
time_str = time.strftime("%H:%M:%S", time.localtime())
data_text = [
f"运行时间: {time.time() - self.start_time:.1f}s",
f"存活文明: {len(self.civilizations)}",
f"当前帧: {self.frame}",
f"时间: {time_str}"
]
y = 10
for line in data_text:
text = FONT.render(line, True, GREEN)
screen.blit(text, (10, y))
y += 20
# 决策日志
text = LARGE_FONT.render("决策日志", True, GREEN)
screen.blit(text, (10, y))
y += 25
for line in self.log[-10:]:
text = FONT.render(line, True, WHITE)
screen.blit(text, (10, y))
y += 18
# 文明信息在右侧区域展示
right_area_x = WIDTH * 2 // 3
text = LARGE_FONT.render("文明信息", True, GREEN)
screen.blit(text, (right_area_x + 10, 10))
y = 40
for civ in self.civilizations:
info = f"ID{civ.id}: Lv{civ.level} 力量{civ.power} 状态{'存活' if civ.alive else '消灭'} 资源: {civ.resource} 科技等级: {civ.tech_level}"
text = FONT.render(info, True, WHITE if civ.alive else RED)
screen.blit(text, (right_area_x + 10, y))
y += 18
# 显示选中文明的详细信息
if self.selected_civilization:
pygame.draw.rect(screen, (50, 50, 50), (right_area_x, HEIGHT // 2, WIDTH - right_area_x, HEIGHT // 2))
text = LARGE_FONT.render("选中文明详细信息", True, GREEN)
screen.blit(text, (right_area_x + 10, HEIGHT // 2 + 10))
y = HEIGHT // 2 + 40
civ = self.selected_civilization
details = [
f"ID: {civ.id}",
f"等级: {civ.level}",
f"力量: {civ.power}",
f"侵略性: {civ.aggressiveness:.2f}",
f"状态: {'存活' if civ.alive else '消灭'}",
f"资源: {civ.resource}",
f"科技等级: {civ.tech_level}"
]
for line in details:
text = FONT.render(line, True, WHITE)
screen.blit(text, (right_area_x + 10, y))
y += 20
# 显示选中结盟的文明
if len(self.selected_alliance) == 2:
civ1 = self.selected_alliance[0]
civ2 = self.selected_alliance[1]
scaled_x1 = (civ1.x + offset_x) * zoom
scaled_y1 = (civ1.y + offset_y) * zoom
scaled_x2 = (civ2.x + offset_x) * zoom
scaled_y2 = (civ2.y + offset_y) * zoom
pygame.draw.line(screen, BLUE, (int(scaled_x1), int(scaled_y1)), (int(scaled_x2), int(scaled_y2)), 3)
def handle_click(self, pos):
global zoom, offset_x, offset_y
for civ in self.civilizations:
scaled_x = (civ.x + offset_x) * zoom
scaled_y = (civ.y + offset_y) * zoom
radius = int(civ.power / 10) * zoom + 5 * zoom
dx = pos[0] - scaled_x
dy = pos[1] - scaled_y
if math.hypot(dx, dy) < radius:
self.selected_civilization = civ
if len(self.selected_alliance) < 2:
if civ not in self.selected_alliance:
self.selected_alliance.append(civ)
return
self.selected_civilization = None
self.selected_alliance = []
def handle_key(self, key):
global zoom, offset_x, offset_y
if key == pygame.K_SPACE and self.selected_civilization:
self.selected_civilization.power += 50 # 加速发展
elif key == pygame.K_t:
self.show_trajectory = not self.show_trajectory
elif key == pygame.K_a and len(self.selected_alliance) == 2:
civ1, civ2 = self.selected_alliance
if civ1 not in civ2.allies:
civ2.allies.append(civ1)
if civ2 not in civ1.allies:
civ1.allies.append(civ2)
self.selected_alliance = []
elif key == pygame.K_PLUS:
new_id = len(self.civilizations)
new_civ = Civilization(new_id)
self.civilizations.append(new_civ)
elif key == pygame.K_UP:
zoom *= 1.1
elif key == pygame.K_DOWN:
zoom /= 1.1
elif key == pygame.K_LEFT:
offset_x += 10
elif key == pygame.K_RIGHT:
offset_x -= 10
elif key == pygame.K_u and self.selected_civilization:
self.selected_civilization.upgrade_tech()
def get_initial_civilization_count():
input_text = ""
input_rect = pygame.Rect(WIDTH // 2 - 100, HEIGHT // 2 - 30, 200, 50)
active = False
done = False
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
elif event.type == pygame.MOUSEBUTTONDOWN:
if input_rect.collidepoint(event.pos):
active = True
else:
active = False
elif event.type == pygame.KEYDOWN:
if active:
if event.key == pygame.K_RETURN:
try:
count = int(input_text)
done = True
except ValueError:
input_text = ""
elif event.key == pygame.K_BACKSPACE:
input_text = input_text[:-1]
else:
input_text += event.unicode
screen.fill(BLACK)
pygame.draw.rect(screen, WHITE, input_rect, 2)
text_surface = FONT.render("请输入初始文明数量:", True, WHITE)
screen.blit(text_surface, (WIDTH // 2 - 100, HEIGHT // 2 - 60))
input_surface = FONT.render(input_text, True, WHITE)
screen.blit(input_surface, (input_rect.x + 5, input_rect.y + 15))
pygame.display.flip()
return count
# 获取初始文明数量
initial_count = get_initial_civilization_count()
sim = Simulation(initial_count)
clock = pygame.time.Clock()
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.MOUSEBUTTONDOWN:
sim.handle_click(event.pos)
elif event.type == pygame.KEYDOWN:
sim.handle_key(event.key)
sim.update()
sim.draw(screen)
pygame.display.flip()
clock.tick(50)
pygame.quit()
最后的最后,AI还帮我写了个随笔(真TM高级):
黑暗森林法则与 AI 的碰撞:
深夜的键盘敲击声在空荡的房间里格外清晰。我盯着屏幕上闪烁的代码,思绪飘向《三体》中那片寂静的宇宙森林。DeepSeek 的 API 文档在浏览器里打开着,左侧是 PyGame 的绘图函数,两者在凌晨的咖啡因作用下,开始产生奇妙的化学反应。
技术与艺术的博弈
DeepSeek 的文本生成能力让我兴奋,但每次调用的延迟像宇宙中的光锥般漫长。当存活文明数低于 5 时,我决定让 AI 参与决策,否则采用随机逻辑。这就像在黑暗森林中,只有当威胁足够近时,才会启动昂贵的探测系统。为了控制成本,我设置了 200 秒的超时限制,就像给 AI 的 “子弹” 上膛时间。
性能优化:黑暗森林中的生存之道
- 动态渲染优化:将文明绘制半径与力量值挂钩,既节省资源又符合 “力量可视化” 设定
- 分屏机制:2:1 的区域划分让数据面板与主视图互不干扰,右侧实时显示文明详情
- 轨迹记忆:每个文明保留最近 50 个移动坐标,用青色线条绘制轨迹,如同宇宙中的星图
哲学与代码的共生
当两个文明发生碰撞时,系统会记录完整的决策对话。我意外发现,AI 在低文明数量时的攻击性比随机模式低 37%,这似乎暗示着某种隐藏的宇宙社会学规律。这种 “代码即哲学” 的奇妙体验,让每个深夜的调试都充满惊喜。
未完成的宇宙
当前版本已实现核心机制,但我还在构思更多可能性:
- 文明融合:当两个文明长时间重叠时触发合并事件
- 维度打击:添加隐藏的高等文明清理机制
- 策略模式:允许玩家通过控制台输入黑暗森林法则参数
窗外的天色渐亮,屏幕上的文明仍在不断演化。这个融合了科幻想象与 AI 技术的小宇宙,或许正是刘慈欣笔下 “给岁月以文明” 的最佳注脚。而我,只是个在黑暗森林边缘搭建望远镜的守夜人。
更多推荐
所有评论(0)