软件工程第二次个人作业

这个作业属于哪个课程 2026软件工程班级
这个作业要求在哪里 软件工程第二次个人作业
这个作业的目标 借助 AIGC 工具完成小游戏开发,同时通过实际运行和测试,对 AI 给出的代码进行判断、修改和完善
学号 102401406
GitHub仓库 https://github.com/kkj11111/Arrow--Game

一箭又一箭小游戏

一、 项目展示

开始界面:

4305fbdba3eff93899983fddcfd9e3a8

游戏对局界面

02_playing

碰撞反馈效果(箭头显示红色晃动 + 失误 +1)

04_shake

提示功能(金色光圈高亮可飞出箭头)

05_hint

选择关卡

09_level_select

通关弹窗

06_won

失败弹窗

07_lost

全部关卡通过界面

80d77e35b35aa765533e2a474ede324b

二、项目介绍

1.游戏规则

棋盘内存在上、下、左、右四个方向的箭头。玩家点击箭头,程序沿着箭头方向做路径检测:

  1. 如果箭头到棋盘边界之间没有其他箭头,箭头飞出棋盘并消除;
  2. 如果路径上存在其他箭头,箭头不能消除,触发晃动并变红提示,同时消耗一次失误;
  3. 失误次数耗尽,本关挑战失败,可以重新开始;
  4. 清除棋盘全部箭头,即为通关,自动进入下一关卡;
  5. 6个关卡:从 4×4 入门到 6×6 最终关,难度递进

2.主要功能

  1. 完整三界面:开始界面、游戏界面、通关/失败弹窗;
  2. 基础玩法:鼠标点击箭头、路径阻挡判断、飞出动画、碰撞晃动、失误计数;
  3. 扩展功能:提示、撤销上一步、自动求解、本局计时、程序合成音效;
  4. UI特点:四方向独立配色(上 = 绿、右 = 橙、下 = 紫、左 = 青);渐变背景;卡片圆角布局,按钮悬浮高亮。

三、实现思路

箭头、方向与关卡的表示

方向用枚举表示,值为 (行增量, 列增量):

class Direction(Enum):
    UP    = (-1, 0)
    DOWN  = ( 1, 0)
    LEFT  = ( 0, -1)
    RIGHT = ( 0,  1)

棋盘用二维列表 grid[row][col],元素要么是 Arrow 对象,要么是 None(空格)。关卡用字符数组描述,方便调试:

LEVEL_1 = [
    ["↑", "·", "·", "·"],
    ["·", "→", "→", "·"],
    ["·", "·", "·", "·"],
    ["·", "←", "·", "·"],
]

核心路径检测逻辑

拿到箭头对应的方向向量,从箭头相邻格子开始,沿着箭头前进方向逐格遍历棋盘。

  • 如果遍历途中遇到其他箭头,判定存在阻挡,箭头无法飞出;
  • 如果一直走到棋盘边界都没有遇到箭头,判定无阻挡,可以飞出消除。

关键代码片段:

def is_path_clear(self, arrow: Arrow) -> bool:
    dr, dc = arrow.direction.value
    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

一开始向上 / 向左检测时,AI 生成的代码写成了 while r >= 0 and c >= 0 但没同时判断上界,导致在边缘箭头点击时报 IndexError。修复方式是统一用 in_bounds(r, c) 封装四个方向的边界判断,循环条件收敛成一行。

游戏状态管理

新建GameState类统一管理全部游戏状态:

  1. 场景状态:start开始界面 / game游戏中 / win通关 / lose失败/select_level选关界面;
  2. 关卡信息:当前关卡下标、失误次数、最大失误限制;
  3. 动画状态:晃动计时器、飞行动画计时器;
  4. 历史栈:用于撤销功能,保存每一步箭头布局快照。

关卡可解性自检与 AI 求解

为什么要自检?因为手工摆关卡很容易摆出 "死锁"—— 没有任何箭头能飞出去但棋盘还没清空。利用一个性质:消除箭头只会减少阻挡,绝不会产生新的阻挡,所以只要反复找出所有当前可飞的箭头并消除,能清空就是可解的,否则不可解。

def is_solvable(grid):
    board = Board(grid)
    while board.remaining() > 0:
        movable = [a for row in board.grid for a in row
                   if a is not None and board.is_path_clear(a)]
        if not movable:
            return False
        for a in movable:
            board.remove(a)
    return True

levels.solve() 在同样的模拟过程中记录消除顺序,就得到了一条通关点击序列 —— 这也是 "提示" 和 "自动求解" 的底层逻辑。随机关卡生成时,随机摆箭头后用 is_solvable() 过滤,最多重试若干次,保证给玩家的关一定能过。

计时修复说明

开发过程发现一个Bug:通关弹窗打开后得分会持续像倒计时一样变化。
原因:用时是实时调用time.time()计算,通关之后没有冻结结束时间,弹窗打开时钟继续走动,导致得分持续变动。
解决方案:通关或者失败时记录finish_time,胜负已定之后,读取用时直接使用已经冻结的时间,不再实时计算。

四、 AIGC 使用过程记录

子任务 借助何种AIGC技术 AI实现或提供了什么 效果 人工修改
路径阻挡检测逻辑 DeepSeek 生成四方向遍历检测代码,判断前方是否有箭头 基础逻辑正确,向上检测存在数组越界风险 修改边界循环条件,增加棋盘范围判断,修复越界问题
UI界面升级(无尾翼箭头+卡片布局) Kimi 生成绘制箭头、圆角卡片、渐变背景、彩色按钮的pygame绘图代码 绘图框架可用,箭头带有多余尾翼 删掉尾翼绘制代码,调整四方向配色,增加信息徽章,优化卡片阴影
碰撞晃动与飞出动画 GitHub Copilot 补全箭头晃动、飞出位移动画框架,动画计时器 动画可运行,晃动频率过快 调整动画帧计数,降低晃动幅度,增加飞出平滑插值;后续自己修复通关计时不冻结的bug
实现撤销功能历史栈 DeepSeek 提出使用栈保存棋盘快照的方案,给出深拷贝保存箭头状态的示例代码 快照保存逻辑可行,但最初只保存棋盘箭头,没有同步保存失误次数,撤销失误操作时失误计数不会回退 在每一次快照中同时记录棋盘布局、当前失误数;区分普通飞出操作和碰撞失误操作,撤销时完整恢复对局全部状态

总结:AIGC可以快速搭建代码框架,但是边界条件、业务细节需要人工阅读、调试、修改,不能直接复制就交付。

  1. 通关后计时持续跳动 Bug:通关弹窗弹出用时、得分持续变化。修复:游戏结束冻结finish_time时间戳。
  2. 路径检测数组越界闪退:边缘箭头点击发生索引报错。修复:封装棋盘边界判断函数。
  3. UI 文字重叠、弹窗排版错乱:新增按钮后元素互相压住,通关 / 失败弹窗高低不一致。修复:重新规划全部控件 Y 坐标,统一弹窗布局参数。
  4. 碰撞动画抖动严重:AI 生成动画参数导致箭头晃动抽搐。修复:调小位移,增加动画帧数。
  5. 关卡解锁存档失效:通关一关,下一关依旧锁定。修复:通关成功立刻持久化写入存档。
  6. 动画期间可以重复点击:动画播放中点击箭头造成游戏状态错乱。修复:增加忙碌标记,动画运行时屏蔽鼠标交互。

五、 测试结果

手工完成全部作业规定的测试用例:

编号 测试内容 预期结果 实际结果 是否通过
T01 点击前方无阻挡的箭头 箭头飞出棋盘并消失 箭头平滑飞出,成功移除 通过
T02 点击前方有阻挡的箭头 箭头不消失,失误次数减 1 箭头晃动,失误计数加一 通过
T03 点击位于边缘且朝向棋盘外的箭头 箭头正常消失,不发生越界错误 边缘箭头正常消除,程序无崩溃报错 通过
T04 消除本关全部箭头 显示通关结果并进入下一关 全部箭头消除后弹出通关弹窗,可以跳转下一关 通过
T05 失误次数耗尽 显示失败结果,允许重新挑战 失误达到上限弹出失败弹窗,支持重置关卡 通过
T06 游戏进行中重新开始 箭头布局和失误次数恢复初始状态 棋盘箭头、失误计数全部重置为本关初始值 通过
T07 使用撤销功能 棋盘和失误计数回退到上一步 布局、失误数正确恢复 通过
T08 使用提示功能 高亮可以飞出的箭头 金色光圈正确显示可飞箭 通过

六、PSP表格

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

七、 心得体会

本次作业使用Pygame完成“一箭又一箭”小游戏,借助多种AIGC编程工具辅助开发。AI可以快速生成基础代码,节省大量重复编码时间,但是AI输出的代码经常忽略边界情况,会出现数组越界、状态没有冻结、UI不符合设计需求等问题,需要仔细阅读代码、调试修复。

通过本次项目,我理解了网格类小游戏的开发流程:数据定义、核心算法实现、状态管理、界面渲染、动画处理。重点掌握了射线式路径检测的实现思路,明白了游戏中动画计时器的用法。

同时我也体会到,AIGC只是辅助工具,开发者需要能够读懂每一段关键代码,并且做好充分测试,才能够保证程序逻辑正确。本次作业多次提交Git commit,模拟真实软件开发迭代过程,让我对版本控制也有更深的理解。在开发过程中很多 Bug 不是逻辑完全错误,而是边界场景没有考虑到,这也提醒我写程序的时候要多关注边缘情况,做好测试。


posted @ 2026-09-19 22:14  cwfeng  阅读(45)  评论(0)    收藏  举报