2026 秋软件工程个人作业(第二次)
「一箭又一箭」小游戏 —— 软件工程课程第二次个人作业
| 项目 | 内容 |
|---|---|
| 这个作业属于哪个课程 | H202601 软件工程与软件工程实践 |
| 这个作业要求在哪里 | 2026 秋软件工程个人作业(第二次) |
| 这个作业的目标 | 使用 Python 和 AIGC 完成 "一箭又一箭" 小游戏 |
| 学号 | 102401126 |
| GitHub 仓库 | https://github.com/diahull/arrow-game |
一、项目展示
演示 GIF(开始 → 选关 → 游戏 → 飞出 → 碰撞 → 通关):

开始界面(两步选关:先选难度,再选关卡):


游戏过程(第 1 关,顶栏显示关卡 / 剩余箭头 / 失误次数 / 倒计时 / 难度):

箭头飞出动画(沿方向滑出并淡出):

碰撞反馈(箭头晃动、格子变红、提示 "被阻挡!",失误次数 -1):

通关界面与失败界面:


二、项目介绍
游戏规则
-
棋盘由网格组成,每个格子中有一支箭头,方向为上、下、左、右四种;
-
点击一支箭头后,程序检测它前进方向上直到棋盘边界之间是否还有其他箭头:
-
前方无阻挡 → 箭头飞出棋盘并消失;
-
前方有阻挡 → 箭头晃动、变红并提示 "被阻挡",同时消耗 1 次失误机会;
-
-
清空本关全部箭头即可通关并进入下一关;
-
失误次数耗尽或超时未清完则本关失败,可重新开始;
-
每关有时间限制,顶栏实时倒计时,通关时显示本关用时;难度越高时间越短。
界面设计
游戏包含三个阶段的界面:
-
开始界面(两步选关):第一步选择难度(简单 / 普通 / 中等 / 困难),第二步进入该难度下选择第 1~4 关,左下角 "返回难度" 可重选;
-
游戏界面:顶部信息栏(当前关卡、剩余箭头数量、剩余失误次数、剩余倒计时、当前难度)+ 棋盘 + 提示 / 重新开始 / 返回菜单按钮;
-
结果界面:通关界面(显示本关用时,"下一关" 按钮)、失败界面(区分 "时间到" 与 "失误用尽",可重新开始)、全部通关后有 "恭喜通关全部关卡" 界面。
主要功能与特色
-
四档难度 + 两步选关:简单 / 普通 / 中等 / 困难,难度越高棋盘越大、箭头越多、失误越少、时间越短;
-
关卡递进:同一难度下越往后越难(每过一关棋盘列数 +1、箭头 +1、时间 -4 秒);
-
程序化关卡生成:所有关卡由生成器按 "反向构造法" 随机生成,保证每关都可通关,且开局至少存在一支被阻挡的箭头;
-
限时挑战:每关倒计时,超时判负,通关时显示本关用时;
-
四种方向箭头用不同颜色区分(↑红 / →绿 / ↓蓝 / ←橙),降低识别成本;
-
飞出动画(沿方向滑出并淡出)与碰撞反馈(晃动 + 变红 + "被阻挡!" 浮动文字);
-
"提示" 功能:高亮一支当前可飞出的箭头;
-
快捷键:
R重新开始、H提示、ESC返回菜单; -
音效由程序首次运行时用 Python 标准库即时合成,无第三方素材、无版权问题。
三、实现思路
1. 箭头、方向与关卡的表示
方向用整数常量表示,配合方向向量便于路径检测:
UP, RIGHT, DOWN, LEFT = 0, 1, 2, 3
DIR_VEC = {UP: (0, -1), RIGHT: (1, 0), DOWN: (0, 1), LEFT: (-1, 0)}
箭头用网格坐标 + 方向表示:Arrow(col, row, direction)。棋盘 Board 持有一组箭头,负责路径检测与移除。关卡是一份字典:列数、行数、失误次数、限时、箭头列表。由于后期改为全部随机生成,关卡数据不再手工维护,而是由 generator.py 按 "难度 + 关卡序号" 作为随机种子现场生成。
2. 路径检测(核心)
对一支箭头,从它所在格子出发,沿其方向一格一格检查,只要在出界之前遇到任意箭头即被阻挡;走到边界仍未遇到箭头则畅通:
def is_blocked(self, arrow):
dx, dy = DIR_VEC[arrow.direction]
c, r = arrow.col + dx, arrow.row + dy
while 0 <= c < self.cols and 0 <= r < self.rows:
if self.arrow_at(c, r) is not None:
return True
c += dx
r += dy
return False
关键点在于 while 0 <= c < self.cols and 0 <= r < self.rows:保证边界情况正确 —— 位于边缘、朝向棋盘外的箭头(如第 1 行朝上的 ↑、最右列朝右的 →)循环直接不进入,返回 "无阻挡",不会发生数组越界。
3. 关卡可解性校验(贪心求解器)
移除一支 "当前可移除" 的箭头只会减少阻挡、不会让局面变差,因此用贪心即可判断关卡是否可通关:
def solve(board):
b = board.copy()
order = []
while b.arrows:
cand = b.removable_arrows()
if not cand: # 没有可移除箭头但仍有剩余 → 死局
return False, order
b.remove(cand[0])
order.append(cand[0])
return True, order
早期手工设计关卡时,正是这个求解器发现第 4 关存在死局(同一行里 → 与 ← 互相指向对方,形成无法解开的环),调整关卡数据后重新校验通过。改为程序化生成后,该求解器仍作为自动化测试保留。
4. 程序化关卡生成(反向构造法)
需求是 "难度越高棋盘越大、箭头越多,且全部随机生成"。随机撒箭头很容易生成死局,我让 AI 设计了反向构造法:从空棋盘开始逐格放置箭头,每次放置时要求新箭头在当前已放置箭头构成的局面下是可飞出的。最后一个放入的箭头就是第一个飞出的箭头,按放入逆序一定能清空全部箭头 —— 关卡天然可通关。
\# 放置时:从四个方向里挑一个"在当前已放置箭头下畅通"的方向
free = [d for d in range(4) if not blocked_by_placed(col, row, d)]
arrows.append((col, row, choice(free)))
此外加了两条保险:① 开局至少存在一支被阻挡的箭头(否则一关全是直接能点的,没有解谜性);② 生成后再用贪心求解器复验,通不过就换随机种子重试。难度按 DIFFICULTY_PROFILES 表配置,每过一关列数 +1、箭头 +1、时间 -4 秒。
5. 状态机与动画
游戏用简单状态机管理界面流转:
menu(选难度 → 选关卡)→ playing → clear → playing(下一关)
↘ over → playing(重新开始)
全部通关 → all_clear
飞出动画 FlyingArrow:沿方向按时间插值滑出棋盘并淡出;碰撞反馈 BumpArrow:箭头沿方向小幅晃动、格子变红、浮动文字 "被阻挡!"。点击被阻挡箭头会先扣失误次数,等碰撞动画播完再进入失败界面,避免瞬间跳转。
四、AIGC 使用过程
本次开发使用 豆包(Doubao) 作为 AIGC 编程助手,共记录了 6 次有代表性的协作过程。
| 子任务 | 借助何种 AIGC 技术 | AI 实现或提供了什么 | 效果如何 | 人工修改 |
|---|---|---|---|---|
| 需求分析与架构设计 | 豆包 | 给出模块划分(logic/ui/game/levels)与界面状态机建议 | 框架清晰,开发顺利 | 确认文件结构,确定 4 个关卡与失误次数 |
| 四方向路径检测 | 豆包 | 生成四种方向的路径检测代码 | 上 / 下 / 左 / 右检测逻辑正确 | 手工补充 "边缘朝外箭头不越界" 用例,发现并修复校验脚本的解包 bug |
| 关卡设计 | 豆包 | 设计关卡并编写贪心求解器 | 求解器能自动验证通关顺序 | 人工发现第 4 关死局,调整箭头方向后重新校验通过 |
| 碰撞动画 | 豆包 | 实现晃动、变红与浮动文字 | 反馈明显 | 调整晃动幅度与动画时长,避免抖动过强 |
| 自动化测试 | 豆包 | 生成路径检测与游戏流程测试用例 | 26 个用例全部通过 | 补充 "每关开局存在被阻挡箭头"" 关卡无重复位置 " 等检查 |
| 程序化关卡生成器 | 豆包 | 给出 "反向构造法" 生成思路与代码 | 随机关卡天然可通关 | 手工加了 "开局至少一支被阻挡" 与求解器复验两条保险 |
记录 1:需求分析与架构设计
提出的要求:向 AI 描述作业要求(点击式箭头解谜、四种方向、路径检测、失误次数、3 个以上关卡、图形界面),请求给出代码结构设计。
AI 完成:建议拆分为 "核心逻辑(与界面无关)" 与 "游戏表现层" 两层,核心逻辑包含箭头、棋盘、路径检测、求解器,表现层包含状态机、动画、UI 按钮;并建议用常量字典表示方向向量。
效果:模块划分让路径检测可以先脱离图形界面单独测试,后续写自动化测试非常方便。
人工修改:确认了最终文件结构(logic.py / levels.py / generator.py / game.py / ui.py / sounds.py),并决定关卡数量为 4 关、每关失误次数 3~4 次。
记录 2:四方向路径检测
提出的要求:实现 "判断箭头前进方向上是否还有其他箭头" 的函数,要求处理上、下、左、右四种方向和边界。
AI 完成:生成了基于方向向量逐格扫描的检测代码。
效果:主体逻辑正确。
人工修改:我补充了需求文档中的两个示例做验证(→ · · ↑ · 中朝右箭头被阻挡;边界箭头可直接飞出)。同时发现校验脚本里求解器返回格式和脚本预期不一致,人工修复;又补了 "位于边缘且朝向棋盘外的箭头不发生越界" 的测试用例。
记录 3:关卡设计与可解性校验
提出的要求:请 AI 设计 3 个以上关卡,并确保存在合理的通关顺序。
AI 完成:先设计关卡数据,再编写贪心求解器自动验证每一关可通关。
效果:求解器输出第 4 关 "不可通关"—— 该关存在 → 与 ← 互相指向对方的箭头,形成死局。
人工修改:把其中一支改为朝外方向,解除环状依赖,重新运行求解器后 4 关全部通过。这让我理解了一个重要规则:同一行里 → 在 ← 左侧会互相阻挡形成死局,除非其中一支位于边缘朝外。
记录 4:碰撞动画
提出的要求:被阻挡的箭头要有 "晃动、变色或文字" 等明显反馈。
AI 完成:实现了碰撞动画类 —— 箭头沿方向小幅晃动、所在格子变红、上方浮现 "被阻挡!" 文字并逐渐上飘消失。
效果:反馈清晰,且失误次数同步 -1。
人工修改:初始晃动幅度偏大、频率偏高,我调整了幅度系数和动画时长,让抖动更自然。
记录 5:自动化测试
提出的要求:编写测试覆盖作业要求的 T01~T06 场景。
AI 完成:生成基于 unittest 的测试文件,覆盖四方向路径检测、边缘越界、关卡可解性、通关 / 失败 / 重新开始流程,共 26 个用例。
效果:全部通过,且测试驱动我发现了截图脚本和冒烟脚本中的问题。
人工修改:补充了 "每关开局都存在被阻挡箭头"" 关卡内无重复坐标 "两个检查;后期加入难度系统后又补了" 时间随难度递减 ""关卡递进" 两条断言。
记录 6:程序化关卡生成器
提出的要求:要求 "难度越高棋盘越大、箭头越多,且关卡全部随机生成",但随机撒箭头很容易出现死局。
AI 完成:提出 "反向构造法"—— 从空棋盘逐格放置,每次只挑在当前局面下畅通的方向,按放入逆序必可通关。
效果:生成的关卡天然可通关,无需逐关手工验证。
人工修改:我发现纯反向构造生成的关卡有时全部箭头开局就能飞(太简单),于是加了 "开局至少一支被阻挡" 的校验;并保留贪心求解器对每个生成关卡复验一遍,不通过就换种子重试。
五、测试结果
自动化测试(python3 -m unittest discover -s tests -v)与手工试玩相结合。手工试玩对每个难度的关卡各完整通关一遍,确认存在合理通关顺序。
| 编号 | 测试内容 | 预期结果 | 实际结果 | 是否通过 |
|---|---|---|---|---|
| T01 | 点击前方无阻挡的箭头 | 箭头飞出棋盘并消失 | 箭头沿方向滑出并淡出,剩余箭头数 -1 | 通过 |
| T02 | 点击前方有阻挡的箭头 | 箭头不消失,失误次数减 1 | 箭头晃动变红提示 "被阻挡!",失误次数 -1 | 通过 |
| T03 | 点击位于边缘且朝向棋盘外的箭头 | 箭头正常消失,不发生越界错误 | 边缘箭头直接飞出,程序无异常 | 通过 |
| T04 | 消除本关全部箭头 | 显示通关并进入下一关 | 弹出 "第 X 关 通关!",点击 "下一关" 进入下一关 | 通过 |
| T05 | 失误次数耗尽 | 显示失败并允许重新开始 | 弹出 "闯关失败",点击 "重新开始" 恢复本关 | 通过 |
| T06 | 游戏进行中重新开始 | 箭头布局和失误次数恢复 | 棋盘恢复初始布局,失误次数恢复为满值 | 通过 |
自动化测试共 26 个用例全部通过:
Ran 26 tests in 0.321s
OK
六、PSP 表格
| 任务 | 预估耗时(小时) | 实际耗时(小时) | 差异(小时) |
|---|---|---|---|
| 需求分析与游戏设计 | 0.5 | 0.4 | -0.1 |
| Python 与图形库学习 | 1.0 | 1.0 | 0 |
| 游戏界面实现 | 2 | 2.5 | +0.5 |
| 路径与碰撞逻辑实现 | 3.0 | 2.5 | -0.5 |
| 关卡设计 | 2.0 | 2.5 | +0.5 |
| AIGC 辅助开发 | 2.0 | 3.0 | +1.0 |
| 测试与修改 | 2.0 | 2.5 | +0.5 |
| README 与博客撰写 | 1.5 | 2.0 | +0.5 |
| 合计 | 14.0 | 16.4 | +2.4 |
七、心得体会
AI 带来的帮助
-
架构建议省去了大量试错:AI 一开始就建议把 "路径检测" 这类核心逻辑与图形界面分离,这让逻辑可以先独立测试,也让代码更容易解释;
-
代码生成速度快:路径检测、状态机、动画、程序化生成器等常见模式,AI 生成的初版代码可运行率较高,我只需要做边界与细节修正;
-
测试用例补全:AI 生成的测试覆盖了作业要求的 T01~T06,还自动验证了每个关卡的可通关性,这在手工试玩之外多了一层保障。
出现的问题
-
关卡死局:最初手工设计的第 4 关存在
→与←互相指向对方的箭头,任何顺序都无法通关。这类问题靠眼睛很难发现,最终是靠贪心求解器自动检测出来的; -
边界越界风险:AI 初版代码在向上检测时曾对行号判断不够严谨,通过补充 "边缘朝外箭头" 测试用例后修正;
-
AI 生成的工具脚本自身也有 bug:关卡校验脚本与求解器返回格式不一致、GIF 工具把连续相同帧合并等,都需要人工阅读输出、定位并修复;
-
随机生成关卡太简单:纯反向构造法生成的关卡有时开局全部箭头都能直接飞,没有解谜性,需要人工增加 "开局至少一支被阻挡" 的约束。
收获
通过这次作业,我完整经历了一次 "需求分析 → AIGC 协作开发 → 自动化验证 → 试玩修正 → 文档交付" 的小型软件开发流程。最大的体会是:AI 是高效的 "结对程序员",但理解代码、设计测试、发现死局这些 "把关" 工作必须由自己完成——AI 给出代码,而我对每一行关键逻辑负责。
附:如何运行
pip install -r requirements.txt
python3 main.py
测试:
python3 -m unittest discover -s tests -v
```# 「一箭又一箭」小游戏 —— 软件工程课程第二次个人作业
| 项目 | 内容 |
| ---------- | ------------------------------------------------------------------------------ |
| 这个作业属于哪个课程 | <课程链接> |
| 这个作业要求在哪里 | <作业链接> |
| 这个作业的目标 | 使用 Python 和 AIGC 完成 "一箭又一箭" 小游戏 |
| 学号 | XXXXXXXX |
| GitHub 仓库 | [https://github.com/diahull/arrow-game](https://github.com/diahull/arrow-game) |
***
## 一、项目展示
演示 GIF(开始 → 选关 → 游戏 → 飞出 → 碰撞 → 通关):

开始界面(两步选关:先选难度,再选关卡):

游戏过程(第 1 关,顶栏显示关卡 / 剩余箭头 / 失误次数 / 倒计时 / 难度):

箭头飞出动画(沿方向滑出并淡出):

碰撞反馈(箭头晃动、格子变红、提示 "被阻挡!",失误次数 -1):

通关界面与失败界面:


(发布博客时请将 `screenshots/` 目录下的图片一并上传到博客系统。)
***
## 二、项目介绍
### 游戏规则
* 棋盘由网格组成,每个格子中有一支箭头,方向为上、下、左、右四种;
* 点击一支箭头后,程序检测它**前进方向**上直到棋盘边界之间是否还有其他箭头:
* 前方无阻挡 → 箭头飞出棋盘并消失;
* 前方有阻挡 → 箭头晃动、变红并提示 "被阻挡",同时消耗 1 次失误机会;
* 清空本关全部箭头即可通关并进入下一关;
* 失误次数耗尽或**超时未清完**则本关失败,可重新开始;
* 每关有时间限制,顶栏实时倒计时,通关时显示本关用时;难度越高时间越短。
### 界面设计
游戏包含三个阶段的界面:
1. **开始界面(两步选关)**:第一步选择难度(简单 / 普通 / 中等 / 困难),第二步进入该难度下选择第 1\~4 关,左下角 "返回难度" 可重选;
2. **游戏界面**:顶部信息栏(当前关卡、剩余箭头数量、剩余失误次数、剩余倒计时、当前难度)+ 棋盘 + 提示 / 重新开始 / 返回菜单按钮;
3. **结果界面**:通关界面(显示本关用时,"下一关" 按钮)、失败界面(区分 "时间到" 与 "失误用尽",可重新开始)、全部通关后有 "恭喜通关全部关卡" 界面。
### 主要功能与特色
* **四档难度 + 两步选关**:简单 / 普通 / 中等 / 困难,难度越高棋盘越大、箭头越多、失误越少、时间越短;
* **关卡递进**:同一难度下越往后越难(每过一关棋盘列数 +1、箭头 +1、时间 -4 秒);
* **程序化关卡生成**:所有关卡由生成器按 "反向构造法" 随机生成,保证每关都可通关,且开局至少存在一支被阻挡的箭头;
* **限时挑战**:每关倒计时,超时判负,通关时显示本关用时;
* 四种方向箭头用不同颜色区分(↑红 / →绿 / ↓蓝 / ←橙),降低识别成本;
* 飞出动画(沿方向滑出并淡出)与碰撞反馈(晃动 + 变红 + "被阻挡!" 浮动文字);
* "提示" 功能:高亮一支当前可飞出的箭头;
* 快捷键:`R` 重新开始、`H` 提示、`ESC` 返回菜单;
* 音效由程序首次运行时用 Python 标准库即时合成,无第三方素材、无版权问题。
***
## 三、实现思路
### 1. 箭头、方向与关卡的表示
方向用整数常量表示,配合方向向量便于路径检测:
UP, RIGHT, DOWN, LEFT = 0, 1, 2, 3
DIR_VEC = {UP: (0, -1), RIGHT: (1, 0), DOWN: (0, 1), LEFT: (-1, 0)}
箭头用网格坐标 + 方向表示:`Arrow(col, row, direction)`。棋盘 `Board` 持有一组箭头,负责路径检测与移除。关卡是一份字典:列数、行数、失误次数、限时、箭头列表。由于后期改为全部随机生成,关卡数据不再手工维护,而是由 `generator.py` 按 "难度 + 关卡序号" 作为随机种子现场生成。
### 2. 路径检测(核心)
对一支箭头,从它所在格子出发,沿其方向一格一格检查,只要在出界之前遇到任意箭头即被阻挡;走到边界仍未遇到箭头则畅通:
```c
def is_blocked(self, arrow):
dx, dy = DIR_VEC[arrow.direction]
c, r = arrow.col + dx, arrow.row + dy
while 0 <= c < self.cols and 0 <= r < self.rows:
if self.arrow_at(c, r) is not None:
return True
c += dx
r += dy
return False
关键点在于 while 0 <= c < self.cols and 0 <= r < self.rows:保证边界情况正确 —— 位于边缘、朝向棋盘外的箭头(如第 1 行朝上的 ↑、最右列朝右的 →)循环直接不进入,返回 "无阻挡",不会发生数组越界。
3. 关卡可解性校验(贪心求解器)
移除一支 "当前可移除" 的箭头只会减少阻挡、不会让局面变差,因此用贪心即可判断关卡是否可通关:
def solve(board):
b = board.copy()
order = []
while b.arrows:
cand = b.removable_arrows()
if not cand: # 没有可移除箭头但仍有剩余 → 死局
return False, order
b.remove(cand[0])
order.append(cand[0])
return True, order
早期手工设计关卡时,正是这个求解器发现第 4 关存在死局(同一行里 → 与 ← 互相指向对方,形成无法解开的环),调整关卡数据后重新校验通过。改为程序化生成后,该求解器仍作为自动化测试保留。
4. 程序化关卡生成(反向构造法)
需求是 "难度越高棋盘越大、箭头越多,且全部随机生成"。随机撒箭头很容易生成死局,我让 AI 设计了反向构造法:从空棋盘开始逐格放置箭头,每次放置时要求新箭头在当前已放置箭头构成的局面下是可飞出的。最后一个放入的箭头就是第一个飞出的箭头,按放入逆序一定能清空全部箭头 —— 关卡天然可通关。
\# 放置时:从四个方向里挑一个"在当前已放置箭头下畅通"的方向
free = [d for d in range(4) if not blocked_by_placed(col, row, d)]
arrows.append((col, row, choice(free)))
此外加了两条保险:① 开局至少存在一支被阻挡的箭头(否则一关全是直接能点的,没有解谜性);② 生成后再用贪心求解器复验,通不过就换随机种子重试。难度按 DIFFICULTY_PROFILES 表配置,每过一关列数 +1、箭头 +1、时间 -4 秒。
5. 状态机与动画
游戏用简单状态机管理界面流转:
menu(选难度 → 选关卡)→ playing → clear → playing(下一关)
↘ over → playing(重新开始)
全部通关 → all_clear
飞出动画 FlyingArrow:沿方向按时间插值滑出棋盘并淡出;碰撞反馈 BumpArrow:箭头沿方向小幅晃动、格子变红、浮动文字 "被阻挡!"。点击被阻挡箭头会先扣失误次数,等碰撞动画播完再进入失败界面,避免瞬间跳转。
四、AIGC 使用过程
本次开发使用 豆包(Doubao) 作为 AIGC 编程助手,共记录了 6 次有代表性的协作过程。
| 子任务 | 借助何种 AIGC 技术 | AI 实现或提供了什么 | 效果如何 | 人工修改 |
|---|---|---|---|---|
| 需求分析与架构设计 | 豆包 | 给出模块划分(logic/ui/game/levels)与界面状态机建议 | 框架清晰,开发顺利 | 确认文件结构,确定 4 个关卡与失误次数 |
| 四方向路径检测 | 豆包 | 生成四种方向的路径检测代码 | 上 / 下 / 左 / 右检测逻辑正确 | 手工补充 "边缘朝外箭头不越界" 用例,发现并修复校验脚本的解包 bug |
| 关卡设计 | 豆包 | 设计关卡并编写贪心求解器 | 求解器能自动验证通关顺序 | 人工发现第 4 关死局,调整箭头方向后重新校验通过 |
| 碰撞动画 | 豆包 | 实现晃动、变红与浮动文字 | 反馈明显 | 调整晃动幅度与动画时长,避免抖动过强 |
| 自动化测试 | 豆包 | 生成路径检测与游戏流程测试用例 | 26 个用例全部通过 | 补充 "每关开局存在被阻挡箭头"" 关卡无重复位置 " 等检查 |
| 程序化关卡生成器 | 豆包 | 给出 "反向构造法" 生成思路与代码 | 随机关卡天然可通关 | 手工加了 "开局至少一支被阻挡" 与求解器复验两条保险 |
记录 1:需求分析与架构设计
提出的要求:向 AI 描述作业要求(点击式箭头解谜、四种方向、路径检测、失误次数、3 个以上关卡、图形界面),请求给出代码结构设计。
AI 完成:建议拆分为 "核心逻辑(与界面无关)" 与 "游戏表现层" 两层,核心逻辑包含箭头、棋盘、路径检测、求解器,表现层包含状态机、动画、UI 按钮;并建议用常量字典表示方向向量。
效果:模块划分让路径检测可以先脱离图形界面单独测试,后续写自动化测试非常方便。
人工修改:确认了最终文件结构(logic.py / levels.py / generator.py / game.py / ui.py / sounds.py),并决定关卡数量为 4 关、每关失误次数 3~4 次。
记录 2:四方向路径检测
提出的要求:实现 "判断箭头前进方向上是否还有其他箭头" 的函数,要求处理上、下、左、右四种方向和边界。
AI 完成:生成了基于方向向量逐格扫描的检测代码。
效果:主体逻辑正确。
人工修改:我补充了需求文档中的两个示例做验证(→ · · ↑ · 中朝右箭头被阻挡;边界箭头可直接飞出)。同时发现校验脚本里求解器返回格式和脚本预期不一致,人工修复;又补了 "位于边缘且朝向棋盘外的箭头不发生越界" 的测试用例。
记录 3:关卡设计与可解性校验
提出的要求:请 AI 设计 3 个以上关卡,并确保存在合理的通关顺序。
AI 完成:先设计关卡数据,再编写贪心求解器自动验证每一关可通关。
效果:求解器输出第 4 关 "不可通关"—— 该关存在 → 与 ← 互相指向对方的箭头,形成死局。
人工修改:把其中一支改为朝外方向,解除环状依赖,重新运行求解器后 4 关全部通过。这让我理解了一个重要规则:同一行里 → 在 ← 左侧会互相阻挡形成死局,除非其中一支位于边缘朝外。
记录 4:碰撞动画
提出的要求:被阻挡的箭头要有 "晃动、变色或文字" 等明显反馈。
AI 完成:实现了碰撞动画类 —— 箭头沿方向小幅晃动、所在格子变红、上方浮现 "被阻挡!" 文字并逐渐上飘消失。
效果:反馈清晰,且失误次数同步 -1。
人工修改:初始晃动幅度偏大、频率偏高,我调整了幅度系数和动画时长,让抖动更自然。
记录 5:自动化测试
提出的要求:编写测试覆盖作业要求的 T01~T06 场景。
AI 完成:生成基于 unittest 的测试文件,覆盖四方向路径检测、边缘越界、关卡可解性、通关 / 失败 / 重新开始流程,共 26 个用例。
效果:全部通过,且测试驱动我发现了截图脚本和冒烟脚本中的问题。
人工修改:补充了 "每关开局都存在被阻挡箭头"" 关卡内无重复坐标 "两个检查;后期加入难度系统后又补了" 时间随难度递减 ""关卡递进" 两条断言。
记录 6:程序化关卡生成器
提出的要求:要求 "难度越高棋盘越大、箭头越多,且关卡全部随机生成",但随机撒箭头很容易出现死局。
AI 完成:提出 "反向构造法"—— 从空棋盘逐格放置,每次只挑在当前局面下畅通的方向,按放入逆序必可通关。
效果:生成的关卡天然可通关,无需逐关手工验证。
人工修改:我发现纯反向构造生成的关卡有时全部箭头开局就能飞(太简单),于是加了 "开局至少一支被阻挡" 的校验;并保留贪心求解器对每个生成关卡复验一遍,不通过就换种子重试。
五、测试结果
自动化测试(python3 -m unittest discover -s tests -v)与手工试玩相结合。手工试玩对每个难度的关卡各完整通关一遍,确认存在合理通关顺序。
| 编号 | 测试内容 | 预期结果 | 实际结果 | 是否通过 |
|---|---|---|---|---|
| T01 | 点击前方无阻挡的箭头 | 箭头飞出棋盘并消失 | 箭头沿方向滑出并淡出,剩余箭头数 -1 | 通过 |
| T02 | 点击前方有阻挡的箭头 | 箭头不消失,失误次数减 1 | 箭头晃动变红提示 "被阻挡!",失误次数 -1 | 通过 |
| T03 | 点击位于边缘且朝向棋盘外的箭头 | 箭头正常消失,不发生越界错误 | 边缘箭头直接飞出,程序无异常 | 通过 |
| T04 | 消除本关全部箭头 | 显示通关并进入下一关 | 弹出 "第 X 关 通关!",点击 "下一关" 进入下一关 | 通过 |
| T05 | 失误次数耗尽 | 显示失败并允许重新开始 | 弹出 "闯关失败",点击 "重新开始" 恢复本关 | 通过 |
| T06 | 游戏进行中重新开始 | 箭头布局和失误次数恢复 | 棋盘恢复初始布局,失误次数恢复为满值 | 通过 |
自动化测试共 26 个用例全部通过:
Ran 26 tests in 0.321s
OK
六、PSP 表格
| 任务 | 预估耗时(小时) | 实际耗时(小时) | 差异(小时) |
|---|---|---|---|
| 需求分析与游戏设计 | 0.5 | 0.4 | -0.1 |
| Python 与图形库学习 | 1.0 | 1.0 | 0 |
| 游戏界面实现 | 2 | 2.5 | +0.5 |
| 路径与碰撞逻辑实现 | 3.0 | 2.5 | -0.5 |
| 关卡设计 | 2.0 | 2.5 | +0.5 |
| AIGC 辅助开发 | 2.0 | 3.0 | +1.0 |
| 测试与修改 | 2.0 | 2.5 | +0.5 |
| README 与博客撰写 | 1.5 | 2.0 | +0.5 |
| 合计 | 14.0 | 16.4 | +2.4 |
七、心得体会
AI 带来的帮助
-
架构建议省去了大量试错:AI 一开始就建议把 "路径检测" 这类核心逻辑与图形界面分离,这让逻辑可以先独立测试,也让代码更容易解释;
-
代码生成速度快:路径检测、状态机、动画、程序化生成器等常见模式,AI 生成的初版代码可运行率较高,我只需要做边界与细节修正;
-
测试用例补全:AI 生成的测试覆盖了作业要求的 T01~T06,还自动验证了每个关卡的可通关性,这在手工试玩之外多了一层保障。
出现的问题
-
关卡死局:最初手工设计的第 4 关存在
→与←互相指向对方的箭头,任何顺序都无法通关。这类问题靠眼睛很难发现,最终是靠贪心求解器自动检测出来的; -
边界越界风险:AI 初版代码在向上检测时曾对行号判断不够严谨,通过补充 "边缘朝外箭头" 测试用例后修正;
-
AI 生成的工具脚本自身也有 bug:关卡校验脚本与求解器返回格式不一致、GIF 工具把连续相同帧合并等,都需要人工阅读输出、定位并修复;
-
随机生成关卡太简单:纯反向构造法生成的关卡有时开局全部箭头都能直接飞,没有解谜性,需要人工增加 "开局至少一支被阻挡" 的约束。
收获
通过这次作业,我完整经历了一次 "需求分析 → AIGC 协作开发 → 自动化验证 → 试玩修正 → 文档交付" 的小型软件开发流程。最大的体会是:AI 是高效的 "结对程序员",但理解代码、设计测试、发现死局这些 "把关" 工作必须由自己完成——AI 给出代码,而我对每一行关键逻辑负责。
附:如何运行
pip install -r requirements.txt
python3 main.py
测试:
python3 -m unittest discover -s tests -v
```

浙公网安备 33010602011771号