软件工程个人作业(第二次)

一箭又一箭 —— Python + Tkinter 小游戏开发实践

项目 内容
这个作业属于哪个课程 首页 - H202601软件工程与软件工程实践 - 福州大学 - 班级博客 - 博客园
这个作业要求在哪里 2026 秋软件工程个人作业(第二次) - 作业 - H202601软件工程与软件工程实践 - 班级博客 - 博客园
这个作业的目标 使用 Python 和 AIGC 完成“一箭又一箭”小游戏
学号 052401324
GitHub 仓库 12aa950/-Python-Tkinter-

一、项目展示

屏幕截图 2026-09-18 202325

游戏界面(第 1 关):

game

通关界面:

clear

失败界面:

fail


二、项目介绍

本项目用 Python + Tkinter 实现了一个“一箭又一箭”风格的点击式箭头解谜小游戏。Tkinter 是 Python 自带的图形库,因此无需安装任何第三方依赖,只要电脑上有 Python 即可运行,非常轻量。

游戏规则

  • 棋盘上分布着带方向的箭头,方向分上、下、左、右四种;
  • 点击一个箭头,程序检查它“前进方向上到棋盘边界之间”是否还有其他箭头;
  • 前方无阻挡 → 箭头沿该方向飞出棋盘并被消除;
  • 前方有阻挡 → 箭头不消失,并触发闪红、晃动的碰撞反馈,同时失误次数减 1;
  • 清空本关全部箭头 → 通关并进入下一关;
  • 失误次数耗尽 → 本关失败,可重新开始。

界面设计

游戏包含“开始界面 / 游戏界面 / 通关界面 / 失败界面”四种画面。游戏界面顶部有状态栏,显示当前关卡、剩余箭头数、剩余失误次数,并提供“重新开始”按钮;中间是 5×5 的棋盘,每个箭头用带方向的蓝色三角表示。

主要功能

  • 鼠标点击选择箭头;
  • 四种方向的路径阻挡判定;
  • 箭头飞出动画与碰撞反馈动画;
  • 失误次数系统;
  • 3 个手工设计并经程序验证可通关的关卡;
  • 通关、失败、重开、关卡切换完整流程。

三、实现思路

3.1 数据表示

用二维字符列表表示棋盘,一个字符代表一个格子:

字符 含义
. 空格
^ 上箭头
v 下箭头
< 左箭头
> 右箭头

方向与其单位位移(行偏移, 列偏移)用字典映射:

DIR_DELTA = {
    "^": (-1, 0),   # 上
    "v": (1, 0),    # 下
    "<": (0, -1),   # 左
    ">": (0, 1),    # 右
}

关卡直接写成字符串列表,例如第 1 关:

[
    "..^..",
    "..^..",
    ".....",
    "<.<.<",
    ".....",
]

3.2 路径检测(核心)

这是整个游戏最关键的函数。从被点击的箭头出发,沿着它的前进方向一步步走:每走一步先判断是否越界——越界说明一路畅通,可以飞出;如果在棋盘内遇到了其他箭头,说明被阻挡。

def is_blocked(board, r, c):
    """判断 (r,c) 处箭头在前进方向上、到达边界之前是否还有其他箭头。"""
    rows, cols = len(board), len(board[0])
    arrow = board[r][c]
    dr, dc = DIR_DELTA[arrow]
    nr, nc = r + dr, c + dc
    while 0 <= nr < rows and 0 <= nc < cols:
        if board[nr][nc] != ".":      # 路径上遇到其他箭头 → 被阻挡
            return True
        nr += dr
        nc += dc
    return False                      # 一路畅通到达边界 → 可以飞出

边界判断 0 <= nr < rows and 0 <= nc < cols 很关键:它保证了点击边缘朝外的箭头不会发生数组越界。测试项 T03 专门验证了这一情况。

3.3 关卡可解性验证

为了确保每个关卡都能通关,我写了一个求解器:反复找出“前方无阻挡”的箭头并消除,直到清空或卡住;如果最终全部清空,说明该关存在合理的消除顺序。

def solvable(board):
    grid = [list(row) for row in board]
    rows, cols = len(grid), len(grid[0])
    while True:
        removed = False
        for r in range(rows):
            for c in range(cols):
                if grid[r][c] != "." and not is_blocked(grid, r, c):
                    grid[r][c] = "."
                    removed = True
        if not removed:
            break
    return all(grid[r][c] == "." for r in range(rows) for c in range(cols))

程序在启动时会对全部关卡执行 solvable() 自检,若某关不可解会直接报错退出,从而保证交付的关卡一定是可通关的。

3.4 点击处理与动画

点击处理根据 is_blocked 的结果分两条路:

  • 前方无阻挡 → 锁定输入、播放“飞出”动画,动画结束后从棋盘移除该箭头并刷新剩余数量;
  • 前方有阻挡 → 失误次数减 1,箭头闪红并左右晃动作为碰撞反馈,动画结束后若失误次数为 0 则进入失败界面。
def on_click(self, r, c):
    if self._locked:
        return
    if self.board[r][c] == ".":
        return
    if is_blocked(self.board, r, c):
        self._locked = True
        self.lives -= 1
        self._update_info()
        self._flash_collision(r, c, lambda: self._after_collision(r, c))
    else:
        self._locked = True
        self._fly_out(r, c)

动画通过 Tkinter 的 after(ms, callback) 定时回调实现:飞出时按方向逐步移动箭头坐标,碰撞时让箭头左右小幅度摆动并变色,结束后统一进入通关/失败判断。


四、AIGC 使用过程

本项目全程使用 Trae(Coding Agent)辅助开发,下面记录 3 次具有代表性的协作过程。

记录 1:路径检测与边界处理

项目 内容
子任务 路径检测
借助的 AIGC Trae
提出要求 “写一个函数,判断某箭头在前进方向上、到棋盘边界之间是否还有其他箭头,要求不越界”
AI 完成情况 生成了 is_blocked 的初版逻辑,能沿方向逐格判断并返回是否受阻
实际效果 初版把“越界”与“遇到箭头”的判断顺序写得比较简略,边界情况不够稳
人工修改 我补充并强调了轮巡时的边界条件 0 <= nr < rows and 0 <= nc < cols,并用测试项 T03(边缘朝外箭头)验证不越界后通过

记录 2:碰撞晃动动画的调试

项目 内容
子任务 碰撞动画
借助的 AIGC Trae
提出要求 “点击被阻挡的箭头时,让它闪红并左右晃动作为反馈”
AI 完成情况 生成了晃动回调,试图通过一个“方向反查”辅助函数来重建箭头坐标
实际效果 该辅助函数引用了一个未定义的属性 self._quiver_dir,运行时直接报错,程序无法继续
人工修改 我重写了晃动逻辑:直接利用碰撞当时仍在棋盘上的箭头方向 ch = self.board[r][c] 重建多边形坐标,左右摆动若干帧后归位并恢复颜色,运行后正常

记录 3:失误/重新开始机制的回归修复

项目 内容
子任务 失误次数与重开机制
借助的 AIGC Trae
提出要求 “失误次数耗尽要显示失败;重新开始要恢复本关初始布局和失误次数”
AI 完成情况 第一版给 start_level 加了 resume 参数,试图在不同场景下决定是否重置“失误次数”
实际效果 但这导致“重新开始”走复用分支时失误次数没有被清零;写自动化测试(T05/T06)时立刻暴露,重开后失误仍为 0
人工修改 我去掉了多余的 resume 分支,让每次进入/重开本关都统一重置失误次数和棋盘,测试随即全部通过

小结:AI 在生成整体框架和大部分代码上效率很高,但在“状态是否被正确重置”“边界是否越界”这类细节上容易埋坑。借助自动化测试把它们揪出来,是很有效的手段。


五、测试结果

测试方式:编写自动化测试脚本 test_game.py,在“无头”模式下驱动游戏逻辑与流程(把动画回调替换为同步回调以便不启动事件循环验证规则)。共 15 项断言全部通过。

编号 测试内容 预期结果 实际结果 是否通过
T01 点击前方无阻挡的箭头 箭头飞出棋盘并消失 箭头数量减 1 通过
T02 点击前方有阻挡的箭头 箭头不消失,失误次数减 1 箭头保留、失误 -1 通过
T03 点击位于边缘且朝向棋盘外的箭头 箭头正常消失,不越界错误 无越界,正常消除 通过
T04 消除本关全部箭头 显示通关并进入下一关 剩余箭头为 0、可进入第 2 关 通过
T05 失误次数耗尽 显示失败并允许重新开始 触发失败、重开后失误复位 通过
T06 游戏进行中重新开始 箭头布局和失误次数恢复 两者均恢复初始 通过

除自动化测试外,我还对设计好的 3 个关卡逐一用求解器验证了存在合理通关顺序,并实际试玩确认操作流畅、动画反馈正常。


六、PSP 表格

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

七、心得体会

这次作业让我第一次完整体验了“用 AIGC 从零做一个小型应用”的流程。游戏本身难度不大,但麻雀虽小五脏俱全:二维坐标、方向判断、路径检测、动画反馈、状态管理和关卡流程,几乎把课本里的知识点都串了一遍。

AI 带来的帮助:Trae 生成框架代码的速度非常快,接口设计、绘制逻辑这些重复性工作基本交给它之后,我可以用更多精力去关注游戏规则本身。特别是路径检测、飞行动画这类结构清楚的代码,AI 一次生成的可用度很高。

出现的问题:最大的坑集中在细节上——边界越界、状态重置、动画回调引用不存在的方法,这些都是“看起来能跑、一测就翻车”的问题。这让我意识到,AI 生成代码绝不能写完就跑,必须配合针对性测试来验证。

我的收获:一是对“判定 → 反馈 → 状态更新 → 流程推进”这类游戏主循环有了更清晰的认识;二是学会了写自动化测试来检验规则,而不是只在界面上点几下;三是明白了可靠的 AI 协作方式——不仅要会提需求,还要会审查、会测试、会修复 AI 留下的细节问题。AI 是很好的助手,但最终对代码负责的还是自己。

posted @ 2026-09-18 20:48  jisl  阅读(4)  评论(0)    收藏  举报