mywinnies

 

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

项目 内容
这个作业属于哪个课程 2026年春季软件工程
这个作业的要求在哪里 [P] 结对项目:花见小路
我在这个课程的目标是 学习到软件工程中的各种最佳实践,然后实际运用到学习和未来的工作中
这个作业在哪个具体方面帮助我实现目标 学习了解结对编程相关知识,进行结对编程实践

结对项目:博客问题清单

→ 📖 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) 请说明在这样一个规则判定类模块中,如何避免“漏判”“错判”或分支顺序错误等问题。

在设计的时候我们首先研读了规则,确定有没有规则隐含的或者没有提到的情况,经过我们对于每一轮情况的推理之后发现平局以及进入下一局的情况没有具体描述,所以我们对于每一轮可能出现的平局以及进入下一局情况又进行了梳理,最终得到精确的规则判定模块。然后我们又结合课程指导书上的要求编写测试样例进行测试,确保正确性。

测试

→ 📖 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) 请说明你对“先写测试再实现”与“先实现再补测试”两种方式的理解。

“先写测试再写实现”更强调完善的设计之后再进行编码,依托构造测试样例时候的思考去尽可能得到完善的情况总结,进而一次完成代码的编写。

“先实现再补测试”则是考虑先快速进行开发,然后通过开发之后的重新思考得到在开发时没有考虑到的情况,避免了开发时的思维被写测试时的思维所束缚,写出能且只能通过自己测试样例的代码。

总结

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

2026/3/27 17:15

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

第一部分主要就是一个规则的引入与判定,比较简单,但是由于需要学习新的语法等技术所以还是有一些难度。在结对编程的过程中体会到了一些好处,两个人可以各自发挥所长,在同一时间完成不同的准备工作之后再交互交流,共同快速完成第一部分的代码编写工作。同时实时的代码审查也可以避免在一些细微的地方犯错,一失足成千古恨。

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) 请说明在编码实现时,可以采取哪些设计思想、考虑哪些设计冗余,来提高既存代码适应需求变更的能力。

单一职责思想:每个函数只做一件事,这样简化了每个函数的功能,减小了代码的粒度,当有新的需求或者之前的需求改变的时候,可以进行更简单的拆分、更换、组装。

防御性编程:考虑更多的特殊情况乃至未定义情况,这样可以在面向更广的需求和用户的时候表现出更好的适应能力。

减少硬编码:显著减少代码当中的硬编码,可以转而进行一些符号约定,而后体现在注释当中,这样在需求变更的时候可以更快,更全地找到需要修改的地方。

头脑风暴环节

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

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

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

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

总结

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

2026/3/27 21:22

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

第二部分主要是一个字符串解析和逻辑推导的任务,在设计和编码上比第一阶段稍微要难一些,但是在两人协作的情况下还是能解决。但是我们也发现一个问题,即使是结对编程,在写代码的过程中导航员就在复审的前提下,还是会出现bug,需要在后面的测试和代码重审中才能发现。比如我们T2中出现了一个错误逻辑导致把弃牌的得分错误加给了弃牌者,应该是一个很明显的错误,但是在第一遍编码的时候两个人都没有发现。所以后期的测试样例编写和代码重审还是很重要的。

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) 请说明在编码实现时,可以采取哪些设计思想、考虑哪些设计冗余,来提高既存代码适应需求变更的能力。

策略与机制解耦:将描述机制的代码与描述策略的代码拆分开来。比如如何“解析历史记录”的机制代码与如何“选择卡牌”的策略代码拆开,这样如果以后我们需要换一种更高级的算法,只需要替换策略模块,底层的数据解析依然完美适用。

参数化冗余:针对卡牌的分数和策略权重的赋值,避免在代码里直接写死硬编码,而是统一定义在顶部的常量字典或配置对象中,如果未来游戏规则发生平衡性调整,我们只需修改一处配置即可。

软件度量

→ 📖 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加起来都要复杂,是一个综合性的全面决策过程。但是由于前两部分我们都是结对编程,大家对于前面的解析和推导等模块的编写都有印象,所以虽然不能直接复用代码,但是还是可以很快地完成这些基础性的部分。然后就是策略的编写,两个人能够不断地推翻对方的设想,不断相互优化与互补,这应该也是结对编程的一个重要优势。

结对项目总结

结对过程回顾和反思

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

202604070949625

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

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

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

罗神非常的强,完全带飞。

优点:

  1. 学习新的编程语言非常快,对于那些语法也掌握得很熟练。
  2. 编码能力非常强,基本上除了我们一开始对于整个方向的讨论以外,罗神基本上是一边想一边写,没有什么停顿。
  3. 非常耐心,我卡顿或者写错的时候能够帮助我更好地思考以及改正,而没有嫌我拖后腿。

缺点:

  1. 写代码的时候喜欢疯狂抖腿,比较影响我思考。

对结对编程的理解

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

优点

  1. 显著降低了代码的bug出现概率。
  2. 有一个人监督的话减少了一些摸鱼的想法。

缺点:两个人一开始合作的时候还是有点紧张,害怕自己行动慢了或者写出来错误,需要一定时间磨合。

理解:我觉得结对编程就是实时的代码审查和思维交流,把一些原本串行的过程合并到了一起进行,就是为了适配敏捷开发。

代码实现提交

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

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

posted on 2026-04-09 10:09  一只大黄熊  阅读(38)  评论(0)    收藏  举报

导航