软件工程第二次作业

用 Python + AIGC 开发"一箭又一箭"箭头解谜小游戏

项目 内容
这个作业属于哪个课程 https://edu.cnblogs.com/campus/fzu/202601SofwareEngineering
这个作业要求在哪里 https://edu.cnblogs.com/campus/fzu/202601SofwareEngineering/homework/16717
这个作业的目标 使用 Python 和 AIGC 完成"一箭又一箭"小游戏
学号 102401529
GitHub 仓库 https://github.com/Aranya12138/Arrow-Game

一、项目展示

游戏共 8 个关卡(全部为"长箭"玩法),另有无限挑战模式。运行方式:安装依赖后 python main.py,或直接双击仓库 Release 中的 一箭又一箭.exe(PyInstaller 打包,无需 Python 环境)。

开始界面 游戏界面 碰撞反馈
start game collision
关卡选择 进阶关卡(满格) 通关结算(星级)
select advanced win

二、项目介绍

游戏规则

  • 棋盘上散布着若干箭头,每支箭头占据一个或多个格子(长箭),可以在中间拐弯
  • 点击一支箭头:若它头部前方到棋盘边界之间没有其他箭头,它就会沿自身轨道滑出棋盘并被消除;
  • 若前方有阻挡,箭头无法飞出并消耗一次失误机会(伴随撞击回弹、晃动、变红和"被阻挡"飘字);
  • 清空全部箭头即可过关;失误耗尽则本关失败,可重新开始。

界面设计

  • 开始界面:标题、玩法说明面板、开始游戏 / 继续游戏 / 选择关卡;
  • 游戏界面:HUD 信息栏(关卡、剩余箭头、用时、失误机会圆点)+ 操作按钮(重新开始、撤销、提示、自动求解);
  • 通关 / 失败 / 全部通关结算界面,通关显示星级(★,按剩余失误评定)与用时。

主要功能与特色

  • 8 个可通关关卡:第 1~2 关入门(5×5),第 5~8 关为满格关卡(36/36 个格子全部被箭头占据),全部经解算器验证存在通关顺序;
  • 长箭玩法:箭头横跨多格、可在中间拐弯(最多双拐角),箭尾与箭头衔接连成一体,拐弯处补画实心圆保证连贯;
  • 颜色系统:按"相邻箭头不同色"做图着色(确定性贪心,每关至少 3 种颜色);
  • 拓展功能:提示(高亮可飞出箭头)、AI 自动求解(逐步演示通关)、星级评价 + 计时、关卡选择、撤销上一步(快照恢复)、保存游戏进度(本地 JSON 存档,可"继续游戏")、无限挑战(随机生成必然可通关的关卡)、合成音效、PyInstaller 打包为 exe;
  • 零外部素材:全部界面用代码绘制,音效用标准库合成,中文字体使用系统微软雅黑——不涉及任何版权素材。

三、实现思路

1. 箭头、方向与关卡的表示

  • 方向用字符 → ← ↑ ↓ 表示,映射到行/列增量:DIRECTIONS = {"→": (0, 1), "←": (0, -1), "↑": (-1, 0), "↓": (1, 0)}
  • 单格箭头(基础规则,保留在代码中并由单元测试覆盖)就是棋盘格子里的一个方向字符;
  • 长箭用一串相邻格子表示(paths),头部方向 = 最后一段的行进方向。例如 [(0,0),(0,1),(0,2),(1,2)] 表示一支先向右、再向下的拐角长箭,头部在 (1,2) 指向下;
  • 关卡数据集中在 levels.pygrid 决定棋盘尺寸,paths 列出全部长箭。游戏核心逻辑(logic.py)完全不依赖 pygame,方便单元测试。

2. 路径检测方法(核心)

规则只有一句话:从箭头头部出发沿其方向走到边界,途中遇到任意其他箭头(或其身体)即被阻挡。单格箭头的检测:

def is_path_clear(grid, row, col):
    arrow = grid[row][col]
    if arrow == ".":
        return None
    dr, dc = DIRECTIONS[arrow]
    r, c = row + dr, col + dc          # 从箭头前方一格开始, 不含自身
    while 0 <= r < rows and 0 <= c < cols:   # 走到棋盘边界为止
        if grid[r][c] != ".":          # 途中遇到箭头 => 被阻挡
            return False
        r += dr
        c += dc
    return True                        # 无阻挡 => 可飞出

长箭只检测头部前方(身体在头部后面,不可能挡自己):

def path_blocked(self, idx):
    p = self.paths[idx]
    head = p["cells"][-1]
    dr, dc = DIRECTIONS[p["dir"]]
    r, c = head[0] + dr, head[1] + dc
    while 0 <= r < rows and 0 <= c < cols:
        if self.occupied(r, c):        # 任意箭头的身体都算阻挡
            return True
        r += dr
        c += dc
    return False

边界处理:越界(走出棋盘)直接结束循环,因此贴边且朝外的箭头必然畅通,不会有数组越界问题。

3. 关卡可解性验证(贪心解算器)

设计关卡时最容易犯的错误是死锁(如同一列两支箭头互相指向对方,永无解)。我实现了一个贪心解算器自动验证:反复把"当前所有可飞出"的箭头全部移除,若最终棋盘清空则说明有解。正确性在于:移除一支可飞出的箭头只会让其他箭头更容易飞出、绝不会产生新的阻挡,所以只要存在解,贪心一定能找到。所有关卡(含随机生成的)都必须通过这个验证才能发布。

4. 随机关卡生成(无限挑战)

采用逆向出题法:按"移除顺序的逆序"放置箭头——每支新箭的头部前方到边界之间不得经过已放置箭头的身体,且不得穿过自身。这样按放置的逆序移除箭头时,每一支被移除的箭头都满足"前方无阻挡",生成的每一关都必然可通关。用随机游走式生长(长蛇在空格中行走、随机拐弯)保证高填充率,实际可达满格 36/36。

四、AIGC 使用过程

本次开发全程借助 Claude Code(Anthropic 的 AI 编程助手,在 VS Code 中以对话方式辅助开发)。以下是 5 次最有代表性的协作过程(完整记录见仓库 AIGC使用记录.md):

子任务 借助何种 AIGC 技术 AI 实现或提供了什么 效果如何 人工修改
环境搭建与依赖排查 Claude Code 排查出 python 命令是 Microsoft Store 空壳别名(改用 py 启动器); 检测到 git 未安装并引导安装; 排查出官方 pygame 无 Python 3.14 预编译包且源码编译失败, 改用完全兼容的 pygame-ce 一次排查成功, 游戏库正常安装运行 提供 git 提交身份; 确认 pygame-ce 兼容性
路径检测 Claude Code 生成上/下/左/右四个方向的路径检测代码(is_path_clear / GameLogic.click) 四个方向的单元测试一次通过, 无越界错误 核对"不含箭头自身"这一边界条件后确认实现正确
关卡设计 Claude Code 设计 8 个关卡并编写贪心解算器自动验证每一关都存在通关顺序 解算器先后抓出 4 处死锁: 同列箭头互指 ×2、同排 L 形对向互锁 ×2(首版第 5 关 ↓↑ 互指死锁等); 逐处修正后全部通过验证 死锁全部由解算器发现, 人工按"外侧箭头解锁内侧箭头"调整方向
测试用例编写 Claude Code 生成 T01~T06 测试用例及关卡数据校验 首轮运行 2 个用例失败: T01 把第 1 关箭头数记成 5(实际 4 个); T06 的点击顺序导致 (0,0) 在被点前已变为可飞出 人工核对关卡布局后修正断言与点击顺序
存档恢复崩溃修复 Claude Code 修复"继续游戏"后崩溃: 存档 JSON 序列化把格子元组变成列表, 恢复后作为字典键时不可哈希(TypeError: unhashable type: 'list'), 且点击占据判定失效; 修复为路径格子在构建/恢复时统一规范化为元组 新增 JSON 往返回归测试与端到端回归(恢复后点被挡长箭并绘制不崩溃), 27 个测试全部通过 崩溃由本人真机试玩发现并反馈; 根因定位与修复由 AI 完成

其他 AI 协作还包括:碰撞动画(撞击回弹+晃动+变红+飘字)、图着色配色(相邻不同色)、随机关卡生成器(含一处"游走绕回自锁"Bug 的修复)、音效合成、PyInstaller 打包等,共 20 余条记录。

五、测试结果

采用自动化测试 + 手工试玩双轨。自动化测试共 27 个单元用例 + 端到端试玩脚本py -m unittest discover -s tests -vpy tools/smoke_test.py),全部通过。

编号 测试内容 预期结果 实际结果 是否通过
T01 点击前方无阻挡的箭头 箭头飞出棋盘并消失 箭头消失, 失误次数不变 通过
T02 点击前方有阻挡的箭头 箭头不消失, 失误次数减 1 箭头保留, 失误 3→2 通过
T03 点击位于边缘且朝向棋盘外的箭头 箭头正常消失, 不发生越界错误 正常消失, 无越界 通过
T04 消除本关全部箭头 显示通关并进入下一关 状态变为通关, 结算后进入下一关 通过
T05 失误次数耗尽 显示失败并允许重新开始 连点被挡箭头 3 次进入失败结算, 重开后恢复 通过
T06 游戏进行中重新开始 箭头布局和失误次数恢复 布局/失误/状态全部恢复初始 通过

补充测试(全部通过):

  • 四个方向的路径检测各覆盖"自由"与"阻挡"两种情况(单格箭头基础规则);
  • 8 个关卡可解性(贪心解算)、每关至少有一个初始被阻挡的箭头、棋盘数据合法性;
  • 长箭合法性(相邻、不重复、不越界、不与单格箭头重叠);
  • 随机生成 60 个关卡(不同种子)全部可通关且数据合法;
  • 撤销快照恢复、求解指引、存档读写容错(缺失/损坏不崩溃);
  • 端到端:自动试玩 8 关(点击 4/6/7/9/8/10/10/11 次通关)、界面层通关/失败流程、自动求解一键通关、无限挑战通关、存档持久化与继续游戏、全部画面渲染自检。

手工试玩记录见仓库 测试记录.md(各关试玩表格)。

六、PSP 表格

任务 预估耗时(小时) 实际耗时(小时) 差异(小时)
需求分析与游戏设计 0.5 0.5 0
Python 与图形库学习 0.5 0.5 0
游戏界面实现 1 2 +1
路径与碰撞逻辑实现 2 2 0
关卡设计 1 1 0
AIGC 辅助开发 2 3 +1
测试与修改 2 2 0
README 与博客撰写 2 2 0
合计 11 13 +2

七、心得体会

AI 带来的帮助

Claude Code 在这门作业里几乎承担了一个"结对编程搭档"的角色:从环境排障(pygame 在 Python 3.14 下装不上、git 未安装)到路径检测、动画、关卡生成、测试、打包,都给了我很大帮助。最大的感受是速度:每次提出需求("箭头加弯""方向多样化""满格关卡"),几分钟内就能看到可运行的版本,试玩反馈 → 修改 → 再试玩的循环非常快,让我能把精力放在"游戏应该长什么样"而不是"代码怎么写"上。

AI 出的问题

AI 生成的代码绝不是"拿来即用"。开发过程中解算器和测试抓到了大量真实 Bug,都记在 AIGC 使用记录里:

  • 关卡死锁:AI 设计的关卡多次出现"同列两支箭头互相指向对方""同排 L 形对向互锁",这类错误人眼很难一次看全,是贪心解算器救的场;
  • 测试期望值错误:AI 自己写的测试用例把第 1 关箭头数记错、点击顺序写反,首轮 2 个用例失败,需要人工核对关卡布局来修正——AI 写的测试也会骗人,必须自己核对;
  • 存档恢复崩溃:JSON 序列化把元组变列表,恢复后字典键不可哈希直接崩溃,是我真机试玩"继续游戏"时撞出来的;
  • 随机生成器自锁 Bug:随机游走可能绕回并指向自己走过的格子,导致无限挑战可能抽到无解局——解算器发现后修复。

自己的收获

  • 明白了AI 出活、人来把关的分工:AI 擅长把想法快速变成代码,但正确性必须靠测试体系(解算器 + 单元测试 + 端到端试玩)来兜底;
  • 体会到了版本管理的价值:实际开发中每个功能一个 commit、每个 Bug 修复一个 fix commit,回头看 commit 就像一本开发日记。

项目仓库:https://github.com/Aranya12138/Arrow-Game · 完整测试记录见 测试记录.md · 完整 AIGC 协作记录见 AIGC使用记录.md

posted @ 2026-09-17 19:37  Aranya12138  阅读(16)  评论(0)    收藏  举报