项目 | 内容 |
---|---|
这个作业属于哪个课程 | 2025年春季软件工程(罗杰、任健) |
这个作业的要求在哪里 | [P] 结对项目:影蛇舞 |
我在这个课程的目标是 | 学习软件工程知识,通过团队协作开发一个具备实际应用价值的软件,从需求分析、设计、开发到测试和部署,完整经历软件开发生命周期,提高工程实践能力。 |
这个作业在哪个具体方面帮助我实现目标 | 通过与他人合作开发,完成一个项目,提高代码能力、代码审查能力和沟通能力 |
结对项目:博客问题清单
请将本文件在代码仓库外复制一份,一边阅读和完成结对项目、一边填写入代码仓库外的版本,或采取简记、语音备忘等方式记载较复杂问题的要点之后再补充。请不要将本文档内的作答提交到代码仓库。
Chapter.0 Belua multorum es capitums.(你是多首的怪物。)
引入
→ 📖 Q0.1(P) 请记录下目前的时间。
2025/03/23 16:00
调查
→ 📖 Q0.2(I) 作为本项目的调查:请如实标注在开始项目之前对 Wasm 的熟悉程度分级,可以的话请细化具体的情况。
I. 没有听说过;
II. 仅限于听说过相关名词;
III. 听说过,且有一定了解;
IV. 听说过,且使用 Wasm 实际进行过开发(即便是玩具项目的开发)。
III.
总结
→ 📖 Q0.3(P) 请记录下目前的时间。
2025/03/23 16:20
Chapter.1 不畏迷茫,只管前进。(迷子でもいい、前へ進め。)
结对过程
→ 📖 Q1.1(P) 请记录下目前的时间。
2025/03/23 16:23
→ 📖 Q1.2(P) 请在完成任务的同时记录,并在完成任务后整理完善:
- 浏览任务要求,参照 附录A:基于 PSP 2.1 修改的 PSP 表格,估计任务预计耗时;
- 完成编程任务期间,依次做了什么(比如查阅了什么资料,随后如何进行了开发,遇到了什么问题,又通过什么方式解决);
Personal Software Process Stages | 个人软件开发流程 | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
PLANNING | 计划 | 5 | 5 |
- Estimate | - 估计这个任务需要多少时间 | 150 | 80 |
DEVELOPMENT | 开发 | 120 | 55 |
- Analysis & Design Spec | - 需求分析 & 生成设计规格(确定要实现什么) | 5 | 10 |
- Technical Background | - 了解技术背景(包括学习新技术) | 5 | 5 |
- Coding Standard | - 代码规范 | 15 | 3 |
- Design | - 具体设计(确定怎么实现) | 15 | 2 |
- Coding | - 具体编码 | 35 | 20 |
- Code Review | - 代码复审 | 15 | 5 |
- Test Design | - 测试设计(确定怎么测,比如要测试哪些情景、设计哪些种类的测试用例) | 15 | 5 |
- Test Implement | - 测试实现(设计/生成具体的测试用例、编码实现测试) | 15 | 5 |
REPORTING | 报告 | 25 | 20 |
- Quality Report | - 质量报告(评估设计、实现、测试的有效性) | 10 | 10 |
- Size Measurement | - 计算工作量 | 5 | 5 |
- Postmortem & Process Improvement Plan | - 事后总结和过程改进计划(总结过程中的问题和改进点) | 10 | 5 |
TOTAL | 合计 | 150 | 80 |
- 首先分析了任务文档,然后对问题建模得出最优决策方案,之后进行代码实现,最后编写单元测试。
测试
→ 📖 Q1.3(P) 请说明针对该任务,你们设计和实现测试的方法及过程,包括但不限于:出于对需求的哪些考虑设计了哪些测试用例、如何评估所设计测试的有效性 等等。
在设计和实现上,我们先分析了问题,发现蛇只有4节,因此向前或左转/右转均不会与自己发生碰撞,最优的决策即为贪心地前往曼哈顿距离下离果子最近的点。
我们的决策过程如下:
- 对于4个方向按距离果子的曼哈顿距离排序,然后从最优的方向开始选择
- 对于某个前进方向,我们检查选择该方向后蛇是否会与自己相撞,以及是否会碰壁
- 如果上述检查均通过,则返回该方向,否则依次检查次优的方向
- 如果所有的方向都没有通过检查,则返回方向0
对于测试,我们借用了官方的函数greedy_snake_fn_checker,并随机生成N组合法的蛇身坐标以及果子坐标作为输入参数,调用该函数N次以进行测试
我们设N=2000以覆盖尽可能多的情况,通过大量的随机测试以保证测试的有效性。
→ 📖 Q1.4(I) 请说明单元测试对软件开发的作用。
- 单元测试能够及时发现并修复代码中的错误和缺陷,确保每个模块的正确性。
- 有了单元测试,开发者可以放心地对代码进行重构,而不必担心破坏已有功能。
- 单元测试为代码提供了辅助理解,帮助开发者理解模块的功能和接口,易于维护和扩展。
总结
→ 📖 Q1.5(P) 请记录下目前的时间,并根据实际情况填写 附录A:基于 PSP 2.1 修改的 PSP 表格 的“实际耗时”栏目。
2025/03/23 17:44
→ 📖 Q1.6(I) 请写下本部分的心得体会。
非常的轻松,非常的愉快(赞赏)
Chapter.2 即使迷茫,也要前行。(迷子でもいい、迷子でも進め。)
结对过程
→ 📖 Q2.1(P) 请记录下目前的时间。
2025/03/23 18:44
→ 📖 Q2.2(P) 请在完成任务的同时记录,并在完成任务后整理完善:
- 浏览任务要求,参照 附录A:基于 PSP 2.1 修改的 PSP 表格,估计任务预计耗时;
- 完成编程任务期间,依次做了什么(比如查阅了什么资料,随后如何进行了开发,遇到了什么问题,又通过什么方式解决);
Personal Software Process Stages | 个人软件开发流程 | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
PLANNING | 计划 | 5 | 5 |
- Estimate | - 估计这个任务需要多少时间 | 100 | 245 |
DEVELOPMENT | 开发 | 60 | 200 |
- Analysis & Design Spec | - 需求分析 & 生成设计规格(确定要实现什么) | 10 | 15 |
- Technical Background | - 了解技术背景(包括学习新技术) | 5 | 10 |
- Coding Standard | - 代码规范 | 3 | 5 |
- Design | - 具体设计(确定怎么实现) | 2 | 20 |
- Coding | - 具体编码 | 25 | 80 |
- Code Review | - 代码复审 | 5 | 10 |
- Test Design | - 测试设计(确定怎么测,比如要测试哪些情景、设计哪些种类的测试用例) | 3 | 20 |
- Test Implement | - 测试实现(设计/生成具体的测试用例、编码实现测试) | 7 | 40 |
REPORTING | 报告 | 35 | 35 |
- Quality Report | - 质量报告(评估设计、实现、测试的有效性) | 25 | 20 |
- Size Measurement | - 计算工作量 | 5 | 5 |
- Postmortem & Process Improvement Plan | - 事后总结和过程改进计划(总结过程中的问题和改进点) | 10 | 15 |
TOTAL | 合计 | 100 | 245 |
对于T2,我们首先尝试了在T1的基础上加上对障碍的判断,但很快发现这样会导致蛇无法通过绕远来避开障碍;随后我们尝试使用dfs进行搜索并输出搜索路径上的方向,但随后发现dfs无法记录蛇过往的运动时的搜索状态导致在原地转圈;最终,我们使用bfs进行最短路的搜索,并用蛇头移动的多元组x,y,nx,ny
记录已搜索过的状态,每次调用决策函数时返回bfs到果子的路径上的第一步动作。
代码可复用性与需求变更
→ 📖 Q2.3(P) 请说明针对该任务,你们对 🧑💻 T1
中已实现的代码进行了哪些复用和修改。
我们对于单元测试的数据生成函数进行了复用,并在T1基础上增加了障碍的随机生成。
对于进行动作决策的代码部分,我们从仅根据距离函数判断更改为使用bfs搜索,除了对输入的处理有复用之外,其他部分的代码均有更改。
→ 📖 Q2.4(I) 请说明在编码实现时,可以采取哪些设计思想、考虑哪些设计冗余,来提高既存代码适应需求变更的能力。
可以采用模块化与参数化设计的思想,对代码中的不同逻辑部分分别进行封装并定义输入参数设定的接口,虽然增加了冗余度,但使得代码更加灵活可变,在题目设定改变时仅需改动参数配置而无需更改代码。
头脑风暴环节
**→ 📖 Q2.5(P) **只吃一个食物可满足不了贪吃蛇的欲望,请一起思考并简述以下场景中贪吃蛇的策略:
在 🧑💻 T2
的基础上,场地里不再是只有 1 个果子,而是总共有 n 个果子 (1 < n < 10 ),果子随机分布在场地中且不会刷新,保证不与障碍物重叠,保证每个果子均可达,且至少存在一条成功吃掉所有果子的路线,其余条件保持不变,请你找出一条吃完所有果子的行动路径。
先判断是否有果子位于死路中,即模拟让蛇前往每个果子后,此时蛇是否可达任一其他果子,如果发现存在死路里的果子,由题可知这样的果子最多只有一个,因此将其作为最后一个到达的果子。之后,可以随机选择其他的果子并前往,最后前往位于死路里的果子(如果存在)。
总结
→ 📖 Q2.6(P) 请记录下目前的时间,并根据实际情况填写 附录A:基于 PSP 2.1 修改的 PSP 表格 的“实际耗时”栏目。
2025/03/23 23:40
→ 📖 Q2.7(I) 请写下本部分的心得体会。
由于在打包后的WASM中无法调试,在遇到意料之外的报错时无法及时定位,比较折磨。
Chapter.3 这就是我的前进、到我出场了!!!!!(It's MyGO!!!!!)
结对过程
→ 📖 Q3.1(P) 请记录下目前的时间。
2025/03/27 11:12
→ 📖 Q3.2(P) 请在完成任务的同时记录,并在完成任务后整理完善:
- 浏览任务要求,参照 附录A:基于 PSP 2.1 修改的 PSP 表格,估计任务预计耗时;
- 完成编程任务期间,依次做了什么(比如查阅了什么资料,随后如何进行了开发,遇到了什么问题,又通过什么方式解决);
Personal Software Process Stages | 个人软件开发流程 | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
PLANNING | 计划 | 5 | 5 |
- Estimate | - 估计这个任务需要多少时间 | 360 | 450 |
DEVELOPMENT | 开发 | 300 | 400 |
- Analysis & Design Spec | - 需求分析 & 生成设计规格(确定要实现什么) | 20 | 20 |
- Technical Background | - 了解技术背景(包括学习新技术) | 20 | 30 |
- Coding Standard | - 代码规范 | 10 | 10 |
- Design | - 具体设计(确定怎么实现) | 30 | 40 |
- Coding | - 具体编码 | 180 | 265 |
- Code Review | - 代码复审 | 10 | 10 |
- Test Design | - 测试设计(确定怎么测,比如要测试哪些情景、设计哪些种类的测试用例) | 10 | 10 |
- Test Implement | - 测试实现(设计/生成具体的测试用例、编码实现测试) | 20 | 15 |
REPORTING | 报告 | 55 | 45 |
- Quality Report | - 质量报告(评估设计、实现、测试的有效性) | 30 | 25 |
- Size Measurement | - 计算工作量 | 10 | 10 |
- Postmortem & Process Improvement Plan | - 事后总结和过程改进计划(总结过程中的问题和改进点) | 15 | 10 |
TOTAL | 合计 | 360 | 450 |
在读完题目后,我们第一时间想到了使用深度强化学习的方法来解决这个复杂的博弈问题。
为了便于训练与测试,我们先用python复现了交互的环境,再用pytorch框架训练了一个小型的DQN,为了适配不同的场地大小,我们分别训练了两个网络SnakeNet_5和SnakeNet_8。
之后,我们上网查阅有关assemblyscript的资料,试图将网络导入wasm中,但经调查最后发现无法以较高的封装方式(如ONNX)进行导入,只能将权重全部展开为多维数组,硬编码进assembly-script。
同时,为了在wasm中推理,我们还实现了卷积层、线性层和ReLU的算子(由ChatGPT生成)。
然后,将assembly-script调试好后,我们让python的环境生成了几组随机的状态,输入给pytorch模型与wasm,比较他们的预测结构是否一致。
最后,经过与T2的算法的对弈测试,证明了我们的SnakeNet的性能。
需求建模和算法设计
→ 📖 Q3.3(P) 请说明你们如何建模这一需求。
T3需要我们在一个多条蛇对弈的环境中进行决策,我们将这个环境进行如下建模
- 状态
为了便于DQN感知,我们将传入的蛇和果子的坐标进行转换,变为一个三通道的张量,每个通道是一个NxN的二维矩阵。
具体而言,第一个通道代表自己的位置,蛇头处为1,蛇颈(第二节)为-1,其他蛇身忽略,因为决策时不需要考虑自己蛇身的影响;
第二个通道代表其他蛇的位置,其他蛇的蛇头为1,蛇身为-1;
我们将蛇头与蛇身区分开是为了让DQN学习到蛇的运动方式以及当前运动朝向。
第三个通道代表果子,有果子处为1
上述的三个通道的其他位置均为0
由此,每一时刻的状态建模为(3, N, N)的张量
- 动作
蛇的动作空间为一个有4个取值的离散空间,代表上、左、下、右
- 奖励
为了训练蛇吃到更多的果子,我们进行了如下的奖励设计
-
- 吃到果子得到+10
-
- 死亡得到-10
-
- 每次获得与最近的果子的距离的变化量,接近+1,远离-1
-
- 只要当前时刻存活就获得+0.2
-
- 场上每死一条蛇,获得+5。尽管很难直接杀死其他蛇,但我们希望通过这个设计让AI蛇在合适的时机出手
-
- 当场上死了n条蛇时,每吃一个果子额外有5*n的加分,距离的变化量导致的奖惩也从1变为1+n。我们希望在场上的蛇较少时,AI蛇能够尽可能向果子发起冲锋。
-
策略模型
为了平衡性能与开销,我们使用了一个小网络来作为DQN,如下所示
class SnakeNet(nn.Module):def __init__(self, board_size, num_actions=4):super().__init__()self.board_size = board_sizeself.num_actions = num_actionsinput_channels = 3 # self, others, fruitself.conv1 = nn.Conv2d(input_channels, 16, kernel_size=3, padding=1)self.conv2 = nn.Conv2d(16, 32, kernel_size=3, padding=1)self.fc1 = nn.Linear(32 * board_size * board_size, 64)self.fc2 = nn.Linear(64, num_actions)def forward(self, x):x = F.relu(self.conv1(x))x = F.relu(self.conv2(x))x = x.view(x.shape[0], -1)x = F.relu(self.fc1(x))x = self.fc2(x)return x
在这里,我们希望通过加入卷积层使AI蛇学习到局部的信息,加快策略的收敛。
该网络接受一个(3, N, N)的局面状态,输出4维向量,代表当前状态\(s_t\)下每个动作\(a_t\)对应的状态动作价值\(q_\theta(a_t|s_t)\)
→ 📖 Q3.4(P) 请说明针对该任务,你们采取了哪些策略来优化决策。具体而言,怎么避免死亡?怎么吃到更多果子?如何编程实现。
我们策略的优化过程即为DQN决策模型的训练过程。
我们使用了经典的强化学习算法Q-learning来对上面的网络进行训练。
Q-learning使用两个共享架构与权重但更新频率不同的DQN网络进行学习,其中一个网络负责与环境交互并采集经验
\(<s_t,a_t,r_t,s_{t+1}>\),另一个网络负责从经验池采样,并通过最优Bellman方程更新对\(q_\theta(a_t|s_t)\)的估计。两个网络中的前者会以一定时间间隔将后者网络更新后的权重复制过来。
由于Q-learning学到的是所有动作的价值,在进行交互的时候输出价值最大的动作,因此,为了平衡对其他动作的探索和已学习经验的利用,我们还使用了\(\epsilon-greedy\)的机制,在每次选择动作时有\(\epsilon\)的概率进行随机选择。 \(\epsilon\)的取值会逐渐减小,类似于退火。
在训练环境中,还需要加入其他的蛇与AI进行博弈,对于这些蛇,我们选用基于T2
修改而来的启发式算法进行决策。具体而言,我们输入最近的果子位置,并将其他蛇的前三节作为障碍(只有前三节在移动前后保持不变),调用T2算法得到动作方向。如果T2返回-1,则使用T1算法,直接忽略障碍向最近的果子接近。我们将该算法作为baseline。
在上述奖励函数的激励下,我们已能训练出表现优异的AI蛇,在此基础之上,我们对网络训练时的超参数以及奖励函数进行了一定程度的调优,确定了表现最佳的训练参数。
软件度量
→ 📖 Q3.5(P) 请说明你们如何量度所实现的程序模块的有效性,例如:“如何说明我们的程序模块对弈能力很强?”尝试提出一些可能的定量分析方式。
我们的python环境具有随机生成对局的能力,经过AI蛇与baseline算法的多轮对弈,SnakeNet_5和SnakeNet_8均能在90%以上的对局中存活到最后,说明具有了较强的避障与得分能力。并且,有很大概率最后只有AI蛇存活,证明了上述奖励设计的有效性。
定量分析:
我们将4snakes的对局进行了调整,不断重新开始并累计所有得分,在100场对局后,我们的AI蛇得分比baseline高出大约40%:
=== FINAL RESULTS ===
Snake scores:
Snake 1: 3392 points (survived) spent 897.349ms
Snake 2: 2666 points (died in round 25) spent 0.635ms
Snake 3: 2374 points (survived) spent 2.087ms
Snake 4: 2473 points (died in round 25) spent 0.216ms
总结
→ 📖 Q3.6(P) 请记录下目前的时间,并根据实际情况填写 附录A:基于 PSP 2.1 修改的 PSP 表格 的“实际耗时”栏目。
2025/3/29 16:00
→ 📖 Q3.7(I) 请写下本部分的心得体会。
战斗,爽!
都什么年代还用传统优化算法
结对项目总结
结对过程回顾和反思
→ 📖 Q4.1(P) 提供两人在讨论的结对图像资料。
→ 📖 Q4.2(P) 回顾结对的过程,反思有哪些可以提升和改进的地方。
时间控制有待改进;
debug应更加理性
→ 📖 Q4.3(I) 锐评一下你的搭档!并请至少列出三个优点和一个缺点。
优点:
- 有debug耐心
- 有钻研精神
- 做代码审查时非常专注
缺点:
- 资料查阅能力有待提升
- 代码编写能力稍有不足
- 对debug的察觉不够敏锐
对结对编程的理解
→ 📖 Q4.4(I) 说明结对编程的优缺点、你对结对编程的理解。
优点是随时都有代码检查,降低bug的遗留概率,缺点是减小了工程开发中的并行程度。
我对结对编程的理解是两个人共同进行开发,分为主驾驶与副驾驶(导航员),主驾驶员负责实现代码,导航员负责上层规划与错误检查。导航员及时地对驾驶员的反馈能够极大地提高代码编写后的质量。
代码实现提交
→ 📖 Q4.5(P) 请提供你们完成代码实现的代码仓库链接。
https://github.com/amxm41949/yingSheWu
附录
附录A:基于 PSP 2.1 修改的 PSP 表格
Personal Software Process Stages | 个人软件开发流程 | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
PLANNING | 计划 | ||
- Estimate | - 估计这个任务需要多少时间 | ||
DEVELOPMENT | 开发 | ||
- Analysis & Design Spec | - 需求分析 & 生成设计规格(确定要实现什么) | ||
- Technical Background | - 了解技术背景(包括学习新技术) | ||
- Coding Standard | - 代码规范 | ||
- Design | - 具体设计(确定怎么实现) | ||
- Coding | - 具体编码 | ||
- Code Review | - 代码复审 | ||
- Test Design | - 测试设计(确定怎么测,比如要测试哪些情景、设计哪些种类的测试用例) | ||
- Test Implement | - 测试实现(设计/生成具体的测试用例、编码实现测试) | ||
REPORTING | 报告 | ||
- Quality Report | - 质量报告(评估设计、实现、测试的有效性) | ||
- Size Measurement | - 计算工作量 | ||
- Postmortem & Process Improvement Plan | - 事后总结和过程改进计划(总结过程中的问题和改进点) | ||
TOTAL | 合计 |