软件工程第二次作业
软件工程第二次个人作业:用 Python 与 Codex 完成“一箭又一箭”
| 项目 | 内容 |
|---|---|
| 这个作业属于哪个课程 | 202601 软件工程 |
| 这个作业要求在哪里 | 软件工程课程第二次个人作业 |
| 这个作业的目标 | 使用 Python 和 AIGC 完成“一箭又一箭”小游戏 |
| 姓名、学号 | 杜玉鹤,162404109 |
| GitHub 仓库 | myp208116/arrow-game |
一、项目展示
游戏采用浅色背景和深绿色状态面板。首页可开始第一关,也可直接选择三个关卡。游戏界面中,棋盘与状态信息分开放置,剩余箭头、机会和计时始终可见。
开始界面

游戏过程与碰撞


发生碰撞时,被点击的箭头变红并晃动,阻挡它的格子显示淡红色背景,底部显示提示。动画结束后扣除一次机会,两个箭头都保留在棋盘上。
通关与失败


演示 GIF

以上截图与 GIF 由自动化脚本向实际游戏事件处理函数传入鼠标事件,经过同一套更新和绘制流程生成。演示展示开始、碰撞、提示、第一关通关、进入下一关、失败和重新开始。它属于自动化演示,本人试玩情况另行记录。
二、项目介绍
1. 游戏规则
每个箭头占一格,朝向上、下、左、右之一。点击后检查它到棋盘边界之间的所有前方格子:
- 没有其他箭头:沿指定方向飞出,然后从棋盘删除。
- 存在其他箭头:播放碰撞反馈,保留箭头,扣 1 次机会。
- 每关初始有 3 次机会,机会耗尽后失败,可重新开始。
- 清空本关后显示结果,再由玩家点击进入下一关。
- 空白格、棋盘外和右键点击均不扣次数。
2. 关卡设计
| 关卡 | 棋盘大小 | 箭头数量 | 设计重点 |
|---|---|---|---|
| 初见 | 5 × 5 | 12 | 理解前方阻挡与边界 |
| 交错 | 6 × 6 | 24 | 观察行列之间的消除顺序 |
| 回响 | 7 × 7 | 38 | 处理更密集的阻挡关系 |
三关均包含四个方向,关卡数据固定保存在 JSON 文件中。重新开始恢复同一布局,便于复盘。可解构造和求解器均已验证三关存在完整通关顺序。
3. 附加功能
实现了首页选关、合法箭头提示、计时、星级、本次运行内的完成标记、切换窗口后自动暂停,以及 Windows 独立程序打包。
提示会高亮一枚当前可以移除的箭头,不扣机会,但会影响星级。通关星级为 max(1, 3 - 失误次数 - 提示次数)。星级不依赖操作速度,计时只用于展示。
三、实现思路
1. 模块划分
core.py 实现箭头、棋盘、路径判断和游戏状态;levels.py 负责生成与加载关卡;app.py 负责界面、鼠标事件、动画和场景切换。这样的划分允许先测试规则,再测试界面流程。
2. 箭头和方向表示
每个箭头包含行号、列号、方向三个字段。棋盘使用字典保存占用关系:键是 (row, col),值是箭头对象。
| 方向 | 行变化 dr | 列变化 dc |
|---|---|---|
| 上 | -1 | 0 |
| 下 | 1 | 0 |
| 左 | 0 | -1 |
| 右 | 0 | 1 |
内部统一使用行列坐标,绘制时才转换为屏幕的 x、y。先统一约定可以避免把“向上”错误写成“向左”。
3. 路径检测
核心扫描逻辑如下,对应 Board.path():
dr, dc = arrow.direction.value
row, col = arrow.row + dr, arrow.col + dc
cells = []
while self.inside(row, col):
cells.append((row, col))
row, col = row + dr, col + dc
随后 blocker() 在这些格子中寻找第一个被占用的位置;若没有阻挡物,can_exit() 返回真。
从下一格开始是为了排除箭头自己。每次先判断行列是否在棋盘内,再访问占用信息,所以向上、向左不会出现负下标误判。只检查当前方向,位于后方或其他行列的箭头不会阻挡它。
一次检测最多扫描一整行或一整列,时间复杂度为 O(max(行数, 列数))。本项目最多 7 × 7 格,无需引入复杂的空间索引。
4. 如何保证关卡可解
如果完全随机设置位置和方向,可能出现两枚箭头相向而行、互相阻挡的死局。因此生成阶段先选定占用格,然后重复以下步骤:
- 在当前剩余占用格中,找出存在无遮挡出口的“位置、方向”组合。
- 随机选择一个组合,为这个格子确定方向。
- 从临时占用表中删除它,记录操作顺序。
- 所有格子都处理完成后,将箭头完整保存为关卡。
按记录顺序消除时,每一步前方都没有尚未消除的箭头,因此构造过程本身提供了一个解。程序还使用独立求解器逐轮移除合法箭头,复核正式关卡。
求解器可以使用贪心方法,因为移除箭头只会减少障碍,不会让其他箭头的路径变得更差。如果棋盘仍非空但没有任何合法箭头,才返回无解。
5. 动画和游戏状态
场景分为开始、游戏和结果三类。暂停是游戏场景的一个状态。点击箭头时建立动画对象,动画期间不接受新的箭头点击;动画结束后才更新逻辑棋盘、失误次数及胜负状态。
这个顺序避免重复点击造成重复扣次数,也让最后一枚箭头真正飞出之后再出现通关界面。玩家在动画中点击重新开始,会清除旧动画并重建游戏状态。
四、AIGC 使用过程
使用工具:Codex。本次开发在一次连续对话中按阶段推进,以下是实际发生的三个代表性协作环节,没有虚构额外的历史提问或本人手工改码经历。
| 子任务 | 我提出的要求或依据 | AI 实现或提供了什么 | 实际效果 | 人工修改 |
|---|---|---|---|---|
| 规则与关卡 | 提供作业链接和完整正文,要求完成四方向判断、三关及失误机制 | 拆分 Board/Game;实现路径扫描、关卡构造、独立求解器和规则测试 | 首轮 14 项规则测试通过;三关全部可解;生成器验证 125 个布局 | 本人补充完整原题,确认需求;此阶段未手改代码 |
| 界面与动画 | 按原题要求制作开始、游戏、结果界面,加入飞出、碰撞和重新开始 | 实现 pygame 界面、动画锁定、碰撞后结算、鼠标操作和切关;添加提示、计时、星级 | 通过真实鼠标事件跑通三关,连点与动画中重开行为正常,生成截图和 GIF | 本人确认“杜玉鹤 / 162404109”及目标仓库;实现由 AI 完成 |
| 测试、复核与交付 | 按原题 T01—T06、README、博客和真实 AI 记录要求整理交付 | 执行 23 项测试、检查实际截图、修正菜单文字间隔和计时截断问题、准备说明文档并打包 | 23 项测试通过;截图覆盖开始、碰撞、通关与失败;独立程序启动检查结果见仓库记录 | 本人完成实际试玩,并根据真实投入补充 PSP 与学习心得。 |
开发过程中保留了逐阶段更新的 docs/development.md 和 Git 提交。具体出现并修正的问题包括:
- 开始界面的说明文字离装饰箭头过近。检查实际截图后,将文字下移,保留间隔。
- 首版游戏循环将帧间隔限制为 0.05 秒,遇到低帧率会少计时。代码复核后改为使用实际帧间隔,暂停时仍停止累计。
- 发布初期,GitHub 连接账号与目标仓库不一致,没有写权限;重新连接本人账号后已完成上传。本机 Git 直连失败,因此通过 GitHub 接口按真实开发阶段导入,各阶段代码树与本地完全一致。原始编号、开发时间及线上提交对应关系保存在
docs/publication.md,原始本地历史也保留在交付包中。
五、测试结果
测试环境为 Windows、Python 3.14.5、pygame-ce 2.5.8。共 23 项自动化测试通过,失败 0 项,错误 0 项。测试采用 unittest,界面测试使用 pygame 的离屏显示模式和真实事件处理函数。
| 编号 | 测试内容 | 预期结果 | 实际结果 | 是否通过 |
|---|---|---|---|---|
| T01 | 前方无阻挡 | 飞出并消失,不扣次数 | 四方向均可消除,动画后只删除一次 | 通过 |
| T02 | 前方有阻挡 | 不消失,机会减 1 | 四方向阻挡识别正确,红色晃动后扣 1 次 | 通过 |
| T03 | 边缘且向外 | 正常消失,无越界 | 四条边分别验证通过 | 通过 |
| T04 | 清空本关 | 显示通关并进入下一关 | 三关依次清空,前两关能切关,最后一关能返回或重玩 | 通过 |
| T05 | 失误耗尽 | 显示失败并允许重开 | 第三次碰撞结束后失败,重开恢复 3 次机会 | 通过 |
| T06 | 中途重新开始 | 布局和次数恢复 | 恢复初始布局、机会、计时和提示;取消旧动画 | 通过 |
额外验证了空白点击、鼠标右键、后方箭头不阻挡、最近阻挡物、求解器识别死局、20 次连续点击仅处理一次、失焦暂停和提示正确性。
测试报告保存在 docs/test-report.json 和 docs/test-output.txt。
本人试玩记录
在完成自动化测试后,我又对三个正式关卡进行了实际试玩,确认游戏流程、碰撞反馈、重新开始和通关功能均能正常使用。
| 关卡 | 本人试玩 | 结果 |
|---|---|---|
| 初见 | 已完成 | 可正常通关 |
| 交错 | 已完成 | 可正常通关 |
| 回响 | 已完成 | 可正常通关 |
六、PSP 表格
预估值在实现开始前记录,表示 AI 建议的学生实施预算。实际值由本人根据真实投入填写;差异 = 实际 - 预估。AIGC 辅助开发一行只统计独立阅读、交互及检查 AI 输出的时间,避免与编程、测试等阶段重复计算。
| 任务 | 预估耗时(小时) | 实际耗时(小时) | 差异(小时) |
|---|---|---|---|
| 需求分析与游戏设计 | 0.5 | 1.0 | +0.5 |
| Python 与图形库学习 | 0.5 | 1.0 | +0.5 |
| 游戏界面实现 | 1.5 | 1.5 | 0.0 |
| 路径与碰撞逻辑实现 | 1.0 | 1.5 | +0.5 |
| 关卡设计 | 0.5 | 1.0 | +0.5 |
| AIGC 辅助开发 | 0.5 | 1.0 | +0.5 |
| 测试与修改 | 1.0 | 0.5 | -0.5 |
| README 与博客撰写 | 1.0 | 0.5 | -0.5 |
| 合计 | 6.5 | 8.0 | +1.5 |
七、开发总结与心得
本次协作中,AI 的直接帮助是把完整作业拆成规则、关卡、界面、测试和交付几个部分,并快速形成可以运行和验证的实现。规则被独立出来后,不需要每次打开窗口就能检查四个方向和边界条件;界面完成后,再使用鼠标事件验证动画和切关流程。
这次实际复核也说明,“代码能运行”仍不足以保证体验完整。菜单说明文字与装饰图案的间隔需要看真实截图;计时是否准确需要检查帧间隔处理;上传到 GitHub 还取决于账号权限与网络。这些问题必须结合实际结果判断,不能仅根据 AI 对自己的说明下结论。
理解本项目的关键是三个约定:坐标统一使用行列、路径只扫描前方、删除箭头不会增加阻挡。在此基础上,碰撞检测、可解关卡构造和贪心求解都可以解释清楚。Git 中分阶段保存规则、界面、修复和文档,也便于回看每一步做了什么。
实际试玩时,我曾因为没有看清箭头前方是否存在阻挡而误点。后来我发现,通关时可以优先消除当前能够直接飞出棋盘的箭头,再根据棋盘变化逐步判断后续顺序。阅读代码后,我也理解了 Board.path() 的基本作用,即沿箭头方向逐格检查路径,判断前方是否存在其他箭头,从而确定该箭头能否移除。
八、运行方法与资料
安装运行依赖后执行 python main.py,或使用本地交付的 ArrowGame.exe。详细步骤、操作说明和资源位置见仓库 README。
参考资料:
所有箭头、按钮和关卡由本项目代码创建,未使用商业原游戏或他人的完整项目及素材。程序调用系统中文字体,不分发字体文件。

浙公网安备 33010602011771号