周末整理代码仓库时,发现之前拿 Python 折腾过一个小项目:人生模拟器。当时想法很简单——网上很多“人生重开模拟器”要么是网页小游戏,要么逻辑写得太复杂,不太适合拿来学编程。于是决定自己手搓一个纯 Python 控制台版本,用属性和随机事件驱动整个人生流程,跑完之后还能得到不同的人生结局。
做完之后发现,这件事比想象中有意思:它不只是一个玩具,背后其实包含了非常经典的编程思想——状态建模、事件驱动、随机抽样、批量统计。而且把“人生五连”跑出来之后,真的会有一种“原来换个参数,人生就完全不一样”的感觉。本文就把这个项目的完整实现过程、核心代码、运行效果和优化思路整理出来,分享给同样想用 Python 做小游戏或模拟类项目的读者。
1. 人生模拟器是什么,为什么用 Python 写
1.1 什么是人生模拟器
“人生模拟器”是一种常见的文字类模拟游戏:给角色设定初始属性,然后按照时间线逐年推进,每一步可能触发随机事件,不同事件会影响角色的健康、智力、财富、幸福等属性,最终根据属性状态给出一个结局。
最简单的版本一眼就能看懂,但真正动手写起来会发现很多值得思考的问题:
- 属性之间如何互相影响?
- 随机事件怎么保证不“一刀切”?
- 如何让最终结局看起来合理?
- 代码结构怎么设计,之后才方便加新事件?
这些问题的答案,恰好就是软件设计和数据处理的基础能力。所以“人生模拟器”很适合作为 Python 入门后的第一个综合练手项目。
1.2 为什么选择纯 Python 实现
纯 Python 实现有两个好处。第一,不需要安装任何第三方依赖,只使用标准库中的random和dataclasses,做到“复制即运行”,对新手非常友好。第二,核心逻辑集中在数据结构和流程控制上,方便把重点放在“怎么设计”而不是“怎么用框架”。
如果你之后想把它做成 Web 版本,完全可以把同一套属性模型和事件池迁移到 Flask 或者 FastAPI 后端,再把输出改成 JSON,前端用 Vue 或 React 渲染。本文的核心价值在于底层逻辑设计,这部分是通用的。
1.3 你能从这个项目中学到什么
- Python 类设计与属性管理
- 随机事件与加权随机抽样
- 基于年龄分阶段的游戏流程控制
- 多结局判定规则设计
- 批量模拟与结果统计
- 随机种子固定、日志记录等工程经验
2. 项目设计思路
动手写代码之前,先梳理核心设计。好的模拟类项目通常会拆成几层:人物状态层、事件层、流程控制层、结局判定层。
2.1 人物属性模型
角色是模拟的核心,属性包括:
| 属性 | 含义 | 初始值 | 范围 |
|---|---|---|---|
| health | 健康值,影响能否继续活下去 | 80 | 0 - 100 |
| intelligence | 智力值,影响学历和关键事件 | 50 | 0 - 100 |
| wealth | 财富值,影响生活质量和婚姻概率 | 20 | 0 - 100 |
| happiness | 幸福值,影响结局综合评分 | 70 | 0 - 100 |
| education | 学历等级,用数字表示 | 0 | 0 - 5 |
| career | 职业名称 | 无业 | 字符串 |
| married | 婚姻状态 | False | 布尔值 |
| children | 子女数量 | 0 | 整数 |
这里没有设计“年龄”为属性,而是用循环控制年龄推进,这样流程更直观。
2.2 事件驱动机制
人生模拟器最核心的机制是“随机事件”。每个年龄段有不同的事件池,比如儿童期应该是上学、生病、玩耍,青年期应该是工作、恋爱、考试,老年期应该是退休、健康问题、家庭团聚。
流程上采用“一年一事件”的节奏:
- 根据当前年龄找到对应阶段。
- 从该阶段的事件池里按权重抽取一个事件。
- 把事件效果应用到角色属性上。
- 记录事件日志。
- 年龄加一,进入下一年。
这种“事件池按年龄阶段划分”的做法非常清晰,后续想扩展事件,只需往对应池子里加一条记录。
2.3 五种人生结局
为了让项目具备“可重玩性”,结局不能只看单一属性。我设计了综合评价分:
score = health * 0.25 + intelligence * 0.20 + wealth * 0.30 + happiness * 0.25四个属性加权求和后,按照分数区间映射为五种结局:
| 结局 | 分数区间 | 风格 |
|---|---|---|
| 人生赢家 | 80 - 100 | 全属性优秀 |
| 安稳人生 | 65 - 79 | 平平淡淡但顺利 |
| 波折人生 | 50 - 64 | 有高光也有低谷 |
| 困难剧本 | 35 - 49 | 经受了很多压力 |
| 硬核剧本 | 0 - 34 | 低属性爆炸开局 |
正因为结局是加权综合结果,同一套代码每次运行都会产生不同的“人生”,这就是标题里“五种人生”的由来。
3. 环境准备与版本说明
3.1 运行环境
这个项目使用 Python 3.8 及以上版本即可,没有外部依赖。我在本地测试时使用的是 Python 3.10,在 Windows 和 macOS 终端都能正常运行。
如果你还没有 Python 环境,建议直接安装官方 Python 3.10 或 3.11,安装时记得勾选 “Add Python to PATH”。
3.2 项目文件结构
项目非常简单,只有一个文件:
life_simulator/ └── life_simulator.py你也可以拆分成模块,例如:
life_simulator/ ├── main.py # 入口和交互 ├── person.py # 人物类 ├── events.py # 事件池 └── simulator.py # 模拟主流程本文为了方便阅读,先把核心逻辑集中在一个文件里展示,最后会给出工程化拆分建议。
4. 核心代码实现
4.1 定义人物类 Person
先定义角色属性。为了让属性变化更可控,我提供了一个apply_effects方法,统一处理事件带来的属性增减。
# -*- coding: utf-8 -*- """ 生命模拟器 LifeSimulator 纯 Python 实现的多结局人生模拟小游戏 """ import random # 学历等级映射 EDU_NAMES = { 0: "小学", 1: "初中", 2: "高中", 3: "本科", 4: "硕士", 5: "博士", } class Person: """模拟人物""" def __init__(self, name="无名者"): self.name = name self.age = 0 self.health = 80 self.intelligence = 50 self.wealth = 20 self.happiness = 70 self.alive = True self.education = 0 self.career = "无业" self.married = False self.children = 0 self.events = [] @property def education_text(self): return EDU_NAMES.get(self.education, "未知") def apply_effects(self, effects): """把事件效果映射到人物属性上,并限制在 0-100 之间""" for key, value in effects.items(): if key == "health": self.health = max(0, min(100, self.health + value)) elif key == "intelligence": self.intelligence = max(0, min(100, self.intelligence + value)) elif key == "wealth": self.wealth = max(0, min(100, self.wealth + value)) elif key == "happiness": self.happiness = max(0, min(100, self.happiness + value)) def add_event(self, title, desc): self.events.append(f"{self.age}岁:{title}:{desc}")这里要注意max(0, min(100, ...))的作用:角色属性不会低于 0,也不会超过 100,避免了属性溢出带来的异常结局。
4.2 年龄阶段与事件池
年龄阶段划分直接影响事件池的选择。我把人生划分为五个阶段:
# 人生阶段常量 CHILDHOOD = 1 # 0-5 岁 SCHOOL = 2 # 6-17 岁 YOUTH = 3 # 18-30 岁 MIDDLE = 4 # 31-55 岁 ELDER = 5 # 56-100 岁 def get_stage(age): """根据年龄返回人生阶段""" if age < 6: return CHILDHOOD elif age < 18: return SCHOOL elif age < 31: return YOUTH elif age < 56: return MIDDLE else: return ELDER事件池使用字典存储,每个事件包含描述、属性变化和其他权重:
EVENT_POOL = { CHILDHOOD: [ ("在院子里和小伙伴追逐打闹,摔破了膝盖。", {"health": -3, "happiness": 2}, 10), ("父母给你读了一本《安徒生童话》。", {"intelligence": 1, "happiness": 4}, 10), ("第一次见到大海,被无边无际的蓝色震撼。", {"happiness": 5, "intelligence": 1}, 8), ("因为玩具被抢和邻居小孩打了一架。", {"happiness": -2, "health": -2}, 6), ("发了一次高烧,父母照顾了整整一周。", {"health": -8, "happiness": -1}, 4), ], SCHOOL: [ ("期中考试考了第一名,被评为学习标兵。", {"intelligence": 3, "happiness": 3}, 12), ("参加学校运动会,800米拿了个名次。", {"health": 4, "happiness": 2}, 10), ("迷上了一本小说,上课时偷偷看完。", {"happiness": 3, "intelligence": -1}, 10), ("和同桌因为一道数学题争论了一下午。", {"intelligence": 2, "happiness": 1}, 12), ("近视度数加深,第一次戴上了眼镜。", {"health": -2, "intelligence": 1}, 7), ("参加兴趣社团,认识了很多朋友。", {"happiness": 4, "intelligence": 1}, 8), ("升学压力很大,连续几周睡眠不足。", {"health": -4, "intelligence": 2, "happiness": -2}, 10), ], YOUTH: [ ("通过校招拿到了心仪的 offer。", {"wealth": 10, "happiness": 5}, 12), ("第一次独自去陌生城市旅行。", {"happiness": 6, "wealth": -5}, 8), ("熬夜写方案,然后发现自己胖了3斤。", {"health": -3, "intelligence": 2, "wealth": 3}, 10), ("和恋人确立了恋爱关系。", {"happiness": 8}, 10), ("工作受挫被领导批评,深夜一个人吃泡面。", {"happiness": -5, "wealth": -1}, 8), ("尝试考取专业证书,每天下班学习两小时。", {"intelligence": 3, "wealth": 3, "happiness": -1}, 10), ("被朋友拉着参与一次小创业,没赚也没亏。", {"wealth": 2, "intelligence": 2}, 6), ], MIDDLE: [ ("在公司晋升为团队负责人。", {"wealth": 12, "intelligence": 2, "happiness": 4}, 10), ("参加同学聚会,感慨岁月不饶人。", {"happiness": 2}, 10), ("体检报告显示血压偏高,开始养生。", {"health": -5, "wealth": -2}, 8), ("投资理财迎来一次小丰收。", {"wealth": 12, "happiness": 3}, 8), ("父母身体抱恙,来回奔波医院和单位。", {"health": -2, "happiness": -6, "wealth": -5}, 6), ("和孩子因为教育问题大吵一架。", {"happiness": -6}, 8), ("工作进入瓶颈期,想辞职又不敢。", {"happiness": -5, "intelligence": 1}, 8), ], ELDER: [ ("退休后开始学书法和太极拳。", {"health": 3, "happiness": 5}, 12), ("和老伴一起回了趟年轻时生活的地方。", {"happiness": 6}, 10), ("生了一场大病,住了半个月医院。", {"health": -12, "wealth": -8, "happiness": -3}, 6), ("孙辈绕膝,家里特别热闹。", {"happiness": 7}, 10), ("老朋友突然去世,感到生命无常。", {"happiness": -5, "health": -2}, 6), ("每天早起逛公园,身体反而越来越硬朗。", {"health": 5, "happiness": 3}, 12), ], }事件抽取使用random.choices,按照权重随机选择,这样高权重事件出现概率更高:
def random_event(stage): """从某个年龄阶段的事件池中按权重抽取一个事件""" pool = EVENT_POOL[stage] weights = [item[2] for item in pool] desc, effects, _ = random.choices(pool, weights=weights, k=1)[0] return desc, effects4.3 关键人生节点
除了随机事件,人生还有几个固定“里程碑”,比如 6 岁入学、18 岁高考、22 岁毕业、28 岁婚姻、30 岁生子、60 岁退休。这些节点用独立函数处理,方便理解。
def apply_key_milestones(person): """处理关键人生节点""" if person.age == 6: person.education = max(person.education, 1) person.add_event("入学", "背上书包走进小学,人生新的开始。") person.intelligence += 3 elif person.age == 12: person.education = max(person.education, 2) person.add_event("升入初中", "开始学习新的科目,世界变大了。") person.intelligence += 3 elif person.age == 15: person.education = max(person.education, 3) person.add_event("中考", "考上了理想的高中。") person.intelligence += 3 elif person.age == 18: score = person.intelligence + random.randint(-10, 15) if score >= 55: person.education = max(person.education, 4) person.career = "大学生" person.add_event("高考", "考上一所不错的大学。") person.intelligence += 5 else: person.education = max(person.education, 3) person.career = "打工人" person.add_event("高考", "成绩一般,提前进入社会打工。") person.wealth += 8 elif person.age == 22: if person.career == "大学生": person.career = random.choice(["程序员", "工程师", "教师", "销售", "公务员"]) person.add_event("毕业", f"大学毕业,成为了一名{person.career}。") person.wealth += 8 elif person.career == "打工人": person.career = "熟练工" person.add_event("转正", "凭踏实肯干成了工厂里的熟练工。") person.wealth += 6 elif person.age == 28: if not person.married: marry_prob = 0.5 + person.happiness / 200 + person.wealth / 200 if random.random() < marry_prob: person.married = True person.add_event("结婚", "遇到了想共度一生的人。") person.happiness += 6 person.wealth -= 15 elif person.age == 30: if person.married and random.random() < 0.6: person.children += 1 person.add_event("生子", "家里多了一个小生命。") person.happiness += 8 person.wealth -= 10 elif person.age == 35: allowed_careers = ["程序员", "工程师", "教师", "销售", "公务员", "熟练工"] if person.career not in allowed_careers: person.career = "创业者" person.add_event("创业", "三十而立,决定去闯一闯。") elif random.random() < 0.4: person.wealth += 10 person.add_event("升职", "事业进入上升期,收入增加。") elif person.age == 60: if person.career not in ["无业", "大学生", "创业者"]: person.career = f"退休{person.career}" person.add_event("退休", "辛苦半生,终于可以歇一歇了。") else: person.career = "退休" person.add_event("退休", "到了退休年龄,开始享受生活。")4.4 结局判定
属性模拟完成后,需要根据综合评分生成结局。
def get_ending(person): """根据综合评分判定人生结局""" score = ( person.health * 0.25 + person.intelligence * 0.20 + person.wealth * 0.30 + person.happiness * 0.25 ) if score >= 80: ending, detail = "人生赢家", "身体健康,头脑清晰,财富自由,家庭幸福,这辈子值了!" elif score >= 65: ending, detail = "安稳人生", "生活细水长流,虽然没大风大浪,但也平安顺遂。" elif score >= 50: ending, detail = "波折人生", "有高光也有低谷,这是普通人的真实写照。" elif score >= 35: ending, detail = "困难剧本", "生活给的压力不小,但你还是坚持走完了一生。" else: ending, detail = "硬核剧本", "这一生实在不容易,建议重新投胎再来一次!" return ending, score, detail4.5 模拟主流程
模拟主流程是整个项目的核心,它要把年龄推进、随机事件、关键节点、死亡判定串联起来:
def simulate(name="无名者", max_age=90, event_prob=0.6, seed=None): """模拟一个人从出生到老去的过程""" if seed is not None: random.seed(seed) person = Person(name) while person.alive and person.age < max_age: # 年龄增长带来的自然衰退 if person.age > 40: person.health -= random.randint(0, 1) if person.age > 60: person.health -= random.randint(0,