[P] 结对项目:花见小路
| 项目 | 内容 |
|---|---|
| 这个作业属于哪个课程 | 2026年春季软件工程 |
| 这个作业的要求在哪里 | [P]结对项目:花见小路 |
| 我在这个课程的目标是 | 锻炼团队协作开发能力 |
| 这个作业在哪个具体方面帮助我实现目标 | 深入体会结对编程,提高了两人合作的协作开发能力 |
→ 📖 Q0.0(P) 【你可以在结对结束后补充】如果你的代码仓库包含 AIGC 的部分,列举使用的工具、模型和使用范围。若未使用则填写:本组提交的全部代码不包含AI补全或生成的部分。
Chapter 1 的判定模块代码(不含测试),使用 ChatGPT
Chapter 2 的实现函数代码(不含测试),使用 Gemini
Chapter 3 的行动逻辑实现函数代码,参考 Gemini;响应逻辑实现函数代码,使用 ChatGPT
Chapter.0 wasm从安装到入门
引入
→ 📖 Q0.1(P) 请记录下目前的时间。
2026/4/2 22:07
调查
→ 📖 Q0.2(I) 【你可以在结对结束后另行补充。】作为本项目的调查:
请如实标注在开始项目之前对 Wasm 的熟悉程度分级,可以的话请细化具体的情况。(分别回答两人各自的情况)
I. 没有听说过;
II. 仅限于听说过相关名词;
III. 听说过,且有一定了解;
IV. 听说过,且使用 Wasm 实际进行过开发(即便是玩具项目的开发)。
I. 没有听说过
请如实标注在开始项目之前对桌游花见小路的熟悉程度分级,可以的话请细化具体的情况。(分别回答两人各自的情况)
I. 不了解玩法和规则;
II. 听说过,且有一定了解;
I. 不了解玩法和规则;
总结
→ 📖 Q0.3(P) 请记录下目前的时间。
2026/4/2 22:50
Chapter.1 七色之缨
结对过程
→ 📖 Q1.1(P) 请记录下目前的时间。
2026/4/11 9:46
→ 📖 Q1.2(P) 请在完成任务的同时记录,并在完成任务后整理完善:
- 浏览任务要求,参照 附录A:基于 PSP 2.1 修改的 PSP 表格,估计任务预计耗时;
- 完成编程任务期间,依次做了什么(例如查阅了哪些资料、如何设计判定逻辑、如何设计测试样例、遇到了什么问题、如何解决)。
查阅资料
设计判定逻辑:大体设计好框架(先判定小轮直接获胜,再判定最终决胜),利用LLM生成最终代码
设计测试样例:手动编写(部分情况由LLM提供思路指导)
遇到的问题与解决
- 代码最初编译错误,在分析之后得知wasm不支持数组作为函数形参时直接传长度,故进行了修改,修改后得以解决。
设计
→ 📖 Q1.3(P) 请说明你们为这个判定模块设计了哪些中间量或辅助函数;如果没有额外设计,也请说明为什么认为直接实现已经足够清晰。
设计的中间量:
my_count/op_count:统计双方分别获得多少倾心标记;my_score/op_score:统计双方的分数;tiers:数组,在最终判定阶段使用,把G、F、D/E、A/B/C四档列出,方便按规则从高到低比较。
辅助函数:
stats():处理“统计某一方的数量和分值”这件事。
→ 📖 Q1.4(I) 请说明在这样一个规则判定类模块中,如何避免“漏判”“错判”或分支顺序错误等问题。
先写好明确的分支框架,确定骨干,再依次填充血肉。
测试
→ 📖 Q1.5(P) 请说明你们设计了哪些测试用例,这些测试分别覆盖了哪一类规则或边界情况。
针对 hanamikoji_judge() 函数设计了一组测试用例,具体如下:
-
分值胜利条件(≥11 分)
当一方倾心标记总分达到或超过 11 分时判定胜负。[1, 1, 1, 1, 1, 0, 0]:玩家得分 12,判定胜利[-1, -1, -1, -1, -1, 0, 0]:对手得分 12,判定失败[-1, -1, -1, -1, 1, 1, 1]:玩家得分 12,对手 9,判定胜利
-
标记数量胜利条件(≥4 枚)
当一方获得的标记数量达到或超过 4 枚,且无人达到 11 分时判定胜负。[1, 1, 1, 1, 0, 0, 0]:玩家获得 4 枚,判定胜利[-1, 1, 0, 0, -1, -1, -1]:对手获得 4 枚,判定失败
-
前两轮平局情况
在第 1、2 轮中,若双方均未满足胜利条件,则返回平局。[1, 1, 1, 0, 0, 0, 0][-1, -1, 1, 1, 0, 1, -1]
-
第三轮分值比较
第三轮结束时,若双方均未提前获胜,则按总分比较。[-1, 0, 0, 0, 0, 0, 1]:玩家分值更高,判定胜利[0, 0, 0, -1, 1, 1, -1]:对手分值更高,判定失败
-
第三轮高权重标记判定
当总分相同时,按最高权重标记决定胜负。[0, -1, 0, -1, 0, 0, 1]:双方同分,玩家拥有更高权重标记,判定胜利[1, 1, 0, 0, 0, -1, 0]:对手高权重标记更优,判定失败
-
第三轮完全平局
当总分及标记分布均无法区分胜负时,返回平局。[0, 0, 0, 0, 0, 0, 0][1, -1, 0, 0, 0, 0, 0][-1, 1, 0, 1, -1, 0, 0]
-
非法输入情况
当输入数组长度不为 7 时,应返回错误结果。[1, 1, 1, 1, 1, 1][1, 1, 1, 1, 1, 1, 1, 1]
→ 📖 Q1.6(I) 请说明你对“先写测试再实现”与“先实现再补测试”两种方式的理解。
- 先写测试再实现,可以加深对于需求的理解,提高代码质量,减少后期修改的次数。
- 先实现再补测试,可以加快开发速度,但可能需要花费更多的时间来修复bug,并且测试可能质量不高。
总结
→ 📖 Q1.7(P) 请记录下目前的时间,并根据实际情况填写 附录A:基于 PSP 2.1 修改的 PSP 表格 的“实际耗时”栏目。
2026/4/11 11:33
| Personal Software Process Stages | 个人软件开发流程 | 预估耗时(分钟) | 实际耗时(分钟) |
|---|---|---|---|
| PLANNING | 计划 | ||
| - Estimate | - 估计这个任务需要多少时间 | 5 | 4 |
| DEVELOPMENT | 开发 | ||
| - Analysis & Design Spec | - 需求分析 & 生成设计规格(确定要实现什么) | 10 | 12 |
| - Technical Background | - 了解技术背景(包括学习新技术) | 10 | 8 |
| - Coding Standard | - 代码规范 | 3 | 2 |
| - Design | - 具体设计(确定怎么实现) | 15 | 14 |
| - Coding | - 具体编码 | 35 | 38 |
| - Code Review | - 代码复审 | 8 | 7 |
| - Test Design | - 测试设计(确定怎么测,比如要测试哪些情景、设计哪些种类的测试用例) | 8 | 9 |
| - Test Implement | - 测试实现(设计/生成具体的测试用例、编码实现测试) | 10 | 9 |
| REPORTING | 报告 | ||
| - Quality Report | - 质量报告(评估设计、实现、测试的有效性) | 2 | 2 |
| - Size Measurement | - 计算工作量 | 1 | 1 |
| - Postmortem & Process Improvement Plan | - 事后总结和过程改进计划(总结过程中的问题和改进点) | 3 | 1 |
| TOTAL | 合计 | 110 | 107 |
→ 📖 Q1.8(I) 请写下本部分的心得体会。
第一部分中规中矩,有明确的规则,只需要根据分支优先级实现即可,印象比较深刻的是测试部分,尽量做到了全覆盖。
Chapter.2 不祥之影
准备
→ 📖 Q2.1(P) 请记录下目前的时间。
2026/4/11 11:53
→ 📖 Q2.2(P) 请在完成任务的同时记录,并在完成任务后整理完善:
- 浏览任务要求,参照 附录A:基于 PSP 2.1 修改的 PSP 表格,估计任务预计耗时;
- 完成编程任务期间,依次做了什么(比如查阅了什么资料,随后如何进行了开发,遇到了什么问题,又通过什么方式解决);
- 查阅资料:课程组提供的 README 文档,主要聚焦于对四种操作的规则解释,以及回合制规则。
- 进行开发:首先使用 Gemini 生成初步函数框架,随后根据 README 文档的样例编写本地测试用例。
- 遇到问题:发现 AI 生成的内容并不能通过样例!
- 解决方式:
- 首先仔细阅读规则,理解后对本地样例的期望输出进行人工推算比对,修改为正确的输出;
- 然后逐行检查代码,初步发现“取舍操作”逻辑错误:当前逻辑为弃置的两张牌计入得分,期望逻辑为不计入得分;
- 修改相关代码后,发现仍然不能通过测试,再次检查后发现对于回合制逻辑存在误解:当前逻辑为双方各连续行动 4 回合,期望逻辑为交替行动;
- 修改相关代码后,通过本地测试。提交课程组测试,也顺利通过。
代码可复用性与需求变更
→ 📖 Q2.3(P) 请说明针对该任务,你们对 🧑💻 T1 中已实现的代码进行了哪些复用和修改。
没有进行复用或修改。
→ 📖 Q2.4(I) 请说明在编码实现时,可以采取哪些设计思想、考虑哪些设计冗余,来提高既存代码适应需求变更的能力。
- 模块化设计:将功能拆分为独立的模块,每个模块负责一个具体的功能。这样,当需求变更时,只需要修改相关模块,而不需要修改整个程序。
- 接口设计:为每个模块提供清晰的接口,使得模块之间的交互更加明确。这样,当需求变更时,只需要修改接口的实现,而不需要修改调用该接口的其他模块。
- 数据抽象:将数据抽象为类或结构体,使得数据的使用更加灵活。这样,当需求变更时,只需要修改数据结构,而不需要修改使用该数据结构的代码。
头脑风暴环节
→ 📖 Q2.5(P) 头脑风暴环节:
我们终于快要开始让程序玩游戏了!请尝试分析:T2 中不带 X 的操作记录比起实际对局多出了多少信息?如果加上 X,也就是失去了这部分信息的话,如何处理对小轮结束后状态的估计?
T2中不带 X 的操作比实际对局多出了:
- 对手密约放置的牌和取舍弃置的牌
- 初始状态下从牌库中移除的牌(因为其他所有牌都使用过了,剩下的那张牌就是这张)
加起来共4张牌,意味着加上 X 就会失去4张牌的信息。
如果这4张牌的类型均相同(例如4张G或4张F),也可以确定小轮结束后的状态;但如果这4张牌存在类型不同的牌,小轮结束后的状态就无法确定了,只能通过对已知的牌进行统计,来确定哪些种类的牌在这4张牌中可能出现。
总结
→ 📖 Q2.6(P) 请记录下目前的时间,并根据实际情况填写 附录 A:基于 PSP 2.1 修改的 PSP 表格 的“实际耗时”栏目。
2026/4/11 14:21
| Personal Software Process Stages | 个人软件开发流程 | 预估耗时(分钟) | 实际耗时(分钟) |
|---|---|---|---|
| PLANNING | 计划 | ||
| - Estimate | - 估计这个任务需要多少时间 | 5 | 6 |
| DEVELOPMENT | 开发 | ||
| - Analysis & Design Spec | - 需求分析 & 生成设计规格(确定要实现什么) | 15 | 18 |
| - Technical Background | - 了解技术背景(包括学习新技术) | 10 | 20 |
| - Coding Standard | - 代码规范 | 3 | 3 |
| - Design | - 具体设计(确定怎么实现) | 20 | 22 |
| - Coding | - 具体编码 | 50 | 48 |
| - Code Review | - 代码复审 | 10 | 9 |
| - Test Design | - 测试设计(确定怎么测,比如要测试哪些情景、设计哪些种类的测试用例) | 10 | 8 |
| - Test Implement | - 测试实现(设计/生成具体的测试用例、编码实现测试) | 12 | 10 |
| REPORTING | 报告 | ||
| - Quality Report | - 质量报告(评估设计、实现、测试的有效性) | 3 | 2 |
| - Size Measurement | - 计算工作量 | 2 | 1 |
| - Postmortem & Process Improvement Plan | - 事后总结和过程改进计划(总结过程中的问题和改进点) | 3 | 1 |
| TOTAL | 合计 | 143 | 148 |
→ 📖 Q2.7(I) 请写下本部分的心得体会。
第二部分,难点在于规则的理解,因为相比前一部分规则更加深入了。同时还有 rust 语法的问题,起初似乎二维数组不能作为 wasm 的返回值,后来结合课程组的测试程序与搜索引擎,解决了问题。
Chapter.3 道途之荆
准备
→ 📖 Q3.1(P) 请记录下目前的时间。
2026/4/11 16:15
→ 📖 Q3.2(P) 请在完成任务的同时记录,并在完成任务后整理完善:
- 浏览任务要求,参照 附录A:基于 PSP 2.1 修改的 PSP 表格,估计任务预计耗时;
- 完成编程任务期间,依次做了什么(比如查阅了什么资料,随后如何进行了开发,遇到了什么问题,又通过什么方式解决);
难点在于策略算法的确定,我们首先查阅了花见小路的规则,随后面对面线下交流头脑风暴,最后确定策略算法,具体见下文。
头脑风暴环节
→ 📖 Q3.3(P) 头脑风暴环节:
假设提供更充裕的时间和资源,这个游戏中你能找到的最优策略有可能是什么形式的?进行调研并总结分析。你还可以在任务结束后试着实现(不计分)。
为了达成胜利有两条互斥的道路可选:
- 分数达到 11 分
- 获得 4 枚标记
然而游戏的牌库设计决定了分数高的更难获得,分数低的更易获得,因此这两条路线可以认为相反。
理想化最优策略应该基于当前历史记录、当前手牌、当前计分板决定选择哪条路,再决定怎么行动。
- 历史记录可以确定牌库里还剩那些牌,以及对手当前情况
- 当前手牌会实时影响行动,例如如果有 ABC 牌就马上 1 掉,反之更换策略
- 当前计分板决定选择那条路达成胜利
如果真的要实现这种策略,需要考虑的因素太多,而且需要实时计算,因此很难实现,分支太多。接入 AI(如 LLM) 可以认为是一种退而求其次的方法。
需求建模和算法设计
→ 📖 Q3.4(P) 请说明针对该任务,你们采取了哪些策略来优化决策。具体而言,怎么选择行动类型?选牌如何更优?如何编程实现。
本任务中,我们实现了一个基于优先级的贪心决策系统,用于在“花见小路”游戏中进行稳定且高效的行动选择,宏观表现为尽量争取获得 4 个倾心标记以达到胜利,因此优先选择保留分数低的牌。整体策略分为两个阶段:行动类型选择策略与具体选牌策略,并结合历史记录进行状态约束。
一、行动类型选择策略
1.1 行动优先级设计
我们为四种行动设定固定优先级:
2(取舍) > 4(竞争) > 1(密约) > 3(赠予)
该优先级的设计基于以下原则:
- 2(取舍)优先级最高:用于主动清理高价值牌,避免关键牌被对手利用
- 4(竞争)次优先:可同时控制高低价值牌分布,保底一张低价值牌收入囊中
- 1(密约)稳定收益:收割低价值牌,以尽快达到 4 个倾心标记
- 3(赠予)最低优先级:这个行动最不稳定,留到最后
1.2 行动次数约束处理
每一小轮中,每种行动只能执行一次,因此:
- 通过
history解析历史记录 - 按我方行动轮次提取已使用行动
- 在优先级列表中跳过已使用行动
1.3 实现方式
- 将
history按空格切分为 token 序列 - 利用
index % 2判断是否属于己方行动 - 提取每个 token 首字符(1~4)记录已使用行动
- 使用
HashSet存储已用行动类型
二、选牌策略
2.1 手牌建模
将手牌转换为字符数组,并按收益排序:
A, B, C = 低价值(2分)
D, E = 中价值(3分)
F = 高价值(4分)
G = 最高价值(5分)
通过分值函数进行排序:
hand.sort_by_key(|c| card_value(c))
2.2 各行动的选牌策略
(1)行动 2:取舍
策略:丢弃最高价值的两张牌
-
目标:减少高价值牌进入对手选择空间
-
方法:
选择 hand[n-1], hand[n-2]
(2)行动 4:竞争
策略:构造极端对比组合
将手牌分为四张:
(最高 + 最低) + (次高 + 次低)
-
目标:
- 强迫对手在“高+低”之间做权衡
- 保证至少一张低价值牌进入己方区域
(3)行动 1:密约
策略:最小价值牌保底
- 选择当前手牌中最小的一张
hand[0]
(4)行动 3:赠予
策略:最拉的行动放到最后摆烂
- 选择最高的三张牌
- 避免我们想要的低价值牌被拿走
hand[n-1], hand[n-2], hand[n-3]
三、响应策略
在对手执行 3 或 4 时:
3.1 对手执行 3(赠予)
- 直接选择字典序最小的一张牌
min(opponent_cards)
目标:尽快达到 4 个标记
3.2 对手执行 4(竞争)
- 将两组牌分别排序
- 进行字典序比较:
- 先比较最小牌
- 再比较次小牌
- 选择整体较小的一组
目标:尽量选到小价值牌,尽快达到 4 个标记
四、程序实现结构
整体实现分为两大模块:
4.1 响应模块
- 解析对手提供牌型
- 直接输出选择结果(-X 或 -XY)
- 具体算法见上文
4.2 行动模块
流程如下:
1. 解析 history → 识别己方已用行动
2. 手牌排序(按价值)
3. 按优先级尝试行动:
2 → 4 → 1 → 3
4. 对可用行动执行对应选牌策略
5. 返回 action string
代码可复用性与需求变更
→ 📖 Q3.5(P) 请说明针对该任务,你们对 🧑💻 T2 中已实现的代码进行了哪些复用和修改。
没有复用或修改。
→ 📖 Q3.6(I) 请说明在编码实现时,可以采取哪些设计思想、考虑哪些设计冗余,来提高既存代码适应需求变更的能力。
- 写好注释,便于后续维护;
- 明确分支,如响应和行动分为两个部分;
- 为方便后续迭代新增新的行动,可以设计冗余接口。
软件度量
→ 📖 Q3.7(P) 请说明你们如何量度所实现的程序模块的有效性,例如:“如何说明我们的程序模块决策能力很强?”,尝试提出一些可能的定量分析方式或测试方式。
如何说明我们的程序模块决策能力很强?
可能的测试方式:构造一个基于全随机策略(顾名思义,每回合的行动随机,每次行动使用的牌随机)指导的程序,与我们的程序进行重复比赛,若我们的程序显著由于全随机策略的程序,说明我们的程序模块决策能力很强。
总结
→ 📖 Q3.8(P) 请记录下目前的时间,并根据实际情况填写 附录A:基于 PSP 2.1 修改的 PSP 表格 的“实际耗时”栏目。
2026/4/11 18:56
| Personal Software Process Stages | 个人软件开发流程 | 预估耗时(分钟) | 实际耗时(分钟) |
|---|---|---|---|
| PLANNING | 计划 | ||
| - Estimate | - 估计这个任务需要多少时间 | 5 | 5 |
| DEVELOPMENT | 开发 | ||
| - Analysis & Design Spec | - 需求分析 & 生成设计规格(确定要实现什么) | 20 | 16 |
| - Technical Background | - 了解技术背景(包括学习新技术) | 15 | 12 |
| - Coding Standard | - 代码规范 | 3 | 2 |
| - Design | - 具体设计(确定怎么实现) | 25 | 24 |
| - Coding | - 具体编码 | 55 | 65 |
| - Code Review | - 代码复审 | 10 | 11 |
| - Test Design | - 测试设计(确定怎么测,比如要测试哪些情景、设计哪些种类的测试用例) | 10 | 9 |
| - Test Implement | - 测试实现(设计/生成具体的测试用例、编码实现测试) | 12 | 13 |
| REPORTING | 报告 | ||
| - Quality Report | - 质量报告(评估设计、实现、测试的有效性) | 3 | 2 |
| - Size Measurement | - 计算工作量 | 2 | 1 |
| - Postmortem & Process Improvement Plan | - 事后总结和过程改进计划(总结过程中的问题和改进点) | 2 | 1 |
| TOTAL | 合计 | 163 | 161 |
→ 📖 Q3.9(I) 请写下本部分的心得体会。
第三部分可发挥地方很多,是最有意思的部分。我和队友首先讨论了什么策略能最快实现,想到了一种“傻瓜策略”,即每次决策都选第一个,后续分析发现这个策略可以认为是随机策略。
后来我们进行深入思考,加入让我们自己来玩这个游戏,我会用什么策略。发现人脑比代码能表示的算法复杂得多,针对许多的状态能有不同的策略,这要实现起来太难了。因此我们进行了抽象,抓大放小,总结了一套贪心策略,原则为优先选择保留分数低的牌,并进一步基于这个原则对每种分支进行推演。最终在代码中实现了。自己打自己发现全是平局(为什么)。
结对项目总结
结对过程回顾和反思
→ 📖 Q4.1(P) 提供两人在讨论的结对图像资料。

→ 📖 Q4.2(P) 回顾结对的过程,反思有哪些可以提升和改进的地方。
- 双方没有统一代码风格,导致代码可读性较差,后续维护困难;
- 沟通不畅,导致部分代码重复编写,效率低下;
- 分工不明确,导致部分任务进度缓慢。
→ 📖 Q4.3(I) 锐评一下你的搭档!并请至少列出三个优点和一个缺点。
优点:
- 规则理解透彻,思路清晰;
- 编程能力强,代码质量高;
- 沟通能力强,能够有效协调工作。
缺点:过于依赖 AI。
对结对编程的理解
→ 📖 Q4.4(I) 说明结对编程的优缺点、你对结对编程的理解。
优点:提高编程效率,提高编程乐趣,方便快速头脑风暴。
缺点:容易产生代码风格冲突,分工不明确,容易两个和尚没水喝。
我的理解:结对编程是一种高效、有趣的编程方式,可以促进团队成员之间的交流与合作,提高编程效率。两个人最好遵循一个人导航,一个人实现的原则,这样不容易产生冲突;但在必要的时候也要切换任务中心,扬长避短;在面临困难决策时,可以互相讨论,共同决策,激发灵感。现代编程中,AI 逐渐成为我们的编程助手,感觉也算一种结对编程。
代码实现提交
→ 📖 Q4.5(P) 请提供你们完成代码实现的代码仓库链接。

浙公网安备 33010602011771号