[P] 结对项目:花见小路

项目 内容
这个作业属于哪个课程 首页 - 2026年春季软件工程 - 北京航空航天大学 - 班级博客 - 博客园
这个作业的需求在哪里 [P] 结对项目:花见小路 - 作业 - 2026年春季软件工程 - 班级博客 - 博客园
我在这个课程的目标是 通过学习理论和进行实践,熟悉软件开发的全流程,提升自己的学习能力和丰富开发经验
这个作业在哪个具体方面帮助我实现目标 学习了解结对编程相关知识,进行结对编程实践

结对项目:博客问题清单

请将本文件在代码仓库外复制一份,一边阅读和完成结对项目、一边填写入代码仓库外的版本,或采取简记、语音备忘等方式记载较复杂问题的要点之后再补充。请不要将本文档内的作答提交到代码仓库。

→ 📖 Q0.0(P) 【你可以在结对结束后补充】如果你的代码仓库包含 AIGC 的部分,列举使用的工具、模型和使用范围。若未使用则填写:本组提交的全部代码不包含AI补全或生成的部分。

本组在T3部分使用了Claude(claude-sonnet-4-6模型)辅助编程。AI生成的内容包括一些工具函数,以及两处bug修复和一处策略优化。所有AI生成的commit都在message里用[AIGC]做了标注。T1和T2的代码全部纯手工编写,不包含任何AI生成的部分。

Chapter.0 wasm从安装到入门

引入

→ 📖 Q0.1(P) 请记录下目前的时间。

2026/3/27 14:33

调查

→ 📖 Q0.2(I) 【你可以在结对结束后另行补充。】作为本项目的调查:

请如实标注在开始项目之前对 Wasm 的熟悉程度分级,可以的话请细化具体的情况。(分别回答两人各自的情况)

II. 仅限于听说过相关名词;

I. 没有听说过;

II. 仅限于听说过相关名词;

III. 听说过,且有一定了解;

IV. 听说过,且使用 Wasm 实际进行过开发(即便是玩具项目的开发)。

请如实标注在开始项目之前对桌游花见小路的熟悉程度分级,可以的话请细化具体的情况。(分别回答两人各自的情况)

I. 不了解玩法和规则;

I. 不了解玩法和规则;

II. 听说过,且有一定了解;

总结

→ 📖 Q0.3(P) 请记录下目前的时间。

2026/3/27 14:58

Chapter.1 七色之缨

结对过程

→ 📖 Q1.1(P) 请记录下目前的时间。

2026/3/27 15:08

→ 📖 Q1.2(P) 请在完成任务的同时记录,并在完成任务后整理完善:

  1. 浏览任务要求,参照 附录A:基于 PSP 2.1 修改的 PSP 表格,估计任务预计耗时;
  2. 完成编程任务期间,依次做了什么(例如查阅了哪些资料、如何设计判定逻辑、如何设计测试样例、遇到了什么问题、如何解决)。

首先上网查询各种语言的相关特性和与项目的合适度,结合AI的意见进行了变成语言的选择。接着完成了相关编译环境的配置。然后去搜索了AssemblyScript的相关语法进行学习。最后关于判定逻辑设计和测试样例设计主要是针对课程组给出的判定规则一条一条进行翻译,再自行举例确定没有遗漏。

Personal Software Process Stages 个人软件开发流程 预估耗时(分钟) 实际耗时(分钟)
PLANNING 计划 10 5
- Estimate - 估计这个任务需要多少时间 10 5
DEVELOPMENT 开发 145 95
- Analysis & Design Spec - 需求分析 & 生成设计规格(确定要实现什么) 10 5
- Technical Background - 了解技术背景(包括学习新技术) 60 40
- Coding Standard - 代码规范 5 5
- Design - 具体设计(确定怎么实现) 10 5
- Coding - 具体编码 30 20
- Code Review - 代码复审 10 5
- Test Design - 测试设计(确定怎么测,比如要测试哪些情景、设计哪些种类的测试用例) 10 5
- Test Implement - 测试实现(设计/生成具体的测试用例、编码实现测试) 10 10
REPORTING 报告 20 15
- Quality Report - 质量报告(评估设计、实现、测试的有效性) 5 5
- Size Measurement - 计算工作量 5 5
- Postmortem & Process Improvement Plan - 事后总结和过程改进计划(总结过程中的问题和改进点) 10 10
TOTAL 合计 175 120

设计

→ 📖 Q1.3(P) 请说明你们为这个判定模块设计了哪些中间量或辅助函数;如果没有额外设计,也请说明为什么认为直接实现已经足够清晰。

整个判定模块只有两个人各自的得分和倾心标记数这两类变量,没有额外设计辅助函数。因为阅读规则之后发现规则已经说的非常清晰,包含了所有可能出现的情况,所以只需要逐步翻译规则即可得到正确清晰的代码。同时整个判断过程不算特别复杂,就放在一个函数里面已经足够整洁。

→ 📖 Q1.4(I) 请说明在这样一个规则判定类模块中,如何避免“漏判”“错判”或分支顺序错误等问题。

主要就是让代码结构跟规则文档保持一致,对着规则一条一条写if-else就不容易漏。分支顺序比较关键,比如要先判11分再看倾心标记数,这个顺序规则里没有明说但逻辑上有先后。然后就是测试要覆盖每条分支,包括那些不太常见的边界比如第三小轮完全对称的平局。

测试

→ 📖 Q1.5(P) 请说明你们设计了哪些测试用例,这些测试分别覆盖了哪一类规则或边界情况。

我们按照指导书上的情况逐条编写了测试样例:[1, 1, 1, 1, 1, 0, 0]对应“一方分值达到 11 分而获胜”;[1, 0, 0, 0, 0, 1, 1]对应“一方获得至少 4 枚倾心标记而获胜”;[1, 1, 1, -1, -1, 0, 0]对应“前两小轮结束时尚未满足胜利条件,应返回 0”;[1, 1, 1, 1, -1, -1, 0]对应“第三小轮结束时,总分不同,由总分高者获胜”;[1, 1, 1, -1, 0, 0, 1]、[1, 1, 1, 0, 0, -1, 1]、[-1, -1, -1, 0, 0, 1, -1]、[1, 1, 0, 0, 0, -1, 0]对应“第三小轮结束时,总分相同,由最高档位倾心标记判定胜负”;[1, -1, 0, -1, 1, 0, 0]对应“第三小轮结束时平局,应返回 2“。

→ 📖 Q1.6(I) 请说明你对“先写测试再实现”与“先实现再补测试”两种方式的理解。

像T1这种需求明确的任务我觉得先写测试更好,输入输出规则里都写好了,先把用例列出来再写代码,写完直接跑就知道对不对。但是T3那种策略类的就不太行了,你没法提前说”最优选牌”是什么,只能先写一个能跑的版本再通过对战来验证。所以两种方式各有适用场景,不能一概而论。

总结

→ 📖 Q1.7(P) 请记录下目前的时间,并根据实际情况填写 附录A:基于 PSP 2.1 修改的 PSP 表格 的“实际耗时”栏目。

2026/3/27 17:15

→ 📖 Q1.8(I) 请写下本部分的心得体会。

T1我是导航员,一开始有点不适应,总觉得应该自己上手写。后来发现导航员确实能看到一些驾驶员注意不到的东西,因为不用分心在语法和打字上可以专注看逻辑。AS对我们都是新语言,前期查了不少文档,好在跟TypeScript差不多,上手还算快。城宇学东西挺快的,AS的语法他很快就上手了,写起来比我流畅。

Chapter.2 不祥之影

准备

→ 📖 Q2.1(P) 请记录下目前的时间。

2026/3/27 19:30

→ 📖 Q2.2(P) 请在完成任务的同时记录,并在完成任务后整理完善:

  1. 浏览任务要求,参照 附录A:基于 PSP 2.1 修改的 PSP 表格,估计任务预计耗时;
  2. 完成编程任务期间,依次做了什么(比如查阅了什么资料,随后如何进行了开发,遇到了什么问题,又通过什么方式解决);

在T1的时候就已经对语法进行了学习,所以T2主要就是进一步理解规则和具体任务之后开始编码,中间没有遇到什么难点。但是中间写出来了一个逻辑上的bug,花费了一些时间编写测试样例和重审代码找出来。

Personal Software Process Stages 个人软件开发流程 预估耗时(分钟) 实际耗时(分钟)
PLANNING 计划 5 5
- Estimate - 估计这个任务需要多少时间 5 5
DEVELOPMENT 开发 105 70
- Analysis & Design Spec - 需求分析 & 生成设计规格(确定要实现什么) 15 5
- Technical Background - 了解技术背景(包括学习新技术) 5 5
- Coding Standard - 代码规范 5 5
- Design - 具体设计(确定怎么实现) 10 5
- Coding - 具体编码 40 30
- Code Review - 代码复审 10 5
- Test Design - 测试设计(确定怎么测,比如要测试哪些情景、设计哪些种类的测试用例) 10 5
- Test Implement - 测试实现(设计/生成具体的测试用例、编码实现测试) 10 10
REPORTING 报告 20 20
- Quality Report - 质量报告(评估设计、实现、测试的有效性) 5 5
- Size Measurement - 计算工作量 5 5
- Postmortem & Process Improvement Plan - 事后总结和过程改进计划(总结过程中的问题和改进点) 10 10
TOTAL 合计 130 95

代码可复用性与需求变更

→ 📖 Q2.3(P) 请说明针对该任务,你们对 🧑‍💻 T1 中已实现的代码进行了哪些复用和修改。

在T2的任务中我们没有对T1的代码进行复用。 T1的目标是正确计算分数和判断胜负,而 T2的目标是解析对战记录并模拟推演双方卡牌数量和指示物所有权,两者的输入输出结构完全不同。可以看做T2的输出是T1的输入,他们是两个阶段,所以我们没有代码复用。

→ 📖 Q2.4(I) 请说明在编码实现时,可以采取哪些设计思想、考虑哪些设计冗余,来提高既存代码适应需求变更的能力。

把解析和逻辑分开比较重要。比如T2里字符串怎么切、怎么提取行动类型是解析层,拿到结果之后怎么更新棋盘是逻辑层,这两层拆干净的话以后改输入格式只动解析,改规则只动逻辑。另外数据结构上用数组存棋盘状态比用一堆单独变量好,以后角色数量变了也不用大改。

头脑风暴环节

→ 📖 Q2.5(P) 头脑风暴环节:

我们终于快要开始让程序玩游戏了!请尝试分析:T2 中不带 X 的操作记录比起实际对局多出了多少信息?如果加上 X,也就是失去了这部分信息的话,如何处理对小轮结束后状态的估计?

多出的信息:多出了对手暗置的底牌和对手废弃的牌的具体信息。

加上X如何估计状态:只能通过已有信息(比如自己的牌,对手展示的牌)结合总的牌的分布来进行排除法,从而确定一个大致范围,对手的牌在这个范围里面可以看成均匀分布,然后可以按照这个概率来进行决策或者胜率预测等。

总结

→ 📖 Q2.6(P) 请记录下目前的时间,并根据实际情况填写 附录 A:基于 PSP 2.1 修改的 PSP 表格 的“实际耗时”栏目。

2026/3/27 21:22

→ 📖 Q2.7(I) 请写下本部分的心得体会。

继续当导航员,比T1难一些因为状态多容易搞混。印象最深的是那个弃牌得分的bug,城宇写的时候我在旁边看着居然也没发现,跑测试才暴露出来。说实话这个bug属于我导航没做好,应该当时就能注意到的。这种逻辑错误光靠肉眼很难抓到,思路本身有偏差两个人都觉得没问题。还好城宇后面坚持多写了几个测试用例,不然这个bug可能拖更久。

Chapter.3 道途之荆

准备

→ 📖 Q3.1(P) 请记录下目前的时间。

2026/4/1 14:00

→ 📖 Q3.2(P) 请在完成任务的同时记录,并在完成任务后整理完善:

  1. 浏览任务要求,参照 附录A:基于 PSP 2.1 修改的 PSP 表格,估计任务预计耗时;
  2. 完成编程任务期间,依次做了什么(比如查阅了什么资料,随后如何进行了开发,遇到了什么问题,又通过什么方式解决);

预估大概3到6个小时。先搭了T3的项目框架和常量定义,然后写了一些基础的字符转换工具函数。接着开始处理核心问题:怎么从历史记录里判断当前轮到谁、我用过哪些行动了。这里遇到一个比较麻烦的点,就是历史记录里对手的盟约和弃牌都是X,得通过X来反推哪些token是自己的。之后就是写策略了,先写了一个cardScore评分函数给每张牌打分,考虑了牌的基础分值、当前棋盘上这个角色的争夺状态、以及手里同类牌的数量。选牌的时候盟约选最高分的藏起来,弃牌选最低分的扔掉。赠予和竞争比较复杂,用了minimax的思路:枚举所有可能的选牌组合,假设对手会做出对他最有利的选择,然后在这个前提下最大化自己的收益。中间碰到一个问题是AS不支持闭包,递归枚举组合的时候没法捕获外部变量,最后用模块级的全局变量绕过去了。编译的时候还有i8到i32隐式转换的警告,加了显式类型转换解决。跑测试的时候发现了几个bug,比如respondToCompete返回的分组顺序不对导致引擎校验失败,还有parseUsedActions在对手有pending offer的时候会把对手的行动算成自己的,这些都一个一个修了。最后自对战测试跑通,没有出现非法行动。

头脑风暴环节

→ 📖 Q3.3(P) 头脑风暴环节:

假设提供更充裕的时间和资源,这个游戏中你能找到的最优策略有可能是什么形式的?进行调研并总结分析。你还可以在任务结束后试着实现(不计分)。

花见小路本质上是一个不完美信息的两人零和博弈,跟德州扑克有点类似。如果时间和资源充裕的话,比较有前途的方向是反事实遗憾最小化(CFR),这个方法在德州扑克AI Libratus里面用过,通过大量自博弈迭代可以逼近纳什均衡。另一个思路是蒙特卡洛树搜索,对对手手牌进行随机采样然后搜索最优行动。深度强化学习理论上也可以但是训练起来比较困难,而且花见小路的状态空间其实不算特别大,感觉用不太到神经网络。最优策略大概率是一个混合策略,也就是带随机性的,因为纯策略太容易被针对了。我们目前用的启发式评分加一层minimax算是一个比较实用的折中方案,在2秒的时限内完全够用。

需求建模和算法设计

→ 📖 Q3.4(P) 请说明针对该任务,你们采取了哪些策略来优化决策。具体而言,怎么选择行动类型?选牌如何更优?如何编程实现。

行动类型的选择上我们定了一个优先级:弃牌 > 盟约 > 赠予 > 竞争。弃牌最安全因为两张牌直接出局不给对手选择机会,盟约可以藏一张好牌留给自己,赠予和竞争都会让对手有选牌的机会所以尽量往后放。

选牌方面我们设计了一个cardScore评分函数,综合考虑三个因素:牌本身的分值(G是5分比A的2分值钱)、当前棋盘状态(中立的角色价值最高因为还在争夺,对手已经拿到的也值得争取翻转,自己已经拿到的就没必要再堆了)、还有手里同类牌的协同效应(同一个角色的牌越多越容易拿到那个token)。盟约就选分最高的藏起来,弃牌选分最低的两张扔掉,额外做了一个优化就是已经拿到token的角色如果还有重复牌就优先丢弃。

赠予和竞争用了minimax的思路。赠予的时候枚举所有选3张牌的组合,假设对手会挑走对他最有价值的那张,在这个前提下找出让自己剩下2张总价值最高的方案。竞争类似,枚举所有选4张的组合再枚举3种分组方式,假设对手会选走价值更高的那组,找出让自己拿到的那组价值最大的方案。

实现上主要的难点是组合枚举,因为手牌有重复所以是多重集组合而不是普通组合。写了一个递归函数来生成所有选法。AS不支持闭包所以用了模块级全局变量来存递归过程中的临时结果。手牌最多6张,选3或4张的组合数不超过几十个,计算量很小,每步决策不到1毫秒。

代码可复用性与需求变更

→ 📖 Q3.5(P) 请说明针对该任务,你们对 🧑‍💻 T2 中已实现的代码进行了哪些复用和修改。

T3没有直接调用T2的代码,但是在设计思路上有不少复用。T2里面的parseCharToInt做字符到索引的映射,T3里面重新写了一个cardIndex用charCodeAt实现更简洁。T2的calcCurrentState按空格分割历史、按行动类型解析、用减号分割选择的这套解析模式,T3的parseUsedActions和detectPendingOffer基本上沿用了同样的套路。T2里面处理3号和4号行动时区分出牌者和响应者各拿到什么牌的逻辑,T3在respondToGift和respondToCompete里也用到了类似的思路。不过T3跟T2最大的区别是T3是站在自己的视角看问题,board里面1是自己-1是对手,而且对手的盟约和弃牌是X看不到具体内容,所以新加了inferMyParity通过X来判断己方身份,这是T2里完全不需要的。

→ 📖 Q3.6(I) 请说明在编码实现时,可以采取哪些设计思想、考虑哪些设计冗余,来提高既存代码适应需求变更的能力。

从T3的经验来看,最重要的一点是把评估函数和选牌逻辑分开。我们的cardScore负责打分,selectForGift和selectForCompete负责在打分基础上做决策,这样以后想换更厉害的评估方式只改cardScore就行,选牌的框架不用动。另外行动类型的优先级最好也别写死,我们现在硬编码了弃牌优先,但如果对手风格不同可能需要调整,做成可配置的会更灵活。

软件度量

→ 📖 Q3.7(P) 请说明你们如何量度所实现的程序模块的有效性,例如:”如何说明我们的程序模块决策能力很强?”,尝试提出一些可能的定量分析方式或测试方式。

最直接的方式就是跟其他组对战看胜率。除此之外也可以自己实现一些基线策略来比较,比如写一个纯随机出牌的对手,如果我们的策略对随机策略胜率不到80%那肯定有问题。还可以让程序跟自己对战很多局看先后手胜率是否均衡。另外就是看非法行动率,跑足够多的对局确保程序不会产生引擎拒绝的非法行动。我们实际测试中每步决策不到1毫秒,远在2秒限时内,也没有出现过非法行动。

总结

→ 📖 Q3.8(P) 请记录下目前的时间,并根据实际情况填写 附录A:基于 PSP 2.1 修改的 PSP 表格 的”实际耗时”栏目。

2026/4/1 17:10

Personal Software Process Stages 个人软件开发流程 预估耗时(分钟) 实际耗时(分钟)
PLANNING 计划 15 15
- Estimate - 估计这个任务需要多少时间 15 15
DEVELOPMENT 开发 180 160
- Analysis & Design Spec - 需求分析 & 生成设计规格(确定要实现什么) 20 20
- Technical Background - 了解技术背景(包括学习新技术) 10 10
- Coding Standard - 代码规范 5 5
- Design - 具体设计(确定怎么实现) 40 35
- Coding - 具体编码 70 55
- Code Review - 代码复审 15 15
- Test Design - 测试设计(确定怎么测,比如要测试哪些情景、设计哪些种类的测试用例) 10 10
- Test Implement - 测试实现(设计/生成具体的测试用例、编码实现测试) 10 10
REPORTING 报告 25 15
- Quality Report - 质量报告(评估设计、实现、测试的有效性) 10 5
- Size Measurement - 计算工作量 5 5
- Postmortem & Process Improvement Plan - 事后总结和过程改进计划(总结过程中的问题和改进点) 10 5
TOTAL 合计 220 190

→ 📖 Q3.9(I) 请写下本部分的心得体会。

T3我当驾驶员,压力比之前当导航员大不少,自己写的时候才发现之前看城宇写觉得简单其实真上手没那么容易。策略部分是整个项目最需要动脑子的地方,不像T1和T2基本上照着规则翻译就行。好在前两个任务我虽然是导航员但全程参与了,解析历史记录、更新棋盘这些写起来比较顺。minimax那块多亏城宇,一开始我只想到贪心,城宇提出赠予和竞争应该考虑对手的反应,这个思路直接把策略拉高了一个档次。AS不支持闭包这个坑也是城宇帮我想到用全局变量绕过去的,不然我可能得卡很久。

结对项目总结

结对过程回顾和反思

→ 📖 Q4.1(P) 提供两人在讨论的结对图像资料。

img

→ 📖 Q4.2(P) 回顾结对的过程,反思有哪些可以提升和改进的地方。

T1和T2阶段朱城宇当驾驶员罗春宇当导航员,T3的时候交换了角色。整体来看结对编程的效果还是不错的,两个人互相盯着确实能少犯一些低级错误,而且遇到设计上的问题可以马上讨论不用等。不过也有几个可以改进的地方:一是测试写得太晚了,T1和T2都是先写完代码再补测试,如果一开始就把测试样例想好可能能更早发现问题;二是T3一开始设计的时候对一些边界情况(比如pending offer的处理)考虑得不够充分,导致后面花了不少时间修bug;三是跨组对战这件事拖到最后才做,应该更早跟其他组联系交换wasm模块。

→ 📖 Q4.3(I) 锐评一下你的搭档!并请至少列出三个优点和一个缺点。

城宇是一个很靠谱的搭档,合作起来很舒服。

优点:思路清晰,讨论方案的时候总能很快抓住关键点,T3的minimax就是他先想到的,对整体策略的提升帮助很大。代码写得规范,命名和注释都很到位,我T3当驾驶员的时候参考他之前写的代码很容易就能看懂。测试意识很强,每次写完都会主动想怎么测,T2那个弃牌bug就是他坚持多写几个用例才抓出来的。

缺点:偶尔会在命名上纠结比较久,一个变量名能想好几分钟,虽然最后确实规范但是节奏会慢下来。

对结对编程的理解

→ 📖 Q4.4(I) 说明结对编程的优缺点、你对结对编程的理解。

优点:两个人一起写确实能覆盖更多情况,一个人想不到的边界另一个人可能正好注意到。遇到设计上的纠结可以当场讨论,不用自己来回犹豫。

缺点:效率上有时候不如一个人干,因为两个人需要先在思路上达成一致才能往下推进,意见不同还得花时间说服对方。

理解:结对编程本质上是用一部分效率换更高的代码质量和更少的返工。对于规则明确、逻辑复杂的任务比较合适,对于需要大量试错和探索的任务可能就不太划算了。

代码实现提交

→ 📖 Q4.5(P) 请提供你们完成代码实现的代码仓库链接。

https://github.com/yizhidahuangxiong/BUAASE2026-PairProgramming

posted @ 2026-04-10 09:36  lcy012  阅读(7)  评论(0)    收藏  举报