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

2026秋软件工程个人作业(第二次)——利用AIGC完成"一箭又一箭"小游戏

项目 内容
这个作业属于哪个课程 H202601软件工程与软件工程实践
这个作业要求在哪里 2026秋软件工程个人作业(第二次)
这个作业的目标 使用 Python 和 AIGC 完成"一箭又一箭"小游戏
学号 102402144
GitHub 仓库 仓库链接

目录

  1. 项目展示
  2. 项目介绍
  3. 实现思路
  4. AIGC使用过程
  5. 测试结果
  6. PSP表格
  7. 心得体会

一、项目展示

开始界面

image

游戏过程

游戏演示

通关界面

image

失败界面

image


二、项目介绍

游戏规则

棋盘由若干格子组成,每个格子里有一个带方向的箭头(上 / 下 / 左 / 右),或者为空。 点击一个箭头,程序会检查它前进方向上的路径:

如果箭头与棋盘边界之间没有其他箭头阻挡,该箭头飞出棋盘并被消除;
如果路径上存在其他箭头,该箭头不能被消除,会晃动并变红作为碰撞提示, 同时消耗一次失误机会;
清除本关全部箭头即可进入下一关;
失误次数耗尽时本关失败,可以重新开始。
例如下面这一行,第一个箭头朝右,但右侧仍有其他箭头,因此不能飞出:

→  ·  ·  ↑  ·

而这一行最后一个箭头朝右,右侧没有其他箭头,因此可以飞出:

↑  ·  ·  ·  →

界面设计

界面包含开始界面、关卡选择、游戏界面和通关 / 失败界面。

  • 开始界面:标题、开始按钮
  • 游戏界面:当前关卡、棋盘、剩余箭头数、剩余失误次数、重新开始按钮
  • 结果界面:通关/失败提示

具体效果已经在上方展示

主要功能

基础功能

  • 具有可以正常显示和操作的图形化游戏界面;
  • 棋盘中包含上、下、左、右四种方向的箭头;
  • 能够通过鼠标点击选择箭头;
  • 能够正确判断箭头前进方向上是否存在其他箭头;
  • 前方无阻挡时,箭头能够飞出棋盘并消失;
  • 前方有阻挡时,箭头不能消失,并给出明显的碰撞反馈;
  • 设置失误次数,并在界面中显示剩余次数;
  • 至少设计 3 个可以正常通关的关卡;
  • 清空当前关卡全部箭头后,能够显示通关结果并进入下一关;
  • 失误次数耗尽时,能够显示失败结果;
  • 提供重新开始功能,使当前关卡恢复到初始状态。

附加功能

  • 更多关卡或关卡选择;
  • 得分、计时或星级评价;
  • 提示功能;
  • 撤销上一步;
  • 保存游戏进度;
  • 随机生成可通关的关卡;
  • AI 自动求解当前关卡;
  • 更流畅的动画和音效;
  • 将程序打包为可执行文件;

三、实现思路

1. 箭头与方向的表示

方向

属性

  • 四个反向的元组表示

方法

  • 返回特定方向的行列增量
  • 返回特定方向的字符表示
class Direction(Enum):
    """箭头的四个方向。value 为 (行增量, 列增量)。"""

    UP = (-1, 0)
    DOWN = (1, 0)
    LEFT = (0, -1)
    RIGHT = (0, 1)

    @property
    def delta(self) -> tuple[int, int]:
        """返回该方向的行、列增量。"""
        return self.value

    @property
    def symbol(self) -> str:
        """返回该方向在关卡数据中的单字符符号,如 'U'。"""
        return _DIRECTION_SYMBOLS[self.name]

箭头

属性

  • 方向
  • 在棋盘上位置

方法

  • 返回方向的字符表示,用于储存关卡进度
@dataclass(frozen=True)
class Arrow:
    """棋盘上的一个箭头。"""

    row: int
    col: int
    direction: Direction

    @property
    def symbol(self) -> str:
        return self.direction.symbol

2. 棋盘的表示

棋盘

主要属性

  • 自身的行列长度
  • 储存箭头的二维数组

主要方法

  • 根据储存的关卡数据构造出棋盘
  • 判断某个箭头前方是否有箭头(即路径检测方法)
  • 箭头被移除之后清除箭头
class Board:
    """游戏棋盘:一组带方向的箭头 + 路径检测。"""

    def __init__(self, rows: int, cols: int) -> None:
        self.rows = rows
        self.cols = cols
        self._grid: list[list[Arrow | None]] = [[None] * cols for _ in range(rows)]

    @classmethod
    def from_grid(cls, grid: list[str]) -> "Board":
        """从字符串列表构造棋盘。

        每个字符串是一行,字符含义:
            'U' / 'D' / 'L' / 'R' = 该方向箭头
            '.'                   = 空格

        所有行必须等长,否则抛出 ValueError。
        """
        if not grid:
            raise ValueError("关卡棋盘不能为空")

        cols = len(grid[0])
        for index, line in enumerate(grid):
            if len(line) != cols:
                raise ValueError(
                    f"第 {index} 行长度为 {len(line)},与第 0 行的 {cols} 不一致"
                )

        board = cls(len(grid), cols)
        for r, line in enumerate(grid):
            for c, char in enumerate(line):
                if char == EMPTY_CELL:
                    continue
                if char not in SYMBOL_TO_DIRECTION:
                    raise ValueError(
                        f"第 {r} 行第 {c} 列出现非法字符 {char!r},"
                        f"只允许 {EMPTY_CELL} 或 U/D/L/R"
                    )
                board._grid[r][c] = Arrow(r, c, SYMBOL_TO_DIRECTION[char])
        return board
def is_path_clear(self, arrow: Arrow) -> bool:
        """判断 arrow 沿其朝向到棋盘边界之间是否没有其他箭头。

        从箭头前一个格子开始沿方向逐格前进,直到走出棋盘:
        途中遇到任何箭头即视为被阻挡。

        箭头位于边缘且朝向棋盘外时,扫描区间为空,直接判定为可飞出,
        不会访问越界下标(对应测试 T03)。

        若传入的箭头不属于本棋盘,直接抛出 ValueError —— 否则会基于
        另一个棋盘的布局给出看似合理却错误的结果。
        """
        if self.arrow_at(arrow.row, arrow.col) is not arrow:
            raise ValueError(f"箭头 {arrow} 不属于当前棋盘")
        dr, dc = arrow.direction.delta
        r, c = arrow.row + dr, arrow.col + dc
        while self.in_bounds(r, c):
            if self._grid[r][c] is not None:
                return False
            r += dr
            c += dc
        return True
def remove(self, arrow: Arrow) -> None:
        """把箭头从棋盘上移除。"""
        if self._grid[arrow.row][arrow.col] is not arrow:
            raise ValueError(f"棋盘上不存在箭头 {arrow}")
        self._grid[arrow.row][arrow.col] = None

关卡的表示

主要属性

  • 当前关卡编号
  • 当前棋盘(即上方的棋盘对象)
  • 失误次数,剩余箭头,等分,用时等关卡状态信息

主要方法

  • 重新开始,回复棋盘初始状态
  • 撤销上一步操作
  • 存档
  • 关卡失败时弹窗
  • 关卡成功时进入下一关
  • 退出游戏时存档

四、AIGC使用过程

AI工具: cluade code + deepseek

一、让AI读取作业发布页面的html,根据要求作业要求生成PRD,作为项目指导

Snipaste_2026-09-19_10-35-41

二、让AI根据PRD自行完成作业要求的基础功能的实现和测试

Snipaste_2026-09-19_10-59-26

三、我亲自游玩游戏进行测试,发现问题并让AI修改

主要问题

  • Bug:开始菜单不点按钮,随机点击任意处也会进入第一关
  • 改进:使得游戏画面可缩放

Snipaste_2026-09-19_11-10-43

四、让AI根据PRD自行完成作业要求的附加功能的实现和测试

Snipaste_2026-09-19_17-08-02

五、我亲自游玩游戏后,向AI提出修改建议

建议

  • 增加按钮反馈,使得鼠标放置在箭头上箭头会放大高亮,让用户知晓他选中了这个箭头

五、测试结果

AI自行编写自动化测试程序,进行共166项测试,全部通过,此处仅展示前20条,其中前6条为作业要求测试项

编号 测试内容 预期结果 实际结果 是否通过
T01 点击前方无阻挡的箭头 箭头飞出棋盘并消失 点击后箭头从棋盘移除,剩余箭头数由 1 变 0,失误次数不变,并登记飞出动画
T02 点击前方有阻挡的箭头 箭头不消失,失误次数减 1 箭头仍在原格,剩余箭头数不变,失误次数由 3 变 2,并登记碰撞动画
T03 点击位于边缘且朝向棋盘外的箭头 箭头正常消失,不发生越界错误 边缘四个方向朝外的箭头均正常飞出,未出现 IndexError
T04 消除本关全部箭头 显示通关并进入下一关 清空后显示通关界面;点击主按钮进入第 2 关,关卡号递增、布局与失误次数重置
T05 失误次数耗尽 显示失败并允许重新开始 失误归零后显示失败界面;点击主按钮可重新开始本关
T06 游戏进行中重新开始 箭头布局和失误次数恢复 点击"重新开始"按钮后,棋盘恢复初始布局,失误次数恢复为上限
T07 四方向在空旷棋盘上均可飞出 上下左右四个方向都判定为可飞出 四个方向全部返回可飞出
T08 四方向各放一个阻挡物 四个方向都判定为被阻挡 四个方向全部返回被阻挡
T09 方向的行列增量 四个方向的 (行, 列) 增量正确 与预期一致
T10 点击空格 不改变任何状态 盘面与失误次数均未变
T11 路径上只有空格不构成阻挡 R.... 中的 R 可飞出 判定为可飞出
T12 不同行不同列的箭头不构成阻挡 斜向箭头不影响判定 判定为可飞出
T13 移除阻挡物后被挡箭头转为可飞出 阻挡消失后判定反转 移除后由"被阻挡"变为"可飞出"
T14 传入不属于本棋盘的箭头 报错而非返回错误答案 抛出 ValueError,未静默返回错误结果
T15 4×5 棋盘逐格逐方向穷举检测 不访问越界下标 全部格子 × 四个方向均无异常
T16 1×1 棋盘四个方向 四个方向均可飞出且不越界 全部通过
T17 四角箭头朝外 均判定为可飞出 全部通过
T18 棋盘字符串与网格互转 转换后与原字符串一致 往返一致
T19 空格上没有箭头 空格返回 None 符合预期
T20 剩余箭头计数 计数正确 UDLR 行返回 4

六、PSP表格

任务 预估耗时(分钟) 实际耗时(分钟) 差异(分钟)
agent自动化需求分析与游戏设计 10 2 +8
游戏框架学习 120 60 -60
agent自动化基础功能代码实现(含测试与修改) 5 20 -15
人工测试 5 5 0
agent自动化附加功能代码实现(含测试与修改) 20 20 0
博客撰写 120 60 -60
合计 280 167 -127

七、心得体会

初次接触小型游戏的开发,我对框架一头雾水,通过学习AI生成的PRD以及自行查阅相关资料,我逐渐能理解各个模块的依赖关系和总体框架。我学习到,模块的具体实现其实不难,难的是如何组织代码框架,同时各个模块之间的耦合度不宜过高,否则后期维护和修改代码的时候会牵一发而动全身,极大的提高修改的难度。同时,一次性考虑所有的需求并实现代码在实际开发中是难以做到的,我们需要先搭建一个骨架,实现基本的对象和功能,再以此为基础慢慢往外扩展。我学习到,低风险的代码可以全权交给AI实现,高风险的代码,例如关乎用户的账号密码等,必须由人工审计和测试。

posted on 2026-09-20 18:45  slantingbell  阅读(8)  评论(0)    收藏  举报