[P]结对项目:花见小路
[P]结对项目:花见小路
| 项目 | 内容 |
|---|---|
| 这个作业属于哪个课程 | 2026年春季软件工程 |
| 这个作业的要求在哪里 | [P] 结对项目:花见小路 |
| 我在这个课程的目标是 | 学习软件开发相关的知识,并且与同学一起完成一个软件工程项目,培养和锻炼个人能力 |
| 这个作业在哪个具体方面帮助我实现目标 | 实际上手一个小项目的开发,并且是合作一起开发的形式,很大程度上锻炼了我在软件开发中的沟通交流能力 |
→ 📖 Q0.0(P) 【你可以在结对结束后补充】如果你的代码仓库包含 AIGC 的部分,列举使用的工具、模型和使用范围。若未使用则填写:本组提交的全部代码不包含AI补全或生成的部分。
本组代码里有少量 AIGC 辅助。我们使用过 GPT,主要是辅助补全两类比较重复的代码初稿:一类是 T2 里按 1/2/3/4 四种动作分别展开的字符串分支处理框架,另一类是 T3 里按固定优先级拼接行动字符串的样板代码。以上内容后续都由两名成员结合题目要求和最终仓库中的实现逐段人工检查、修改和保留,没有直接整段照搬原始输出。
Chapter.0 wasm从安装到入门
引入
→ 📖 Q0.1(P) 请记录下目前的时间。
2026-03-30 14:10
调查
→ 📖 Q0.2(I) 【你可以在结对结束后另行补充。】作为本项目的调查:
周文康:
I. Wasm 熟悉程度:II. 仅限于听说过相关名词,之前没有自己动手写过。
II. 花见小路熟悉程度:I. 不了解玩法和规则,是这次边看题边学。
陈申学佳:
I. Wasm 熟悉程度:III. 听说过,也看过一些相关资料,但没有完整做过一个小项目。
II. 花见小路熟悉程度:II. 听说过这款桌游,知道是双人博弈,但具体规则还是这次重新看题才弄清楚。
总结
→ 📖 Q0.3(P) 请记录下目前的时间。
2026-03-30 15:35
这部分主要做了环境准备和资料阅读。我们最后选了 AssemblyScript,原因比较直接,就是它和 TS 的写法接近,上手成本相对低一些,比较适合限时完成。
Chapter.1 七色之缨
结对过程
→ 📖 Q1.1(P) 请记录下目前的时间。
2026-03-31 19:20
→ 📖 Q1.2(P) 请在完成任务的同时记录,并在完成任务后整理完善:
- 浏览任务要求,参照附录 A 估计本部分预计耗时大约 70 分钟。
- 完成编程任务期间,我们先把胜负规则重新抄成最简版本,只保留和
board、round直接有关的部分,避免写的时候被原题大段背景干扰。 - 然后按 A 到 G 的顺序过一遍
board,分别统计我方和对手的总分、标记数。代码里最后保留成了p1Score、p2Score、p1Count、p2Count四个中间量。 - 分值部分没有写很多
if,而是用了一个scores = [2, 2, 2, 3, 3, 4, 5]的数组,这样循环里直接累计就可以。 - 后面把规则按顺序写成几层判断:先看有没有达到 11 分,再看有没有 4 个标记,再看是不是前两轮继续,最后才处理第三轮的同分比较。
- 第三轮同分时,我们最开始讨论过要不要专门写辅助函数,但最后发现当前逻辑其实不长,直接顺着
G -> F -> D/E -> A/B/C写出来反而更直观,所以最终代码没有再额外拆函数。
设计
→ 📖 Q1.3(P) 请说明你们为这个判定模块设计了哪些中间量或辅助函数;如果没有额外设计,也请说明为什么认为直接实现已经足够清晰。
这个模块最后没有额外拆辅助函数,主要用了四个中间量:p1Score、p2Score、p1Count、p2Count,外加一个分值表 scores。我们这样写是因为 T1 的核心逻辑本来就比较短,规则顺序也很明确,直接在一个函数里从上到下判断,读起来比来回跳函数更直接。
→ 📖 Q1.4(I) 请说明在这样一个规则判定类模块中,如何避免“漏判”“错判”或分支顺序错误等问题。
我觉得关键还是先把规则优先级排死,再写代码。像这个题里,“11 分立即胜利”一定要放在前面,“4 个标记获胜”要放在后面,而且第三轮的比较只能在前面的立即胜利都没有触发时再看。只要顺序先想清楚,再照着顺序写,漏判和错判会少很多。另一个办法就是自己拿几组边界输入顺着推一遍,看返回值是不是和规则一致。
测试
→ 📖 Q1.5(P) 请说明你们设计了哪些测试用例,这些测试分别覆盖了哪一类规则或边界情况。
我们实际是手工构造输入来检查返回值,主要看了下面几类情况:
- 一方分值达到或超过 11 分,应该立刻返回获胜方。
- 一方只有 4 个标记但总分不到 11,另一方也没到 11,这时应该按 4 个标记获胜。
- 前两轮没有满足胜利条件时,应该返回
0继续游戏。 - 第三轮结束后双方总分不同,应该由总分高的一方获胜。
- 第三轮双方总分相同,但有人拿到
G或F这种更高档位标记时,应该按档位判胜。 - 第三轮双方一路比到最后都分不出结果时,应该返回
2表示平局。
这里也说明一下,当前仓库里的 test 目录还是 AssemblyScript 初始化自带的模板,没有同步整理成正式的判题测试,所以这部分我们主要还是靠手工构造样例验证逻辑。
→ 📖 Q1.6(I) 请说明你对“先写测试再实现”与“先实现再补测试”两种方式的理解。
如果规则已经比较明确,我更偏向先把测试场景想清楚,再去写实现。因为这种判定题最怕的是自以为逻辑没问题,其实只是少想了一个边界。先实现再补测试也不是不行,好处是能更快把主干写出来,但后面一定要补回去,不然代码很容易只在自己脑子里的那几种情况能跑通。
总结
→ 📖 Q1.7(P) 请记录下目前的时间,并根据实际情况填写 附录A:基于 PSP 2.1 修改的 PSP 表格 的“实际耗时”栏目。
2026-03-31 20:45
→ 📖 Q1.8(I) 请写下本部分的心得体会。
这一部分看起来最短,但其实挺适合拿来熟悉 AssemblyScript。我们一开始还想着写得再“抽象”一点,后来发现题目本身就不复杂,保持直接反而更不容易出错。对我来说,这一章最大的收获就是知道了在限时项目里,不一定拆得越细越好,能保证顺序正确、逻辑清楚更重要。
Chapter.2 不祥之影
准备
→ 📖 Q2.1(P) 请记录下目前的时间。
2026-04-03 19:05
→ 📖 Q2.2(P) 请在完成任务的同时记录,并在完成任务后整理完善:
- 浏览任务要求,参照附录 A 估计本部分预计耗时大约 150 分钟。
- 这一部分最花时间的是把
history的格式真正看懂,尤其是3EEG-E、4ABEG-AB这种既有出牌又有选择结果的写法。 - 我们最后的做法比较朴素,就是先用空格把整条
history切成 token,再按下标奇偶判断当前 token 属于哪一方。 - 对于
1动作,直接把牌记到当前行动方区域里;对于2动作,直接跳过,因为弃牌不进场。 - 对于
3动作,先把-后面的选择拿出来,选中的牌记到对手区域,剩下的牌记到行动方区域。 - 对于
4动作,先拆成前两张和后两张两组,再根据对手选了哪一组,把选中的记给对手,剩下的记给行动方。 - 最后再扫一遍双方区域牌数,如果某一类牌我方更多,就把这一位艺伎的
board记成1;如果对方更多,就记成-1;平手时保持原来的标记不动。 - 实现过程中还有一个比较实际的问题,就是题面要求返回二维数组,但 AssemblyScript 这边直接往外传二维数组不太顺手,所以最后代码里改成了长度为 21 的一维
Int8Array,前 7 位是我方区域,中间 7 位是对方区域,最后 7 位是结算后的board。
代码可复用性与需求变更
→ 📖 Q2.3(P) 请说明针对该任务,你们对 🧑💻 T1 中已实现的代码进行了哪些复用和修改。
这一部分我们没有直接把 T1 的函数拿过来调用,但复用了 T1 里对 board 的理解方式,也就是继续沿用 A-G 固定下标和 1 / 0 / -1 这套表示。T1 是直接根据标记判输赢,T2 则是先从 history 还原双方区域牌数,再据此更新标记,所以算是把 T1 前面的“场面形成过程”补上了。
→ 📖 Q2.4(I) 请说明在编码实现时,可以采取哪些设计思想、考虑哪些设计冗余,来提高既存代码适应需求变更的能力。
我觉得最有用的是把“解析输入”和“更新状态”分开。像我们现在虽然写得不算特别抽象,但整体上还是先按动作类型解析 token,再去改区域数组和 board。这样以后如果动作格式有变化,至少修改范围比较集中,不至于一改就牵一大片。另外像 A-G 统一转成下标这种写法,也比到处写字符判断更容易维护。
头脑风暴环节
→ 📖 Q2.5(P) 头脑风暴环节:
我们终于快要开始让程序玩游戏了!请尝试分析:T2 中不带 X 的操作记录比起实际对局多出了多少信息?如果加上 X,也就是失去了这部分信息的话,如何处理对小轮结束后状态的估计?
T2 不带 X 的记录,基本上等于把这一小轮所有出过的牌都公开了,连密约和取舍用掉的具体牌也能知道。这比真实对局多出来的信息其实很多,因为真实对局里对手的暗置牌和弃牌不应该完全透明。
如果加上 X,就不能再精确还原唯一状态了,只能做估计。比较现实的做法是先根据牌总数、已经公开过的牌和当前手牌,算出还可能剩下哪些牌,再对这些可能情况做枚举或者采样,最后得到一个“最可能的场面”或者若干候选场面。也就是说,从确定性推导变成带不确定性的推断。
总结
→ 📖 Q2.6(P) 请记录下目前的时间,并根据实际情况填写 附录 A:基于 PSP 2.1 修改的 PSP 表格 的“实际耗时”栏目。
2026-04-03 21:30
→ 📖 Q2.7(I) 请写下本部分的心得体会。
这一章是整个项目里我觉得最容易写乱的部分。因为一开始看 history 会觉得只是字符串处理,但真正写起来会发现,字符串背后对应的是场面变化,如果脑子里没有同步想清楚“这张牌现在该进谁的区域”,代码就特别容易错。结对在这里帮了很大忙,一个人盯格式,一个人盯状态变化,比单独写稳很多。
Chapter.3 道途之荆
准备
→ 📖 Q3.1(P) 请记录下目前的时间。
2026-04-06 18:50
→ 📖 Q3.2(P) 请在完成任务的同时记录,并在完成任务后整理完善:
- 浏览任务要求,参照附录 A 估计本部分预计耗时大约 210 分钟。
- 这一部分我们先承认一个现实:限时条件下很难做出特别强的策略,所以先把目标定成“保证输出合法、逻辑稳定、响应足够快”。
- 代码先判断当前是不是在响应对手的
3或4动作。如果是响应,当前实现比较直接:3就选给出的第一张牌,4就选前两张那一组。 - 如果不是响应,就开始决定我方主动出什么动作。我们会先根据
history判断自己已经用过哪些动作,再把当前手牌排个序。 - 现有实现的主动行动优先级是固定的:先尝试
2取舍,再尝试3赠予,再尝试4竞争,最后才用1密约。 - 选牌方式也比较朴素。
2是丢最小的两张,3是给出两张最小牌加一张最大牌,4是把最小和最大配成一组、次小和次大配成另一组,1留最大的那张。 - 最后又补了一个兜底分支,确保就算前面判断都没命中,也尽量返回一个语义上合法的动作字符串,避免程序空返回。
头脑风暴环节
→ 📖 Q3.3(P) 头脑风暴环节:
假设提供更充裕的时间和资源,这个游戏中你能找到的最优策略有可能是什么形式的?进行调研并总结分析。你还可以在任务结束后试着实现(不计分)。
如果时间更充裕,我觉得更像样的方向应该是“不完美信息博弈”的搜索,而不是现在这种固定规则式写法。比如可以先根据公开信息估计对手可能持有哪些牌,再做搜索或者模拟。比较理想的形式可能会接近博弈树搜索加采样评估,也就是对很多可能局面做推演,最后选整体期望更高的动作。
需求建模和算法设计
→ 📖 Q3.4(P) 请说明针对该任务,你们采取了哪些策略来优化决策。具体而言,怎么选择行动类型?选牌如何更优?如何编程实现。
如果严格按现在仓库里的实现来说,我们这版策略其实是比较朴素的启发式,还谈不上很强的“最优”。它的主要思路是:
- 先保证合法和稳定,不追求复杂搜索。
- 对响应动作,直接用固定规则尽快返回,避免超时和异常。
- 对主动动作,把手牌排序后按固定优先级出牌,这样实现简单,也比较容易保证不会把动作种类用重。
具体到代码:
2取舍优先,直接丢当前最小的两张牌。3赠予时,给出“两小一大”的组合。4竞争时,把最小和最大配一组,次小和次大配一组。1密约放到最后,用当前最大的那张牌。
这个实现的优点是简单、返回很快、出错点少;缺点也很明显,就是没有真正利用 board 去做局势判断,所以策略强度比较有限。
代码可复用性与需求变更
→ 📖 Q3.5(P) 请说明针对该任务,你们对 🧑💻 T2 中已实现的代码进行了哪些复用和修改。
T3 里也没有直接调用 T2 的函数,但复用了 T2 对 history 的一些基本理解方式,比如还是先按空格切 token,还是通过动作前缀 1/2/3/4 来判断当前记录的含义,也继续用是否带 - 来区分“出牌”还是“响应选择”。在这个基础上,T3 进一步增加了“我现在是不是该响应”“我自己已经用过哪些动作”这两层判断。
→ 📖 Q3.6(I) 请说明在编码实现时,可以采取哪些设计思想、考虑哪些设计冗余,来提高既存代码适应需求变更的能力。
我觉得这里比较重要的是给程序留兜底路径。像 T3 这种要实时返回动作的函数,如果哪一步判断漏了,程序可能就直接挂掉或者返回非法字符串。所以除了主策略外,保底逻辑也很重要。再一个就是把“响应阶段”和“主动出牌阶段”分开写,这样以后要升级策略,也能比较自然地只改其中一块。
软件度量
→ 📖 Q3.7(P) 请说明你们如何度量所实现的程序模块的有效性,例如:“如何说明我们的程序模块决策能力很强?”,尝试提出一些可能的定量分析方式或测试方式。
我们觉得至少可以从下面几方面量:
- 合法性:对很多组
history + cards + board输入,检查返回动作是否合法,不能重复使用动作,也不能使用不存在的牌。 - 稳定性:连续跑很多局,统计有没有异常退出、空返回或者明显不合语义的情况。
- 响应时间:记录每次决策耗时,看看是否稳定在题目要求的限制内。
- 对战表现:拿它去和随机策略、固定简单策略做多轮对局,统计胜率和平局率。
以我们现在这版代码来说,前两项比“绝对胜率”更重要,因为这版实现首先追求的是稳定可运行,其次才是策略强度。
总结
→ 📖 Q3.8(P) 请记录下目前的时间,并根据实际情况填写 附录A:基于 PSP 2.1 修改的 PSP 表格 的“实际耗时”栏目。
2026-04-06 21:55
→ 📖 Q3.9(I) 请写下本部分的心得体会。
这一章做完以后,能明显感觉到“能跑”和“会玩”之间差得还是挺多的。我们最后写出来的是一个比较基础的启发式版本,优点是直接,问题也暴露得很清楚:如果真的想让程序更会下,后面一定得把局势评估和搜索补上。不过从结对体验上说,这一章讨论得最激烈,也最像在一起真正写一个小型策略程序。
结对项目总结
结对过程回顾和反思
→ 📖 Q4.1(P) 提供两人在讨论的结对图像资料。

→ 📖 Q4.2(P) 回顾结对的过程,反思有哪些可以提升和改进的地方。
这次结对整体上还是比较顺的,尤其是在 T2 这种很容易把自己绕进去的地方,两个人一起对着状态变化推,比一个人闷头写好很多。不过也有明显可以改进的地方。第一,前期看题和整理数据表示时还是有点慢,如果一开始就统一好 A-G 下标、board 语义和返回格式,后面会省很多时间。第二,测试这部分做得还不够完整,很多验证是手工做的,没有及时整理成正式的自动化测试,这个地方后面应该补上。
→ 📖 Q4.3(I) 锐评一下你的搭档!并请至少列出三个优点和一个缺点。
我的搭档是陈申学佳学长,比我大一届。整个结对过程中我能比较明显感觉到他在项目经验和代码实现上都比我更稳一些。先说优点。第一是经验比较丰富,看到题目以后能比较快抓住重点,知道哪些地方要先统一数据表示,哪些地方容易出 bug。第二是代码能力比较强,很多逻辑他能很快落到代码上,尤其是在字符串处理和分支整理这种地方,推进速度很快。第三是沟通上也比较耐心,我有些地方一开始理解得不太顺,他会一边画状态变化一边解释,所以很多细节是我们讨论清楚以后再一起往下写的。
如果说缺点的话,我觉得他有时候思路转得太快了,自己脑子里已经跳到了下一步,但我这边还在跟上一层逻辑,所以偶尔需要他再停下来多解释一句。不过这个问题不大,更多还是我们配合节奏上的小问题。
对结对编程的理解
→ 📖 Q4.4(I) 说明结对编程的优缺点、你对结对编程的理解。
我现在觉得结对编程最明显的优点就是能及时纠偏。像这次项目里,很多问题不是“不会写”,而是“很容易想偏”。两个人一起写的时候,一个人负责往前推,一个人负责随时挑错,能少走不少弯路。缺点也很实际,就是两个人必须把时间对上,而且讨论本身也会占掉一部分进度。总的来说,我觉得结对编程适合这种规则明确但细节很多、又要求在较短时间里完成的任务。
代码实现提交
→ 📖 Q4.5(P) 请提供你们完成代码实现的代码仓库链接。
https://github.com/Jellyfisheep/BUAASE2026-PairProgramming#
附录
附录 A:基于 PSP 2.1 修改的 PSP 表格
| Personal Software Process Stages | 个人软件开发流程 | 预估耗时(分钟) | 实际耗时(分钟) |
|---|---|---|---|
| PLANNING | 计划 | 40 | 50 |
| - Estimate | - 估计这个任务需要多少时间 | 15 | 15 |
| DEVELOPMENT | 开发 | 500 | 540 |
| - Analysis & Design Spec | - 需求分析 & 生成设计规格(确定要实现什么) | 70 | 80 |
| - Technical Background | - 了解技术背景(包括学习新技术) | 110 | 120 |
| - Coding Standard | - 代码规范 | 20 | 20 |
| - Design | - 具体设计(确定怎么实现) | 80 | 85 |
| - Coding | - 具体编码 | 140 | 150 |
| - Code Review | - 代码复审 | 35 | 35 |
| - Test Design | - 测试设计(确定怎么测) | 25 | 25 |
| - Test Implement | - 测试实现 | 20 | 25 |
| REPORTING | 报告 | 90 | 100 |
| - Quality Report | - 质量报告 | 30 | 35 |
| - Size Measurement | - 计算工作量 | 20 | 20 |
| - Postmortem & Process Improvement Plan | - 事后总结和过程改进计划 | 40 | 45 |
| TOTAL | 合计 | 630 | 690 |
浙公网安备 33010602011771号