软件工程个人作业-2
| 项目 | 内容 |
|---|---|
| 这个作业属于哪个课程 | H202601 软件工程与软件工程实践 |
| 这个作业要求在哪里 | 2026 秋软件工程个人作业(第二次) |
| 这个作业的目标 | 使用 Python 和 AIGC 完成“一箭又一箭”小游戏 |
| 学号 | 102401335 |
| GitHub 仓库 | HangLog-tech/software_engineering_work_2 |
一、项目展示
1. 开始界面

2. 游戏过程


3. 通关界面

4. 失败界面

二、项目介绍
1. 游戏规则
浏览器/微信中的“一箭又一箭”是一类点击式箭头解谜游戏。本项目实现的是它的基础玩法:
- 棋盘由网格组成,每格最多一个箭头,方向为上、下、左、右之一;
- 点击箭头后,程序沿箭头方向直线检测:
- 前方没有其他箭头阻挡 → 箭头飞出棋盘并消除;
- 前方存在其他箭头 → 箭头不能消除,抖动、闪红并浮出
-1,失误次数 -1;
- 清空本关全部箭头即通关并进入下一关;
- 失误次数耗尽则本关失败,可重新开始。
规则示例:
→ · · ↑ ·
第一个箭头朝右,右侧仍有箭头阻挡,不能飞出;
↑ · · · →
最后一个箭头朝右,右侧没有箭头,可以飞出棋盘。
2. 界面设计
- 开始界面:标题、方向箭头装饰、开始游戏 / 继续游戏 / 关卡选择 / 随机关卡 / 退出游戏;
- 游戏界面:顶部标题与关卡进度;信息栏显示剩余箭头、失误次数、用时;右上角电灯泡提示按钮;中央棋盘;底部撤销 / 重新开始 / 退出关卡;
- 结算界面:通关时显示用时、本关得分、三星评价与累计总分;失败时显示剩余箭头与用时;
- 关卡选择界面:按进度解锁 6 个关卡,显示各关最佳星级。
3. 主要功能
基础功能:
- 四种方向箭头、鼠标点击交互、直线路径阻挡判断;
- 飞出消除与碰撞反馈(抖动 + 闪红 + 浮字);
- 失误次数显示与判负、通关切关、重新开始;
- 6 个经程序验证可通关的关卡。
扩展功能:
- 计时、得分与三星评价;
- 撤销上一步;
- 电灯泡提示:成功消除箭头后 60 秒冷却,冷却结束才可用;
- 关卡选择、随机关卡(保证可通关);
- 本地保存进度与未完成对局(支持“继续游戏”);
- 退出关卡(保留对局进度);
- 程序合成的简单音效(无外部素材)。
4. 项目特色
- 关卡可解性有保障:所有固定关卡都通过“贪心消除”程序化验证;随机关卡在生成时反复检测,确保 100% 可通关;
- 零外部资源:音效由代码合成,不依赖任何图片/音频素材,仓库干净;
- 可复盘的结构:入口、状态机、游戏逻辑、界面、存档、音效分模块编写,便于测试与迭代;
- 可持续迭代:提供 17 项 pytest 单元测试,覆盖路径检测、关卡可解性、计分星级与存档恢复等。
5. 运行方式
pip install -r requirements.txt
python main.py
三、实现思路
1. 数据结构:箭头、方向与关卡
方向用四种字符表示,棋盘是一个二维数组(空位为 None,箭头为 "U"/"D"/"L"/"R");关卡数据直接用字符串数组描述,直观且便于手工调整:
DIRS = {"U": (-1, 0), "D": (1, 0), "L": (0, -1), "R": (0, 1)}
DIR_CHARS = {"U": "↑", "D": "↓", "L": "←", "R": "→"}
LEVELS = [{
"name": "初识箭头",
"mistakes": 3,
"grid": [
"→..↑.",
".....",
"..↓.←",
".....",
"..↓..",
],
}]
2. 路径检测
沿箭头方向逐格前进,遇到任意箭头即视为阻挡;循环条件同时检查越界,因此“贴边朝外”的箭头会直接到达边界外,不会数组越界:
def can_fly_out(self, row, col):
direction = self.board[row][col]
dr, dc = DIRS[direction]
r, c = row + dr, col + dc
while self.in_bounds(r, c):
if self.board[r][c] is not None: # 射线上有箭头 → 被阻挡
return False
r, c = r + dr, c + dc
return True # 到达边界外 → 可飞出
3. 状态机与模块划分
游戏有四个界面状态:start(开始)、playing(游戏)、win(通关)、fail(失败),另有 level_select(关卡选择)。主循环每帧处理事件 → 更新(计时、动画、胜负判定)→ 绘制。
代码按职责拆分为多个模块:
main.py 程序入口(仅启动应用)
app.py 状态机、事件处理、更新与主循环
game.py 棋盘、路径检测、随机关卡生成与计分
levels.py 6 个关卡数据
ui.py 字体、按钮、箭头绘制与动画
sound.py 程序合成音效
storage.py 本地进度存档(save.json)
tests/ 单元测试
4. 动画与反馈
- 飞出:箭头沿方向平移并渐隐(约 0.28 秒);
- 碰撞:原位抖动并闪红,同时浮出“-1”提示;
- 动画期间锁定棋盘输入,避免快速连点导致状态错乱(浮动文字不锁输入)。
5. 计分与星级
本关得分 = 1000 + 剩余失误 × 150 - 用时秒数 × 5(最低 100)
星级:0 失误 = 3 星;失误不超过上限一半 = 2 星;其余 = 1 星
6. 随机关卡的可解性保证
由于“消除只会让路径更通畅”,只要不断消除当前可飞出的箭头,若最终能全部清空则关卡可解。生成随机布局后立即用该方法验证,并优先保留“初始超过一半箭头被阻挡”的布局,避免出现一开局就能无脑全消的关卡:
def greedy_solve(game):
while not game.cleared:
cell = find_one_flyable(game) # 找一个当前可飞出的箭头
if cell is None:
return False # 出现互锁 → 判为不可解
game.click(*cell)
return True
四、AIGC 使用过程
以下记录来自真实开发过程,均保留了对 AI 输出的验证与人工修改。
| 子任务 | 借助何种 AIGC 技术 | AI 实现或提供了什么 | 效果如何 | 人工修改 |
|---|---|---|---|---|
| 需求分析与原型设计 | DeepSeek | 根据作业要求整理玩法规则、界面线框、关卡设计与测试用例 | 结构完整,可直接指导开发 | 逐关检查可解性,发现原设计存在两处“箭头面对面互锁”的死局并重新设计 |
| 路径检测与核心逻辑 | DeepSeek | 生成棋盘、四方向路径检测、失误与关卡管理代码 | 基本可用,边界情况需验证 | 补充“失败后不再接受点击”守卫;修正浮动提示文字不应锁住棋盘输入的问题 |
| 界面与动画 | DeepSeek | 生成棋盘绘制、圆角箭头棋子、飞出/抖动动画与按钮 | 视觉与交互符合预期 | 大棋盘改为格尺寸自适应;提示改为右上角电灯泡并加冷却倒计时角标 |
| 提示冷却、计分与存档 | DeepSeek | 实现计时计分、星级、进度存档与“继续游戏” | 功能完整,状态流转基本正确 | 修正失败界面“重新开始”语义(应重玩本关而不是回到第 1 关);退出关卡改为保存对局 |
| 测试与联调 | DeepSeek | 编写 pytest 单元测试与无头界面流程测试 | 能发现真实问题,回归快 | 解决 Python 3.14 无 pygame 官方安装包的问题(改用 API 兼容的 pygame-ce);修复中文编码与存档隔离问题 |
几个代表性的人工修改细节:
- 死局修正:AI 最初的关卡设计里,同一行存在“→”与“←”相对的情况,二者永远互相阻挡。我编写了贪心求解脚本逐关验证,重新设计后才得到现在 6 个可零失误通关的关卡。
- 输入锁定范围:最初“-1”浮动文字动画也会锁住输入,导致点击手感迟滞。我确认该动画只是视觉反馈后,将其从输入锁判定中排除,只保留飞出/碰撞动画锁输入。
- 按钮语义:失败界面的“重新开始”最早期实现成重新从第 1 关开始,测试发现后改为重玩当前关。
五、测试结果
1. 单元测试(pytest)
python -m pytest tests -q
# 17 passed
覆盖内容:四方向路径检测、阻挡判定、飞出/失误/重新开始、失误耗尽、6 个固定关卡与随机关卡的可解性、存档快照恢复、计分与星级规则。
2. 功能测试用例
| 编号 | 测试内容 | 预期结果 | 实际结果 | 是否通过 |
|---|---|---|---|---|
| T01 | 点击前方无阻挡的箭头 | 箭头飞出棋盘并消失 | 与预期一致 | ✔ |
| T02 | 点击前方有阻挡的箭头 | 箭头不消失,失误次数减 1 | 抖动闪红并浮出“-1”,失误 3→2 | ✔ |
| T03 | 点击位于边缘且朝向棋盘外的箭头 | 箭头正常消失,不发生越界错误 | 与预期一致,无越界异常 | ✔ |
| T04 | 消除本关全部箭头 | 显示通关并进入下一关 | 显示“本关通过!”结算面板 | ✔ |
| T05 | 失误次数耗尽 | 显示失败并允许重新开始 | 显示“挑战失败”,可重新开始本关 | ✔ |
| T06 | 游戏进行中重新开始 | 箭头布局和失误次数恢复 | 与预期一致 | ✔ |
| T07 | 六关依次通关 | 最终显示全部通关 | 显示“全部通关!” | ✔ |
| T08 | 四个方向各验证一次阻挡/飞出 | 判定结果与理论一致 | 与预期一致 | ✔ |
| T09 | 点击空白格 | 状态无变化 | 与预期一致,不扣失误 | ✔ |
| T10 | 动画播放期间连续点击 | 不出现重复消除或失误误扣 | 与预期一致 | ✔ |
| T11 | 每关按可解顺序试玩 | 均可零失误通关,无死局 | 与预期一致 | ✔ |
补充说明:T01~T11 通过“无头自动化流程测试 + 手工试玩”共同验证;自动化测试脚本模拟鼠标点击遍历全部关卡,保证回归时结果稳定。
3. 测试中发现并修复的问题(部分)
- 提示按钮在成功消除后会重置 60 秒冷却,需在测试中按真实时间推进验证;
- 动画期间点击二次消除导致连消:通过输入锁修复;
- 9×9 大棋盘在 800×600 窗口内溢出:改为格尺寸按棋盘规模自适应。
六、PSP 表格
| 任务 | 预估耗时(小时) | 实际耗时(小时) | 差异(小时) |
|---|---|---|---|
| 需求分析与游戏设计 | 2 | 2 | 0 |
| Python 与图形库学习 | 3 | 4 | +1 |
| 游戏界面实现 | 4 | 4.5 | +0.5 |
| 路径与碰撞逻辑实现 | 4 | 5 | 1 |
| 关卡设计 | 2 | 1 | -1 |
| AIGC 辅助开发 | 4 | 4 | 0 |
| 测试与修改 | 3 | 2.5 | -0.5 |
| README 与博客撰写 | 3 | 2 | -1 |
| 合计 | 25 | 25 | 25 |
七、心得体会
这次作业让我第一次完整地用 AIGC 走了一遍“需求分析 → 设计 → 编码 → 测试 → 文档”的流程,感受最深的有三点。
第一,AI 能显著加快起步,但验证必须由人完成。无论是关卡布局、路径检测还是界面绘制,AI 给的初版都能跑,但真正的问题往往藏在细节里:互锁的死局、失败的按钮语义、动画锁输入的范围。这些问题不靠“看起来对”,而要靠写脚本验证、逐关试玩和针对性的测试去发现。
第二,测试是人与 AI 协作的安全网。为 can_fly_out 写的单元测试、对 6 个关卡与随机关卡的贪心可解性验证、模拟点击的无头流程测试,让我在多次重构之后,依然能快速确认没有把功能改坏。AI 可以很快地改代码,但“改完还是对的”要靠测试兜底。
第三,结构清晰才能持续迭代。一开始所有代码都堆在 main.py 里,随着功能变多越来越难改;拆成入口 / 状态机 / 逻辑 / 界面 / 存档后,每次加功能只需要动一两个文件。AI 在重构时非常高效,但前提是我能说清楚“每个模块负责什么”。
最后,AI 生成的代码由我负责。这次我保留了关键修改的过程记录,也尽量让每一段关键代码都能讲清楚“为什么这样写”。这种“AI 产出 + 人工把关”的模式,是这次作业最大的收获。
浙公网安备 33010602011771号