软件工程第二次作业

软件工程课程第二次个人作业

项目 内容
这个作业属于哪个课程 202601 软件工程
这个作业要求在哪里 软件工程第二次个人作业要求
这个作业的目标 使用 Python 和 AIGC 完成"一箭又一箭"小游戏
学号 102401328
GitHub 仓库 https://github.com/majc888888/arrow-puzzle

一、项目展示

开始界面

start

游戏界面

QQ20260922-231751

通关界面

clear

失败界面

over

演示GIF

新演示

二、项目介绍

游戏规则:棋盘上分布着上、下、左、右四种方向的箭头。点击某个箭头时,程序检查它前进方向到棋盘边界之间的路径——

  • 路径上没有其他箭头:箭头沿方向飞出棋盘并消除(绿色飞出动画)
  • 路径上有其他箭头:箭头晃动变红提示碰撞,并消耗 1 次失误机会
  • 清空本关全部箭头 → 通关,进入下一关;失误次数耗尽 → 本关失败

主要功能

功能 说明
图形界面 开始 / 选关 / 游戏 / 通关 / 失败 / 全部通关六种界面,状态机管理
四方向路径检测 沿箭头方向逐格扫描至边界,判断是否有阻挡
飞出与碰撞反馈 飞出为绿色平移+淡出动画;碰撞为红色晃动+顶部文字提示
失误机制 每关 3 次失误,界面实时显示剩余次数
关卡系统 8 个难度递增的关卡,均经求解器验证可通关
重新开始 一键恢复当前关卡初始布局与失误次数
撤销上一步 记录点击前快照,点错可回退,恢复布局与失误次数
提示功能 每关 3 次,高亮当前可安全飞出的箭头
计时与星级 实时显示用时,通关按剩余失误数评 1~3 星
关卡选择 选关界面 + 通关解锁机制

特色

  • 关卡内置求解器(回溯搜索),开发阶段即验证每一关确实存在通关顺序,避免出现无解关卡;

三、实现思路

1. 数据表示

箭头用数据类表示,只有三个属性——行、列、方向:

@dataclass(frozen=True)
class Arrow:
    row: int
    col: int
    direction: Tuple[int, int]   # (dr, dc),如 UP=(-1,0)

方向用行/列增量表示,天然与二维坐标对应:UP=(-1,0)、DOWN=(1,0)、LEFT=(0,-1)、RIGHT=(0,1)。

关卡用字符串数组表示,一个字符就是一个格子,直观且易改:

"U...R",     # 第 0 行
".....",
"..D..",     # 第 2 行
".....",
"U...L",     # 第 4 行

U/D/L/R 表示箭头,. 表示空位。加载时逐字符扫描即可还原出箭头列表。

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

这是本游戏最核心的逻辑:判断被点击箭头的前进方向上、到边界之间是否还有其他箭头

思路:从箭头的下一格开始,沿方向向量逐格推进,直到越界;途中只要遇到任何一个仍在本场的箭头位置,即为被阻挡:

def is_blocked(arrow, occupied, rows, cols):
    dr, dc = arrow.direction
    r, c = arrow.row + dr, arrow.col + dc   # 从下一格出发,跳过自身
    while 0 <= r < rows and 0 <= c < cols:  # 循环条件同时完成边界判断
        if (r, c) in occupied:
            return True                     # 途中遇到其他箭头 → 阻挡
        r += dr
        c += dc
    return False                            # 顺利走到边界 → 可飞出

三个关键点:

  • 起点是下一格而非当前位置,天然排除自身
  • 边界判断写在 while 条件里,越界前循环就终止,从根源上避免数组越界
  • occupied 是当前仍在场箭头的位置集合(set),查询 O(1)。整个检测最坏 O(行/列长度),非常轻量

3. 状态机与动画

游戏用五个状态管理流程:开始 → 游戏中 → 单关通关 / 失败 → 全部通关。

点击箭头后的处理分两条线:

  • 可飞出:立即从场上箭头列表移除(不再参与阻挡判定),加入"飞出动画"队列;动画按 进度 = t / 时长 沿方向平移并淡出,动画结束即彻底消失;
  • 被阻挡:箭头留在场上(仍参与阻挡),加入"晃动动画"队列,绘制时用 sin(t*40) 产生水平抖动并改用红色;同时失误次数减 1,减到 0 进入失败状态。

通关判定放在每帧更新中:场上无箭头飞出动画全部结束,才弹出通关面板——保证玩家能看到最后一支箭头完整飞出的过程。

4. 关卡可解性验证

实现了一个带回溯的求解器 find_solution:每一步在所有"当前可飞出"的箭头中任选一个消除并递归,能清空即存在通关顺序。开发时用它批量验证关卡,也顺带用于自动化测试。

设计关卡时还总结出一个死锁陷阱:同一行/列上两个箭头若"面对面"(如 → 在左、← 在右且中间无遮挡),则互相阻挡、永远无解。第一版关卡数据里就出现过这种布局,靠求解器验证发现后修正。

5. 扩展功能实现

撤销上一步:点击箭头前把 (箭头列表快照, 失误数) 压入历史栈;点「撤销」弹栈恢复。由于 Arrow 是不可变对象(frozen=True),浅拷贝列表即可安全保存状态:

# 点击箭头时
self.history.append((list(self.arrows), self.mistakes))

# 撤销时
arrows, mistakes = self.history.pop()
self.arrows, self.mistakes = arrows, mistakes

提示功能:新增 find_next_move——遍历场上箭头,返回第一个前方无阻挡者。它相当于求解器的"单步版":由于"可飞出"的箭头点击后必然消除且不扣失误,提示任意一个可飞箭头都是安全的一步。点击提示后用橙色光圈高亮目标箭头,每关限 3 次。

计时与星级:load_level 时记录起点时间,游戏进行中每帧更新已用时;通关瞬间冻结用时并计算星级——星级 = 剩余失误数(无失误 3 星、1 失误 2 星、2 失误 1 星),用 ★☆ 字符渲染在通关面板上。

关卡选择:新增选关状态与 8 宫格按钮网格;用 unlocked 变量记录已解锁进度,通关第 N 关后解锁第 N+1 关,未解锁按钮呈灰色不可点。

四、AIGC 使用过程

本项目全程与 AIGC 工具 DeepSeek 协作开发,以下记录 3 次有代表性的真实协作过程:

子任务 借助何种 AIGC 技术 AI 实现或提供了什么 效果如何 人工修改
核心路径检测算法 DeepSeek 根据作业规则生成 is_blocked 函数:沿方向逐格扫描、while 条件内做边界判断,并附带四方向单元测试 一次通过,四方向与边缘用例全部正确 无需修改;人工阅读代码确认"从下一格出发"排除了自身干扰
调试 Windows 下中文字体崩溃 DeepSeek 初版用 pygame.font.match_font 按名称查找中文字体 实际运行报 TypeError: expected str, bytes or os.PathLike object, not int —— pygame 2.6.1 扫描 Windows 注册表字体时对某些条目处理异常 AI 定位原因后改为直接使用系统字体文件路径(msyh.ttc / simhei.ttf 等),绕开注册表扫描,问题解决
关卡设计与可解性验证 DeepSeek 设计 4 个难度递增的字符串关卡,并实现回溯求解器 find_solution 自动验证每关可通关 第一版部分关卡(同一直线上两箭头面对面互相阻挡)被求解器判定死锁 分析死锁原因后重新设计关卡布局,再次用求解器验证全部通过

五、测试结果

自动化测试基于 unittest 编写(python -m unittest discover -s tests -v),共 21 个用例全部通过;同时进行了手工试玩验证。

编号 测试内容 预期结果 实际结果 是否通过
T01 点击前方无阻挡的箭头 箭头飞出棋盘并消失 箭头移出场,播放绿色飞出动画后消失 通过
T02 点击前方有阻挡的箭头 箭头不消失,失误次数减 1 箭头红色晃动,失误 3→2,且仍参与阻挡 通过
T03 点击位于边缘且朝向棋盘外的箭头 正常消失,不发生越界错误 正常飞出,无异常(边界判断在 while 条件中完成) 通过
T04 消除本关全部箭头 显示通关并进入下一关 弹出通关面板,点击后正确载入下一关 通过
T05 失误次数耗尽 显示失败并允许重新开始 第 3 次碰撞后弹出失败面板,可重开 通过
T06 游戏进行中重新开始 箭头布局和失误次数恢复 布局与失误次数均恢复到本关初始状态 通过

调试过程中曾发现并修复的问题:

  1. 测试初次运行 3 个用例失败——原因是测试辅助函数调用 load_level 后未把状态置为"游戏中",update() 因状态不符直接返回(属于测试代码问题,游戏逻辑无错误);
  2. 开始界面按钮位置错误与图例文字重叠。

六、PSP 表格

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

七、心得体会

这次的作业是我第一次利用 AIGC 来协作开发一个小游戏,其中有许多的感悟:

  1. AI 改变的是我们写代码的比重,不是做软件的流程。需求拆解、结构设计、验证测试这些环节依然必须由人主导。本次开发中真正花时间的不是写代码,而是:验证关卡可解、运行程序、看截图、修 Bug——恰好都是 AI 替代不了的部分。

  2. AI 生成的代码必须要经过本地运行 + 验证,不能直接相信它。 两次典型经历:字体加载代码在 AI 的逻辑里完全正确,但一到 Windows 实机就因注册表问题崩溃;AI 设计的第一版关卡里存在"面对面死锁",肉眼难以察觉,靠求解器才暴露。要通过实际运行来验证AI说的到底对不对。

  3. 测试先行回报极高。先把作业的 T01~T06 转成 11 个自动化用例,之后每次改动(修复字体、调整布局)都能一键回归。

posted @ 2026-09-22 23:35  MaJincheng  阅读(9)  评论(0)    收藏  举报