[P] 结对项目:花见小路
[P] 结对项目:花见小路
| 项目 | 内容 |
|---|---|
| 这个作业属于哪个课程 | https://edu.cnblogs.com/campus/buaa/BUAA_SE_2026_LR |
| 这个作业的要求在哪里 | https://edu.cnblogs.com/campus/buaa/BUAA_SE_2026_LR/homework/15648 |
| 我在这个课程的目标是 | 学习现代软件工程的基本思想,理解敏捷开发、结对编程、软件项目管理等方法,并能够在实践中应用这些方法完成软件项目。 |
| 这个作业在哪几个具体方面帮助我实现目标 | 通过结对开发、测试复盘提升软件工程实践能力。 |
→ 📖 Q0.0(P) 【你可以在结对结束后补充】如果你的代码仓库包含 AIGC 的部分,列举使用的工具、模型和使用范围。若未使用则填写:本组提交的全部代码不包含AI补全或生成的部分。
- 使用了cursor和copilot等ai模型,在chapter3的优化部分辅助编程同时提供可靠的思路。
Chapter.0 wasm从安装到入门
引入
→ 📖 Q0.1(P) 请记录下目前的时间。
2026年4月9日 19:10
调查
→ 📖 Q0.2(I) 【你可以在结对结束后另行补充。】作为本项目的调查:
请如实标注在开始项目之前对 Wasm 的熟悉程度分级,可以的话请细化具体的情况。(分别回答两人各自的情况)
I. 没有听说过;
II. 仅限于听说过相关名词;
III. 听说过,且有一定了解;
IV. 听说过,且使用 Wasm 实际进行过开发(即便是玩具项目的开发)。
I. 没有听说过;
请如实标注在开始项目之前对桌游花见小路的熟悉程度分级,可以的话请细化具体的情况。(分别回答两人各自的情况)
I. 不了解玩法和规则;
II. 听说过,且有一定了解;
I. 不了解玩法和规则;
总结
→ 📖 Q0.3(P) 请记录下目前的时间。
2026年4月9日 20:00
Chapter.1 七色之缨
结对过程
→ 📖 Q1.1(P) 请记录下目前的时间。
2026年4月9日 20:20
→ 📖 Q1.2(P) 请在完成任务的同时记录,并在完成任务后整理完善:
- 浏览任务要求,参照 附录A:基于 PSP 2.1 修改的 PSP 表格,估计任务预计耗时;
- 完成编程任务期间,依次做了什么(例如查阅了哪些资料、如何设计判定逻辑、如何设计测试样例、遇到了什么问题、如何解决)。
| Personal Software Process Stages | 个人软件开发流程 | 预估耗时(分钟) | 实际耗时(分钟) |
|---|---|---|---|
| PLANNING | 计划 | 5 | 5 |
| - Estimate | - 估计这个任务需要多少时间 | 5 | 5 |
| DEVELOPMENT | 开发 | 70 | 42 |
| - Analysis & Design Spec | - 需求分析 & 生成设计规格(确定要实现什么) | 10 | 6 |
| - Technical Background | - 了解技术背景(包括学习新技术) | 10 | 6 |
| - Coding Standard | - 代码规范 | 5 | 5 |
| - Design | - 具体设计(确定怎么实现) | 10 | 8 |
| - Coding | - 具体编码 | 15 | 10 |
| - Code Review | - 代码复审 | 5 | 3 |
| - Test Design | - 测试设计(确定怎么测,比如要测试哪些情景、设计哪些种类的测试用例) | 5 | 2 |
| - Test Implement | - 测试实现(设计/生成具体的测试用例、编码实现测试) | 10 | 2 |
| REPORTING | 报告 | 15 | 13 |
| - Quality Report | - 质量报告(评估设计、实现、测试的有效性) | 5 | 5 |
| - Size Measurement | - 计算工作量 | 5 | 2 |
| - Postmortem & Process Improvement Plan | - 事后总结和过程改进计划(总结过程中的问题和改进点) | 5 | 6 |
| TOTAL | 合计 | 90 | 60 |
在完成任务期间,我们首先分析了游戏规则:花见小路是一个回合制游戏,有7个位置,每个位置有不同分数。胜负判定基于得分、倾心数量和等级比较。然后设计了判定逻辑,包括得分计算、棋子计数和等级比较。使用了Rust语言实现WASM模块。设计了测试用例覆盖各种胜负情况。遇到了WASM编译问题,通过查阅文档解决。
设计
→ 📖 Q1.3(P) 请说明你们为这个判定模块设计了哪些中间量或辅助函数;如果没有额外设计,也请说明为什么认为直接实现已经足够清晰。
设计了以下中间量与辅助函数:
score_and_count(board):统计双方总分和标记数;p1_score/p2_score:用于“11分立即胜”;p1_count/p2_count:用于“至少4枚标记立即胜”;TIER_ORDER = [G, F, D/E, A/B/C]:用于第三轮同分的最高档位比较。
这样做把“规则条文”映射到独立步骤,降低分支顺序错误风险,阅读和测试都更清晰。
→ 📖 Q1.4(I) 请说明在这样一个规则判定类模块中,如何避免“漏判”“错判”或分支顺序错误等问题。
为了避免漏判和错判,我们首先全面分析了游戏规则,确保所有胜负条件都被覆盖。然后,通过单元测试覆盖各种边界情况,包括得分达到11、拥有4个棋子、第三轮平局等。分支顺序按照优先级排列:先检查得分,再检查棋子数量,最后处理第三轮特殊情况。代码复审时仔细检查逻辑,确保没有遗漏。
测试
→ 📖 Q1.5(P) 请说明你们设计了哪些测试用例,这些测试分别覆盖了哪一类规则或边界情况。
- 用例1:己方分值≥11,返回1(11分胜)。
- 用例2:对手标记≥4,返回-1(对手4标记胜)。
- 用例3:第三轮总分不同,总分高者胜。
- 用例4:第三轮同分,按最高档位判胜。
- 用例5:第三轮同分且档位也无法区分,返回2(平局)。
- 用例6:第一轮未满足胜利条件,返回0(继续游戏)。
- 用例7:第二轮未满足胜利条件,返回0(继续游戏)。
- 用例8:4标记与11分冲突场景,验证11分优先。
→ 📖 Q1.6(I) 请说明你对“先写测试再实现”与“先实现再补测试”两种方式的理解。
"先写测试再实现"(TDD)有助于明确需求和接口,促进模块化设计,但可能增加初始开发时间。"先实现再补测试"更直观,适合快速原型,但可能导致测试不充分或遗漏边界情况。我们在项目中采用了先实现再补测试的方式,因为规则相对简单,且已有样例指导。
总结
→ 📖 Q1.7(P) 请记录下目前的时间,并根据实际情况填写 附录A:基于 PSP 2.1 修改的 PSP 表格 的“实际耗时”栏目。
2026年4月9日 21:20
(实际耗时已在Q1.2表格中填写)
→ 📖 Q1.8(I) 请写下本部分的心得体会。
通过本次结对编程,我们学习了Wasm开发和游戏规则实现。PSP表格帮助我们更好地规划时间,提高了开发效率。结对编程促进了知识共享和代码质量。未来将继续应用这些实践。
Chapter.2 不祥之影
准备
→ 📖 Q2.1(P) 请记录下目前的时间。
2026年4月9日 21:30
→ 📖 Q2.2(P) 请在完成任务的同时记录,并在完成任务后整理完善:
- 浏览任务要求,参照 附录A:基于 PSP 2.1 修改的 PSP 表格,估计任务预计耗时;
- 完成编程任务期间,依次做了什么(比如查阅了什么资料,随后如何进行了开发,遇到了什么问题,又通过什么方式解决);
| Personal Software Process Stages | 个人软件开发流程 | 预估耗时(分钟) | 实际耗时(分钟) |
|---|---|---|---|
| PLANNING | 计划 | 10 | 10 |
| - Estimate | - 估计这个任务需要多少时间 | 10 | 10 |
| DEVELOPMENT | 开发 | 120 | 95 |
| - Analysis & Design Spec | - 需求分析 & 生成设计规格(确定要实现什么) | 15 | 12 |
| - Technical Background | - 了解技术背景(包括学习新技术) | 20 | 15 |
| - Coding Standard | - 代码规范 | 5 | 5 |
| - Design | - 具体设计(确定怎么实现) | 20 | 18 |
| - Coding | - 具体编码 | 30 | 25 |
| - Code Review | - 代码复审 | 10 | 8 |
| - Test Design | - 测试设计(确定怎么测,比如要测试哪些情景、设计哪些种类的测试用例) | 10 | 7 |
| - Test Implement | - 测试实现(设计/生成具体的测试用例、编码实现测试) | 10 | 5 |
| REPORTING | 报告 | 20 | 15 |
| - Quality Report | - 质量报告(评估设计、实现、测试的有效性) | 5 | 5 |
| - Size Measurement | - 计算工作量 | 5 | 3 |
| - Postmortem & Process Improvement Plan | - 事后总结和过程改进计划(总结过程中的问题和改进点) | 10 | 7 |
| TOTAL | 合计 | 150 | 120 |
在完成任务期间,我们首先分析了任务要求:根据一整小轮的完整记录推算双方场面牌数与结算后倾心标记。复用了 T1 中得分计算的思路,但需要处理不同的行动类型(1,2,3,4)。设计了数据结构来跟踪秘密和公开牌。实现了行动解析和状态更新逻辑。遇到了字符串解析问题,通过仔细处理分隔符解决。设计了测试用例覆盖各种行动组合,包括秘密出牌、赠送、竞争、混合行动和边界情况如空历史。丰富了测试用例,从1个增加到8个,提高了代码覆盖率和可靠性。
代码可复用性与需求变更
→ 📖 Q2.3(P) 请说明针对该任务,你们对 🧑💻 T1 中已实现的代码进行了哪些复用和修改。
我们复用了 T1 中得分计算的核心逻辑,特别是 score_and_count 函数的思路,用于统计牌数。但由于 T2 的需求不同(推算状态而非判定胜负),我们没有直接调用 T1 的函数,而是重新实现了类似的计数逻辑。修改了数据结构,从简单的 board 数组扩展到跟踪秘密和公开牌的二维数组。复用了更新 board 的逻辑,但调整为基于牌数比较而非直接赋值。
→ 📖 Q2.4(I) 请说明在编码实现时,可以采取哪些设计思想、考虑哪些设计冗余,来提高既存代码适应需求变更的能力。
为了提高代码适应需求变更的能力,我们采用了模块化设计,将不同行动类型的处理分离到独立函数中,便于扩展新的行动类型。使用了抽象的数据结构,如统一的牌计数数组,避免硬编码特定逻辑。添加了冗余的错误检查和断言,确保输入有效性,防止未来需求变更导致的崩溃。设计了可复用的辅助函数,如 add_cards 和 take_one_from_hand,提高代码复用性。考虑了性能冗余,使用了高效的数据结构如数组而非哈希表。
头脑风暴环节
→ 📖 Q2.5(P) 头脑风暴环节:
我们终于快要开始让程序玩游戏了!请尝试分析:T2 中不带 X 的操作记录比起实际对局多出了多少信息?如果加上 X,也就是失去了这部分信息的话,如何处理对小轮结束后状态的估计?
不带 X 的操作记录比实际对局多出了对手秘密行动的详细信息。在实际对局中,玩家只能看到自己的牌和公开的行动,但不带 X 的记录暴露了对手的所有秘密牌选择。这多出了对手手牌的完整信息,使得状态推算是确定性的。
如果加上 X(表示未知牌),状态估计变得不确定。我们需要考虑所有可能的对手手牌组合。对于每个 X,枚举可能的牌,并计算所有可能状态的概率分布。可以使用蒙特卡洛方法或穷举小牌库来估计最可能的状态。最终输出可能是状态的期望值或置信区间,而不是确定值。
总结
→ 📖 Q2.6(P) 请记录下目前的时间,并根据实际情况填写 附录 A:基于 PSP 2.1 修改的 PSP 表格 的“实际耗时”栏目。
2026年4月9日 23:50
(实际耗时已在Q2.2表格中填写)
→ 📖 Q2.7(I) 请写下本部分的心得体会。
通过 T2 任务,我们深入理解了游戏状态管理的复杂性。复用 T1 代码的思想提高了开发效率,但也需要灵活调整以适应新需求。头脑风暴环节让我们思考了信息不对称对AI设计的影响,为后续决策任务奠定了基础。丰富测试用例显著提高了代码的可靠性和覆盖率,确保了各种边界情况都被正确处理。
Chapter.3 道途之荆
准备
→ 📖 Q3.1(P) 请记录下目前的时间。
2026年4月10日 14:30
→ 📖 Q3.2(P) 请在完成任务的同时记录,并在完成任务后整理完善:
- 浏览任务要求,参照 附录A:基于 PSP 2.1 修改的 PSP 表格,估计任务预计耗时;
- 完成编程任务期间,依次做了什么(比如查阅了什么资料,随后如何进行了开发,遇到了什么问题,又通过什么方式解决);
| Personal Software Process Stages | 个人软件开发流程 | 预估耗时(分钟) | 实际耗时(分钟) |
|---|---|---|---|
| PLANNING | 计划 | 20 | 20 |
| - Estimate | - 估计这个任务需要多少时间 | 20 | 20 |
| DEVELOPMENT | 开发 | 190 | 175 |
| - Analysis & Design Spec | - 需求分析 & 生成设计规格(确定要实现什么) | 25 | 20 |
| - Technical Background | - 了解技术背景(包括学习新技术) | 20 | 18 |
| - Coding Standard | - 代码规范 | 5 | 5 |
| - Design | - 具体设计(确定怎么实现) | 35 | 32 |
| - Coding | - 具体编码 | 50 | 48 |
| - Code Review | - 代码复审 | 20 | 18 |
| - Test Design | - 测试设计(确定怎么测,比如要测试哪些情景、设计哪些种类的测试用例) | 15 | 14 |
| - Test Implement | - 测试实现(设计/生成具体的测试用例、编码实现测试) | 20 | 20 |
| REPORTING | 报告 | 30 | 25 |
| - Quality Report | - 质量报告(评估设计、实现、测试的有效性) | 10 | 8 |
| - Size Measurement | - 计算工作量 | 5 | 5 |
| - Postmortem & Process Improvement Plan | - 事后总结和过程改进计划(总结过程中的问题和改进点) | 15 | 12 |
| TOTAL | 合计 | 240 | 220 |
在本任务中,我们先分析了 T3 的输入输出形式,明确程序需要在不完全信息下,根据历史行动、当前手牌和场面状态,从四类行动中选出一个最优动作。随后我们阅读并比较了 basic、improved、search 等不同版本的策略实现,最终确定采用 t3-rust-improved 作为提交版本,因为它在决策质量和运行效率之间取得了较好的平衡。开发过程中主要做了三件事:一是整理历史记录解析逻辑,判断自己已经用过哪些行动;二是设计统一的牌价值函数,对不同局面下的牌进行量化评分;三是分别为四种行动实现独立的选牌规则,再把所有候选行动放在同一个评分框架里比较。过程中遇到的主要问题是,单纯看牌面分值会导致策略过于短视,因此后来加入了场面归属和比分追赶的加权因子,使得决策更稳定。最后通过自我对弈和 benchmark 反复比较,确认 improved 版表现优于基础策略,并且运行开销可接受。
头脑风暴环节
→ 📖 Q3.3(P) 头脑风暴环节:
假设提供更充裕的时间和资源,这个游戏中你能找到的最优策略有可能是什么形式的?进行调研并总结分析。你还可以在任务结束后试着实现(不计分)。
如果有更充裕的时间和计算资源,这个游戏中的更优策略很可能会采用“信息集博弈搜索”或“自我对弈学习”的形式,而不是仅依靠手工启发式规则。因为花见小路本质上是一个带隐藏信息、带对手博弈的顺序决策问题,最理想的做法是同时建模“我方当前收益”“对手可能手牌”“对手在不同策略下的反应”。从调研和分析来看,较强的方案大致有以下几类。
第一类是 Minimax 或 Alpha-Beta 剪枝搜索。它适用于把行动树显式展开的情况,优点是决策逻辑清晰,可以直接比较行动的未来收益;缺点是状态空间增长很快,而花见小路中还存在隐藏信息,所以必须先对未知信息做枚举或估计,否则搜索无法完整展开。
第二类是信息集搜索或蒙特卡洛树搜索(MCTS)。这类方法适合不完全信息博弈,可以先对未知牌面进行采样,再在采样得到的局面上反复模拟对局,通过统计胜率来指导当前选择。相比纯启发式,它更有可能找到非直观但长期收益更高的动作。
第三类是强化学习或自我对弈训练,例如通过大量 self-play 生成局面数据,再学习“局面价值函数”和“行动概率分布”。如果训练充分,模型可以自动学出人不容易直接手写的策略模式,比如什么时候故意让出低价值牌,什么时候用竞争动作制造对手两难。
不过结合本项目的规模、实现周期和运行限制,我们认为完全搜索或学习型方案实现成本过高,因此最终采用了 improved 版本的启发式策略。这个版本虽然不是理论最优,但实现难度可控、决策速度快,也更容易解释和调试,适合作为课程项目中的最终提交版本。
需求建模和算法设计
→ 📖 Q3.4(P) 请说明针对该任务,你们采取了哪些策略来优化决策。具体而言,怎么选择行动类型?选牌如何更优?如何编程实现。
我们最终采用的 t3-rust-improved 策略,核心思想是“先统一评估单张牌价值,再分别设计四种行动的局部最优规则,最后把所有可选行动放在同一个评分体系里比较”。这样做的好处是实现简单清晰,同时可以兼顾局面感知和运行效率。
首先,在牌价值评估上,我们定义了 card_utility() 函数。它不是只看这张牌本身对应位置的基础分值,而是把三个因素综合起来:一是该位置本身的分值高低;二是该位置当前由谁占据;三是双方当前总分是否已经接近胜利线。具体来说,如果某个位置已经被我方占领,那么继续投入同类牌的边际收益会降低,所以乘上较小的系数;如果被对手占领,那么这类牌更有反超价值,所以乘上较大的系数;如果任一方已经接近 11 分,那么高价值牌会被进一步放大,用来加速取胜或阻止对手。这种做法使得策略不再只是“拿大牌”,而是“在当前局面下拿更值的大牌”。
其次,在行动类型选择上,我们不是预先写死“第几轮就该用哪种行动”,而是先读取历史,通过 used_actions_from_history() 判断四类行动里哪些还可用,再为每个可用行动计算一个候选得分。最后取分数最高的那个行动。这使得程序能够根据实时手牌和棋盘情况动态决定,而不是依靠固定顺序。
四种行动的选牌策略如下。对于行动 1(秘密出牌),策略最直接,就是从当前手牌中选择 card_utility 最大的那张牌,因为这张牌最值得保留下来并投入到自己可控的行动中。对于行动 2(弃两张牌),我们枚举所有两张组合,选择总价值最小的一对弃掉,等价于“尽可能少损失有用资源”。对于行动 3(给对手三张挑一张),我们枚举所有三张组合,计算“总价值减去其中最大价值”,因为对手通常会拿走他能获得的最好那张,所以这个指标可以近似刻画我方最终能保留多少收益。对于行动 4(分成两组让对手二选一),我们枚举四张牌的所有分组方式,重点比较两组中较差那一组的价值,并尽量让这个最差值变大。这样无论对手选哪一组,我方都不至于损失过大。
在编程实现上,我们先为每种行动写一个独立函数,如 action_1_best()、action_2_worst_pair()、action_3_best_offer() 和 action_4_best_contest(),使每个函数只负责一种动作的最优选牌逻辑。之后在 best_normal_action() 中统一调用这些函数,把每个动作及其得分放入候选列表,再排序选出最优动作。对需要响应对手选择的情况,则通过 pending_choice() 识别当前是在回应赠送动作还是回应竞争动作,再调用 answer_gift() 或 answer_contest()。整体上,这是一套“局势加权 + 极小化对手收益”的启发式决策框架。
代码可复用性与需求变更
→ 📖 Q3.5(P) 请说明针对该任务,你们对 🧑💻 T2 中已实现的代码进行了哪些复用和修改。
在 T3 的实现中,我们复用了 T2 中不少和“历史记录解析”有关的基础能力。比如 base_of() 仍然用于把带后缀的动作还原成基础动作串;infer_round_start_and_me() 用于根据历史记录推断当前视角下谁先手、谁是我方;used_actions_from_history() 则直接沿用了“根据动作历史识别已用行动”的思路。这些函数虽然原本服务于状态推演任务,但在 T3 中同样重要,因为策略决策必须先知道哪些行动还没被用掉。
与此同时,我们也对整体结构做了明显修改。T2 的目标是“根据一轮记录还原场面状态”,因此重点在于状态更新与信息恢复;而 T3 的目标是“在当前局面下输出一个动作”,重点变成了价值评估和策略选择。为此我们新增了 card_utility()、pair_value() 等评分函数,用于刻画单张牌和一对牌的战略价值;又新增了四类行动各自的选牌函数,以及统一比较行动收益的 best_normal_action()。换句话说,T2 中复用的是解析层和辅助层,T3 中新写的是决策层和博弈层。
→ 📖 Q3.6(I) 请说明在编码实现时,可以采取哪些设计思想、考虑哪些设计冗余,来提高既存代码适应需求变更的能力。
为了让代码在需求变化时更容易扩展,我们在实现中尽量采用了“分层”和“模块化”的设计思想。最底层是历史记录解析、手牌解析和棋盘分数计算等通用函数;中间层是牌面价值评估;最上层才是四类行动的策略函数和最终动作选择。这样如果以后规则变更,例如行动格式变化、评分标准变化,或需要引入新的启发式,只需替换某一层的实现,而不必整体重写。
此外,我们还尽量把常量参数独立出来,例如 SCORE 数组和 card_utility() 中使用的若干系数。这样做相当于给策略保留了可调空间,后续可以通过对战结果继续微调,而不需要修改整体控制流程。再比如,把四类行动分别封装成独立函数,也是一种冗余设计:虽然代码行数略多,但可读性更强,后续如果要单独替换某一类行动的决策逻辑,不会影响其他部分。
最后,在鲁棒性上我们也保留了一定冗余。例如 pending_choice() 对输入历史进行防御式判断,best_normal_action() 在没有候选动作时返回默认动作,answer_contest() 在输入长度异常时给出保底返回值。这些设计虽然在理想输入下可能用不上,但可以避免因为边界情况或输入异常导致整个程序失效,从而提高代码面对需求变化时的稳定性。
软件度量
→ 📖 Q3.7(P) 请说明你们如何量度所实现的程序模块的有效性,例如:“如何说明我们的程序模块决策能力很强?”,尝试提出一些可能的定量分析方式或测试方式。
我们认为,一个决策模块是否有效,不能只看它“能不能运行”,更重要的是看它在对局中是否稳定地产生更好的结果。因此可以从以下几个角度进行量化。
首先是最直接的胜率测试。让 t3-rust-improved 与 basic 版本、随机策略版本以及其他候选版本进行大量对战,统计胜率、平局率和平均得分。若 improved 版在足够多轮次下能稳定取得更高胜率,就说明它在整体策略上更优。对于课程项目而言,这种 benchmark 是最直接也最有说服力的指标。
其次是决策耗时。因为 Wasm 模块需要在对局过程中频繁调用,策略再强,如果耗时过高,也会影响整体表现。因此我们需要记录平均决策时间、最大决策时间以及是否会超过给定的时间预算。improved 版本的一个优势就在于它只做有限枚举和启发式比较,通常可以把耗时控制在比较低的范围内。
第三是行为合理性分析。可以人工挑选若干典型局面,例如“某一路已经被对手压制”“我方即将达到 11 分”“手里存在高低价值混合牌”等,观察程序是否会做出符合直觉的动作,比如保留关键牌、弃掉低价值牌、尽量让对手拿不到最大收益。这种测试虽然不如胜率那样客观,但可以帮助我们判断策略是否真正利用了局面信息。
第四是稳定性和鲁棒性测试。包括只剩 1 张、2 张、3 张牌时是否还能正常输出合法动作;所有行动都快用完时是否还能正确选择剩余动作;历史记录中出现遮蔽信息时是否还能正确判断自己可用的行动。一个真正有效的程序,不应只在“正常局面”下工作,而应在边界局面下依然保持正确。
综合来看,我们认为“高胜率 + 低耗时 + 合理行为 + 边界稳定”四个指标共同构成了对 T3 决策模块有效性的评价标准,而 improved 版本正是在这几个方面都较均衡,因此被选为最终版本。
总结
→ 📖 Q3.8(P) 请记录下目前的时间,并根据实际情况填写 附录A:基于 PSP 2.1 修改的 PSP 表格 的“实际耗时”栏目。
2026年4月10日 18:50
(实际耗时已在 Q3.2 表格中填写)
→ 📖 Q3.9(I) 请写下本部分的心得体会。
这一部分是整个项目里最接近“真正 AI 决策问题”的任务。相比前两部分以规则判定和状态还原为主,T3 更强调如何在不完全信息和多种行动选择之间做权衡。我们在实现过程中最大的体会是,单纯依靠直觉写规则往往不够稳定,必须把“牌的价值”明确量化,才能让不同动作在同一标准下比较。
另外,我们也体会到了工程实现中的取舍。理论上,更复杂的搜索和学习方法可能带来更强的策略上限,但在课程项目的时间限制下,能实现、能解释、能稳定运行同样重要。t3-rust-improved 虽然不是最复杂的策略,但它的逻辑清晰、运行代价低、实战表现稳定,这让我们认识到“合适的方案”往往比“最复杂的方案”更有价值。
从结对过程来看,这一章也让我们更深地体验到了结对编程的优势。一人更关注规则和博弈思路,一人更关注代码结构和边界处理,讨论过程中不断修正对策略的理解,最终形成了一个比单独完成更完整的实现。这种共同分析、共同验证的过程,是本部分最有收获的地方。
结对项目总结
结对过程回顾和反思
→ 📖 Q4.1(P) 提供两人在讨论的结对图像资料。

→ 📖 Q4.2(P) 回顾结对的过程,反思有哪些可以提升和改进的地方。
回顾这次结对过程,我们整体配合比较顺畅:前期一起理解任务要求和规则,中期一人更偏向思考策略与算法,一人更偏向实现、调试和测试,后期再一起做 benchmark、检查边界情况并完善文档。这样的分工让效率比较高,也使我们在讨论中能及时发现问题。例如在 T3 的实现中,我们一开始对“高分牌是否一定优先保留”有不同理解,经过一起分析对局场景和代码行为之后,才逐步形成了更稳定的策略。
不过回头看,整个过程仍然有一些可以提升的地方。第一,我们在前两章里对时间记录和过程记录做得还不够同步,很多内容是完成后再回忆补写,如果能边做边记录,后面的整理会更准确。第二,在策略实验阶段,我们虽然做了多轮测试,但对不同版本的对战结果没有做到一开始就统一整理格式,导致比较时多花了一些时间。第三,讨论中有时会先凭直觉争论某个策略是否更优,如果能够更早建立“先写评价指标、再看实验结果”的习惯,就能减少无效来回。
如果以后继续做类似任务,我们觉得可以从三个方面改进:一是更早约定清晰的分工和交接方式,比如谁负责实验、谁负责记录、谁负责最终整合;二是更早建立统一的测试和 benchmark 表格,避免后期重复整理;三是在实现前先把关键决策指标写清楚,用数据推动讨论,而不是只凭感觉判断策略优劣。
→ 📖 Q4.3(I) 锐评一下你的搭档!并请至少列出三个优点和一个缺点。
我的搭档在这次结对中表现得很可靠,整体合作体验很好。
优点方面,第一,他对任务推进节奏把握得比较稳,不会因为某个小问题卡住太久,能够及时把讨论拉回主线。第二,他在代码实现和调试时很细致,尤其是在处理边界情况和检查输入输出格式时,能发现不少容易被忽略的问题。第三,他愿意讨论,也愿意接受反驳,不会一味坚持自己的想法,这让很多策略上的分歧最终都能落到代码和实验结果上解决。第四,他对整体结构有比较清晰的意识,写出来的代码通常比较规整,后续修改时不容易牵一发动全身。
缺点方面,我觉得他有时候会偏谨慎,尤其在方案选择上,容易先考虑“这样做会不会出问题”,导致一些本来可以更快验证的想法推进得稍慢一点。不过这个缺点本身也是优点的另一面,因为正是这种谨慎,帮我们避免了不少低级错误。
对结对编程的理解
→ 📖 Q4.4(I) 说明结对编程的优缺点、你对结对编程的理解。
我对结对编程的理解是,它并不是简单地“两个人一起写同一份代码”,而是一种把分析、设计、实现、验证放在同一个持续沟通过程中的协作方式。它最有价值的地方,不只是多一个人帮忙敲代码,而是在关键节点上能够及时交换视角,减少单人开发时常见的理解偏差和思维盲区。
结对编程的优点比较明显。首先,它能显著提升问题发现的速度,很多逻辑漏洞、边界情况和表述不清的问题,在两个人实时讨论时会更早暴露出来。其次,它有助于知识共享,一个人擅长规则分析,另一个人擅长代码实现或调试时,双方都能在过程中学到对方的方法。再次,它可以提升代码质量,因为代码不是“写完再给别人看”,而是在形成过程中就不断被审视和修正。
当然,结对编程也有缺点。最主要的问题是沟通成本更高,如果分工不清、节奏不一致,可能会让两个人都觉得低效。其次,如果两个人都只停留在口头讨论而没有及时落地验证,也容易出现“讨论很多,推进很慢”的情况。再者,结对编程对双方的参与度要求较高,如果其中一方长期只是旁观,实际效果会大打折扣。
结合这次项目,我觉得结对编程最适合用在“规则不够直观、策略需要反复推敲、实现细节较多”的任务上。像这次 T3 的策略设计,如果一个人单独做,可能会很快写出一个能跑的版本,但不一定能这么快发现其中的短视策略和边界问题。正因为是两个人一起讨论、实现和验证,我们才更容易在有限时间内得到一个比较稳定、也更有说服力的结果。
代码实现提交
→ 📖 Q4.5(P) 请提供你们完成代码实现的代码仓库链接。
https://github.com/404-error64/BUAASE2026-PairProgramming

浙公网安备 33010602011771号