软工第二次作业:一箭又一箭

用 Python + AIGC 完成「一箭又一箭」小游戏

项目 内容
这个作业属于哪个课程 202601软件工程
这个作业要求在哪里 2026秋软件工程个人作业(第二次)
这个作业的目标 使用 Python 和 AIGC 完成「一箭又一箭」小游戏
学号 102401134
GitHub 仓库 second-homework

一、项目展示

1.开始界面:
start.png

2.游戏界面:
game.png

3.通关界面:
win.png

4.GIF演示:
20260917-0857-22.2904251

二、项目介绍

1. 游戏规则

棋盘上放置若干带方向的箭头,方向分为上、下、左、右。玩家用鼠标点击箭头:

  • 如果箭头前方(同一行/列,箭头到边界之间)没有任何其他箭头,箭头沿方向飞出棋盘并消失;
  • 如果前方有箭头阻挡,则不能消除,箭头会产生前冲回弹、变红抖动等碰撞反馈,并扣除 1 次失误;
  • 清除本关全部箭头后进入下一关;
  • 失误次数耗尽时本关失败,可重新开始。

2. 界面设计

游戏包含三个主要界面:

  1. 开始界面:游戏标题、规则说明、开始游戏、选择关卡、退出游戏。
  2. 游戏界面:当前关卡、棋盘、剩余箭头数量、失误次数、重新开始、提示、撤销、主菜单。
  3. 结果界面:通关界面显示本关通关/全部通关,失败界面显示失误耗尽;均可重新开始或返回主菜单。

游戏界面采用深色背景和网格棋盘,箭头飞出时沿方向平移,碰壁时先前冲再回弹,最后抖动变红,反馈比较直观。

3. 主要功能

  • 四种方向箭头;
  • 鼠标点击选择与格子高亮;
  • 同行/同列路径阻挡检测;
  • 飞出动画与碰撞动画;
  • 失误次数、剩余箭头数量统计;
  • 6 个可通关关卡;
  • 通关、失败、下一关、重新开始、返回主菜单;
  • 附加功能:提示、撤销、关卡选择、关卡自动求解与自检。

三、实现思路

1. 箭头和方向如何表示

箭头使用不可变数据类 Arrow 表示:

@dataclass(frozen=True)
class Arrow:
    row: int
    col: int
    direction: str

方向统一使用「行增量, 列增量」表示:

GRID_DIRS = {
    "up": (-1, 0),
    "down": (1, 0),
    "left": (0, -1),
    "right": (0, 1),
}

特别注意:Pygame 绘制用的是屏幕坐标 (x, y),而棋盘逻辑用的是 (row, col)。如果混用,很容易把 up 写成 row += 0,导致向上判断错误。因此项目里棋盘逻辑只使用 GRID_DIRS,UI 层单独使用像素方向 PIXEL_DIRS

2. 路径检测方法

路径检测只关心同一行或同一列、箭头与边界之间的格子:

def blockers(self, arrow):
    dr, dc = GRID_DIRS[arrow.direction]
    r, c = arrow.row + dr, arrow.col + dc
    result = []
    while 0 <= r < self.grid_size and 0 <= c < self.grid_size:
        if (r, c) in self.cells:
            result.append(self.cells[(r, c)])
        r += dr
        c += dc
    return result

先判断边界再查字典,因此四个方向都不会出现数组越界。棋盘用 dict[(row, col)] -> Arrow 保存,路径查询只沿着一条线走,清晰且不易写错。

3. 游戏状态管理

GameState 负责:

  • 当前关卡、箭头集合、剩余失误;
  • click(row, col):返回 flew / blocked / empty
  • 胜负状态:playing / win / lose
  • 撤销历史:每次点击前保存箭头快照和失误数;
  • hint():返回当前可飞出的一个箭头;
  • next_level():进入下一关。

逻辑层完全不 import pygame,可以单独运行单元测试,这是这次优化的重点之一。

4. 动画实现

  • 飞出:逻辑棋盘立即删除箭头,UI 创建一个纯视觉的 FlyingArrow,用 speed * dt 移动并飞出屏幕;
  • 碰壁:先沿箭头方向前冲约 0.28 格,再回弹回原位,然后垂直方向抖动并变红;
  • 失败延迟:失误耗尽后不立即显示失败界面,等回弹动画播放约 0.85 秒后再显示,避免反馈被遮住。

所有动画都使用 dt,所以在 60Hz 和 144Hz 下速度一致。

5. 关卡如何保证可通关

项目实现了贪心求解器 solve():每次移除任意一个当前可飞出的箭头。

正确性说明:删除箭头只会减少其他箭头的阻挡,不会产生新的阻挡。因此如果当前存在一个可飞出箭头,先移除它不会把「有解」变成「无解」;如果某一步没有任何可飞出箭头而棋盘未空,则该关卡必然无解。

tools/check_levels.py 会对每个关卡运行求解器并打印一条解法,防止出现无解关卡。

四、AIGC 使用过程

记录 1:路径检测与方向向量

项目 内容
子任务 实现四个方向的阻挡判断
AIGC 工具 deepseekV4.1flash
我提出的要求 帮我写一个函数,判断箭头沿上/下/左/右前进方向到棋盘边界之间有没有其他箭头
AI 提供的内容 AI 给出了四个方向的条件判断代码,并建议用行列数组表示
实际效果 功能基本可用,但 AI 最初把方向写成 (dx, dy),继续扩展时容易和行列坐标混用
人工修改 我把方向统一改成 (行增量, 列增量),并补充了边界先判断的顺序和 T03 边缘测试

记录 2:逻辑与界面解耦、补充测试

项目 内容
子任务 为 T01-T06 编写自动化测试
AIGC 工具 deepseekV4.1flash
我提出的要求 把路径判断逻辑从 pygame 代码中抽出来,给出可以直接用 unittest 运行的测试
AI 提供的内容 生成了 BoardGameState 和 12 条测试用例,覆盖无阻挡、有阻挡、四个方向、边缘、胜利、失败、重新开始、撤销
实际效果 测试全部通过,证明核心逻辑正确;也发现原代码 import 时就创建窗口,不方便测试
人工修改 我检查了测试用例的阻挡方向,修正了「两个箭头面对面」的用例,并补充了全部关卡可解性测试

记录 3:碰撞反馈与动画帧率

项目 内容
子任务 让碰撞反馈更明显,同时适配高刷新率
AIGC 工具 deepseekV4.1flash
我提出的要求 给碰壁箭头加一个前冲后回弹的动画,并且在不同帧率下速度一致
AI 提供的内容 给出了 bumping + shaking 状态机和正弦缓动公式
实际效果 动画方向正确,但第一版用「每帧移动固定像素」,在 144Hz 下速度变快
人工修改 改为 distance = speed * dt,调整前冲距离和动画时长为 0.32 秒 / 0.28 秒,并加入失败延迟

记录 4:关卡设计与求解器

项目 内容
子任务 设计更多关卡并保证每一关都有解
AIGC 工具 deepseekV4.1flash
我提出的要求 帮我设计几个箭头关卡,并检查是否能全部消除
AI 提供的内容 建议用同心环布局,外层箭头朝外,内层在外层清除后解封;并给出贪心求解器思路
实际效果 6 个关卡全部可解,第 1~6 关分别用 8、13、19、32、40、48 步可以清空
人工修改 我在 tools/check_levels.py 中实现求解器并逐个运行验证,最终关卡数据写进 levels.py

五、测试结果

1. 自动化测试

在项目根目录运行:

python -m unittest discover -s tests -t . -v

测试结果:12 条用例全部通过,覆盖 T01-T06 以及全部关卡可解性检查。

2. 关卡自检

python tools/check_levels.py

运行结果:第 1~6 关全部输出「[通过]」,并打印完整解法。

3. 手工测试记录表

编号 测试内容 预期结果 实际操作 实际结果 是否通过
T01 点击前方无阻挡的箭头 箭头飞出棋盘并消失 点击右侧无阻挡箭头 箭头飞出,剩余箭头 -1
T02 点击前方有阻挡的箭头 箭头不消失,失误次数减 1 点击被挡住箭头 前冲回弹变红,失误 -1
T03 点击位于边缘且朝向棋盘外的箭头 箭头正常消失,不发生越界错误 点击边缘朝外箭头 正常飞出,无异常
T04 消除本关全部箭头 显示通关并进入下一关 按解法清空棋盘 显示通关界面,可点下一关
T05 失误次数耗尽 显示失败并允许重新开始 连续点击被挡箭头 显示失败界面,可重新开始
T06 游戏进行中重新开始 箭头布局和失误次数恢复 消除部分箭头后点重新开始 恢复初始布局和失误数

六、PSP 表格

请根据你的真实开发时间填写「实际耗时」,预估耗时可以按下面建议值调整。

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

七、心得体会

这次作业最大的感受是:AIGC 可以很快给出能运行的代码,但「能运行」和「正确、可维护」之间还有距离,AI搭建的UI界面审美确实一般。

一开始 AI 生成的界面和路径检测已经能完成基本玩法,但继续检查后发现了几个问题:动画按帧移动,在高刷新率下速度不一致;字体回退写在不会抛异常的 SysFont 外层,实际没有作用;逻辑和 Pygame 绑在一起,导致测试必须创建窗口;关卡也只有注释写着「已人工验证」,没有自动化保证。

在修改过程中,我重点做了几件事:把方向统一成行列增量、把逻辑层拆成不依赖 Pygame 的 game_logic.py、用 dt 重写动画、给关卡写了贪心求解器、并补上 T01-T06 的单元测试。这样不仅基础功能更稳定,答辩时也能解释清楚每一段关键代码为什么这样写。

AIGC 特别适合做重复性的代码生成、查资料和给测试思路,但关卡是否可解、边界条件是否正确、动画参数是否合适,仍然需要自己运行和判断。以后再用 AI 写代码,我会先让它给出结构,再自己检查关键逻辑和边界情况。

另外大量的陌生代码对于暂未熟练掌握并且第一次利用Python及其图形库和游戏库插件的我来说无疑是很困难的,但是一个性能还算不上顶尖(flash版本,思考强度未开启至max)的AI模型却能在几分钟内完成大体框架的勾勒和基础代码,并且成功运行。这不得不让我们思考未来的AI和程序员之间的关系应该是怎样的。

posted @ 2026-09-17 16:59  稚知  阅读(14)  评论(0)    收藏  举报