软件工程第二次作业

软件工程第二次个人作业 ——「一箭又一箭」小游戏

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

一、项目展示

玩法演示(本人实际操作录制):

玩法演示

界面截图

开始界面 游戏过程 通关结算 失败结算
开始界面 碰撞反馈 通关 失败

二、项目介绍

2.1 游戏规则

棋盘上散布着朝上、下、左、右四个方向的箭头。点击一个箭头时,程序检查它朝向的那条线上、到棋盘边界之间还有没有别的箭头:

  • 前方没有阻挡:箭头沿朝向滑出棋盘并消失;
  • 前方有箭头阻挡:箭头原地变红晃动,失误次数 +1。

清空本关全部箭头即通关;失误次数(每关 3 次)耗尽则本关失败。

→  ·  ·  ↑  ·        朝右的箭头右边还有箭头,不能飞出
·  ·  ·  ·  →        朝右的箭头右边到边界空无一物,可以飞出

2.2 界面设计

界面按手机竖屏比例设计,共五个画面:开始、选关、游戏、通关结算、失败结算。游戏画面顶部显示关卡名、剩余箭头、用时和失误次数,中部是棋盘,底部是「撤销 / 提示」与「主菜单 / 重新开始」;选关画面列出五张关卡卡片,已通关的显示星级与得分,五关都可直接进入。

2.3 主要功能

基础玩法之外,还实现了这些:

  • 关卡生成器与求解器:生成器反向放置构造必然可解的布局,求解器负责校验关卡;
  • 撤销 / 提示:撤销把棋盘与失误次数一起退回上一步,提示高亮一个确实能飞出的箭头,另配快捷键 U / H
  • 计时与星级评价:每关独立计时,判定即停表;星级由失误 + 提示次数决定,0 次三星、1 次两星、2 次及以上一星;
  • 得分:满分 1000,每次失误 −200,超出参考时长后每秒 −10,保底 200;
  • 四声音效:飞出 / 碰撞 / 撤销 / 通关,波形由代码合成,不引入音频文件;
  • 自动化测试:用例覆盖核心逻辑,一条命令跑完并生成 Markdown 报告。

所有图形均由程序绘制,没有使用任何商业游戏素材;关卡的可解性由求解器逐一校验,测试与截图也都由脚本生成。


三、实现思路

3.1 箭头与方向的表示

一个箭头就是一个格子加一个方向,用不可变的数据类表示:

@dataclass(frozen=True)
class Arrow:
    row: int
    col: int
    direction: str      # 'up' / 'down' / 'left' / 'right'

方向不存成角度或向量,而是存成字符串,再通过一张映射表换算成行列增量:

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

这样做的好处是:方向本身可读,调试和测试输出时直接打印 'up';所有涉及坐标运算的地方都统一查 DIRECTIONS

棋盘内部用字典以 (row, col) 为键存箭头,键就是坐标,取某个格子的箭头是 O(1),消除时直接删键即可。同时保留一份初始布局副本,供「重新开始」复原。

3.2 关卡如何表示

关卡用字符画描述,一个字符代表一个格子:

_RAW_LEVELS = [
    (
        "第 1 关 · 顺藤摸瓜",
        """
        >  >  >  .  ^
        .  v  .  .  .
        .  .  .  <  .
        .  v  >  .  .
        .  .  .  .  .
        """,
    ),
    # ...
]

^ 向上、v 向下、< 向左、> 向右、. 空位。

五个关卡的设计意图如下:

关卡 主题 箭头数 设计意图
1 顺藤摸瓜 8 教学关,出口明显
2 层层推进 10 引入纵向依赖(先清下面才能放走上面)
3 从两端突破 10 上下两排必须从两端往中间逐个清,考顺序感
4 四面楚歌 14 可飞位置分散在四周
5 天罗地网 14 首轮只有少数箭头能飞,依赖链最长

3.3 路径检测

def can_fly_out(self, arrow):
    """判断箭头能否飞出棋盘。"""
    d_row, d_col = DIRECTIONS[arrow.direction]
    row = arrow.row + d_row          # 从箭头的"下一格"开始
    col = arrow.col + d_col

    while self.is_inside(row, col):  # 还在棋盘内
        if (row, col) in self._arrows:
            return False             # 撞到箭头 -> 被阻挡
        row += d_row
        col += d_col

    return True                      # 一路走到棋盘外 -> 畅通

思路是从箭头沿朝向逐格往前走:中途只要碰到任何箭头就判定被阻挡,一路走出棋盘则判定畅通。

其中把越界判断单独抽成 is_inside(),作为循环条件:

def is_inside(self, row, col):
    return 0 <= row < self.rows and 0 <= col < self.cols

如果不这样写,四个方向就要各写一遍边界判断,四份代码里只要有一处把 < 写成 <=,就会出现数组越界或误判贴边箭头。收敛到一处之后,边界只有一种写法,测试也只需要覆盖一遍。

边界情况由 9 项用例覆盖:1×1 的棋盘上四个方向都应能直接飞出,贴边朝外的箭头不应报错。

四、AIGC 使用过程

本次开发全程使用 WorkBuddy 作为 AIGC / Coding Agent 工具,从需求拆解、代码编写到问题排查都有参与。
下面选择几次代表性协作过程。

子任务 借助何种 AIGC 技术 AI 实现或提供了什么 效果如何 人工修改
路径检测 WorkBuddy 提供 can_fly_out():沿朝向逐格前进判断阻挡与出界,越界判断抽成 is_inside() 1×1 棋盘、贴边朝外均不越界 代码一字未改。我只卡了范围:只做同行同列、不加斜向,并要求补 9 项边界用例后才用
关卡设计与可解性 WorkBuddy 没给坐标,而是给了可解性推论(消除只减少障碍,贪心即可判定死锁)和「反向放置」构造法 5 关全部可解;生成器 4000 个布局经求解器复验全部通过 定了关卡设置的难度
修复"按 R 没反应" WorkBuddy 没先改代码,而是写脚本复现,确认代码没坏;真因是输入法接管键盘,入口调 stop_text_input() R 恢复正常,补 2 项用例全过 报了"按 R 没效果"这个现象、没猜原因,否则很可能把本来正确的代码改坏
修复"按得快就不结算" WorkBuddy 查到"判定 → 结算"之间的动画延迟里没锁输入,误点会重置倒计时;还发现失败后点光箭头会被改写成胜利 新增用例通过;反向验证时删掉那道锁,用例立刻失败 问题由我试玩发现,报给ai解决问题

五、测试结果

运行方式(项目根目录):

python tools/run_tests.py

测试

编号 测试内容 预期结果 实际结果 是否通过
T01 点击前方无阻挡的箭头 箭头飞出棋盘并消失 剩余 8 → 7,失误 0 通过
T02 点击前方有阻挡的箭头 箭头不消失,失误次数减 1 剩余 8 → 8,失误 1 通过
T03 点击位于边缘且朝向棋盘外的箭头 箭头正常消失,不发生越界错误 剩余 8 → 6,越界异常=False 通过
T04 消除本关全部箭头 显示通关并进入下一关 本关清空=True,点按钮进入第 2 关=True 通过
T05 失误次数耗尽 显示失败并允许重新开始 耗尽后结果=lose,重开恢复正常=True 通过
T06 游戏进行中重新开始 箭头布局和失误次数恢复 剩余 8/8,失误 0 通过

运行输出:

$ python tools/run_tests.py
==============================================================================
一箭又一箭 · 自动化测试报告
==============================================================================
[PASS] T01      点击前方无阻挡的箭头
         预期:箭头飞出棋盘并消失
         实际:剩余 8 → 7,失误 0
[PASS] T02      点击前方有阻挡的箭头
         预期:箭头不消失,失误次数减 1
         实际:剩余 8 → 8,失误 1
[PASS] T03      点击位于边缘且朝向棋盘外的箭头
         预期:箭头正常消失,不发生越界错误
         实际:剩余 8 → 6,越界异常=False
……
------------------------------------------------------------------------------
合计 34 项,通过 34 项,失败 0 项
Markdown 报告已写入:docs\test_report.md

六、PSP 表格

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

七、心得体会

本次作业里 AIGC 帮我把界面绘制、路径判断、四个音效和测试脚本很快做了出来。它给我的是一个能跑的起点,剩下的判断还得我自己做。

几次最关键的修改都不是读代码读出来的,是我自己玩的时候发现的。窗口比屏幕高,Windows 会把窗口整体上移,标题栏和顶部文字一起被裁掉,得改成按屏幕可用高度等比缩放;胜负判定之后没有锁住输入,连点会让结算界面永远弹不出来,失败之后接着点还能把输的翻成赢;重新开始本关R按了没用;音效第一版刺耳。这几件事只能靠在真实游玩才能发现。

我按贪心算法叫AI先做关卡校验,五关写完跑一遍确认可解才留下;后来写生成器也是反向放的,从一个必然能清空的终点往前推,4000 个布局经求解器复验全部可解。测试脚本也是AI帮忙搭的框架,34 项用例,改完代码跑一条命令就能复跑。回头看,工具确实省了敲代码的时间,但哪些逻辑是对的、哪些关卡真的能通关、哪些界面看着舒服,还是要我自己运行、观察和判断,能不能看出它什么时候在胡说八道。


posted @ 2026-09-17 18:28  caipo  阅读(14)  评论(0)    收藏  举报