软工第二次作业

软工第二次个人作业:使用 Python 与 AIGC 实现“一箭又一箭”

项目内容 内容
这个作业属于哪个课程 https://edu.cnblogs.com/campus/fzu/202601SofwareEngineering
这个作业要求在哪里 https://edu.cnblogs.com/campus/fzu/202601SofwareEngineering/homework/16717
这个作业的目标 使用 Python 和 AIGC 完成“一箭又一箭”小游戏
学号 102401234
GitHub 仓库 cht-hyp/arrow-puzzle-game

一、项目展示

游戏演示视频

开始界面:

start

游戏过程:

classic

提示高亮:

classic_hint

单关通关:

win

全部通关:

all_clear

失败界面:

lose

二、项目介绍

项目名为“一箭一箭又一箭”,使用 Python 与 Pygame 开发。每个箭头占据一个网格,方向为上、下、左或右。点击后检查箭头前方到边界之间是否有其他箭头;没有阻挡就沿方向飞出,有阻挡则回弹、变红并消耗一次失误机会。

目标是清空当前关卡。三个经典关卡分别有 9、16、25 个箭头,棋盘为 3×3、4×4、5×5;同时提供进阶折线模式和可解随机挑战。每关初始有 3 次机会,耗尽后失败;可随时重新开始当前关。清空后显示通关,点击“下一关”继续,第三关结束显示全部通关。

安装和启动命令见 GitHub 仓库的
README。环境为 Windows、Python 3.12.14、Pygame 2.6.1。

三、实现思路

本地项目主要结构

arrow-puzzle-game/
├─ main.py                       # 游戏启动入口
│
├─ game/
│  ├─ game.py                    # 主循环、页面、输入与动画调度
│  ├─ arrow.py                   # 箭头数据结构与方向定义
│  ├─ board.py                   # 基础棋盘和路径检测
│  ├─ session.py                 # 基础模式操作与胜负状态
│  ├─ levels.py                  # 基础模式固定关卡
│  ├─ solver.py                  # 基础模式求解器
│  │
│  ├─ lines.py                   # 进阶线条及棋盘结构
│  ├─ advanced_session.py        # 进阶模式操作逻辑
│  ├─ advanced_levels.py         # 进阶模式固定关卡
│  ├─ advanced_solver.py         # 进阶模式求解器
│  ├─ snake.py                   # 进阶线条飞出动画
│  │
│  ├─ random_levels.py           # 可解随机关卡生成
│  ├─ history.py                 # 撤销快照与历史记录
│  ├─ campaign.py                # 关卡、模式及重开流程
│  ├─ animation.py               # 基础箭头飞出与受阻动画
│  ├─ progress.py                # 游戏成绩与进度保存
│  ├─ modes.py                   # 游戏模式定义
│  ├─ constants.py               # 窗口、状态等常量
│  ├─ theme.py                   # 界面配色与样式
│  └─ ui.py                      # 图标和通用界面绘制
│
├─ tests/
│  ├─ test_logic.py              # 基础规则与路径检测测试
│  ├─ test_solver.py             # 基础求解器测试
│  ├─ test_levels.py             # 固定关卡可解性测试
│  ├─ test_random_levels.py      # 随机关卡生成测试
│  ├─ test_advanced_play.py      # 进阶模式操作测试
│  ├─ test_advanced_solver.py    # 进阶求解器测试
│  ├─ test_animation.py          # 飞出与受阻动画测试
│  ├─ test_ext_hint.py           # 提示功能测试
│  ├─ test_ext_undo.py           # 手动、自动操作撤销测试
│  ├─ test_auto_solve.py         # 自动演示测试
│  └─ test_regression.py         # 游戏流程回归测试
│
├─ tools/
│  ├─ final_check.py             # 最终交付检查脚本
│  └─ capture_ui.py              # 游戏界面截图工具
│
├─ README.md                     # 项目介绍与运行说明
├─ BLOG.md                       # 课程作业博客
└─ requirements.txt              # Python 运行依赖

四方向路径检测

先确认箭头仍在棋盘上,再根据方向取步长 (dr, dc)。从箭头前方的下一格开始,沿当前方向逐格检查;遇到任何箭头即判定为被阻挡,阻挡箭头自身的方向不影响判断。

以下为 game/board.py 中选定方向步长后的核心循环:

row, col = arrow.row + dr, arrow.col + dc
while 0 <= row < self.rows and 0 <= col < self.cols:
    if self.get_arrow(row, col) is not None:
        return False
    row += dr
    col += dc
return True

边缘朝外时下一格已经越界,循环不执行,直接允许消除。检测过程不修改棋盘,实际移除和扣机会由会话负责。

关卡与求解

三个固定模板每次加载都生成独立棋盘,重开不会改变模板。求解器复制棋盘,按坐标顺序查找当前可以飞出的箭头并删除,直到清空;若仍有箭头但无合法操作则返回 None

由于删除箭头只会减少阻挡,不会破坏其他合法删除,所以逐个合法消除即可,没有使用 DFS 或回溯。求解器既用于验证关卡可解性,也被提示功能和自动演示功能复用。程序实际返回的固定关卡和随机关卡均经过求解器验证可解。

动画与状态

逻辑选择会立即更新棋盘状态,动画保存视觉副本,不改变逻辑坐标。飞出速度会随窗口尺寸等比调整,箭头完全离开窗口后动画才结束;被阻挡时,箭头会前移、回弹、晃动并变红约 320ms,不显示额外的文字提示。

成功飞行时允许继续点击其他箭头;同一正在晃动的箭头短暂防连点,防止重复扣机会。最后一箭或最后一次失误发生后,逻辑已结束,界面等待现存动画播放完成再显示结果。重新开始会清理动画和提示。

扩展功能:提示与撤销

提示没有另写一套规则,而是直接复用已有的求解器,对当前剩余棋盘调用 solve(board),取解序列的第一步,用黄色描边加浅黄底色高亮该箭头,1 秒后自动恢复。

solution = solve(self.campaign.session.board)  # 求解当前棋盘,不是关卡初始布局
if not solution:                              # 空棋盘为 [],死锁为 None
    return False
self.hinted_position = solution[0]            # 只取第一步
self.hint_started_at = time.monotonic()       # 主循环计时,不用 time.sleep

求解器原本就接受任意 Board 并在内部复制,因此不需要改动求解器本身。提示是纯提示:调用前后棋盘和失误次数完全不变,也不写入撤销历史。动画进行中或非游戏状态不响应,棋盘一旦变化就清除旧高亮。

撤销使用轻量快照实现。每次成功消除箭头前保存当前棋盘状态;手动消除和自动消除都会记录,被阻挡的点击不写入撤销历史。

@dataclass(frozen=True)
class GameSnapshot:
    board: Board
    remaining_mistakes: int

快照必须和正在变化的棋盘解耦,否则所有历史都会变成当前状态。由于箭头是不可变对象,这里只需要复制棋盘字典并深拷贝一次:

def capture(session):                       # 保存
    return GameSnapshot(session.board.copy(), session.remaining_mistakes)

def restore(session, snapshot):             # 撤销逻辑的简化示意
    session.board.arrows = deepcopy(snapshot.board.arrows)
    if session.remaining_mistakes == 0:
        session.state = GameState.LOSE
    elif session.remaining_arrows == 0:
        session.state = GameState.WIN
    else:
        session.state = GameState.PLAYING

撤销支持连续多步撤销,直到历史为空。Restart、Next Level、Retry 都调用同一个 start_level(),在那里统一清空历史,确保新关卡不会撤销回上一关。动画进行中不执行撤销,避免撤销半完成的动画;空历史时按钮置灰。每次撤销只恢复最近一次成功消除,手动操作和自动操作都包含在内。被阻挡的点击不计入撤销步骤,已经扣除的失误机会也不会返还。自动演示中途停止并等待当前动画结束后,可以逐步撤销已经执行的自动操作。


界面布局

窗口默认 504×896,并且可以自由拖动缩放;拖动时锁定 9:16 比例,棋盘、按钮、字号与飞行速度都从当前窗口尺寸实时算出,所以放大缩小后布局比例不变。棋盘放在 HUD 与按钮行之间居中。

界面尺寸一开始是写成常量的,窗口一动就对不上;改成“以 800×600 为基准、每次绘制按当前窗口重算”之后,同一个缩放比例同时服务棋盘、按钮和文字。飞行速度也必须随窗口等比缩放:窗口变小后如果仍用固定像素速度,箭头会在还没飞出棋盘时动画就结束。棋盘位置则从“紧贴 HUD 下方”改为在 HUD 与按钮之间的区域居中,视觉上更平衡。

四、AIGC 使用过程

子任务 我的要求 AIGC 工具及完成内容 实际效果 人工参与与修改
核心路径检测 根据作业要求,将箭头限定为单格对象,点击后沿箭头方向检查同一行或同一列的阻挡情况,并将核心规则与 GUI 分离 Codex 实现四方向路径检查、GameSession 和对应自动化测试 测试从 5 项增至 20 项并全部通过,覆盖边界、跨空格阻挡、重复点击和终局保护 我先明确了游戏规则和模块分工,要求 AI 不在鼠标事件中重复实现路径检测,并重点检查四方向、边界和重复点击等情况。代码由 AI 编写,我通过阶段报告和测试结果检查实现是否符合原定规则。
关卡设计与求解 至少设计三个可通关关卡,并增加自动求解器,避免仅凭直觉设计关卡 Codex 创建三个固定关卡、Campaign 和逐步合法消除的求解器 33 项测试通过,三关答案均可回放;第一版布局即通过可解性验证 我确定了关卡数量、难度递增和可解性要求,提出利用求解器验证完整通关顺序,并要求保存解序列供后续测试。
连续点击与碰撞反馈改进 实际运行游戏后,希望飞出动画期间可以继续点击其他箭头,同时避免同一箭头重复触发;被阻挡时应有足够明显的反馈 Codex 将动画改为列表管理,保留同箭头防连点,并将文字提示改为独立计时 2 秒 测试从 46 项增至 51 项并全部通过,覆盖并行动画、提示刷新和重开清理 我通过实际操作发现原有输入锁限制了连续点击游玩,因此主动调整交互要求。后续观察到碰撞文字与 HUD 重叠,又要求删除文字提示,改用箭头或线条变红、晃动反馈。
项目回归测试 基础功能完成后,要求对完整游戏进行测试,检查关卡、状态切换、动画及异常操作 Codex 补充答案回放、三关反复重开、HUD 和七类场景退出测试 55 项全部通过,三关可解,依赖检查正常;本轮没有修改游戏核心实现 我将测试独立划分为一个开发阶段,要求覆盖作业规定的 T01~T06,并增加连续点击、动画期间操作和关卡切换等检查。通过测试报告确认基础功能后,再决定进入扩展开发,而不是继续堆叠新功能。
扩展:提示与撤销 在基础版之上增加 提示 和 撤销,提示 必须基于当前剩余棋盘,撤销 应撤销最近一次引起棋盘变化的操作,支持手动和自动消除,被阻挡的点击不计入,同时不破坏原有玩法 DeepSeek Harness 复用现有 Solver 实现一步提示,利用独立快照实现多步撤销,并调整窗口布局,codex对撤销过程做了进一步规范 测试从 55 项增至 87 项并全部通过;后续 UI 美化与扩展回归后,211 项测试、199 个子测试全部通过。原有 Solver 接口未改动 我明确了 提示 只提示、不自动操作,以及 撤销 应恢复哪些状态;要求 AI 复用已有模块,不重新设计规则;我实测发现撤销功能存在的问题,并要求AI进行相应修正
进阶模式与动画改进 在经典模式之外增加带折线的进阶模式,箭头后可连接任意长度、允许多次转折的线条,并尽量铺满棋盘 Codex 实现 LinePiece、AdvancedBoard、进阶路径检测、退出动画及对应求解功能 进阶模式能够显示直线和折线,并沿箭头方向退出;早期版本存在线条过粗、运动不自然和闪烁等问题,后续进行了针对性修改 我参考原游戏规则,纠正了 AI 最初将整条折线作为刚体平移的错误理解,明确要求箭头向前移动、后续线段依次跟随。实际观察动画后,又提出更简单的“头部增长一格、尾部缩短一格”方案,要求中间折线保持稳定,并将线条改为细线、小箭头,减少闪烁和复杂变形。
游戏界面与交互优化 在功能完整的基础上改善界面布局,增加暂停和页面导航,使整体风格简洁、Q 版、色彩柔和且统一 Codex 实现暂停菜单、返回选关与主菜单等交互;ChatGPT 根据运行截图分析布局问题,提供 UI 设计建议和美术素材参考(生成素材图片) 游戏能够显示暂停覆盖层并提供继续、重开和返回操作;原界面存在按钮过长、留白过多、文字拥挤以及暂停后底层仍有 hover 反馈等问题 我多次提供实际运行截图,指出具体视觉和交互问题。随后明确了暂停时底层不能响应鼠标、选关页应使用独立关卡卡片、游戏界面应划分顶部信息区、中央棋盘区和底部操作区等要求,并借助 ChatGPT 探索统一的 Q 版视觉风格;通过实际界面截图检查提示高亮、按钮状态和布局,并提出窗口尺寸、棋盘位置等调整意见。

示例一

对箭头点击流畅度的优化、对提示优化

{CC179808-26C8-4310-B0AE-24097436BA94}

示例二

对飞行动画的调整

{726C7BAC-ED3E-402F-8D1B-E0B0B4E7F822}

示例三

对UI美术的调整

{1D8188D9-EEDB-48A8-8141-A46C55492703}

五、测试结果

编号 测试内容 预期结果 实际结果 是否通过
T01 点击前方无阻挡箭头 飞出并消失 逻辑移除且不扣机会;四方向动画与出窗像素测试通过 通过
T02 点击前方有阻挡箭头 保留箭头,机会减 1 保留数量,扣一次机会;同箭头碰撞期间防连点 通过
T03 边缘朝外箭头 正常消除,不越界 四边朝外检查通过 通过
T04 清空当前关 显示通关,点击进入下一关 最后动画结束显示通关,按钮切换下一关 通过
T05 机会耗尽 显示失败并允许重开 延后显示失败,结束后操作保护与重试通过 通过
T06 游戏过程中重开 布局和机会恢复 三关各反复重开,恢复布局及机会,清空动画和提示 通过
T07 使用提示功能 高亮当前可消除对象且不改变游戏状态 提示正确且不写入撤销历史 通过
T08 撤销成功消除,忽略受阻点击 恢复最近消除的对象,受阻扣除的失误次数不返还 手动与自动操作均可逐步撤销 通过
T09 回放三个正式关卡 所有关卡均能按求解序列清空 三关全部通过 通过
T10 操作进阶折线模式 线条消除与受阻规则正确 点击线身、消除和碰撞测试通过 通过
T11 生成随机挑战 经典与进阶随机棋盘均可解 多种尺寸生成及求解验证通过 通过
T12 中途停止自动演示 停止后可以继续手动操作 棋盘、提示和撤销恢复正常 通过

测试脚本结果:

image

测试已由本人手动试玩,检验无误。

六、PSP 表格

任务 预估耗时(小时) 实际耗时(小时) 差异(小时)
需求分析与游戏设计 0.5 0.5 0
Python 与图形库学习 3 4 1
游戏界面实现 2 5 3
路径与碰撞逻辑实现 2 1 -1
关卡设计 2 1 -1
AIGC 辅助开发 3 5 2
测试与修改 2 3 1
扩展功能(提示与撤销) 1 3 2
README 与博客撰写 3 4 1
合计 18.5 26 8

七、心得体会

一开始我去微信玩了一下原游戏,觉得拆分开来看,玩法似乎没有那么复杂,实际上做的时候才发现比想象中麻烦得多。不说 UI 的布局和设计,前几个固定关卡的设定还算容易,进阶关卡涉及更长的线条、更多的转折和相互阻挡,感觉就没有那么简单了。当前的只是 5*5 棋盘的版本,进阶感觉比基础版简单。后续有机会的话,我也希望能在 GitHub 上继续更新更大棋盘、更多格子的版本。

而等到真正实现逻辑的时候,会发现 AI 自己理解的和我们想要的往往是两码事。AI 会认为棋盘边界就是边界了,于是飞出动画只绘制到棋盘边界;AI 觉得进阶模式中带着长线条的箭头直接平移飞出就好了,却没有考虑后面的线条应该沿着原来的路径依次跟随;提示 “蛇形飞出” 后,AI又想要绘制很复杂且不美观的动画出来;不特别告诉 AI 的话,游戏界面都没有返回主菜单的按键。很多布局和按键不协调的问题,也只有自己测试了才知道,AI只会说测试通过。AI 能帮助建立数据结构、编写界面和生成测试,但 AI 不会,或者说不能直接生成你想要的游戏。即使提示词已经描述得比较详细,AI 解读出的游戏规则、交互方式和视觉效果也不一定与自己一致。

本次作业让我学到了不少。首先是需求分析的重要性。最开始觉得只需要告诉 AI “做一个这样那样的游戏” 就足够了,后来才发现,箭头怎样移动、什么时候触发消除、动画期间能否继续点击、暂停后计时是否停止、撤销是否恢复失误次数,这些看似细小的问题,实际上都需要提前确定。需求越模糊,后续反复修改的成本就越高。

还有模块化开发和版本管理。我把整个项目分成基础架构、核心逻辑、关卡、界面、动画、测试等阶段,尽量让每一阶段只负责一部分内容。这样出现问题时,可以更容易地判断是游戏规则、状态管理还是界面绘制造成的,便于修改和调整。同时,后面一些新的模式也能复用先前的底层。学习使用 Git 则让我能够保留各阶段的成果,不必担心后续扩展破坏已经完成的基础版本。

posted @ 2026-09-20 19:25  JFanH  阅读(16)  评论(0)    收藏  举报