软件工程第二次作业
用 Python + AIGC 开发"一箭又一箭"箭头解谜小游戏
| 项目 | 内容 |
|---|---|
| 这个作业属于哪个课程 | https://edu.cnblogs.com/campus/fzu/202601SofwareEngineering |
| 这个作业要求在哪里 | https://edu.cnblogs.com/campus/fzu/202601SofwareEngineering/homework/16717 |
| 这个作业的目标 | 使用 Python 和 AIGC 完成"一箭又一箭"小游戏 |
| 学号 | 102401529 |
| GitHub 仓库 | https://github.com/Aranya12138/Arrow-Game |
一、项目展示
游戏共 8 个关卡(全部为"长箭"玩法),另有无限挑战模式。运行方式:安装依赖后 python main.py,或直接双击仓库 Release 中的 一箭又一箭.exe(PyInstaller 打包,无需 Python 环境)。
| 开始界面 | 游戏界面 | 碰撞反馈 |
|---|---|---|
![]() |
![]() |
![]() |
| 关卡选择 | 进阶关卡(满格) | 通关结算(星级) |
|---|---|---|
![]() |
![]() |
![]() |
二、项目介绍
游戏规则
- 棋盘上散布着若干箭头,每支箭头占据一个或多个格子(长箭),可以在中间拐弯;
- 点击一支箭头:若它头部前方到棋盘边界之间没有其他箭头,它就会沿自身轨道滑出棋盘并被消除;
- 若前方有阻挡,箭头无法飞出并消耗一次失误机会(伴随撞击回弹、晃动、变红和"被阻挡"飘字);
- 清空全部箭头即可过关;失误耗尽则本关失败,可重新开始。
界面设计
- 开始界面:标题、玩法说明面板、开始游戏 / 继续游戏 / 选择关卡;
- 游戏界面:HUD 信息栏(关卡、剩余箭头、用时、失误机会圆点)+ 操作按钮(重新开始、撤销、提示、自动求解);
- 通关 / 失败 / 全部通关结算界面,通关显示星级(★,按剩余失误评定)与用时。
主要功能与特色
- 8 个可通关关卡:第 1~2 关入门(5×5),第 5~8 关为满格关卡(36/36 个格子全部被箭头占据),全部经解算器验证存在通关顺序;
- 长箭玩法:箭头横跨多格、可在中间拐弯(最多双拐角),箭尾与箭头衔接连成一体,拐弯处补画实心圆保证连贯;
- 颜色系统:按"相邻箭头不同色"做图着色(确定性贪心,每关至少 3 种颜色);
- 拓展功能:提示(高亮可飞出箭头)、AI 自动求解(逐步演示通关)、星级评价 + 计时、关卡选择、撤销上一步(快照恢复)、保存游戏进度(本地 JSON 存档,可"继续游戏")、无限挑战(随机生成必然可通关的关卡)、合成音效、PyInstaller 打包为 exe;
- 零外部素材:全部界面用代码绘制,音效用标准库合成,中文字体使用系统微软雅黑——不涉及任何版权素材。
三、实现思路
1. 箭头、方向与关卡的表示
- 方向用字符
→ ← ↑ ↓表示,映射到行/列增量:DIRECTIONS = {"→": (0, 1), "←": (0, -1), "↑": (-1, 0), "↓": (1, 0)}; - 单格箭头(基础规则,保留在代码中并由单元测试覆盖)就是棋盘格子里的一个方向字符;
- 长箭用一串相邻格子表示(
paths),头部方向 = 最后一段的行进方向。例如[(0,0),(0,1),(0,2),(1,2)]表示一支先向右、再向下的拐角长箭,头部在 (1,2) 指向下; - 关卡数据集中在
levels.py:grid决定棋盘尺寸,paths列出全部长箭。游戏核心逻辑(logic.py)完全不依赖 pygame,方便单元测试。
2. 路径检测方法(核心)
规则只有一句话:从箭头头部出发沿其方向走到边界,途中遇到任意其他箭头(或其身体)即被阻挡。单格箭头的检测:
def is_path_clear(grid, row, col):
arrow = grid[row][col]
if arrow == ".":
return None
dr, dc = DIRECTIONS[arrow]
r, c = row + dr, col + dc # 从箭头前方一格开始, 不含自身
while 0 <= r < rows and 0 <= c < cols: # 走到棋盘边界为止
if grid[r][c] != ".": # 途中遇到箭头 => 被阻挡
return False
r += dr
c += dc
return True # 无阻挡 => 可飞出
长箭只检测头部前方(身体在头部后面,不可能挡自己):
def path_blocked(self, idx):
p = self.paths[idx]
head = p["cells"][-1]
dr, dc = DIRECTIONS[p["dir"]]
r, c = head[0] + dr, head[1] + dc
while 0 <= r < rows and 0 <= c < cols:
if self.occupied(r, c): # 任意箭头的身体都算阻挡
return True
r += dr
c += dc
return False
边界处理:越界(走出棋盘)直接结束循环,因此贴边且朝外的箭头必然畅通,不会有数组越界问题。
3. 关卡可解性验证(贪心解算器)
设计关卡时最容易犯的错误是死锁(如同一列两支箭头互相指向对方,永无解)。我实现了一个贪心解算器自动验证:反复把"当前所有可飞出"的箭头全部移除,若最终棋盘清空则说明有解。正确性在于:移除一支可飞出的箭头只会让其他箭头更容易飞出、绝不会产生新的阻挡,所以只要存在解,贪心一定能找到。所有关卡(含随机生成的)都必须通过这个验证才能发布。
4. 随机关卡生成(无限挑战)
采用逆向出题法:按"移除顺序的逆序"放置箭头——每支新箭的头部前方到边界之间不得经过已放置箭头的身体,且不得穿过自身。这样按放置的逆序移除箭头时,每一支被移除的箭头都满足"前方无阻挡",生成的每一关都必然可通关。用随机游走式生长(长蛇在空格中行走、随机拐弯)保证高填充率,实际可达满格 36/36。
四、AIGC 使用过程
本次开发全程借助 Claude Code(Anthropic 的 AI 编程助手,在 VS Code 中以对话方式辅助开发)。以下是 5 次最有代表性的协作过程(完整记录见仓库 AIGC使用记录.md):
| 子任务 | 借助何种 AIGC 技术 | AI 实现或提供了什么 | 效果如何 | 人工修改 |
|---|---|---|---|---|
| 环境搭建与依赖排查 | Claude Code | 排查出 python 命令是 Microsoft Store 空壳别名(改用 py 启动器); 检测到 git 未安装并引导安装; 排查出官方 pygame 无 Python 3.14 预编译包且源码编译失败, 改用完全兼容的 pygame-ce |
一次排查成功, 游戏库正常安装运行 | 提供 git 提交身份; 确认 pygame-ce 兼容性 |
| 路径检测 | Claude Code | 生成上/下/左/右四个方向的路径检测代码(is_path_clear / GameLogic.click) | 四个方向的单元测试一次通过, 无越界错误 | 核对"不含箭头自身"这一边界条件后确认实现正确 |
| 关卡设计 | Claude Code | 设计 8 个关卡并编写贪心解算器自动验证每一关都存在通关顺序 | 解算器先后抓出 4 处死锁: 同列箭头互指 ×2、同排 L 形对向互锁 ×2(首版第 5 关 ↓↑ 互指死锁等); 逐处修正后全部通过验证 | 死锁全部由解算器发现, 人工按"外侧箭头解锁内侧箭头"调整方向 |
| 测试用例编写 | Claude Code | 生成 T01~T06 测试用例及关卡数据校验 | 首轮运行 2 个用例失败: T01 把第 1 关箭头数记成 5(实际 4 个); T06 的点击顺序导致 (0,0) 在被点前已变为可飞出 | 人工核对关卡布局后修正断言与点击顺序 |
| 存档恢复崩溃修复 | Claude Code | 修复"继续游戏"后崩溃: 存档 JSON 序列化把格子元组变成列表, 恢复后作为字典键时不可哈希(TypeError: unhashable type: 'list'), 且点击占据判定失效; 修复为路径格子在构建/恢复时统一规范化为元组 | 新增 JSON 往返回归测试与端到端回归(恢复后点被挡长箭并绘制不崩溃), 27 个测试全部通过 | 崩溃由本人真机试玩发现并反馈; 根因定位与修复由 AI 完成 |
其他 AI 协作还包括:碰撞动画(撞击回弹+晃动+变红+飘字)、图着色配色(相邻不同色)、随机关卡生成器(含一处"游走绕回自锁"Bug 的修复)、音效合成、PyInstaller 打包等,共 20 余条记录。
五、测试结果
采用自动化测试 + 手工试玩双轨。自动化测试共 27 个单元用例 + 端到端试玩脚本(py -m unittest discover -s tests -v 与 py tools/smoke_test.py),全部通过。
| 编号 | 测试内容 | 预期结果 | 实际结果 | 是否通过 |
|---|---|---|---|---|
| T01 | 点击前方无阻挡的箭头 | 箭头飞出棋盘并消失 | 箭头消失, 失误次数不变 | 通过 |
| T02 | 点击前方有阻挡的箭头 | 箭头不消失, 失误次数减 1 | 箭头保留, 失误 3→2 | 通过 |
| T03 | 点击位于边缘且朝向棋盘外的箭头 | 箭头正常消失, 不发生越界错误 | 正常消失, 无越界 | 通过 |
| T04 | 消除本关全部箭头 | 显示通关并进入下一关 | 状态变为通关, 结算后进入下一关 | 通过 |
| T05 | 失误次数耗尽 | 显示失败并允许重新开始 | 连点被挡箭头 3 次进入失败结算, 重开后恢复 | 通过 |
| T06 | 游戏进行中重新开始 | 箭头布局和失误次数恢复 | 布局/失误/状态全部恢复初始 | 通过 |
补充测试(全部通过):
- 四个方向的路径检测各覆盖"自由"与"阻挡"两种情况(单格箭头基础规则);
- 8 个关卡可解性(贪心解算)、每关至少有一个初始被阻挡的箭头、棋盘数据合法性;
- 长箭合法性(相邻、不重复、不越界、不与单格箭头重叠);
- 随机生成 60 个关卡(不同种子)全部可通关且数据合法;
- 撤销快照恢复、求解指引、存档读写容错(缺失/损坏不崩溃);
- 端到端:自动试玩 8 关(点击 4/6/7/9/8/10/10/11 次通关)、界面层通关/失败流程、自动求解一键通关、无限挑战通关、存档持久化与继续游戏、全部画面渲染自检。
手工试玩记录见仓库 测试记录.md(各关试玩表格)。
六、PSP 表格
| 任务 | 预估耗时(小时) | 实际耗时(小时) | 差异(小时) |
|---|---|---|---|
| 需求分析与游戏设计 | 0.5 | 0.5 | 0 |
| Python 与图形库学习 | 0.5 | 0.5 | 0 |
| 游戏界面实现 | 1 | 2 | +1 |
| 路径与碰撞逻辑实现 | 2 | 2 | 0 |
| 关卡设计 | 1 | 1 | 0 |
| AIGC 辅助开发 | 2 | 3 | +1 |
| 测试与修改 | 2 | 2 | 0 |
| README 与博客撰写 | 2 | 2 | 0 |
| 合计 | 11 | 13 | +2 |
七、心得体会
AI 带来的帮助
Claude Code 在这门作业里几乎承担了一个"结对编程搭档"的角色:从环境排障(pygame 在 Python 3.14 下装不上、git 未安装)到路径检测、动画、关卡生成、测试、打包,都给了我很大帮助。最大的感受是速度:每次提出需求("箭头加弯""方向多样化""满格关卡"),几分钟内就能看到可运行的版本,试玩反馈 → 修改 → 再试玩的循环非常快,让我能把精力放在"游戏应该长什么样"而不是"代码怎么写"上。
AI 出的问题
AI 生成的代码绝不是"拿来即用"。开发过程中解算器和测试抓到了大量真实 Bug,都记在 AIGC 使用记录里:
- 关卡死锁:AI 设计的关卡多次出现"同列两支箭头互相指向对方""同排 L 形对向互锁",这类错误人眼很难一次看全,是贪心解算器救的场;
- 测试期望值错误:AI 自己写的测试用例把第 1 关箭头数记错、点击顺序写反,首轮 2 个用例失败,需要人工核对关卡布局来修正——AI 写的测试也会骗人,必须自己核对;
- 存档恢复崩溃:JSON 序列化把元组变列表,恢复后字典键不可哈希直接崩溃,是我真机试玩"继续游戏"时撞出来的;
- 随机生成器自锁 Bug:随机游走可能绕回并指向自己走过的格子,导致无限挑战可能抽到无解局——解算器发现后修复。
自己的收获
- 明白了AI 出活、人来把关的分工:AI 擅长把想法快速变成代码,但正确性必须靠测试体系(解算器 + 单元测试 + 端到端试玩)来兜底;
- 体会到了版本管理的价值:实际开发中每个功能一个 commit、每个 Bug 修复一个 fix commit,回头看 commit 就像一本开发日记。
项目仓库:https://github.com/Aranya12138/Arrow-Game · 完整测试记录见 测试记录.md · 完整 AIGC 协作记录见 AIGC使用记录.md







浙公网安备 33010602011771号