前几天有个朋友问我,Python基础语法学完了,但不知道拿它做什么。每天跟着教程敲例子,敲完就忘,遇到综合问题还是没思路。我给的建议一直没变过:做个文字冒险游戏。不是因为它简单,而是因为它能把变量、分支、循环、函数、文件读写这些零散知识点全部串起来,逼着你把它们真正用一遍。这个项目做完,你会发现自己突然能独立写点像样的东西了。
文字冒险游戏,简单说就是玩家通过输入命令和程序交互,程序根据当前场景和玩家的选择推进剧情。它不需要图形界面,不需要第三方框架,一个Python文件就能跑起来。对初学者来说,它是性价比最高的练手项目;对有点基础但没做过完整项目的人,它是第一次体验“从零到成品”的完整流程;哪怕你已经会用Python写爬虫或做数据分析,回头做一个小型文字游戏,也能让你重新审视程序结构设计这件事。
这篇文章从环境搭建开始,到核心游戏逻辑,到扩展玩法,再到打包成exe发给朋友玩,最后把我踩过的坑一并列出。全程用我实际测试过的代码和步骤说话,你可以照着一步步做,也可以直接跳到需要的部分。
1. 为什么是文字冒险游戏:一个看似简单却能把基础焊死的项目
1.1 文字冒险游戏到底是个什么东西
文字冒险游戏的核心并不是“文字”,而是“状态机”。你维护一个当前状态,比如玩家在哪个房间、背包里有什么、某个机关有没有被触发;然后不断读取玩家输入的命令,根据当前状态决定输出什么文本、把状态更新成什么样。这个流程和很多看起来高大上的系统,本质上是同一个模式。
我见过不少新手去啃那种几千行的爬虫框架源码,看完了还是一头雾水。但文字冒险游戏不一样,它的逻辑链条很短,短到你可以随时在脑中跑完一遍:
- 程序输出当前场景描述
- 玩家输入命令,比如 go north、take key、help
- 程序解析命令,检查是否合法
- 合法就更新状态并输出新场景,不合法就提示错误
这个循环从头到尾都发生在你自己写的代码里,没有任何黑盒。你能直观感受到每个变量、每个函数在干吗。这种“全程可掌控”的感觉,对建立编程信心太重要了。
1.2 和计算器、爬虫这类练手项目比,它的优势在哪
很多入门教程让人做计算器,做学生信息管理系统,做爬虫。这些项目没有错,但体验和效果差别很大。我整理了一个对比:
| 项目类型 | 主要覆盖的知识点 | 综合难度 | 做完之后的感受 | 可扩展性 |
|---|---|---|---|---|
| 计算器 | 函数、条件、基本运算 | 很低 | 会了,但感觉没什么用 | 弱,做完就结束了 |
| 学生信息管理系统 | 列表、字典、文件读写 | 中等 | 功能重复,界面枯燥 | 一般,往上加模块容易乱 |
| 爬虫 | 网络请求、HTML解析、异常处理 | 高 | 被反爬折磨,知识断层严重 | 强,但新手很难独立调通 |
| 文字冒险游戏 | 变量、类型、字符串、分支、循环、函数、文件、数据结构 | 可控 | 能玩、能展示、能迭代 | 极强,从命令行到GUI都是一路延伸 |
注意最后一行:文字冒险游戏的难度完全由你自己控制。第一版可以只有三两个房间,后面逐步加战斗、加背包、加存档、加随机事件。而爬虫一上来就要面对网络问题、编码问题、反爬问题,任何一个坑都能让新手劝退。
1.3 这个项目到底能练到什么,学完能往哪走
每次有朋友问我学Python学什么,我都会说,先做个文字冒险游戏。因为它几乎覆盖了Python基础语法里的所有关键点:
- 字符串处理:解析玩家输入的指令
- 条件分支:判断当前状态、指令是否合法
- 循环:主游戏循环
- 列表、字典、元组:管理房间地图、背包物品
- 函数:把移动、拾取、查看等操作封装成功能块
- 文件读写:存档、读档,让进度能够持久化
这些点单独拿出来都很简单,但组合在一起,就考验你对程序整体结构的理解。而后面无论你想去做图形界面的游戏、还是做数据分析、写自动化脚本,这些能力都是地基。等你把文字冒险游戏做熟,再回头看那些“用Python做数据分析可视化”的教程,会发现它们的底层逻辑并没有那么神秘。
2. 环境准备:先把Python装明白,后面能省一半的折腾
2.1 安装Python时的关键选择
环境准备这一步,看着简单,实际上很多人都卡在了一道隐形门槛上。对于新学Python的人来说,我建议直接去Python官网下载安装包,不要用那些第三方魔改版,也不要用Windows商店里那个版本。官网的安装包干净、稳定、好排查问题。
选择版本时,选当前最新的稳定版就行。在Windows上安装的时候,有一个勾选项非常关键:Add Python to PATH。很多人忽略这个勾选,结果装完打开命令行一敲python,直接提示“不是内部或外部命令”。如果你已经装完了并且没勾选,也别急着卸载重装,后面第2.2节我会说怎么补救。
安装完成后,打开命令行(Windows按Win+R,输入cmd后回车),敲一下:
python --version能正常打印类似Python 3.12.1这样的版本号,就说明装好了。
2.2 环境变量:装了Python但命令行打不开的排查流程
命令行里敲python没反应,是新手遇到最多的问题。原理其实不复杂:命令行会在PATH环境变量列表里依次查找“python”这个命令,找到就执行,找不到就报错。
排查步骤:
- 确认Python是否真的装了。在开始菜单里找Python文件夹,或者看安装目录,比如
C:\Users\你的用户名\AppData\Local\Programs\Python\Python312\。 - 如果在开始菜单能找到但命令行打不开,就是PATH配置问题。按Win键搜索“环境变量”,点“编辑系统环境变量”,在“高级”选项卡里点“环境变量”,在“系统变量”或“用户变量”中找到
Path,点编辑,把Python的安装目录和Scripts子目录加进去。注意要用;分隔。 - 改完环境变量后,必须新开一个命令行窗口才生效。老窗口不会自动刷新。
还有一个常见情况:Windows下敲python会弹出微软商店。这是因为系统开启了“应用执行别名”。解决办法是在“设置 -> 应用 -> 高级应用设置 -> 应用执行别名”里,把python.exe和python3.exe的开关关掉。
2.3 编辑器:VSCode配置Python环境
代码编辑器的话,我不建议一开始就用那些厚重的IDE,VSCode轻量、免费、插件生态好,作为Python入门编辑器非常合适。
配置起来也不复杂:
- 在VSCode的扩展商店搜索“Python”,安装微软官方那个Python扩展
- 用VSCode打开你的项目文件夹
- 按
Ctrl+Shift+P,输入Python: Select Interpreter,选中你刚安装的Python解释器 - 写一个简单的
.py文件,点右上角的三角形运行按钮,看是否能正常输出
如果你安装了多个Python版本,VSCode允许你在不同项目里选择不同解释器,这是个很实用的功能,后面做不同练手项目时不会互相干扰。
2.4 虚拟环境:项目隔离是必须养成的习惯
我先说结论:从你的第一个正式项目开始,就使用虚拟环境。虚拟环境相当于给你这个项目一个独立的Python包空间,你在这个项目里装的第三方库不会污染全局环境。
文字冒险游戏本身用标准库就能写,不装任何第三方库也能跑,但既然要养成好习惯,最好先把venv顺手用上。
在项目目录下打开命令行,执行:
python -m venv venvWindows下激活:
venv\Scripts\activatemacOS/Linux下激活:
source venv/bin/activate激活后,命令行前面会出现(venv)字样,说明你已经在虚拟环境里了。之后用pip install装的包都会被放在这个虚拟环境内部,不会弄乱系统Python。
3. 核心代码:用一个可运行的完整示例讲透文字冒险游戏的骨架
下面这部分,我会带着你从零构建一个可运行的文字冒险游戏。核心代码没有用什么高深技巧,全部是基础语法,但胜在结构清晰,能直接跑。
3.1 先构思世界:画地图、定物品、想命令
动手写代码之前,先在纸上画出游戏地图。我做的示例是一个“迷雾森林”,三个房间:
- 森林入口:是起始点,有一根火把
- 迷雾小径:向北走,连接营地
- 废弃营地:有一个宝箱,需要铁钥匙才能打开
地图上的每个房间需要记录三样信息:描述文字、可通往的方向、房间里有哪些物品。用Python的字典来建模非常自然。
rooms = { "森林入口": { "description": "你站在森林入口。北边有一条被雾气笼罩的小路。地上扔着一根火把。", "exits": {"北": "迷雾小径"}, "items": ["火把"] }, "迷雾小径": { "description": "浓雾将视线压到了几米以内。东边隐约透出一点灯光。", "exits": {"南": "森林入口", "东": "废弃营地"}, "items": [] }, "废弃营地": { "description": "营地里散落着旧工具,一顶破帐篷里放着一个上了锁的宝箱。", "exits": {"西": "迷雾小径"}, "items": ["铁钥匙"] } }不要小看这一步。用数据结构先把世界描述清楚,后面的游戏逻辑会顺畅很多。
3.2 主循环与命令分发:让玩家真正“玩起来”
游戏的主循环本质上就是一个while循环。每轮循环做四件事:显示当前房间描述,读取玩家输入,解析指令,执行对应的操作。
看这段完整代码,然后我们逐段拆解:
current_room = "森林入口" inventory = [] def show_room(room_name): room = rooms[room_name] print("\n" + room["description"]) if room["items"]: print("你看到地上有:" + "、".join(room["items"])) def move_player(direction): global current_room exits = rooms[current_room]["exits"] if direction in exits: current_room = exits[direction] print(f"你向{direction}走去。") return True print("那边没有路。") return False def take_item(item): room = rooms[current_room] if item in room["items"]: room["items"].remove(item) inventory.append(item) print(f"你捡起了{item}。") else: print("这里没有这个东西。") def show_help(): print("可用命令:") print("go 方向 —— 向指定方向移动,比如 go 北") print("take 物品 —— 捡起地上的物品") print("inventory —— 查看背包") print("help —— 显示帮助") print("quit —— 退出游戏") print("========== 迷雾森林 ==========") print("输入 help 查看帮助。") while True: show_room(current_room) command = input("> ").strip().lower() if command in ("quit", "exit", "q"): print("再见,冒险者。") break elif command in ("help", "h"): show_help() elif command == "inventory": if inventory: print("你的背包里有:" + "、".join(inventory)) else: print("你的背包是空的。") elif command.startswith("go "): move_player(command[3:]) elif command.startswith("take "): take_item(command[5:]) else: print("我听不懂你在说什么。输入 help 查看帮助。")这段代码的核心逻辑是命令分发。我用startswith来识别“go 北”和“take 火把”这类带参数的指令。这里有几个实现细节值得你注意:
command.strip().lower():把输入的多余空格去掉,统一转成小写,这样玩家输入“GO 北”“ Go 北”都能被正确识别。command[3:]:取出go之后的部分,也就是方向。take_item函数里操作列表的方式:先判断物品是否在当前房间,然后把物品从房间的items列表里移除,再添加到背包。这就是一个非常典型的“状态转移”操作。
把这段代码保存为adventure.py,在命令行运行python adventure.py,就能玩了。试着在森林入口输入go 北,走进迷雾小径,再go 东进入废弃营地,捡到铁钥匙,再走回去。已经是一个完整可玩的游戏了。
3.3 让命令解析更健壮:接受同义词和模糊输入
实际写游戏时你会发现,玩家不会和你约定俗成地输入标准命令。有人会输“拿钥匙”,有人会输“捡起钥匙”,还有人只输一个“key”。想让游戏更友好,就得让命令解析更宽容。
一个简单有效的方法:建立同义词表,把用户任意一个说法映射到标准动作。
synonyms = { "拿": "take", "捡": "take", "捡起": "take", "获取": "take", "查看": "look", "帮助": "help", "背包": "inventory", "北": "go", "南": "go", }解析时先把输入拆成单词,再逐词查同义词表,最后合成标准指令。这个技巧能大大提升游戏体验,也让你体验到“规则引擎”的雏形。
对于更复杂的输入,比如“把火把捡起来”,还可以用in关键词来提取物品名:
def extract_item(text): for item_name in ["火把", "铁钥匙", "地图", "药水"]: if item_name in text: return item_name return None这段逻辑就是关键词匹配,虽然简单,却已经是很多聊天机器人、指令系统的起步原型。
3.4 存档与读档:用文件IO让进度能保存下来
玩家玩到一半退出了,下次得从头开始,这就很难受。加一个存档功能,这里正好用到Python的文件读写和JSON序列化。
import json import os SAVE_FILE = "savegame.json" def save_game(): data = { "current_room": current_room, "inventory": inventory, "rooms": rooms } with open(SAVE_FILE, "w", encoding="utf-8") as f: json.dump(data, f, ensure_ascii=False, indent=2) print("游戏已保存。") def load_game(): global current_room, inventory if not os.path.exists(SAVE_FILE): print("没有找到存档文件。") return with open(SAVE_FILE, "r", encoding="utf-8") as f: data = json.load(f) current_room = data["current_room"] inventory = data["inventory"] print("存档已加载。")然后在主循环里加上save和load两个命令对应分支。这里有个容易犯的错:global current_room, inventory。如果一个函数只是“读”全局变量,不需要声明global;但如果函数内部要“重新赋值”或者调用方法修改全局变量指向的对象,就要用global告诉Python你希望操作的是全局变量,而不是在函数内部新建一个同名局部变量。在load_game里,我们给current_room和inventory重新赋值,所以必须加这个声明。我第6.4节会详细讲这个坑。
用JSON的好处是结构清晰、可读性好。不需要引入数据库,不需要复杂的文件格式,对于文字冒险游戏这个体量正合适。
4. 让游戏“活”起来:难度与可玩性的进阶玩法
基础版本的骨架搭好后,游戏已经能玩了。但如果只是走来走去、拿东西,玩家很快就会腻。这时候可以逐步往上加玩法。
4.1 谜题与机关:给游戏增加“要动脑子”的部分
最简单的一种机关,是设置一个开关状态。比如宝箱需要一个触发条件才能打开:
chest_opened = False def use_item(item): global chest_opened if current_room == "废弃营地" and item == "铁钥匙": print("你用铁钥匙打开了宝箱,里面是一枚发光的宝石。") inventory.append("发光宝石") chest_opened = True else: print("现在用不了这个物品。")秘诀就在于:用一个布尔变量chest_opened记录这个机关是否被触发。玩家尝试打开宝箱时,程序检查这个布尔变量,如果为False,提示“宝箱锁着”;如果为True,进入后续剧情。整个游戏的“剧情分支”实际上就是大量布尔变量和字符串变量组合出来的状态空间。
稍微复杂一点的机关,比如密码锁,可以要求玩家先收集多个线索,再在特定房间输入数字。逻辑依然是条件判断,只是嵌套的层次多一点。
4.2 简易战斗系统:数值设计不用复杂,够用就行
文字冒险游戏里的战斗不需要实时动作机制,一个回合制就够。核心逻辑是双方轮流造成伤害,随机数决定每次攻击的浮动数值。
import random def fight(): player_hp = 20 enemy_hp = 12 enemy_name = "森林狼" print(f"你遇到了{enemy_name}!") while player_hp > 0 and enemy_hp > 0: print(f"\n你的生命:{player_hp},{enemy_name}的生命:{enemy_hp}") action = input("攻击 / 防御 > ").strip().lower() if action == "攻击": damage = random.randint(2, 5) enemy_hp -= damage print(f"你挥剑攻击,造成 {damage} 点伤害。") elif action == "防御": print("你摆出防御姿态,准备承受攻击。") else: print("你还在犹豫,错过了回合。") if enemy_hp <= 0: print(f"你击败了{enemy_name}!") return True if action == "防御": enemy_damage = random.randint(0, 2) else: enemy_damage = random.randint(1, 3) player_hp -= enemy_damage print(f"{enemy_name}反击,对你造成 {enemy_damage} 点伤害。") print("你被击败了……") return False这个战斗系统的数值设计很直白:玩家20点血,每次攻击2-5伤害;敌人12点血,每次攻击1-3伤害。防御时敌人的伤害降到0-2。简单推算一下可知玩家胜率很高,这种“略微偏向玩家”的数值设计对新手游戏非常重要,让玩家有紧张感但不会一直被虐。
4.3 背包和道具组合:让“收集”变得有意义
光捡东西不利用,背包就只是个装饰。给道具增加组合逻辑,让“钥匙开宝箱”“火药炸掉石门”“把药草和泉水混合成药水”这类玩法加入游戏。
以药水合成为例:玩家在背包里有“药草”和“泉水”,在指定房间执行组合操作,生成“治疗药水”。
def combine_items(item1, item2): if item1 in inventory and item2 in inventory: if {item1, item2} == {"药草", "泉水"}: inventory.remove("药草") inventory.remove("泉水") inventory.append("治疗药水") print("你把药草和泉水混合在一起,得到一瓶治疗药水。") return True print("这两个东西组合不了。") return False这类玩法的核心价值,是让玩家感受到“收集到的每个东西都不是没用的”。而你在实现上要做的,就是一张“组合配方表”加上条件判断。后面如果想加复杂的配方系统,可以升级成字典,比如recipes[("药草", "泉水")] = "治疗药水",这样新配方不用改函数逻辑,只改数据就行。
4.4 从字典到类:什么时候应该重构代码
当游戏越来越复杂,比如有敌人、任务、多个NPC、随机事件,如果你还是用一堆字典和全局变量,代码会变得很难维护。这时候就该用类来重构。
一个最简单的类化思路,是从“房间”开始:
class Room: def __init__(self, name, description, exits=None, items=None): self.name = name self.description = description self.exits = exits if exits is not None else {} self.items = items if items is not None else [] def describe(self): text = self.description if self.items: text += " 地上有:" + "、".join(self.items) return text再用一个Player类管理玩家的位置和背包:
class Player: def __init__(self, current_room): self.current_room = current_room self.inventory = [] self.hp = 20类的好处是:把数据和对数据的操作捆绑在一起,不再依赖大量全局变量。代码变成“房间对象自己知道自己的出口”,玩家对象自己管理自己的物品。对于刚学完类的学习者来说,这个重构练习比课本上的“定义一个动物类”来得有意义得多。
5. 分享出去:把游戏打包成exe的完整流程
游戏写完,想发给朋友试玩,但朋友电脑上大概率没装Python。这时候需要把Python脚本打包成可执行文件。
5.1 为什么选PyInstaller
Python生态里有好几个打包工具,PyInstaller是其中兼容性最好、使用门槛最低的一个。它支持Windows、macOS、Linux,也支持把程序打包成单文件。对你的文字冒险游戏来说,一个单文件exe可以直接发给朋友,双击就能跑,体验好很多。
5.2 打包实操:从安装到出程序的每一步
在已经激活的虚拟环境下执行:
pip install pyinstaller安装完成后,在项目目录下执行:
pyinstaller --onefile --name AdventureGame adventure.py命令参数说明:
--onefile:把所有依赖都打成一个文件。代价是启动时会有几秒解压时间,但发给别人最方便。--name:生成的文件名,随意。--console:保留命令行窗口。文字冒险游戏依赖命令行输入输出,所以这个选项要保留,不能加--noconsole。
执行完后,dist目录下会出现一个AdventureGame.exe(Windows下)。把这个exe发给任何人,双击就能玩。
如果后续代码改了,重新打包即可。注意:PyInstaller原生只支持打包当前平台的可执行文件,你在Windows上只能生成Windows的exe,在Mac上只能生成Mac程序,跨平台打包需要借助别的方案,比如在对应系统上分别打包,或者用CI流程。
5.3 打包后的常见问题:闪退、资源文件缺失、误报毒
我第一次打包完,双击exe直接就闪退了,窗口一闪而过,什么都没看到。排查方法其实很简单:打开一个命令行窗口,手动输入exe的路径运行,这样报错信息会保留在命令行里,就能看到具体的异常栈。
常见的问题有几种:
- 代码里用了
open("savegame.json")这种相对路径,打包后运行目录变成了系统临时目录,导致找不到文件。解决办法是改用os.path.dirname(sys.executable)或sys._MEIPASS计算资源文件路径。 - 游戏里放了配图、音乐等外部文件,打包时需要用
--add-data把文件一起打进去。资源路径要用前面提到的resource_path函数动态拼接。 - 部分杀毒软件会误报PyInstaller打包出来的exe,这个属于打包工具的已知特性。可以在发布时告诉朋友添加白名单,或者考虑签名。
6. 我踩过的坑:环境与编码问题排查实录
最后这部分,我把自己实际踩过的坑集中整理一下。这些坑没有一个是教科书会教的,但几乎每个Python新手都会遇到。
6.1 最冤的坑:Windows下pip和python找不到,或python弹出商店
有一次我在一台新电脑上装好Python,确认安装目录里有python.exe,但命令行里敲python却弹出了Windows商店。排查后发现问题出在“应用执行别名”。
Windows把python.exe和python3.exe注册成了商店应用的启动别名。当你安装Python时没有重新刷PATH,或者PATH里Python路径排在商店别名后面,系统就会优先启动商店那个“假python”。解决办法:
- 打开“设置 -> 应用 -> 高级应用设置 -> 应用执行别名”
- 找到“python.exe”和“python3.exe”,把开关都关掉
- 新开一个命令行窗口,再试
python --version
另外,推荐以后统一用python -m pip而不是直接敲pip。因为这样能确保pip和当前的python解释器版本匹配,避免“pip装的库,import却说找不到”的诡异情况。
6.2 中文乱码与UnicodeDecodeError
文字冒险游戏大量使用中文,如果代码文件没有用UTF-8编码保存,同时在Windows命令行运行,很容易出现乱码或者报错。
我在写存档功能时,就遇到过读取存档文件时报UnicodeDecodeError: 'gbk' codec can't decode byte。原因很简单:Windows下open()函数默认使用系统编码(GBK)打开文件,而我保存存档时用的是UTF-8,两边不一致,所以读取失败。
解决方法是始终在open()里显式指定编码:
with open(SAVE_FILE, "w", encoding="utf-8") as f: json.dump(data, f, ensure_ascii=False, indent=2) with open(SAVE_FILE, "r", encoding="utf-8") as f: data = json.load(f)同时,Python默认会把源文件解析为UTF-8,所以代码文件本身用UTF-8编码保存就行。但如果你在Windows命令行直接运行一个.py文件,且命令行代码页不是UTF-8,print的中文也可能乱码。最简单的办法是在代码开头加一行:
# -*- coding: utf-8 -*-虽然Python3默认UTF-8源码解析,但这行注释在遇到某些老编码环境时仍是保险措施。但这并不能解决命令行代码页的问题,最稳的是给exe打包后让玩家在正常的窗口里运行,或者在代码里适配。
6.3 打包后资源文件找不到:sys._MEIPASS的经典用法
用PyInstaller的--onefile模式打包后,程序运行时会先把自身解压到一个临时目录,运行完又删除。如果你在代码里用相对路径读取“地图数据.txt”或者“剧情.json”,运行时大概率找不到。
解决办法是写一个通用的资源路径函数:
import sys import os def resource_path(relative_path): base_path = getattr(sys, "_MEIPASS", os.path.abspath(".")) return os.path.join(base_path, relative_path)开发阶段,sys里没有_MEIPASS属性,程序使用当前目录;打包后,sys._MEIPASS指向临时解压目录,资源文件被放在那里。用的时候把open("map.json")改成open(resource_path("map.json"))即可。
6.4 全局变量的“记忆丢失”:作用域理解误区
很多新手在写游戏时,会遇到一个诡异现象:函数里明明修改了变量,但退出函数后值又变回去了。比如:
inventory = [] def pickup(): inventory = ["火把"] print(inventory) pickup() print(inventory) # 输出 [],不是 ["火把"]原因是函数内部inventory = ["火把"]创建了一个新的局部变量,和全局的inventory不是一个东西。要修改全局变量,必须在函数内声明global inventory。但注意,inventory.append("火把")则不需要global声明,因为append修改的是变量指向的列表对象本身,而不是给变量重新赋值。
这个坑相当经典,我在做存档加载功能时踩得很彻底。排查思路也简单:如果我预期的状态没有生效,优先检查相关变量是否被局部同名变量遮蔽。
最后再分享一点个人体会
文字冒险游戏这个项目,最打动我的地方是它的“完成感”。一个游戏从第一行代码到能玩、能存档、能打包发给朋友,这个过程不会太长,但完整得让你能真切感受到“我真的做出了一个软件”。
我个人的经验是:第一版不要贪多,三五个房间、一两个物品、能走通一条完整的剧情线就好。做完这些基础功能,再慢慢加战斗、加谜题、加存档。每一次扩展,都是对代码结构的一次压力测试。你会发现有些东西一开始设计得好,后面扩展就很轻松;有些东西一开始图省事,后面加功能就要返工。这种教训,比看多少架构文章都来得深刻。
等你去体验用类和对象重构字典版本的游戏时,才能真正体会到面向对象设计的价值所在。项目本身虽小,但从简单脚本到清晰工程的完整路径,它全都包含在里面了。