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

项目 内容
这个作业属于哪个课程 课程社区
这个作业的要求在哪里 作业要求
我在这个课程的目标是 学习软件开发流程
这个作业在哪个具体方面帮助我实现目标 学习结对编程、敏捷编程等编程思想,学会规划时间高效完成任务

结对项目:博客问题清单

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

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

本仓库使用ai版本:claude opus 4.6
使用范围:开发前练手项目辅助开发,工具链配置,博弈思路参考,博弈算法得分设计参考

Chapter.0 wasm从安装到入门

引入

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

3月29日 19:00

调查

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

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

I. 没有听说过;

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

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

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

I.没有听说过

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

I. 不了解玩法和规则;

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

I.不了解玩法和规则

总结

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

3月29日 20:00

Chapter.1 七色之缨

结对过程

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

4月5日 9:00

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

  1. 浏览任务要求,参照 附录A:基于 PSP 2.1 修改的 PSP 表格,估计任务预计耗时;
  2. 完成编程任务期间,依次做了什么(例如查阅了哪些资料、如何设计判定逻辑、如何设计测试样例、遇到了什么问题、如何解决)。
  • 参考资料:第一部分主要还在熟悉rust语法和项目运行,参考了几个相关教学博客:rust菜鸟教程rust程序设计中文版
  • 开始编写前使用ai辅助设计了一个用于熟悉流程的小项目,熟悉完整的工具链
  • 首先梳理出胜负规则的逻辑和胜负判定优先级:11分立即胜利 > 4枚标记胜利 > 未结束继续 > 第三轮最终判定
  • 将胜负逻辑按优先级拆分成辅助函数calc_score、count_markers、has_marker_in_tier,依次进行胜负判断
  • 编写单元测试覆盖 6 类不同的胜负判断场景,通过cargo test验证全部通过
  • 遇到问题:主要是模式匹配语法判定比较严格,前期对语法不熟悉导致出现了很多次编译失败的结果,修改主要是逐步拆分匹配判定一路简化方便编写
  • 测试用文件test.js无法用于测试,询问ai后发现是修改package.json导致的问题,修改为.cjs文件

设计

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

  • 辅助函数为进行胜负逻辑判定的三个辅助函数calc_score、count_markers、has_marker_in_tier
  • calc_score(board, side) -> i32:统计指定方拥有的倾心标记总分值。遍历 board,对匹配 side 的标记累加对应的 SCORES 数组值(A/B/C=2, D/E=3, F=4, G=5)
  • count_markers(board, side) -> i32:统计指定方拥有的倾心标记数量
  • has_marker_in_tier(board, side, tier_idx) -> bool:判断指定方在某一档位中是否拥有至少一枚倾心标记。档位按分数从高到低定义
  • 辅助数据结构包括两个数组:SCORES: [i32; 7]:每个角色对应的分值 TIERSTIER_LENS:档位分组索引和每组的元素个数。

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

  • 开始开发之前首先梳理判定规则,根据规则绘制判定流程图,后续开发严格按照流程图实现
  • 开发时按照一个判定一个辅助函数的对应关系进行开发,方便对应流程图减少错误,同时方便后续的单元测试
  • 穷举判定流程,每个分支都设计不同的样例进行组合,保证测试完整的分支流程

测试

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

  1. 总分11分获胜
  2. 四枚标记获胜
  3. 本轮未分出胜负,继续比赛
  4. 第三轮总分最高者获胜
  5. 第三轮总分相同,按最高分数标记分胜负
  6. 第三轮总分相同,最高分数标记相同,平局
  7. 优先级验证:一方获得11分,另一方获得四枚标记

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

  • “先写测试再实现”:先根据需求明确输入输出的预期,然后编写测试用例,最后实现代码使测试通过。好处是能迫使开发者先理清需求、明确边界,实现过程有明确的目标导向,代码完成时天然具备测试覆盖。缺点是如果开发前不能全面把握判定逻辑,可能难以在实现前就设计出完善的测试,需要后续对测试进行调整修改
  • "先实现再补测试":先根据理解快速实现功能,然后补充测试验证正确性。好处是开发速度快,适合探索性开发。缺点是容易遗漏边界情况,补充的测试可能受到实现的影响而不够全面,同时测试时可能收到开发时的惯性思维引导遗漏情况

总结

→ 📖 Q1.7(P) 请记录下目前的时间,并根据实际情况填写 附录A:基于 PSP 2.1 修改的 PSP 表格 的“实际耗时”栏目。
4月5日 11:30
→ 📖 Q1.8(I) 请写下本部分的心得体会。

T1 是整个项目的起步阶段,主要用于熟悉工具链开发,相对简单的判定函数,帮助我熟悉了 Rust + Wasm 的完整开发流程,同时对判定逻辑的逐步拆解也让我对逻辑判定的实现有了更多的实现经验,知道了如何通过拆解任务的方式解决问题,同时也方便队友对辅助函数编写单元测试

Chapter.2 不祥之影

准备

→ 📖 Q2.1(P) 请记录下目前的时间。
4月5日 19:00
→ 📖 Q2.2(P) 请在完成任务的同时记录,并在完成任务后整理完善:

  1. 浏览任务要求,参照 附录A:基于 PSP 2.1 修改的 PSP 表格,估计任务预计耗时;
  2. 完成编程任务期间,依次做了什么(比如查阅了什么资料,随后如何进行了开发,遇到了什么问题,又通过什么方式解决);
  • 阅读 T2 的任务要求,理解输入(history 字符串 + 初始 board)和输出(3×7 二维数组)的格式
  • 重点研究了 history 字符串的解析规则:8 段行动记录、4 种行动类型、赠予和竞争中的选择用 - 连接。参照 README 中的示例表格逐步推导验证
  • 在 T2 目录下创建 t2-rust 项目,额外添加了 js-sys 依赖用于返回 JS 数组。
  • 核心实现思路:按空格分割 history,偶数索引为 P1 的行动、奇数索引为 P2 的行动。对每条行动,根据类型分别处理牌的归属
  • 结算阶段复用了 T1 的比较逻辑:逐种牌比较双方数量,多者拿标记,平手保持原值
  • 遇到的问题:竞争行动(类型4)需要判断对手选了哪一组。解决方法是将选择的牌排序后与两组分别比较
  • 编写了 9 个测试,初次运行时有 2 个竞争相关测试失败,原因是测试预期值写错(遗漏了密约步骤的牌),修正预期值后全部通过
  • 通过课程组的 npm run submit-test 验证

代码可复用性与需求变更

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

  • 直接复用的函数:T2 结算阶段的 board 更新逻辑与 T1 的核心思想一致,通过比较双方同种牌的数量来决定标记归属。T1 中 calc_scorecount_markers 的设计思路被直接应用到 T2 的结算中
  • 未直接导入 T1 函数:T2 是独立的 Rust 项目,没有通过 crate 依赖引入 T1 的代码,而是在 T2 中重新实现了结算逻辑。这是因为 T2 的结算更简单(直接比较牌数),不需要 T1 中完整的胜负判定(11 分、4 标记等条件)
  • 共享的函数:card_to_index 函数(将 A-G 映射到 0-6 索引)是两个任务共用的基础工具,在 T2 中重新实现了一份

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

  • 功能分离:将解析逻辑(字符串 → 结构化数据)和业务逻辑(计算牌数、结算 board)分开。即便输入格式发生变化,只需修改解析部分的函数
  • 参数化设计:如将牌面字符到索引的映射做成独立函数(card_to_index),将分值定义为常量数组(SCORES),如果新增角色或修改分值只需改一处
  • 内部实现与导出接口分离:T2 中 calc_state_internal 返回 Rust 原生类型,calc_current_state 负责转为 JS 类型。如果需要从 Wasm 以外的方式调用(如纯 Rust 测试),内部函数可以直接使用rust的方式进行处理
  • 设计冗余:预留对未知牌(X)的处理能力,虽然 T2 不会出现 X,但在 T3 中会用到

头脑风暴环节

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

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

T2 的不带 X 记录是完全信息,实际对局则存在 7 张隐藏牌(1 张暗弃、1 张己方密约、4 张双方取舍弃牌)的不对称信息;若加上 X,需采用蒙特卡洛模拟补全隐藏牌分布、贝叶斯更新对手信念状态,或结合期望最大化与极小极大算法进行概率推断,将 T2 的确定性计算封装为采样单元以支撑不完全信息决策。

总结

→ 📖 Q2.6(P) 请记录下目前的时间,并根据实际情况填写 附录 A:基于 PSP 2.1 修改的 PSP 表格 的“实际耗时”栏目。
4月5日 22:00
→ 📖 Q2.7(I) 请写下本部分的心得体会。

T2 的核心挑战在于字符串解析。history 格式虽然看起来简单,但赠予和竞争涉及选择、分组,需要仔细处理牌的分配。一个教训是写代码时一定要手动推导预期结果并记录,不能想当然,不然很可能让代码的逻辑变得四不像,后续修改困难

Chapter.3 道途之荆

准备

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

4月6日 9:00

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

  1. 浏览任务要求,参照 附录A:基于 PSP 2.1 修改的 PSP 表格,估计任务预计耗时;
  2. 完成编程任务期间,依次做了什么(比如查阅了什么资料,随后如何进行了开发,遇到了什么问题,又通过什么方式解决);
  • 博弈思路参考花见小路
  • 阅读 T3 任务要求,理解输入(history、cards 手牌、board)和输出(行动字符串)的格式
  • 确定了三类回合:主动行动、回应赠予(从 3 张中选 1 张)、回应竞争(从 2 组中选 1 组)
  • 策略选择:采用 minimax 评估框架。对于赠予和竞争,假设对手会做出对自己最有利的选择(即对我最不利),在最坏情况下选评分最高的方案
  • 实现了组合生成函数 select_cards:从手牌中选 k 张的所有不重复方式。以及 split_into_groups:将 4 张牌拆分为两组的所有不重复方式
  • 设计了评估函数 evaluate_state:综合考虑分差、胜利条件奖励、手牌潜力
  • 遇到的问题:需要正确判断当前是行动回合还是回应回合,通过分析 history 最后一条记录是否包含 - 来区分。
  • 编写 12 个测试覆盖行动有效性、回应有效性、策略合理性、状态解析、组合生成、评估函数
  • 通过自我对战测试(同时作为 P1 和 P2),验证了完整对局流程正常

头脑风暴环节

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

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

如果时间充裕,最优策略可能是用蒙特卡洛树搜索(MCTS)或强化学习——让程序自己跟自己打成千上万局,慢慢学会什么情况下该出牌还是弃牌,甚至能摸透对手的套路。另一种思路是博弈树搜索,把每一步可能的行动和对手回应都画成树,往前算好几步,选对自己最有利的分支。不过花见小路有隐藏信息(看不到对手的暗牌),所以需要加上概率推断,猜对手手里可能有什么牌。

需求建模和算法设计

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

  1. 行动类型选择:遍历所有尚未使用的行动类型(1-4,每种每轮只能用一次),对每种类型穷举所有合法的出牌组合,选择评估得分最高的方案。
  • 选牌策略——Minimax 框架:

  • 密约(类型1):直接评估将该牌暗置后的局面分数。倾向于密约高价值牌(如 G、F),因为这些牌对最终标记归属影响大。

  • 取舍(类型2):弃掉对局面影响最小的牌,保留手中更有价值的牌用于后续行动。

  • 赠予(类型3):假设对手会从 3 张牌中选走对他最有利的那张(minimax),在所有出牌组合的最坏情况中选最好的。策略上倾向于提供同类型或低价值的牌组合,降低对手选择的优势。

  • 竞争(类型4):将 4 张牌拆为两组,假设对手会选对他更有利的组(minimax),在所有拆分方式的最坏情况中选最好的。策略上倾向于让两组价值尽量接近,减小对手选择带来的劣势。

  1. 评估函数:
  • 基础分 = (我方标记总分 - 对手标记总分) × 100
  • 胜利条件奖励:≥11分 → +10000,≥4枚标记 → +8000
  • 手牌潜力:如果手中某种牌打出后能翻转该标记的归属,额外加分
    (分数设计使用ai辅助参考)
  1. 编程实现:通过递归的 select_cards 函数生成所有不重复的组合,split_into_groups 生成所有不重复的分组方式,对每种方案调用 evaluate_state 评估,取最优。

代码可复用性与需求变更

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

  • 直接复用:card_to_index 函数、牌面字符与索引的映射逻辑在 T3 中完全复用。counts_to_stringcards_to_counts 是在 T2 思路基础上新增的工具函数
  • 改造复用:T2 中的 history 解析逻辑被改造为 T3 的 parse_game_state 函数。核心区别是 T2 解析完整一轮记录后计算最终状态,而 T3 需要解析到当前时刻的中间状态(包含未知牌 X)
  • 新增内容:评估函数、组合生成、minimax 决策逻辑是 T3 全新的部分,T2 中没有对应内容

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

  • 模块分离:将游戏状态管理(解析 history)和决策策略(评估函数、行动选择)分开。如果要更换策略(比如从 minimax 换成 MCTS),只需替换决策模块
  • 评估函数参数化:评估函数中的权重(如分差权重 100、胜利奖励 10000 等)可以提取为常量,方便后续调优。
  • 枚举类型建模:用 TurnType 枚举区分行动回合和回应回合,比字符串判断更安全,新增回合类型时编译器会提醒处理
  • 组合生成的通用化:select_cards(hand, k) 可以生成任意 k 的组合,如果规则变更(如新增行动类型需要选 5 张牌),无需修改函数本身

软件度量

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

可以试着通过胜率统计来验证决策能力:让程序跟随机策略、贪心策略以及其他小组的程序对战几百局,看赢多少、输多少、平多少。胜率明显高于 50% 说明比随机强,能稳定赢贪心策略说明有战术深度。

总结

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

4月6日 12:00

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

T3 实现了从"被动解析"(T2)到"主动决策"(T3),从中学习到了决策设计的思路。minimax 框架虽然经典,但实际实现代码时仍需要处理很多细节:如何高效生成所有合法出牌组合、如何设计有区分度的评估函数、如何正确判断当前是行动回合还是回应回合等,从计划到实现仍然有很大的距离

结对项目总结

结对过程回顾和反思

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

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

结对过程中值得改进的方面包括:个人思考时间需要增加,长时间持续对话导致深度思考不足,交流效率需要提升,出现分歧时两人的沟通效率不高。

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

  • 优点:
    • 主动性强,前期带着我主动熟悉工具链和开发环境学习
    • 开发仔细,对于每一个错误都会主动研究相关问题并解决
    • 回馈及时,每轮测试都尽快实现并反馈实现问题,使debug效率提升了很多
  • 缺点:
    • 工具链不够熟悉,前期消耗时间较长

对结对编程的理解

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

代码实现和测试分离解决了我常见的测试不全面问题,同时不同的侧重也让学习效率更高,全面的迅速接触技术领域,两个人一起开发也更加专注
时间协调成本较高,课程多时调度困难,开发任务或测试任务其一比较困难时会使开发效率降低,两个人一起开发并不合算。

代码实现提交

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

附录

附录 A:基于 PSP 2.1 修改的 PSP 表格

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