软件工程第二次作业
一箭又一箭:Python + Pygame 小游戏开发实践
| 项目内容 | 软件工程第二次个人作业:一箭又一箭小游戏 |
|---|---|
| 这个作业属于哪个课程 | 2026 春季软件工程与软件工程实践(福州大学) |
| 这个作业要求在哪里 | 第二次作业:一箭又一箭 |
| 这个作业的目标 | 使用 Python 和 AIGC 完成 "一箭又一箭" 小游戏,掌握图形界面开发、路径判断、碰撞反馈、关卡状态管理等基础能力 |
| 学号 | 102401119 |
| GitHub 仓库 | Lqh5 / 一箭又一箭 |
一、项目展示
1. 开始界面

2. 游戏过程(第 1 关)


游戏过程中,点击 "前方无阻挡" 的箭头,箭头会沿方向飞出并消失;点击 "前方有阻挡" 的箭头,箭头会变成红色并抖动,同时失误次数减 1:

3. 通关界面

4. 失败界面

二、项目介绍
1. 游戏规则
-
棋盘中有若干带方向的箭头(上、下、左、右);
-
点击箭头后,程序沿箭头方向检查其到棋盘边界的路径:
-
若路径上没有其他箭头 → 箭头飞出棋盘并被消除;
-
若路径上存在其他箭头 → 箭头不能消除,产生红色高亮 + 抖动的碰撞反馈,并消耗 1 次失误机会;
-
-
消除本关全部箭头 → 通关,进入下一关;
-
失误次数耗尽 → 本关失败,可重新开始。
2. 界面设计
界面采用深色简约风格,共四种场景(状态机管理):
| 场景 | 内容 |
|---|---|
| 开始界面 | 游戏标题、玩法说明、"开始游戏" 按钮 |
| 游戏界面 | 顶部状态栏(关卡、剩余箭头、失误次数、重新开始按钮)+ 6×6 棋盘 |
| 通关界面 | "恭喜通关" 提示 + "进入下一关" 按钮 |
| 失败界面 | "挑战失败" 提示 + "重新挑战" 按钮 |
3. 主要功能
-
鼠标点击选择箭头;
-
四个方向箭头显示与路径检测;
-
箭头飞出动画、碰撞抖动反馈;
-
失误次数统计与显示;
-
关卡切换(3 个难度递增的关卡,每局随机生成新布局);
-
重新开始功能;
-
启动时自动校验所有关卡可解。
三、实现思路
1. 关卡与箭头的表示
棋盘是一个 6×6 的二维数组 grid[r][c]:None 表示空格,0/1/2/3 表示上 / 右 / 下 / 左四个方向的箭头。关卡不写死,每局开始时由 random_grid(箭头数) 随机生成,并保证一定有解:
def random_grid(num_arrows):
# 反推法:从空棋盘逐个放箭头
grid = [[None] * 6 for _ in range(6)]
placed = []
for _ in range(num_arrows):
# 找出所有"前方无任何已放箭头"的合法位置+方向
# (这样新箭头按放置倒序消除时一定能飞)
...
# 优先选能挡住某个旧箭头的位置,制造解谜依赖
grid[r][c] = d
placed.append((r, c, d))
return grid
三关的难度通过箭头数量区分(4 / 6 / 8 个),每局重开或进下一关都会重新随机。
方向用数字 0-3 表示,并映射为行列增量(注意 grid[r][c] 中 r 是行、c 是列):
DIRECTIONS = {
0: (-1, 0), # 上:行减 1
1: (0, 1), # 右:列加 1
2: (1, 0), # 下:行加 1
3: (0, -1), # 左:列减 1
}
2. 路径检测(核心算法)
点击 (r, c) 处的箭头后,从下一格开始沿箭头方向逐格前进:遇到其他箭头说明被阻挡,返回 False;到达棋盘边界返回 True:
def path_clear(self, r, c):
direction = self.grid[r][c]
if direction is None:
return False
dr, dc = DIRECTIONS[direction]
nr, nc = r + dr, c + dc
while 0 <= nr < GRID_ROWS and 0 <= nc < GRID_COLS:
if self.grid[nr][nc] is not None:
return False # 被其他箭头阻挡
nr += dr
nc += dc
return True # 畅通到边界
3. 点击处理与状态切换
def click(self, r, c):
if self.status != "playing":
return "invalid"
if self.grid[r][c] is None:
return "empty"
if self.path_clear(r, c):
self.grid[r][c] = None # 消除
if self.remaining_arrows() == 0:
self.status = "win" # 通关
return "fly"
else:
self.mistakes_left -= 1 # 碰撞:失误 -1
if self.mistakes_left <= 0:
self.status = "lose" # 失败
return "collision"
4. 关卡可解性保障
关卡数据不是写死的。我用了随机生成器 random_grid():每局开始都从空棋盘逐个放箭头。为了保证生成的棋盘一定有解,我用 "反推法"—— 放新箭头时要求它自己前方没有任何已放箭头,这样 "按放置倒着消除" 就一定能通关;同时优先让新箭头挡住某个旧箭头,制造 "必须先消新箭头" 的解谜依赖。程序启动时还会用 DFS 求解器对生成器做抽检:
def solve_level(grid, max_depth=20):
"""DFS 回溯:枚举所有当前可飞的箭头,逐个尝试消除。
返回 (是否可解, 消除顺序)。"""
g = [row[:] for row in grid]
def dfs(current, order):
if all(cell is None for row in current for cell in row):
return True, order
if len(order) > max_depth:
return False, order
candidates = [(r, c) for r in range(6) for c in range(6)
if current[r][c] is not None and can_fly(current, r, c)]
for r, c in candidates:
saved = current[r][c]
current[r][c] = None
ok, result = dfs(current, order + [(r, c)])
if ok:
return True, result
current[r][c] = saved # 回溯
return False, order
return dfs(g, [])
5. 界面绘制
-
棋盘:双层矩形绘制(背景色 + 边框),鼠标悬停时高亮;
-
箭头:
ArrowButton.points()根据方向计算三角形顶点,pygame.draw.polygon绘制; -
碰撞反馈:碰撞的箭头在 30 帧内显示红色 + 随机抖动偏移;
-
飞出动画:箭头沿方向逐步移动并淡出。
四、AIGC 使用过程
本次开发使用 豆包(Doubao) 作为 AIGC / Coding Agent 工具辅助开发,共记录了 5 次具有代表性的协作过程:
| # | 子任务 | 借助何种 AIGC | AI 实现或提供了什么 | 效果如何 | 人工修改 |
|---|---|---|---|---|---|
| 1 | 路径检测 | 豆包 | 生成四个方向的路径检测代码(path_clear) |
运行时发现方向判断错误:"上" 的方向增量写反,导致箭头方向与实际行为不符 | 人工逐格打印网格定位根因,将方向向量从 (0,-1) 修正为 (-1,0)(行 / 列索引搞混),并补充注释 |
| 2 | 关卡设计 | 豆包 | 生成随机关卡的算法(反推法 + 阻挡关系) | 初版随机生成器能保证可解,但偶尔产出"所有箭头都能直接飞"的无聊局 | 人工改进生成器:放新箭头时优先选"能挡住某个旧箭头"的位置,制造顺序依赖;并用 DFS 对每关抽检 20 次验证可解 |
| 3 | 环境搭建 | 豆包 | 建议安装 pygame 并给出命令 | Python 3.14 无 pygame 预编译包,源码编译时下载 SDL 依赖超时 | 人工检查系统 Python 版本,改用 Python 3.13 + 清华镜像成功安装 pygame 2.6.1 |
| 4 | 界面与反馈 | 豆包 | 生成箭头多边形绘制与碰撞抖动动画 | 界面能显示,但状态栏爱心符号 ❤ 无法渲染成方框 | 人工改用中文字体(微软雅黑)加载,并将失误次数改为数字显示 3 / 3 |
| 5 | 界面优化与加难度 | 豆包 | 生成深蓝渐变背景、箭头描边、按钮 hover 高亮、圆角结果卡片 | 视觉明显提升,但按钮上的 ↻、→ 符号又渲染成方框 | 人工把装饰符号全部换成纯文字("重开""进入下一关"),并把关卡改成每局随机生成(3 关、4/6/8 个箭头难度递增) |
代表性过程 1:路径检测的方向 Bug
我向豆包描述需求:" 棋盘网格用 grid[r][c] 表示,请实现 path_clear(r, c) 判断箭头前方是否畅通。"AI 给出的代码结构基本正确(沿方向逐格前进、遇箭头返回 False、到边界返回 True),但方向映射表定义反了:
\# AI 最初生成的(错误):
DIRECTIONS = {0: (0, -1), 1: (1, 0), ...} # "上"变成了"列减1"
\# 人工修正后:
DIRECTIONS = {0: (-1, 0), 1: (0, 1), ...} # 上=行减1,右=列加1
这个 bug 很隐蔽:grid 是 "先行后列",而 AI 按 "先列后行" 的习惯定义了增量。我通过打印解析后的网格逐格对照,才发现是方向增量错误,而不是解析错误。这提醒我:AI 生成代码不能直接信任,尤其是二维坐标、索引这类细节,必须用测试验证。
代表性过程 2:不可解的关卡
我让 AI 设计关卡。最初版本是写死的 5 个固定关卡,但 AI 给的第 2 关是 "两个箭头面对面互相阻挡" 的布局——这类布局永远无法清除。后来我干脆改成随机生成:每局开始都从空棋盘放箭头。AI 给出了"反推法"思路(放新箭头时保证它前方没东西挡,倒着消一定能通关),但初版生成器有时产出"所有箭头互不挡、随便点"的无聊棋盘。我改进了生成器:放新箭头时优先选"恰好挡在某个旧箭头前方"的位置,这样既保证有解,又制造了真正的解谜顺序依赖。
心得:游戏关卡不是"摆几个箭头"就行,必须保证存在可行的消除顺序;而随机生成更要有算法兜底,光靠随机摆必然出死局。
心得:游戏关卡不是 "摆几个箭头" 就行,必须保证存在可行的消除顺序。写一个自动求解器来验证关卡可解性,比人肉试玩更可靠。
代表性过程 3:Python 3.14 装不上 pygame
AI 给的安装命令是 pip install pygame,但在我的环境(Python 3.14.7)下 pip 找不到预编译包,转而从源码编译,编译过程需要从 libsdl.org 下载 SDL 依赖,最终超时失败(WinError 10060)。我检查后发现系统还装有 Python 3.13,用 3.13 创建虚拟环境后从清华镜像直接下载到了 cp313 的 pygame 2.6.1 wheel,10 秒安装完成。
心得:AI 给出的 "标准答案" 不一定适配你的环境。遇到依赖问题时,先检查 Python 版本和镜像源,换版本 / 换源往往比硬编译更快。
五、测试结果
我编写了自动化测试脚本 test_logic.py,对核心逻辑(不依赖图形界面)进行测试,共 24 项断言全部通过。其中 T00 是对随机关卡生成器的专项测试:每关配置各随机生成 20 个棋盘,用 DFS 求解器验证全部可解,结果 60/60 全部通过。
| 编号 | 测试内容 | 预期结果 | 实际结果 | 是否通过 |
|---|---|---|---|---|
| T00 | 随机关卡生成器:3 关各随机 20 个棋盘 | 全部可解(DFS 验证) | 60/60 可解 | ✅ 通过 |
| T01 | 点击前方无阻挡的箭头 | 箭头飞出棋盘并消失 | 箭头消除,失误次数不变 | ✅ 通过 |
| T02 | 点击前方有阻挡的箭头 | 箭头不消失,失误次数减 1 | 箭头保留,失误减 1 | ✅ 通过 |
| T03 | 点击位于边缘且朝向棋盘外的箭头 | 箭头正常消失,不发生越界错误 | 正常消除,全棋盘 path_clear 遍历无越界 |
✅ 通过 |
| T04 | 消除本关全部箭头 | 显示通关并进入下一关 | 状态变为 win,进入第 2 关(重新随机) | ✅ 通过 |
| T05 | 失误次数耗尽 | 显示失败并允许重新开始 | 状态变为 lose,reset() 恢复正常 |
✅ 通过 |
| T06 | 游戏进行中重新开始 | 箭头布局和失误次数恢复 | 箭头数量、失误次数、状态全部恢复 | ✅ 通过 |
运行截图:
结果: 24 通过, 0 失败
六、PSP 表格
| 任务 | 预估耗时(小时) | 实际耗时(小时) | 差异(小时) |
|---|---|---|---|
| 需求分析与游戏设计 | 1.0 | 1.0 | 0 |
| Python 与图形库学习 | 1.0 | 1.5 | +0.5 |
| 游戏界面实现 | 1.5 | 2.5 | +1.0 |
| 路径与碰撞逻辑实现 | 1.0 | 1.5 | +0.5 |
| 关卡设计与加难度 | 0.5 | 1.5 | +1.0 |
| AIGC 辅助开发 | 1.0 | 1.5 | +0.5 |
| 测试与修改 | 1.0 | 2.0 | +1.0 |
| README 与博客撰写 | 1.0 | 1.0 | 0 |
| 合计 | 8.0 | 12.5 | +4.5 |
说明:实际耗时普遍超出预估,主要原因是环境安装(Python 3.14 装 pygame 失败)与方向 bug 定位花掉了额外时间。这也说明 PSP 中的预估能力需要多练习。
七、心得体会
1. AI 带来的帮助
-
写代码提速明显:路径检测、界面绘制、碰撞动画这类 "套路化" 代码,AI 一次就能给出可用版本,节省了大量查文档时间;
-
帮助理解概念:对于 Pygame 的事件循环、surface 绘制等概念,AI 用中文解释得很清楚;
-
关卡设计有新思路:AI 给出的关卡虽然有问题,但启发了我的设计思路。
2. 出现的问题
-
AI 代码不能直接信任:方向向量定义反了这个 bug 极具隐蔽性 —— 代码结构、逻辑流程全对,就错在一个坐标增量上。这类 bug 靠 "读代码" 很难发现,必须靠打印中间数据 + 自动化测试;
-
环境适配比想象中麻烦:Python 版本太新反而装不上第三方库,换版本解决;
-
关卡可解性需要验证:人眼看着 "差不多" 的关卡,实际可能是死局。
3. 收获
-
完整走了一遍 "需求分析 → 数据结构设计 → 算法实现 → 界面开发 → 测试验证 → 文档撰写" 的软件开发流程;
-
学会了用自动化测试验证游戏逻辑(不依赖 GUI 也能测核心规则);
-
深刻体会到 AIGC 时代的开发模式:AI 生成初稿 + 人工理解验证 + 测试兜底,人仍然是质量的第一负责人。
浙公网安备 33010602011771号