2026秋软件工程个人作业(第二次)—— 一箭又一箭
| 项目 | 内容 |
|---|---|
| 这个作业属于哪个课程 | 202601 软件工程(福州大学 计算机与大数据学院) |
| 这个作业要求在哪里 | 作业链接 |
| 这个作业的目标 | 使用 Python 和 AIGC 完成"一箭又一箭"小游戏 |
| 学号 | 102401414 |
| GitHub 仓库 | https://github.com/shukai123123/arrow-game |
一、项目展示
开始界面 (标题 + 规则说明 + 开始按钮): 游戏界面 (顶栏显示当前关卡 / 剩余箭头 / 剩余失误,右上角重新开始按钮): 点击可飞箭头后 (箭头飞出消失,剩余箭头数更新): 通关界面 (半透明遮罩 + "本关通过" + 下一关按钮): 失败界面 (失误耗尽后红色提示 + 重新开始按钮): 棋盘上有若干带方向的箭头(↑↓←→)。玩家点击某个箭头: 棋盘用二维整数列表表示,每个格子的取值含义如下: 方向到坐标增量的映射: 每个关卡就是一个二维列表,例如第 1 关: 是整个游戏最核心的函数。它从箭头所在格出发,沿方向一格一格走向边界,只要途中遇到任何非空格子(另一个箭头)就立刻判定为"被挡住";一路走到边界都为空则判定为"可飞出"。 为什么这样写就能正确?因为题目约定"路径检测只需判断同一行或同一列、箭头与边界之间是否还有其他箭头",所以沿单一方向线性扫描即可,不需要 BFS/DFS。 关键边界处理:循环条件是 ,天然处理了"箭头本来就在边界上、朝向棋盘外"的情况——此时相邻格就越界,循环不执行,直接返回 True,不会数组越界。 三元组: 界面层(main.py)只负责画图和读鼠标,所有"能不能飞""清没清空"都交给 判断。 设计完关卡后,我没有靠"觉得能通",而是在测试脚本里写了一个贪心校验:反复找任意一个当前可飞的箭头消除,直到清空或死局。结果三个关卡分别用 3、4、4 步清空,证明都存在合法通关顺序。 开发过程中使用了豆包等 AIGC 编程工具辅助,以下记录 3 次有代表性的协作。 我最初的想法很模糊,只知道"箭头前面不能有别的箭"。我把游戏规则原样描述给 AI,问它"这个路径判断最核心的循环应该怎么写"。 AI 给出的关键点让我一下子明白了:要从箭头相邻的下一格开始扫,而不是从箭头自己开始,否则会把自己误判成阻挡。它还提醒我用 我一开始把判断和画图全写在一个函数里,结果想测路径判断时一运行就弹出游戏窗口,很不方便。我把这个困扰告诉 AI,它建议: 这样我就能用 在命令行直接跑测试,不用开窗口。这是这次作业里对我启发最大的一步——原来"能测试"的代码结构和"能跑"的代码结构是两回事。 自测时我发现:当箭头在第一行、方向朝上时,偶尔报 测试(边缘箭头不越界)就稳定通过了。 说明:AI 给出的代码我都没有直接照抄,而是先读懂它在做什么,再自己敲进项目,并补了对应的测试。 运行 ,共 20 个断言全部通过。对应作业要求的 T01~T06: 附加校验:三个关卡分别用 3、4、4 步可解,无死局。 实际耗时普遍高于预估,主要花在"pygame 在 Python 3.14 上装不上、换 pygame-ce"以及界面调优上。 本项目代码与素材均为本人借助 AIGC 工具独立编写,未使用原商业游戏的代码、美术或关卡。二、项目介绍
2.1 游戏规则
2.2 界面设计
START(开始)、PLAYING(游戏中)、LEVEL_CLEAR / GAME_OVER / ALL_CLEAR(结果),由一个状态机驱动。2.3 主要功能与特色
↑↓←→ 绘制。core.py 只做纯数据判断,不 import pygame,因此可以在无图形环境下直接单元测试。三、实现思路
3.1 箭头、方向与关卡的表示
EMPTY = 0 # 空
UP = 1 # 上
DOWN = 2 # 下
LEFT = 3 # 左
RIGHT = 4 # 右
DIRS = {UP: (-1, 0), # 行号减小DOWN: (1, 0), # 行号增大LEFT: (0, -1), # 列号减小RIGHT: (0, 1), # 列号增大
}
→ · ↑
· · ·
· · ←
3.2 路径检测(核心算法)
can_fly(board, row, col)def can_fly(board, row, col):direction = board[row][col]dr, dc = DIRS[direction]r, c = row + dr, col + dc # 从相邻一格开始扫,不看自己while 0 <= r < rows(board) and 0 <= c < cols(board):if board[r][c] != EMPTY: # 路上还有别的箭头 → 挡住return Falser += drc += dcreturn True # 一路走到边界都空 → 可飞
0 <= r < rows and 0 <= c < cols3.3 点击与状态流转
try_click(board, row, col) 封装了一次点击的结果,返回 {ok, flew, blocked}ok=False,无任何变化;flew=True,界面层播放飞出动画;blocked=True,界面层记一次失误并播放抖动动画。core.py3.4 关卡的可解性验证
四、AIGC 使用过程
协作 1:让 AI 帮我理清"路径检测"到底该扫什么
while 0<=r 这种写法天然处理边界。这正好对应上面的 can_fly。协作 2:让 AI 帮我把"逻辑"和"界面"拆开
can_fly、count_arrows、关卡数据抽到一个不 import pygame 的 core.py;main.py 只负责读鼠标、调 core、画图。python test_core.py协作 3:让 AI 帮我排查"向上扫会越界"的 bug
IndexError。我把报错堆栈和 can_fly 贴给 AI。AI 一眼指出:如果循环写成 while True: r += dr 而没有先判断范围,第一行的箭头 r-1 就会变 -1。它建议把"边界判断"写进 while 条件里。改完后 T03
五、测试结果
python test_core.py编号
测试内容
预期结果
实际结果
是否通过
T01
点击前方无阻挡的箭头
箭头飞出棋盘并消失
箭头被置空,
flew=True✅ 通过
T02
点击前方有阻挡的箭头
箭头不消失,失误次数 -1
箭头保留,
blocked=True✅ 通过
T03
点击位于边缘且朝向棋盘外的箭头
正常飞出,不越界
上/下/右边界箭头均可飞,越界访问不抛异常
✅ 通过
T04
消除本关全部箭头
显示通关并进入下一关
三关按验证顺序点完后
level_cleared=True✅ 通过
T05
失误次数耗尽
显示失败并允许重新开始
连续 3 次被挡后累计失误 = MAX
✅ 通过
T06
游戏进行中重新开始
箭头布局和失误次数恢复
深拷贝恢复初始关卡,失误归零
✅ 通过
六、PSP 表格
任务
预估耗时(小时)
实际耗时(小时)
差异(小时)
需求分析与游戏设计
1.0
1.5
+0.5
Python 与图形库学习
1.5
2.0
+0.5
游戏界面实现
2.0
2.5
+0.5
路径与碰撞逻辑实现
1.5
1.5
0
关卡设计
0.5
1.0
+0.5
AIGC 辅助开发
1.0
1.0
0
测试与修改
1.0
1.5
+0.5
README 与博客撰写
1.5
2.0
+0.5
合计
10.0
13.0
+3.0
七、心得体会
test_core.py 一键验证 T01~T06,而不是每次都开着窗口手点。这是这次作业最大的收获。