软件工程 第二次作业
Arrow Escape小游戏开发作业报告
作业信息
| 项目 | 内容 |
|---|---|
| 这个作业属于哪个课程 | 202601 软件工程 |
| 这个作业要求在哪里 | 作业 16717 |
| 这个作业的目标 | 使用 Python 和 AIGC 完成「ARROW ESCAPE」小游戏 |
| 学号 | 182400143 |
| GitHub 仓库 | Ther-ux/Arrow_Escape |
| 项目名称 | Arrow Escape · 箭境 |
| 开发日期 | 2026 年 9 月 |
一、项目展示
项目使用 Python 和 Pygame 实现,包含开始界面、游戏界面、通关界面和失败界面。下面的截图来自当前版本的自动化窗口验证,除了基础流程,还展示了提示、撤销、随机可解关卡、AI 自动求解和 Windows 可执行文件等扩展功能。
开始界面

进阶玩法支持标准模式的难度选择,也支持困难无尽模式。无尽模式在每次通关后随机生成下一关,并保证存在可通关顺序:

普通玩法还提供固定三关、更加紧凑的基础界面:

普通模式运行演示:从普通模式首页进入游戏,展示箭头点击、碰撞反馈、重开和第一关通关流程。

游戏过程

游戏侧栏可以直接看到提示次数、撤销、重开和 AI 求解按钮。
点击可释放的箭头后,箭头会沿当前方向飞出,尾线逐段跟随:

AI 求解会复用正常的路径判断和飞行动画,按钮在运行期间变为“停止 AI”。
点击前方有阻挡的箭头时,箭头不会消失,并产生红色碰撞反馈,同时扣除一次失误机会:

点击“提示”后,程序会消耗一次提示机会并高亮当前可以直接释放的箭头:

完成一次有效点击后可以点击“撤销”,恢复箭头或返还碰撞消耗的失误机会:

进阶模式完整运行演示:包含进阶设置、提示、撤销、AI 自动求解和通关流程。

柔性尾线的转向、跟随和回弹效果由独立演示验证:

通关和失败结果


自动化 UI 流程能够完成三个原始关卡、切换下一关、失败、重开、返回首页和退出窗口。随机生成器和 AI 求解器也通过了关卡清空测试。
二、项目介绍
Windows 打包产物为 dist/ArrowEscape.exe,文件大小约 15.3 MB。项目使用 Python 和 Pygame 实现,包含基础玩法和进阶玩法两种模式。
2.1 游戏规则
棋盘由网格组成,每个非空格子中放置一个朝向上、下、左或右的箭头。玩家点击箭头后,程序检查它朝向棋盘边界的整条直线路径:
- 前方没有其他箭头时,箭头飞出棋盘并从棋盘状态中移除。
- 前方存在其他箭头时,箭头保留在原位置,播放碰撞反馈,并消耗一次失误机会。
- 清除当前关卡的全部箭头后显示通关结果,可以进入下一关。
- 失误机会归零后显示失败结果,可以重新开始当前关卡。
每局默认有 3 次失误机会和 3 次提示机会。点击 R 或“重新开始”可以恢复当前关卡的原始布局;按 Esc 返回首页。
2.2 主要功能
| 功能 | 说明 |
|---|---|
| 基础玩法 | 固定的 3 个原创关卡,使用紧凑棋盘和基础箭头样式 |
| 进阶玩法 | 支持标准模式、简单/中等/困难难度,以及困难无尽模式 |
| 路径判断 | 支持四个方向、边界箭头、近距离和远距离阻挡 |
| 飞出动画 | 非阻塞加速飞出,尾线跟随,末箭动画完成后才显示结果 |
| 碰撞反馈 | 320 ms 左右的前冲、回弹、震动和红色反馈 |
| 提示 | 高亮一支当前可以合法离场的箭头,不自动点击 |
| 撤销 | 撤销最近一次有效点击,恢复飞出的箭头或返还碰撞消耗的机会 |
| AI 求解 | 自动寻找当前合法箭头,按正常动画顺序完成关卡 |
| 随机关卡生成 | 根据难度和箭头数量生成随机布局,并保证存在合法通关顺序 |
| 进度保存 | 保存无尽模式下一关编号,重新启动后继续挑战 |
| 柔性尾线 | 使用 Verlet 节点、距离约束和曲线采样绘制连续尾线 |
2.3 界面设计
项目采用暗色极简科技风:背景接近 #0D1224,使用青蓝色表示主要交互,绿色表示成功,珊瑚红表示碰撞或失败,紫色作为辅助色。按钮和棋盘命中测试共用同一套几何换算,减少“看得见但点不到”的问题。
三、实现思路
3.1 项目结构
main.py 程序入口
core/arrow.py Arrow 模型与四方向枚举
core/path_checker.py 纯路径检测函数
core/board.py 棋盘占用状态
core/game.py 游戏状态机、点击结果和动画计时
core/progress.py 无尽模式进度读写
data/levels.py 关卡数据、难度和可解关卡生成
ui/app.py Pygame 页面、按钮、事件和主循环
ui/rope.py Verlet 柔性尾线模拟
ui/routes.py 尾线路径规划
ui/rope_renderer.py 尾线平滑曲线和抗锯齿绘制
tests/ 路径、状态、界面和尾线测试
tools/smoke_ui.py 自动化窗口流程与截图
tools/rope_demo.py 柔性尾线独立演示
3.2 箭头和棋盘表示
箭头使用不可变数据类表示,核心字段为 row、col 和 direction。方向使用枚举表示,并映射到行列增量:
UP = (-1, 0)
DOWN = (1, 0)
LEFT = (0, -1)
RIGHT = (0, 1)
棋盘使用 (row, col) -> Arrow 的字典保存占用状态。成功点击后立即从字典中删除箭头,绘制层再使用动画副本显示它飞出,因此后续箭头可以马上利用新开放的路径。
3.3 路径检测
core/path_checker.py 中的 is_path_clear() 不依赖 Pygame,也不修改传入的占用集合。算法从箭头的下一格开始,沿箭头方向逐格扫描,直到离开棋盘:
row, col = arrow.row + row_step, arrow.col + col_step
while 0 <= row < rows and 0 <= col < cols:
if (row, col) in occupied:
return False
row += row_step
col += col_step
return True
因此该算法只检查箭头前方同一行或同一列的格子,不会误把后方箭头或其他行列的箭头当作阻挡。若箭头位于边缘且朝向棋盘外,循环不会执行,路径直接判定为畅通。设前方有 k 个格子,时间复杂度为 O(k),额外空间复杂度为 O(1)。
3.4 游戏状态和动画
Game 使用 HOME、PLAYING、SUCCESS 和 FAILED 四种页面状态。点击结果分为 FLYING、COLLISION 和 IGNORED。成功点击只改变棋盘状态并创建飞行动画;失败点击保留箭头、减少一次机会并创建碰撞动画。
动画由主循环中的 update(dt) 推进,不调用 sleep()。这样窗口仍然能够持续处理鼠标、键盘和关闭事件。为了避免多条尾线叠在同一通道,当前飞行尾线结束前不会释放下一支箭头。
3.5 柔性尾线
尾线由一串约 10 px 间距的节点组成,头节点跟随箭头,其他节点通过 Verlet 积分和距离约束逐步跟随。模拟采用固定 240 Hz 子步长、较强阻尼和轨迹横向约束;绘制时使用 Catmull-Rom 曲线插值并进行超采样,得到较平滑的线条。
尾线只负责视觉表现,不参与棋盘规则判断。游戏规则始终以单格箭头和直线路径为准。
3.6 关卡可解性
前三个原创关卡均已通过自动化合法点击序列验证。简单、中等和困难模式按照 4/6/8、6/9/12、8/12/16 个箭头逐关增加;随机关卡生成器在构造时保留合法移除顺序,生成的关卡再由 AI 求解测试完整清空。无尽模式每次通关后生成新的困难关卡,并保存下一关编号。
前三个原始关卡的一个合法顺序如下,坐标从 0 开始:
| 关卡 | 合法点击顺序 |
|---|---|
| L1 初见出口 | (0,1) -> (1,1) -> (2,0) -> (2,4) -> (2,2) -> (4,3) |
| L2 逐步释放 | (0,0) -> (0,2) -> (0,4) -> (2,0) -> (2,2) -> (2,4) -> (4,0) -> (4,2) -> (4,4) |
| L3 交错方向 | (0,1) -> (0,4) -> (1,1) -> (1,4) -> (2,0) -> (2,2) -> (2,4) -> (3,5) -> (3,3) -> (5,1) -> (3,1) -> (5,4) |
四、AIGC 使用过程
本项目使用 Codex 作为 Coding Agent,AIGC 参与了需求拆分、代码实现、测试设计、Bug 修复和界面迭代。以下表格按照作业要求记录 4 次真实开发过程,完整记录保存在 docs/aigc-log.md。
| 子任务 | 我的要求 | 借助何种 AIGC 技术 | AI 实现或提供了什么 | 效果如何 | 人工修改 |
|---|---|---|---|---|---|
| 路径检测(AI-01,2026-09-13) | 覆盖四方向、边界、近距离阻挡、远距离阻挡、无关行列和 blocker 移除;核心逻辑不依赖 UI。 | Codex | 建立不可变 Arrow、Direction 和 is_path_clear(),并补充 pytest 测试。 |
首轮实现出现 AbstractSet 导入错误,测试及时暴露问题。 |
人工审核错误原因,要求改正导入位置并重新运行测试;34 项测试通过。 |
| 棋盘状态和可玩 MVP(AI-02,2026-09-15) | 实现棋盘、鼠标点击、飞出、碰撞、失误计数、重开和关卡切换,并保留关键 Commit。 | Codex | 实现 Board、Game、动画计时、3 个原创关卡及 T01-T06 状态测试。 |
基本流程可运行,但连续点击碰撞中的箭头可能重复扣分。 | 人工审核后要求增加动画期间的箭头锁定和重复点击测试;最后一次反馈结束后才进入失败页,48 项测试通过。 |
| Pygame 界面和基础动效(AI-03,2026-09-15) | 完成开始页、棋盘页、通关页和失败页,采用暗色科技风,并验证真实窗口事件。 | Codex | 实现 main.py、ui/app.py、共用 BoardLayout、按钮命中、飞出/碰撞动画和 UI smoke 脚本。 |
自动窗口流程可以通过三关;代码审查发现返回首页后可能残留旧按钮命中区域。 | 要求按当前页面过滤按钮并补充回归测试;根据截图调整 6×6 棋盘标题间距和失误数字显示。 |
| 柔性尾线和路径分离(AI-04,2026-09-15) | 实现近端先牵引、远端渐进跟随的柔性线条,避免尾线重叠和无控制甩动。 | Codex | 实现 Verlet 节点链、距离约束、阻尼、历史轨迹约束、Catmull-Rom 平滑曲线和统一尾线路径规划。 | 首次曲线采样不足,首次轨迹约束会让远端过早前移。 | 要求增加曲线采样密度,改为横向约束并保留纵向滞后;通过路线间距测试检查尾线分离。68 项测试通过,并生成 121 帧演示 GIF。 |
这些过程说明 AIGC 生成的代码不能直接视为最终实现,必须经过运行、测试、截图检查和人工修改。
五、测试结果
5.1 自动化测试命令
在项目根目录执行:
.venv\Scripts\python.exe -m pytest -q
# 94 passed in 8.33s
.venv\Scripts\python.exe -m compileall -q core data ui tools tests main.py
# PASS
当前测试分布为:路径检测 10 项、游戏状态 23 项、界面交互 14 项、尾线和曲线 7 项、路径规划 1 项、进度保存 3 项,参数化用例展开后共 94 项。
5.2 作业要求测试矩阵
| 编号 | 测试内容 | 预期结果 | 实际结果 | 状态 |
|---|---|---|---|---|
| T01 | 点击前方无阻挡的箭头 | 箭头飞出并消失 | 棋盘立即移除箭头,动画完成后清除动画记录 | PASS |
| T02 | 点击前方有阻挡的箭头 | 箭头保留,失误减 1 | 箭头仍在原位,3 次机会按一次点击减为 2 次 | PASS |
| T03 | 点击边缘且朝向棋盘外的箭头 | 正常消失且不越界 | 1×1 边界测试通过 | PASS |
| T04 | 清除本关全部箭头 | 显示通关并可进入下一关 | L1/L2/L3 自动点击完成,通关和下一关按钮通过 | PASS |
| T05 | 失误次数耗尽 | 显示失败并允许重开 | 连续三次碰撞后进入失败页,重开恢复 3 次机会 | PASS |
| T06 | 游戏中重新开始 | 布局和机会恢复 | 原始箭头、失误次数、动画和计时均恢复 | PASS |
| T07 | 四种方向 | UP/DOWN/LEFT/RIGHT 判断一致 | 参数化路径测试通过 | PASS |
| T08 | 移除 blocker 后再次判断 | 路径变为畅通 | 移除阻挡后路径测试和连续点击测试通过 | PASS |
| T09 | 其他行列箭头 | 不应误判为阻挡 | 无关行列测试通过 | PASS |
| T10 | 重复非法点击 | 一次反馈只扣一次 | 动画期间重复点击均被忽略,反馈结束后才可再次点击 | PASS |
| T11 | 最后一箭 | 动画结束后再显示结果 | 最后一支尾线结束前仍是游戏页,结束后进入通关页 | PASS |
| T12 | 关卡切换 | 新关卡重新初始化 | 下一关布局、机会和动画状态正确重置 | PASS |
| T13 | 提示功能 | 高亮合法箭头且消耗提示次数 | 提示测试通过,每局最多 3 次,不改变棋盘 | PASS |
| T14 | 撤销和 AI 求解扩展 | 撤销可恢复,AI 能自动清关 | 状态、界面和各难度 AI 求解测试通过 | PASS |
| T15 | 正式关卡可通关 | 每关存在合法顺序 | 原始三关及简单/中等/困难生成关卡均通过自动求解;需补本人实际试玩记录 | PASS(自动化)/ RISK(手工) |
5.3 非功能检查
| 检查项 | 结果 |
|---|---|
| 非阻塞动画 | update(dt) 推进动画,没有 time.sleep() |
| Pygame 窗口退出 | UI smoke 脚本注入 QUIT 事件后正常退出 |
| 30 FPS 与 144 FPS | 尾线固定子步长测试结果一致 |
| 绘制纯度 | 重复 draw() 不推进尾线物理状态 |
| 素材来源 | 箭头和界面图形由 Pygame 图元绘制,没有使用商业游戏素材、代码、音效或关卡 |
| 敏感信息 | 当前工作区未发现密码、Cookie、API Key 或 Token |
六、PSP 表格
下表根据开发阶段记录整理,实际耗时为当前开发记录的估算值;提交前可以按照个人真实投入时间修改。
| 任务 | 预估耗时(小时) | 实际耗时(小时) | 差异(小时) |
|---|---|---|---|
| 需求分析与游戏设计 | 1.0 | 0.8 | -0.2 |
| Python 与 Pygame 学习 | 1.0 | 0.8 | -0.2 |
| 游戏界面实现 | 2.0 | 2.0 | 0.0 |
| 路径与碰撞逻辑实现 | 1.5 | 1.2 | -0.3 |
| 关卡设计与可解性验证 | 1.0 | 1.0 | 0.0 |
| AIGC 辅助开发 | 1.5 | 1.8 | +0.3 |
| 测试与修改 | 1.5 | 1.8 | +0.3 |
| README 与博客撰写 | 1.0 | 0.6 | -0.4 |
| 合计 | 10.5 | 10.0 | -0.5 |
耗时偏差的主要原因是:路径检测和基础状态逻辑较容易拆分测试;柔性尾线、曲线连续性、尾线分离和界面截图检查需要多轮迭代,因此测试与 AIGC 协作时间相对增加。
七、心得体会
这次作业让我认识到,一个规则看起来很简单的小游戏,仍然需要同时处理数据模型、路径算法、输入事件、状态切换、动画时序和测试证据。最基础的路径判断如果和界面代码混在一起,后续很难验证四个方向和边界情况;将 is_path_clear() 单独实现并用 pytest 覆盖后,核心规则就可以脱离窗口快速回归。
AIGC 对需求拆分、样板代码生成、测试用例设计和问题定位帮助明显,尤其适合快速搭建 Arrow、Board、Game 等模块。但 AIGC 生成的代码并不一定能直接使用:本项目中曾出现类型导入错误、重复点击重复扣分、尾线远端过早移动和曲线采样不足等问题,都是通过实际运行或测试才发现并修正的。这说明开发者仍然需要理解代码、设计反例、阅读测试失败信息,并对 AI 的实现负责。
在界面实现方面,我体会到“能运行”和“看起来舒服”是两件事。动画不能阻塞事件循环,结果页不能早于最后一条尾线结束,尾线还需要避免重叠;这些问题不能只靠静态阅读代码判断,必须结合逐帧更新、截图和独立演示检查。
后续如果继续完善项目,我会优先补充本人真实试玩记录、完善更系统的关卡解法记录,并进一步增加声音反馈和可选的计时/星级展示。在此基础上再考虑打包和发布,保证附加功能不会影响基础规则的稳定性。
八、运行方式
python -m venv .venv
.\.venv\Scripts\python.exe -m pip install -r requirements-dev.txt
.\.venv\Scripts\python.exe main.py
运行柔性尾线独立演示:
.\.venv\Scripts\python.exe -m tools.rope_demo
运行测试:
.\.venv\Scripts\python.exe -m pytest -q

浙公网安备 33010602011771号