[P] 结对项目:花见小路
| 项目 | 内容 |
|---|---|
| 这个作业属于哪个课程 | 课程社区 |
| 这个作业的要求在哪里 | 作业要求 |
| 我在这个课程的目标是 | 通过一定的软件开发流程,在预计时间内发布"足够好"的符合用户需求的软件,并证明其是可维护和可持续发展的。 |
| 这个作业在哪个具体方面帮助我实现目标 | 在与队友的交流沟通中不断提升沟通能力和表达能力 |
结对项目:博客问题清单
请将本文件在代码仓库外复制一份,一边阅读和完成结对项目、一边填写入代码仓库外的版本,或采取简记、语音备忘等方式记载较复杂问题的要点之后再补充。请不要将本文档内的作答提交到代码仓库。
→ 📖 Q0.0(P) 【你可以在结对结束后补充】如果你的代码仓库包含 AIGC 的部分,列举使用的工具、模型和使用范围。若未使用则填写:本组提交的全部代码不包含AI补全或生成的部分。
本组使用了codex和copilot等ai模型,在chapter2和chapter3的实现部分辅助编程并提供了可靠的思路。
Chapter.0 wasm从安装到入门
引入
→ 📖 Q0.1(P) 请记录下目前的时间。
2026.3.28 19:00
调查
→ 📖 Q0.2(I) 【你可以在结对结束后另行补充。】作为本项目的调查:
请如实标注在开始项目之前对 Wasm 的熟悉程度分级,可以的话请细化具体的情况。(分别回答两人各自的情况)
I. 没有听说过;
II. 仅限于听说过相关名词;
III. 听说过,且有一定了解;
IV. 听说过,且使用 Wasm 实际进行过开发(即便是玩具项目的开发)。
I. 没有听说过
请如实标注在开始项目之前对桌游花见小路的熟悉程度分级,可以的话请细化具体的情况。(分别回答两人各自的情况)
I. 不了解玩法和规则;
II. 听说过,且有一定了解;
I. 不了解玩法和规则
总结
→ 📖 Q0.3(P) 请记录下目前的时间。
2026.3.28 19:28
Chapter.1 七色之缨
结对过程
→ 📖 Q1.1(P) 请记录下目前的时间。
2026.3.28 19:30
→ 📖 Q1.2(P) 请在完成任务的同时记录,并在完成任务后整理完善:
- 浏览任务要求,参照 附录A:基于 PSP 2.1 修改的 PSP 表格,估计任务预计耗时;
- 完成编程任务期间,依次做了什么(例如查阅了哪些资料、如何设计判定逻辑、如何设计测试样例、遇到了什么问题、如何解决)。
预估耗时:45 分钟。
过程记录:
- 先阅读 README 中 Chapter.1 的规则说明,确认输入
board的含义、返回值编码以及第三轮的判定顺序。 - 讨论后决定把判定过程拆成“立即胜利判定”和“第三轮最终判定”两段,避免把所有条件揉在一个大分支里。
- 根据规则先整理出每个角色对应的分值数组
[2, 2, 2, 3, 3, 4, 5],后续统计总分时直接复用。 - 实现
totalScore(board, owner),用于统计某一方当前获得的倾心标记总分。 - 实现
markerCount(board, owner),用于统计某一方已获得的倾心标记数量。 - 实现
highestTier(board, owner),把第三轮平分时的比较规则抽象成档位:G > F > D/E > A/B/C。 - 实现
immediateWinner(board),统一处理“达到 11 分”与“达到 4 枚标记”的立即胜利条件。 - 在主函数中先调用
immediateWinner,若未分胜负,再根据round决定返回0或进入第三轮最终判定。 - 测试阶段按题目要求列出 6 类必测情况,再额外补了一个导出别名
HanamikojiJudge的测试,确保胶水层导出也可用。 - 遇到的主要问题是“4 枚倾心标记获胜”与“11 分获胜”的优先关系容易写乱。最终通过先统一计算双方总分,再按题面顺序集中写在
immediateWinner中解决。 - 完成实现后运行测试,检查样例与自写用例都通过,再整理问题作答。
设计
→ 📖 Q1.3(P) 请说明你们为这个判定模块设计了哪些中间量或辅助函数;如果没有额外设计,也请说明为什么认为直接实现已经足够清晰。
我们设计了 4 个辅助部分:
SCORES:保存 7 个角色对应的分值,避免在多个地方重复写分数常量。totalScore(board, owner):统计某一方当前总分,用于立即胜利判定和第三轮比总分。markerCount(board, owner):统计某一方拥有的倾心标记数量,用于“至少 4 枚标记获胜”的规则。highestTier(board, owner):把第三轮平分后的比较规则离散成 4 个档位,减少主逻辑中的重复判断。immediateWinner(board):把任意小轮结束后的立即胜利条件单独封装,保证主函数结构清晰。
这样拆分后,主函数只保留“先判立即胜利,再看轮次,最后做第三轮比较”的骨架,阅读起来更接近题目原文,也更不容易写错分支顺序。
→ 📖 Q1.4(I) 请说明在这样一个规则判定类模块中,如何避免“漏判”“错判”或分支顺序错误等问题。
我们采用的是“先定流程、再写代码”的方法:先把题面拆成一条严格的判定链路,再逐项落到函数里,而不是编码时临时决定分支。以本题为例,“11 分胜利”、“4 枚标记胜利”、“第三轮比总分”、“第三轮比最高档位”、“平局”等情况本身就有清晰的先后级别,因此只要实现顺序与规则顺序一一对应,误判概率会明显降低。
同时要用有针对性的测试去兜底,重点覆盖边界和易混场景。例如“恰好 11 分”、“恰好 4 枚标记”、“前两轮未分胜负”、“第三轮同分但档位不同”、“第三轮完全一致”等案例,能有效检验是否存在漏分支、错分支或分支优先级写反的问题。
测试
→ 📖 Q1.5(P) 请说明你们设计了哪些测试用例,这些测试分别覆盖了哪一类规则或边界情况。
我们设计了 7 组测试:
board=[0,0,0,1,1,0,1], round=1,结果应为1。覆盖“一方分值达到 11 分立即获胜”。board=[1,1,1,1,0,0,0], round=2,结果应为1。覆盖“一方获得至少 4 枚倾心标记立即获胜”。board=[1,0,0,-1,0,0,0], round=2,结果应为0。覆盖“前两轮尚未满足胜利条件,继续下一轮”。board=[1,1,0,-1,0,-1,0], round=3,结果应为-1。覆盖“第三轮总分不同,由总分高者获胜”。board=[1,0,0,0,0,1,-1], round=3,结果应为1。覆盖“第三轮总分相同,由最高档位倾心标记判胜负”。board=[1,-1,0,0,0,0,0], round=3,结果应为2。覆盖“第三轮总分与最高档位都无法区分,判为平局”。- 直接调用
HanamikojiJudge(...)的别名导出进行测试。覆盖“对外导出接口可正常使用”。
这些用例基本覆盖了题目要求的 6 类核心场景,同时也检查了我们暴露给测试脚本的函数接口是否一致。
→ 📖 Q1.6(I) 请说明你对“先写测试再实现”与“先实现再补测试”两种方式的理解。
这两种开发顺序我理解为两种不同的“控风险策略”。
“先写测试再实现”适用于规则边界明确、输入输出定义稳定的任务。它的价值在于先把预期行为写死,再去实现细节,开发过程目标感更强,也更不容易漏掉题面里的关键条件。本题的胜负判定就属于这类场景。
“先实现再补测试”更适合需求尚在探索、或需要快速验证方案可行性的阶段。优点是推进快,但缺点也明显:开发者容易被已有实现带偏,后补测试时只验证了“代码现在能做什么”,却没严格验证“题目到底要求什么”。
总结
→ 📖 Q1.7(P) 请记录下目前的时间,并根据实际情况填写 附录A:基于 PSP 2.1 修改的 PSP 表格 的“实际耗时”栏目。
2026.3.28 20:24
Chapter.1 的 PSP 记录如下:
| Personal Software Process Stages | 个人软件开发流程 | 预估耗时(分钟) | 实际耗时(分钟) |
|---|---|---|---|
| PLANNING | 计划 | 5 | 5 |
| - Estimate | - 估计任务时间 | 5 | 5 |
| DEVELOPMENT | 开发 | 35 | 41 |
| - Analysis & Design Spec | - 需求分析 | 5 | 6 |
| - Technical Background | - 技术背景了解 | 3 | 4 |
| - Coding Standard | - 代码规范 | 1 | 1 |
| - Design | - 具体设计 | 6 | 7 |
| - Coding | - 具体编码 | 10 | 11 |
| - Code Review | - 代码复审 | 3 | 3 |
| - Test Design | - 测试设计 | 4 | 4 |
| - Test Implement | - 测试实现 | 3 | 5 |
| REPORTING | 报告 | 5 | 8 |
| - Quality Report | - 质量报告 | 1 | 2 |
| - Size Measurement | - 计算工作量 | 1 | 1 |
| - Postmortem & Process Improvement Plan | - 事后总结 | 3 | 5 |
| TOTAL | 合计 | 45 | 54 |
→ 📖 Q1.8(I) 请写下本部分的心得体会。
这部分的体验让我更深刻地体会到:关键不在代码量,而在“规则到程序”的映射是否严谨。先把条件拆开、再按层次组织成辅助函数,主流程会更加的直观,测试设计也更有抓手。沟通能力也有了明显的提升。
Chapter.2 不祥之影
准备
→ 📖 Q2.1(P) 请记录下目前的时间。
2026.4.5 9:30
→ 📖 Q2.2(P) 请在完成任务的同时记录,并在完成任务后整理完善:
- 浏览任务要求,参照 附录A:基于 PSP 2.1 修改的 PSP 表格,估计任务预计耗时;
- 完成编程任务期间,依次做了什么(比如查阅了什么资料,随后如何进行了开发,遇到了什么问题,又通过什么方式解决);
预估耗时:150 分钟。
过程记录:
- 先阅读 README 的 Chapter.2,确认输入
history表示一整小轮的完整公开记录,输出需要同时给出双方场面牌数和更新后的得分标记。 - 结合题面示例,把 4 类行动统一抽象成“是否进入区域、进入谁的区域、是否需要根据响应拆分牌组”三种处理方式。
- 复查
T1中的胜负判定规则,明确本题虽然不需要直接返回胜负,但轮末更新board的规则与T1的输入语义是一致的,因此保留相同的 A-G 顺序和-1/0/1编码。 - 阅读
src/T3/hanamikoji-engine.js,确认课程组在后续任务中对history采用“从当前玩家视角记录、默认第一个行动属于自己”的约定,并确认 3 赠予与 4 竞争的分配方式。 - 设计数据结构时,把“公开区域”和“密约区域”分开累计,最后再合并成输出,避免在处理中途把“暂未翻开的牌”和“已公开得到的牌”混在一起。
- 实现字符串解析逻辑:先按空格拆成 8 个行动,再按
-拆出响应选择;其中1/2直接处理,3/4再根据选择结果把牌分配给提供者和选择者。 - 为了兼容课程组脚本,导出
calc_current_state、calcCurrentState、CalcCurrentState三个名字,并把返回值实现成长度 21 的一维Int8Array,方便测试脚本重组为3x7。 - 额外补了两个本地测试:一个覆盖“完整回放并更新标记”,另一个覆盖“同种礼物数量打平时保持原有倾心标记不变”。
- 遇到的主要问题是
history的视角比较容易写反。最后通过对照 README 样例和T3引擎中historyViews的构造方式,确认奇数位行动属于自己、偶数位行动属于对手,再继续实现。 - 完成实现后编译 AssemblyScript 模块,运行
src/T2/test.js和本地单元测试,确认提供样例与补充用例都通过,再回填问题作答。
代码可复用性与需求变更
→ 📖 Q2.3(P) 请说明针对该任务,你们对 🧑💻 T1 中已实现的代码进行了哪些复用和修改。
我们主要复用了两部分思路。
第一,继续沿用了 T1 中对状态的表示方式:所有和角色相关的数据都固定按 A-G 顺序存放,得分标记仍使用 1 / 0 / -1 表示“倾向自己 / 中立 / 倾向对手”。这样 T2 在轮末更新 board 时,不需要重新设计接口,后续 T3 也能直接接上。
第二,延续了 T1 “把主流程拆成小辅助函数”的写法。在 T2 里我们把加牌、删去赠予中被选的一张、比较竞争分组是否为同一多重集、更新轮末标记等逻辑拆开写,避免把 4 类行动都堆在一个超长分支里。
相较 T1,本题的修改主要在于:从“直接根据既有 board 判定结果”,扩展成“先根据完整行动记录回放本轮区域,再推导新的 board”。
→ 📖 Q2.4(I) 请说明在编码实现时,可以采取哪些设计思想、考虑哪些设计冗余,来提高既存代码适应需求变更的能力。
与其把所有需求一次性揉进同一套逻辑,不如先把会长期复用的部分单独拎出来。
例如,角色顺序固定为 A-G、board 的取值语义、每轮结束后按牌数调整标记,这些本质上是规则层,不会因为题目小改版就变。相反,history 的写法、结果是二维还是一维、要不要处理 X,更像接口层和题面约定,变化频率更高。两类内容如果前期就耦在一起,后面改一个输入输出细节,往往会连带动到核心规则实现。
另外,代码里适度保留“过渡层”其实是在降低未来成本。
这次把公开区和密约区分开,表面上比“直接汇总总牌数”多了一步,但换来的是可扩展性:将来若要支持中途查询公开信息,或者回到带 X 的不完全信息模式,只需要在现有结构上扩展,不必重写整条数据处理的链路。
因此这里的“冗余”不是无效重复,而是有意识地给后续演化预留接口和落点。
头脑风暴环节
→ 📖 Q2.5(P) 头脑风暴环节:
我们终于快要开始让程序玩游戏了!请尝试分析:T2 中不带 X 的操作记录比起实际对局多出了多少信息?如果加上 X,也就是失去了这部分信息的话,如何处理对小轮结束后状态的估计?
T2 的完整记录比真实对局至少多出了两类关键信息:
对手通过 1.密约 和 2.取舍 使用的具体牌。在真实对局进行中,这两类信息对另一方本来是隐藏的。
如果重新引入 X,就会变成不完全信息问题。此时程序不能再只维护一个确定状态,而应该维护“所有仍然可能的状态集合”或某种压缩后的概率分布。例如:
- 根据总牌数约束、自己手牌、已经公开的赠予/竞争记录,枚举或采样所有与
X兼容的对手隐藏用牌方案。 - 对每一种可能方案分别回放,得到一批候选轮末状态。
- 再按需求取结果:如果是保守决策,可以看最坏情况;如果是平均意义上的估计,可以计算每种状态的概率并求期望。
总结
→ 📖 Q2.6(P) 请记录下目前的时间,并根据实际情况填写 附录 A:基于 PSP 2.1 修改的 PSP 表格 的“实际耗时”栏目。
2026.4.5 11:48
Chapter.2 的 PSP 记录如下:
| Personal Software Process Stages | 个人软件开发流程 | 预估耗时(分钟) | 实际耗时(分钟) |
|---|---|---|---|
| PLANNING | 计划 | 10 | 10 |
| - Estimate | - 估计任务时间 | 10 | 10 |
| DEVELOPMENT | 开发 | 120 | 92 |
| - Analysis & Design Spec | - 需求分析 | 15 | 14 |
| - Technical Background | - 技术背景了解 | 10 | 11 |
| - Coding Standard | - 代码规范 | 2 | 2 |
| - Design | - 具体设计 | 18 | 16 |
| - Coding | - 具体编码 | 38 | 28 |
| - Code Review | - 代码复审 | 10 | 8 |
| - Test Design | - 测试设计 | 12 | 7 |
| - Test Implement | - 测试实现 | 15 | 6 |
| REPORTING | 报告 | 20 | 36 |
| - Quality Report | - 质量报告 | 5 | 10 |
| - Size Measurement | - 计算工作量 | 3 | 4 |
| - Postmortem & Process Improvement Plan | - 事后总结 | 12 | 22 |
| TOTAL | 合计 | 150 | 138 |
→ 📖 Q2.7(I) 请写下本部分的心得体会。
这一章做下来,我最大的体会就是:状态建模如果不清楚,后面的实现会一直被拖慢。看上去任务是在做字符串解析,但真正决定难度的,其实是先把动作影响分层想明白:哪些进入密约,哪些进入公开区,哪些根本不参与计分。
还有一点是对“复用”的理解变了。复用并不等于把旧实现整段搬过来,而是继续使用已经验证过的抽象和约定。比如 T1 里确定下来的角色顺序和标记编码,在 T2 里沿用之后,接口衔接会自然很多。相比重新写一版“也能运行”的新代码,这种基于成熟建模的延续更稳,也更有工程价值,也是一个复用“轮子”的过程。
Chapter.3 道途之荆
准备
→ 📖 Q3.1(P) 请记录下目前的时间。
2026.4.05 14:00
→ 📖 Q3.2(P) 请在完成任务的同时记录,并在完成任务后整理完善:
- 浏览任务要求,参照 附录A:基于 PSP 2.1 修改的 PSP 表格,估计任务预计耗时;
- 完成编程任务期间,依次做了什么(比如查阅了什么资料,随后如何进行了开发,遇到了什么问题,又通过什么方式解决);
预估耗时:240 分钟。
过程记录:
- 先阅读 README 的 Chapter.3 和
src/T3/hanamikoji-engine.js,确认接口不是“计算局面”,而是要输出一个合法且尽量有竞争力的行动字符串。 - 继续沿用前两题的 A-G 顺序、
board编码和牌数统计方式。 - 重点梳理
history的视角问题:在 T3 中,自己看到的对手密约和取舍会变成1X、2XX,而赠予和竞争是公开的;这决定了我们不能像 T2 一样恢复完整状态,只能恢复“确定可见的信息”。 - 阅读引擎的行动校验逻辑,确认一条
historytoken 对应的是“一个完整的正常行动”,选择-X或-XY不会单独进入history,而是附在最近一次 3/4 行动后。 - 设计了解析器:尝试分别按“第一个行动属于自己 / 属于对手”两种假设回放历史,通过
X的出现位置、已消耗行动、当前手牌张数是否匹配来反推出哪一种假设成立。 - 在状态表示上,只恢复“自己公开区 + 自己密约区 + 对手公开区”,并把对手
1X/2XX留作未知信息,不强行猜测具体牌型。 - 实现响应策略时,先处理
3赠予和4竞争的-选择场景:对所有可选卡或可选牌组做静态评估,选对自己局面最有利的那个。 - 实现正常出牌策略时,枚举所有当前合法行动:
1的单牌、2的两牌组合、3的三牌组合、4的四牌组合及 3 种分组方式。 - 对每个候选行动,使用一个轻量启发式函数评估“在当前已知信息下,本轮各礼物种类最终更可能倾向谁”,同时给弃牌加入损失惩罚,给赠予/竞争加入对手最优响应的最坏情况评估。
- 遇到的主要问题是:如果只看
history的 token 顺序,很容易把“轮到谁行动”与“谁提供了上一组 3/4 选择”混淆。最后通过把引擎里的抽牌、行动、选择流程逐步模拟一遍,并校验当前手牌张数,解决了这一点。 - 完成策略实现后,补了若干本地小测试,分别验证开局能给出合法行动、在赠予/竞争响应场景下能返回正确格式的
-选择。 - 最后编译
t3-as,运行src/T3/test.js做自对局测试,确认程序能稳定完成整局对战,再整理question.md的作答。
头脑风暴环节
→ 📖 Q3.3(P) 头脑风暴环节:
假设提供更充裕的时间和资源,这个游戏中你能找到的最优策略有可能是什么形式的?进行调研并总结分析。你还可以在任务结束后试着实现(不计分)。
如果时间和资源更充裕,我认为这个游戏里更接近“最优策略”的形式,大概率不是规则堆砌,而是一个建立在不完全信息博弈上的搜索或学习模型。
一种比较直接的方向,是把它建模成类似 Kuhn Poker / Leduc Poker 那样的有限轮次不完全信息对抗,维护“可能手牌与隐藏行动”的信念状态,再用期望收益去评估动作。因为 T3 中真正困难的不是合法出牌,而是:对手的 1X、2XX 到底是什么,导致我们面对的是一组可能局面,而不是单一局面。
如果继续往“理论上更强”的方向推进,比较有可能的形式包括:
- 基于信息集的博弈搜索。把同一观测下无法区分的隐藏状态归并到同一个信息集,在这个层面搜索行动。
- CFR(Counterfactual Regret Minimization)或其变体。对于有限、可枚举的对局树,这类算法很适合逼近双人零和不完全信息博弈的均衡策略。
- MCTS + Determinization。先对未知信息做多次采样,把每次采样得到的完整状态当作确定局面,再做蒙特卡洛搜索,最后汇总动作价值。
- 自博弈强化学习。用程序不断自对弈,通过策略网络或价值网络学习“给定局面下怎样出牌更优”。
如果只从课程项目的时间预算考虑,上述方法都偏重了;但从“最优策略可能是什么样”这个问题本身来看,我认为最终会落到“信念状态 + 对抗搜索 / 学习”的框架,而不是单纯的局部启发式。
需求建模和算法设计
→ 📖 Q3.4(P) 请说明针对该任务,你们采取了哪些策略来优化决策。具体而言,怎么选择行动类型?选牌如何更优?如何编程实现。
我们采用的是“合法动作全枚举 + 轻量启发式打分”的策略。
首先,在每次轮到程序决策时,先解析当前 history,恢复我们能够确定知道的局面:自己已经用过哪些行动、自己公开区和密约区分别有哪些牌、对手公开区有哪些牌、当前是不是在响应对手的 3赠予 或 4竞争。这一步不会试图还原对手 1X/2XX 的具体内容,只保留确定信息。
然后,分两类决策:
- 如果当前是在响应选择:
- 对
3赠予,遍历 3 张候选牌,分别计算“自己拿这张、对手拿剩下两张”后的局面得分,选择最好的一张。 - 对
4竞争,遍历两组候选,分别计算“自己拿这组、对手拿另一组”后的局面得分,选择更优的一组。
- 对
- 如果当前是正常出牌:
- 枚举所有当前合法动作。
1:枚举每一张可密约的牌。2:枚举所有两张弃牌组合。3:枚举所有三张赠予组合,并按“对手会选对我最差的那张”的最坏情况打分。4:枚举所有四张竞争组合,以及同一四张牌对应的 3 种两两分组方式,并按“对手会拿走对我最差的一组”的最坏情况打分。
评分函数的核心思想是:在当前已知牌数下,估计每一种礼物在本轮结束时更可能倾向谁,并按其对应分值加权。与此同时,我们对弃掉高价值、急需争夺的牌加入额外惩罚,因此 2取舍 不会机械地丢掉随机两张牌,而更倾向丢掉当前紧迫性较低的牌。
编程实现上,这种方法的优点是稳定、可控、易于调参,而且因为本题每回合手牌不超过 7 张,合法候选数并不大,完全可以在 2 秒时限内完成枚举与评分。
代码可复用性与需求变更
→ 📖 Q3.5(P) 请说明针对该任务,你们对 🧑💻 T2 中已实现的代码进行了哪些复用和修改。
我们复用了 T2 的几项基础建模:
- 继续使用 A-G 固定顺序作为所有数组的索引顺序。
- 继续使用
cardIndex、加牌统计、多重集比较等底层工具函数思路。 - 继续把“牌数统计”和“轮末标记倾向”看作两层独立信息,而不是混写在一个流程里。
但相较 T2,本题做了两类关键修改:
T2处理的是完整公开记录,可以恢复确定状态;T3则必须处理1X、2XX带来的不完全信息,因此我们把恢复目标从“完整场面”降为“当前可确定的公开信息 + 自己已知的密约信息”。T2的终点是“还原状态”;T3的终点则是“基于状态做决策”。因此在T2的解析和计数逻辑之上,我们新增了候选动作枚举、最坏情况估值、响应选择逻辑等策略层代码。
→ 📖 Q3.6(I) 请说明在编码实现时,可以采取哪些设计思想、考虑哪些设计冗余,来提高既存代码适应需求变更的能力。
这次我们更强调的是先把“看见的信息”和“基于信息做判断”分开来处理,而非上来就把解析和决策揉成一团。前者负责把当前局面梳理出来,后者只需要在这个基础上做选择即可。这样哪怕以后输入格式、规则细节或者评分方式发生变化,受影响的通常也只是某一层,不至于把整套实现一起拖着改。
同时,我们也刻意保留了一些中间状态,不把结果压缩得太早。比如没有直接只存一个总牌数,而是把自己公开区、自己密约区、对手公开区分开记录。表面上看字段多了一点,但它让后续扩展更从容,比如要换成更细的估值方式。
软件度量
→ 📖 Q3.7(P) 请说明你们如何量度所实现的程序模块的有效性,例如:“如何说明我们的程序模块决策能力很强?”,尝试提出一些可能的定量分析方式或测试方式。
我们认为至少可以从三类指标量度策略模块的有效性。
第一类是合法性与稳定性。这是最底线的指标,包括:
- 是否始终返回合法格式的行动。
- 是否会重复使用已经用过的行动类型。
- 是否会在选择场景下返回错误长度的
-X / -XY。 - 在批量自对局或对不同对手的比赛中,是否会出现超时、异常退出、非法动作判负。
第二类是对局结果指标。例如:
- 对固定基线策略(随机策略、贪心策略、只看分值策略)的胜率。
- 先手和后手两种身份下的分别胜率。
- 多轮双循环赛中的净胜场、平均得分、平局率。
第三类是局部决策质量指标。这类指标不直接看整局输赢,而是看程序在关键局面下是否做出了符合预期的决策。例如:
- 在
3赠予场景中,是否优先拿对自己更关键的牌。 - 在
4竞争场景中,是否避免把明显更优的一组白送给对手。 - 在自己已领先某种礼物时,是否会适度减少无意义堆叠,把资源转向更紧张的争夺点。
如果要做更系统的测试,可以准备一批“人类可判断优劣”的局面样本,让程序在这些局面下做选择,再统计它与人工分析一致的比例。这样能更具体地验证策略是否真的“会思考”,而不只是“能跑完一局”。
总结
→ 📖 Q3.8(P) 请记录下目前的时间,并根据实际情况填写 附录A:基于 PSP 2.1 修改的 PSP 表格 的“实际耗时”栏目。
2026.4.05 17:32
Chapter.3 的 PSP 记录如下:
| Personal Software Process Stages | 个人软件开发流程 | 预估耗时(分钟) | 实际耗时(分钟) |
|---|---|---|---|
| PLANNING | 计划 | 15 | 15 |
| - Estimate | - 估计任务时间 | 15 | 15 |
| DEVELOPMENT | 开发 | 185 | 171 |
| - Analysis & Design Spec | - 需求分析 | 20 | 22 |
| - Technical Background | - 技术背景了解 | 15 | 16 |
| - Coding Standard | - 代码规范 | 3 | 3 |
| - Design | - 具体设计 | 35 | 32 |
| - Coding | - 具体编码 | 60 | 54 |
| - Code Review | - 代码复审 | 12 | 10 |
| - Test Design | - 测试设计 | 20 | 16 |
| - Test Implement | - 测试实现 | 20 | 18 |
| REPORTING | 报告 | 40 | 46 |
| - Quality Report | - 质量报告 | 10 | 12 |
| - Size Measurement | - 计算工作量 | 5 | 6 |
| - Postmortem & Process Improvement Plan | - 事后总结 | 25 | 28 |
| TOTAL | 合计 | 240 | 232 |
→ 📖 Q3.9(I) 请写下本部分的心得体会。
这一章最让我有感觉的地方,就是从原来的“把规则做对”转到了“在信息不完整的情况下做选择”。前两章更像是在把题目要求准确还原出来,只要流程和边界都对,结果通常不会偏太多;但到了这一章,很多信息本来就是拿不准的,程序不可能像前面那样依赖完整状态,只能在有限线索里尽量把局面处理稳定。
同时也更能感受到启发式方案的现实意义。它不追求数学意义上的最优,但在时间和状态规模都受限制的前提下,只要策略结构清楚、判断依据说得通、输出始终合法,往往就已比硬追复杂搜索更加合适。对我来说,这一章最大的收获,就是开始更清楚地区分了解了“理论上最好”和“实际上能落地”这两件事了。
结对项目总结
结对过程回顾和反思
→ 📖 Q4.1(P) 提供两人在讨论的结对图像资料。

→ 📖 Q4.2(P) 回顾结对的过程,反思有哪些可以提升和改进的地方。
回顾整个结对过程,我们认为这次合作总体上是顺畅的:前期主要通过共同阅读 README 和规则来统一建模方式,中期围绕 T1/T2/T3 的实现不断切换“驾驶员”和“导航员”角色,一人负责把讨论结果快速落成代码,另一人负责盯规则、挑边界情况和检查测试覆盖。这样的分工让我们在规则类题目上能较快发现“看起来合理、但顺序不对”或“局部合法、整体不对”的问题。
不过过程中也有可以提升的地方。第一,某些阶段我们在动手写代码前讨论得还不够细,导致后面出现返工,例如对 history 视角和回合归属的理解,应该更早画成一张简化时序图。第二,测试设计虽然最终补上了,但如果能更早把“核心场景 checklist”写出来,就能更系统地指导实现,而不是边写边补。第三,在记录问题作答时,我们有时是任务做完后再回忆补写,如果能边做边用更结构化的方式记录,会更方便后续整理博客。
如果下次继续以类似方式合作,我们会优先改进三点:先统一状态模型和输入输出约定,再编码;更早列出测试清单;把过程记录拆成更细的小结,减少事后整理成本。
→ 📖 Q4.3(I) 锐评一下你的搭档!并请至少列出三个优点和一个缺点。
我的搭档是罗浩宇同学!
优点:沟通成本比较低、代码编写合理、测试思维周密。
缺点:过于纠结于某个细节导致推进稍慢。
对结对编程的理解
→ 📖 Q4.4(I) 说明结对编程的优缺点、你对结对编程的理解。
我理解的结对编程,不只是两个人简单地盯着一块屏幕敲代码,而是把原本只存在于个人脑子里的判断过程,一边讨论一边摊开来检查。尤其在这种规则多、边界多、时间又紧的任务里,它最大的作用不是“多一个人看代码”,而是多一个大脑思考,很多容易被忽略的理解偏差,也会在这个过程中提前暴露出来。
实际做下来,结对的好处主要体现在两个地方。一个是规则理解更稳,像输入视角、标记编码等这类容易漏掉的细节,往往能更早被发现;另一个是实现过程更克制,不会一上来就把代码铺得很大,而是先把状态和辅助逻辑理顺,再往下推,所以整体会更清楚,也更不容易写偏。测试这块也一样,因为两个人会不断互相补充,很容易把那些只靠正常流程看不出来的边界情况补上。
不过它也不是没有代价。最明显的是,效率不一定会线性增加,前期沟通本身就要花时间,如果节奏没控制好,很容易两个人一起卡在同一个点上。另外,结对效果很依赖双方是否真的参与思考,如果一方只是旁观,那它就会退化成普通的单人开发。那反而不得当了。
代码实现提交
→ 📖 Q4.5(P) 请提供你们完成代码实现的代码仓库链接。
https://github.com/ywxy12138/Real_BUAASE2026-PairProgramming
附录
附录 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 | 合计 |
浙公网安备 33010602011771号