软件工程第二次个人作业
| 这个作业属于哪个课程 | https://edu.cnblogs.com/campus/fzu/202601SofwareEngineering |
|---|---|
| 这个作业要求在哪里 | https://edu.cnblogs.com/campus/fzu/202601SofwareEngineering/homework/16717 |
| 这个作业的目标 | 使用 Python 和 AIGC 完成"一箭又一箭"小游戏 |
| 学号 | 072408137 |
项目展示



游戏规则
点击箭头后,程序检查它前进方向上的路径。
前方没有其他箭头时,该箭头飞出棋盘并消失。
前方存在阻挡时,箭头变红、向前移动后返回,剩余失误次数减一。
每关初始有三次失误机会,清空棋盘即通关,失误次数耗尽则失败。
点击“重新开始”,恢复当前关卡的初始布局和失误次数。
开发平台
| 项目 | 使用情况 |
|---|---|
| 开发平台 | macOS |
| 编程语言 | Python |
| 图形库 | Pygame |
| 自动测试环境 | Python 3.9.6、Pygame 2.6.1 |
| AIGC 工具 | ChatGPT、Codex |
| 版本管理 | Git、GitHub |
实现思路
-
箭头、方向与关卡的表示
棋盘用字典 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,不用动逻辑代码。
- 路径检测
判断某个箭头能否飞出,核心是沿它的方向一格一格走,检查到边界前是否撞到别的箭头:
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)
- 关卡可解性求解(保证每关能通关)
因为“移除一个箭头只会减少阻挡、不会增加阻挡”,所以只要存在通关顺序,
贪心法(反复移除任意一个当前可飞出的箭头)一定能找到。若某一步没有任何箭头能飞出却还有剩余,则本关死局。
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号