软件工程课程第二次个人作业
一、作业信息
| 项目 | 内容 |
|---|---|
| 这个作业属于哪个课程 | [https://edu.cnblogs.com/campus/fzu/202601SofwareEngineering] |
| 这个作业要求在哪里 | [https://edu.cnblogs.com/campus/fzu/202601SofwareEngineering/homework/16717] |
| 这个作业的目标 | 使用 Python 和 AIGC 完成“一箭又一箭”小游戏,熟悉对AI的使用结果调试 |
| 学号 | [102401622] |
| GitHub 仓库 | [https://github.com/drkalerq/work1] |
二、项目展示
-
开始界面:标题“一箭又一箭” + 玩法提示 + “开始游戏”按钮。
![image]()
-
游戏过程:棋盘上的彩色箭头、顶部信息栏(关卡 / 剩余箭头 / 剩余失误)、底部“重新开始 / 提示”按钮。
![image]()
-
通关界面:半透明遮罩 + “本关通关!” + “下一关”按钮。
![image]()
-
失败界面:半透明遮罩 + “失败” + “重新开始”按钮。
![image]()
三、项目介绍
游戏规则
棋盘由网格组成,每格最多一个箭头,方向为上 / 下 / 左 / 右。点击箭头时:
- 若它前进方向上直到棋盘边界之间没有其他箭头,箭头沿该方向飞出棋盘并消失;
- 若路径上存在其他箭头,箭头不能飞出,会晃动并变红作为碰撞反馈,同时消耗 1 次失误;
- 清空本关全部箭头 → 通关,进入下一关;失误次数耗尽 → 失败,可重新开始。
界面与功能
- 三个界面:开始界面、游戏界面、通关/失败界面(全部用 Pygame 绘制)。
- 游戏界面显示:当前关卡名、剩余箭头数、剩余失误数、棋盘、重新开始按钮、提示按钮。
- 动画反馈:箭头飞出有平移淡出动画;被挡时有左右晃动 + 变红的反馈。
- 共 4 个可通关关卡(3×3 → 6×6,难度递增),均经程序验证存在通关顺序。
- 扩展功能:提示按钮(高亮一个当前可飞出的箭头,复用关卡求解逻辑)。
特色
- 核心逻辑(
game.py)与界面(main.py)分离,逻辑可单独用unittest测试,不依赖 Pygame。 - 自带关卡可解性求解器,提交前自动校验每一关都能通关,避免“看似能玩其实死局”。
四、实现思路
1. 箭头、方向与关卡的表示
- 棋盘用字典
arrows: (row, col) -> Arrow保存当前还活着的箭头,row/col为网格坐标。 - 方向用字符
'U'/'D'/'L'/'R',并映射到位移量,便于统一处理四个方向:
class Arrow:
DIRS = {'U': (-1, 0), 'D': (1, 0), 'L': (0, -1), 'R': (0, 1)}
- 关卡用字典描述:
rows / cols / mistakes / arrows(箭头列表(行, 列, 方向))。
这样做的好处是“数据即关卡”,改关卡只改levels.py,不用动逻辑代码。
2. 路径检测
判断某个箭头能否飞出,核心是沿它的方向一格一格走,检查到边界前是否撞到别的箭头:
def is_path_clear(self, arrow):
dr, dc = Arrow.DIRS[arrow.direction]
r = arrow.row + dr
c = arrow.col + dc
while 0 <= r < self.rows and 0 <= c < self.cols: # 还在棋盘内才检查
if (r, c) in self.arrows:
return False # 路上有别的箭头 -> 被挡住
r += dr
c += dc
return True # 一路畅通直到边界 -> 可飞出
关键点:while 的循环条件 0 <= r < rows and 0 <= c < cols 同时完成了边界判断——
一旦走出棋盘就停止,因此不会数组越界,天然处理了“边缘且朝外的箭头”(T03)。
点击时根据返回值决定飞出还是碰撞:
def click(self, row, col):
if self.state != 'playing':
return None
arrow = self.arrows.get((row, col))
if arrow is None:
return None
if self.is_path_clear(arrow):
del self.arrows[(row, col)] # 飞出并消失
if not self.arrows:
self.state = 'won'
return ('fly', arrow)
else:
self.mistakes += 1 # 碰撞:失误 +1
if self.mistakes >= self.mistakes_allowed:
self.state = 'lost'
return ('block', arrow)
3. 关卡可解性求解(保证每关能通关)
因为“移除一个箭头只会减少阻挡、不会增加阻挡”,所以只要存在通关顺序,
贪心法(反复移除任意一个当前可飞出的箭头)一定能找到。若某一步没有任何箭头能飞出却还有剩余,则本关死局。
def is_level_solvable(level):
remaining = {(r, c): d for (r, c, d) in level['arrows']}
rows, cols = level['rows'], level['cols']
def clear(r, c, d):
dr, dc = Arrow.DIRS[d]
r2, c2 = r + dr, c + dc
while 0 <= r2 < rows and 0 <= c2 < cols:
if (r2, c2) in remaining:
return False
r2 += dr; c2 += dc
return True
while remaining:
progressed = False
for (r, c), d in list(remaining.items()):
if clear(r, c, d):
del remaining[(r, c)]; progressed = True; break
if not progressed:
return False
return True
提交前我对 levels.py 里 4 关逐一调用该函数,全部返回 True,确认都可通关。
五、AIGC 使用过程
| 子任务 | 借助何种 AIGC 技术 | AI 实现或提供了什么 | 效果如何 | 人工修改 |
|---|---|---|---|---|
| 路径检测算法 | hy3 | 给出四方向统一用位移量 DIRS 表示、并沿方向遍历检测是否撞箭头的 is_path_clear 思路与代码 |
向上/下/左/右判断均正确,且用 while 边界条件天然避免越界 |
我补充了中文注释,并单独写了 T03 边界用例验证“边缘朝外箭头不报错” |
| 关卡可解性验证 | hy3 | 给出贪心求解器 is_level_solvable,自动判断关卡是否存在通关顺序 |
能正确识别死局,我据此校验并挑选了 4 个可通关关卡 | 我把它也用于“提示”功能(找当前可飞出的箭头),并在博客中解释其原理 |
| 碰撞动画与反馈 | hy3 | 补全“被挡箭头晃动 + 变红”的 shaking 计时器,以及“飞出”平移动画的 flying 列表 |
晃动与飞出动画基本可用,视觉反馈清晰 | 我调整了晃动幅度(amp=6)和飞出速度(FLY_SPEED=16),并给箭头加了黑色描边更清晰 |
六、测试结果
环境:Python 3.13 + Pygame 2.6,运行 python -m unittest test_game -v,9 项全部通过。
| 编号 | 测试内容 | 预期结果 | 实际结果 | 是否通过 |
|---|---|---|---|---|
| T01 | 点击前方无阻挡的箭头 | 箭头飞出棋盘并消失 | 剩余箭头数 -1,返回 fly |
✅ |
| T02 | 点击前方有阻挡的箭头 | 箭头不消失,失误次数减 1 | 箭头仍在,失误 +1,返回 block |
✅ |
| T03 | 点击位于边缘且朝向棋盘外的箭头 | 正常消失,不越界报错 | 四角/四边朝外箭头均 fly,无异常 |
✅ |
| T04 | 消除本关全部箭头 | 显示通关(state=won) | 最后一箭点击后 state=='won' |
✅ |
| T05 | 失误次数耗尽 | 显示失败(state=lost) | 失误达上限后 state=='lost' |
✅ |
| T06 | 游戏进行中重新开始 | 箭头布局和失误次数恢复 | restart() 后箭头数、失误、状态均复位 |
✅ |
| — | 全部关卡可通关 | 4 关均存在通关顺序 | is_level_solvable 全部为 True |
✅ |
| — | 整关模拟通关 | 按策略能逐关通关 | TestPlayThrough 4 关均到达 won |
✅ |
七、PSP 表格
下表为示例,请按你自己的真实耗时填写“实际耗时”一列。
| 任务 | 预估耗时(小时) | 实际耗时(小时) | 差异(小时) |
|---|---|---|---|
| 需求分析与游戏设计 | 0.5 | [ 0.5 ] | [ 0 ] |
| Python 与图形库学习 | 1.0 | [ 1.0 ] | [ 0 ] |
| 游戏界面实现 | 1.0 | [ 0.5 ] | [ -0.5 ] |
| 路径与碰撞逻辑实现 | 1.0 | [ 1.0 ] | [ 0 ] |
| 关卡设计 | 1.0 | [ 1.0 ] | [ 0 ] |
| AIGC 辅助开发 | 2.0 | [ 3.0 ] | [ 1.0 ] |
| 测试与修改 | 1.0 | [ 1.0 ] | [ 0 ] |
| README 与博客撰写 | 2.0 | [ 3.0 ] | [ 1.0 ] |
| 合计 | 9.0 | [ 10.5 ] | [ 1.5 ] |
八、心得体会
- AI 带来的帮助:路径检测、可解性求解、碰撞动画这类“有固定模式”的代码,AI 一次就能给出可运行骨架,
我主要在做“理解与微调”——比如UI设计提示,重启游戏失效的修复。 - 自己的收获:把“游戏规则”翻译成“数据结构 + 算法”的过程,比我预想中更锻炼抽象能力;
尤其是is_path_clear用while边界条件同时完成越界保护、碰撞判定用“移除箭头只会减少阻挡”来说明贪心正确的那一步,
让我真正理解了为什么这个游戏“一定存在通关顺序”。在AI生成结果后部分功能可能无法实现,要经过测试后才能发现问题。




浙公网安备 33010602011771号