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

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

项目 内容
这个作业属于哪个课程 https://edu.cnblogs.com/campus/fzu/2026-01SoftwareEngineeringandSoftwareEngineeringPractice
这个作业要求在哪里 https://edu.cnblogs.com/campus/fzu/2026-01SoftwareEngineeringandSoftwareEngineeringPractice/homework/16718
这个作业的目标 使用 Python 和 AIGC 完成“一箭又一箭”小游戏
学号 102401428
GitHub 仓库 <Winksong/arrow-again-arrow>

一、项目展示

  • 开始界面

image

  • 效果展示

image

image

image

二、项目介绍

游戏棋盘中包含若干带方向的箭头,箭头方向分为上、下、左、右四种。玩家点击某个箭头后,程序检查该箭头前进方向上的路径:

  • 如果箭头与棋盘边界之间没有其他箭头阻挡,该箭头飞出棋盘并被消除;
  • 如果路径上存在其他箭头,该箭头不能被消除,并通过移动后返回、晃动、变色或文字等方式提示碰撞;
  • 点击被阻挡的箭头会消耗一次失误机会;
  • 清除本关全部箭头后即可进入下一关;
  • 失误次数耗尽时,本关失败,可重新开始。

三、实现思路

1. 数据表示

  • 方向:字典映射到单位位移,遍历时直接累加。

    DIRS = {"up": (-1, 0), "down": (1, 0), "left": (0, -1), "right": (0, 1)}
    
  • 箭头Arrow(row, col, direction, state, anim_t),朝向用字符串,与关卡字符画直接对应。

  • 棋盘dict[(row, col) → Arrow],按坐标取箭头 O(1),不用为空格填占位符。

  • 关卡:字符串布局,↑↓←→ 为箭头、· 为空格,方便手工调整。

    {
        "name": "第 1 关 · 入门",
        "rows": 3, "cols": 3, "mistakes": 3,
        "layout": """
    →·↑
    ·↓·
    ↑··
    """,
    }
    

2. 路径检测(核心判定)

点击箭头后,沿其方向逐格前进直到边界,途中遇到任何箭头即被阻挡。用生成器实现:

def iter_path(self, arrow: Arrow) -> Iterator[tuple[int, int]]:
    dr, dc = arrow.delta
    r, c = arrow.row + dr, arrow.col + dc
    while self.in_bounds(r, c):        # ✅ 先判后取
        yield r, c
        r += dr
        c += dc

def path_clear(self, arrow: Arrow) -> bool:
    return not any(
        self.arrow_at(r, c) is not None for r, c in self.iter_path(arrow)
    )

坑一:边界判断必须放在 while 条件里。 若写成「先取下一格坐标、再判越界」,向上/向左到边界时 r = -1,Python 负下标不报错,会静默绕到棋盘另一端,判定出错却无异常,人工试玩几乎发现不了(对应 T03)。

坑二:flying 状态的箭头不算阻挡。 arrow_at() 里过滤了它,否则会出现「箭头已飞出还挡着后面」的情况:

def arrow_at(self, r, c):
    a = self.cells.get((r, c))
    if a is None or a.state == FLYING:
        return None
    return a

3. 逻辑与界面分离

规则全部放在不依赖 pygame 的 model.py,界面只负责绘制与转发点击。

文件 职责 依赖 pygame
model.py 规则与数据(Arrow / Board / Session)
solver.py 可解性校验与求解器
levels.py 关卡数据
view.py 绘制
app.py 主循环、状态机、事件

好处是核心逻辑可直接用 pytest 测试,不用开窗口(SDL_VIDEODRIVER=dummy,配在 tests/conftest.py)。Session.click() 返回三态字符串,把判定与界面表现解耦:

if self.board.path_clear(arrow):
    arrow.state = FLYING
    return CLICK_FLY          # 界面播飞出动画
arrow.state = BLOCKED
self.mistakes_left -= 1       # 界面播碰撞反馈
return CLICK_BLOCKED

4. 关卡可通关性

solver.solve() 用贪心求可行解:每轮扫一遍剩余箭头,挑一个前方无阻挡的消除,重复到清空;某轮找不到就说明互相挡死了。

坑三:求解器循环上限。 早期 solvable() 写成 len(remaining),而它每轮都在变小,导致大棋盘提前退出、把有解误判成无解。正确做法是固定循环「箭头总数」次。

坑四:手工摆关卡极易摆出无解死局,肉眼看不出来。 本项目手工设计了 9 个候选布局,7 个被求解器判定为无解

↑·↓·
·→·←      ← 看似正常,实际箭头互相指着
↓···
·←↑·

因此关卡一律先用反向构造solver.generate)产出候选,再人工挑选:

按「拆除顺序的逆序」放置箭头。放第 k 个时,棋盘上只有「会在它之后才被拆掉」的箭头,只要新箭头路径上没有它们即可。

构造时即保证有解,省掉了暴力验证。最后用 tools/playtest.py 跑 5000 局随机乱点,量化验证难度递增。

5. 状态与动画

  • 箭头状态机idle → flying → 移出idle → blocked → idle
  • 动画锁输入:动画期间忽略点击,防止连点刷失误。
  • 胜负判定延后:通关放在飞出动画播完的回调里,避免动画没播完就弹卡片。
  • 失败路径统一:失误耗尽走碰撞反馈而非飞出动画,故状态同步抽成 _sync_result_state() 统一处理。

四、AIGC 使用过程

本次开发全程使用 AIGC 辅助,以下为几次有代表性的记录:

子任务 借助何种 AIGC 技术 AI 实现或提供了什么 效果如何 人工修改
路径检测与核心模型 Deepseek 生成方向映射、棋盘类与四方向路径检测代码 基本可用 首版把边界判断写在取下一位之后,向上/向左会触发 Python 负下标静默绕回。
阻挡判定 Deepseek 生成按同行同列扫描阻挡箭头的逻辑 有缺陷 首版把 flying 状态也当作阻挡,出现「箭头已飞出仍挡着后面」。人工在 arrow_at() 中过滤该状态
关卡生成 Deepseek 初版用「随机摆盘 + 暴力验证」,7×7 棋盘生成一次约 15 秒 不符合要求 改为「按消除顺序逆序放置」的反向构造法,构造时即保证有解,生成变为瞬时
求解器 Deepseek 生成可解性校验函数 有缺陷 循环上限误写为 len(remaining),而该值每轮递减,会把有解误判为无解。人工改为固定循环箭头总数次
碰撞与飞出动画 Deepseek 生成晃动 + 闪红 + 浮动文字的碰撞反馈,以及加速飞出动画 基本可用 人工调整晃动时长、飞出加速度等参数,使手感更干脆
自动化测试 Deepseek 按 T01~T06 生成 pytest 测试 首次运行有 3 个用例失败 分析发现是测试里的布局数据本身设计有误(如把 ↓ 放在底边其实能直接飞出),修正测试数据后全部通过
界面布局 Deepseek 生成开始界面与标题绘制 标题旁装饰箭头与文字重叠 人工把装饰箭头移到标题两侧

五、测试结果

编号 测试内容 预期结果 实际结果 是否通过
T01 点击前方无阻挡的箭头 箭头飞出棋盘并消失 与预期一致 通过
T02 点击前方有阻挡的箭头 箭头不消失,失误次数减 1 与预期一致 通过
T03 点击位于边缘且朝向棋盘外的箭头 箭头正常消失,不发生越界错误 与预期一致 通过
T04 消除本关全部箭头 显示通关并进入下一关 与预期一致 通过
T05 失误次数耗尽 显示失败并允许重新开始 与预期一致 通过
T06 游戏进行中重新开始 箭头布局和失误次数恢复 与预期一致 通过
T07 点击空格或棋盘外 不计入失误次数 与预期一致 通过
T08 斜线方向不算阻挡 箭头正常飞出 与预期一致 通过
T09 阻挡箭头的朝向不影响判定 仍被判定为阻挡 与预期一致 通过
T10 动画播放期间点击 忽略点击,不重复扣失误 与预期一致 通过

六、PSP 表格

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

七、心得体会

  1. AI 最大的帮助是把会做的事变快:界面绘制、动画、测试骨架这些模式化的代码,描述清楚需求后几秒钟就能拿到能跑的版本,把精力留给了规则设计。
  2. AI 也会犯错,而且错得很自信:这次测试用例里的布局数据就有三处自相矛盾,如果自己不真正理解路径检测规则,根本看不出是测试错了还是代码错了。
  3. 逻辑与界面分离后,核心规则可以脱离窗口直接测试,这为后续其他项目的开发起到了启示作用。
posted @ 2026-09-16 08:40  Winksong  阅读(9)  评论(0)    收藏  举报