2026 年北航敏捷软工结对项目:募集卡
欢迎来参加 2026 年北航计算机学院敏捷软工的结对编程项目!🎉
结对编程项目的设计,意在为大家提供相对陌生的问题背景和技术场景,从而促使大家在相对集中的工作时间内和伙伴一起活用各自的经验和知识,完成一次结对编程、极限编程的体验。
在接下来的任务描述中,会出现标注为类似 🧑💻 T1 (依序编号)的编程任务,每一个编程任务会对应本代码仓库下的一个文件夹,例如 /T1 对应代码实现任务 T1 的内容。
相关编码实现请在本仓库的基础上进行。
在接下来的任务描述中,会出现标注为类似 📖 Q1.1(P/I) (依序编号,可能含子任务)的问题。
标记为 (P) 的问题,需要两个人经过相互讨论给出共同的答案:体现在提交的博客中,相关内容两个人的答案相同。
标记为 (I) 的问题,需要两个人经过独立思考给出各自的答案:体现在提交的博客中,相关内容两个人的答案不同。
请依照任务指导在完成结对编程项目的同时,作答各项问题。
问题列表位于:仓库的 question.md 文件,请不要将本文档内的作答提交到代码仓库。
每组(两人)只需提交一份,仓库至少在课程结束前开源在主流代码托管平台(例如 GitHub、Gitee、GitCode 等)。
每组(两人)需要各自提交一份,并发表在课程 cnblogs 社区。博客的内容即为对应 question.md 文件两人各自的内容。
本项目设计上是一个限时完成的项目,课程组对项目的预想总完成时间(包括编码、编码时简单作答的时间,不包括思考、后续依个人情况完善作答等难以界定的时间)为 12 小时。希望大家尽量确保连贯、成块的时间完成本结对编程项目,单次保证至少 1~2 小时的投入,推荐尽量一次至少完成一个章节。
因此,对每项任务,会提供课程组的预期实现时间供参考;同时,会要求记录每项任务的开始时间和结束时间,相关时间记录应当和代码仓库的 commit 记录相对应。
如果中间有所中断,请诚实地记录每个分段的起止时间。
对时间的要求并不会影响得分,大家如实记载即可。**一种情况例外:**由于本项目包含对各位的代码实现进行“比较”、确定“性能分”的部分,有意采用远远超出时限的大工作量来取得更好性能的工作不会获得合理工时工作以上的得分。
本项目应当以结对编程的形式完成。具体来说,期望大家采取两人线下(或线上)的方式,在一台机器上,一边讨论、一边工作,一位成员担当“驾驶员”、另一位成员担当“导航员”。
🚫 请不要采用对任务进行分解、两位成员互不沟通而各自完成某一部分的方式来完成本项目。
本项目是重视体验、而非重视结果的设计,除去占比 30% 的性能分部分,均不涉及完成结果之间的对比。余下分数以编程实现完成既定目标为主,未能达标也会根据大家的体验情况和项目投入给出较为合理(一般不低于相关任务满分 60%)的评分—— 所谓大家的“投入”,是指在博客/问题作答中体现出的结对过程。
本项目的设计是基于“无需使用 AI 代码补全、Agent、claw 以及类似的 Vibe Coding 方式编辑代码”的原则进行设计的。如果你希望使用此类内容,你需要遵守以下规则:
- 区分人类和 AI 提交:你需要保证每次 commit 只包含人类修改的代码或只包含 AI 生成的代码,并且在 commit message 上明确注明,并且仓库中必须保留每次提交记录。
- 如果你使用 Vibe Coding 方式,你需要在测试环节上格外仔细。
对于博客作业,你应当:
- 不使用 AIGC 文本对博客作业的问题进行直接作答。(引用除外,引用 AIGC 生成的文本时注明来源)
→ 📖 Q0.0(P) 【你可以在结对结束后补充】如果你的代码仓库包含 AIGC 的部分,列举使用的工具、模型和使用范围。若未使用则填写:本组提交的全部代码不包含AI补全或生成的部分。
本项目的分数上限为 150 分,如果超出了 150 分——那说明你们很厉害,但溢出分数将不计算。
| 项目 | 满分 | 预计耗时 |
|---|---|---|
| 博客文档(对各问题的回答) | 50 分 | / |
| 引入 - Guide:WebAssembly(无编程任务) | 0 分 | 60 min |
| 引入 - 寻芳之径(无编程任务) | 0 分 | 0~60 min |
| 编程任务第一部分 - 七色之缨(不涉及性能分) | 30 分 | 30~60 min |
| 编程任务第二部分 - 不祥之影(不涉及性能分) | 40 分 | 120~180 min |
| 编程任务第三部分 - 道途之荆(涉及性能分) | 40 分 | 180~360 min |
那么,请和你的搭档坐在一起,开始吧!
⏲️ 本章内容的参考完成时间为 60 分钟。
如何在 Web 场景中,为多种编程语言提供统一的运行环境而尽可能地利用其语言设计本身、工具链和生态环境所带来的效能优势?
——噔噔咚!突然问这样的问题实在是很生硬的引入,一般而言谁会去想啦!但是这里是极限编程的结对项目,请和你的搭档一起让大脑活跃起来——之后会一直保持这样的节奏!准备好 question.md,在代码仓库外复制一份,请边阅读边填写入代码仓库外的版本,或采取简记、语音备忘等方式记载较复杂问题的要点之后再补充。那么首先——
→ 📖 Q0.1(P) 请记录下目前的时间。
回到一开始的问题,或许大家很容易会想到 “😠 聊这个我可就不困了!JS,我讨厌你!” JS 作为 Web 开发的某种意义上的标准语言,在以其敏捷、多范式等等设计特点创建并不断发展 Web 生态的同时,也在通过标准化、编程语言本身及编译器和运行时的发展而不断演进。Web 应用的开发范式高速迭代,和软件工程领域的最前端紧密相连——时至今日,Web 浏览器引擎本身就已成为一套统一的运行环境和交互接口:从 ChromeOS 到小程序、再到诸如 Typora、VSCode、QQ、2023 年敏捷软工团队项目 Ficus 等等各种各样基于 Electron 的桌面应用......似乎从某一天开始,万事万物都可以“运行在浏览器里”。
然而!这并不像听起来那样美好得理所应当——即便 Google、Mozilla、Apple 等等企业和来自开源社区的力量日复一日地使用各类手段来优化其 JS 运行时的性能,似乎我们还是比想象中更快地到达了瓶颈。JS 语言以及 Web 生态的既有特性使得 Web 开发环境不成比例地倾斜向开发效率而非执行效率,而 Web 场景新的边界正将对性能的企盼以及那些沉疴痼疾不断地向外扩散——人们开始要求在浏览器里运行一段复杂的图形学算法、一个神经网络模型、又或者一个紧凑的模拟器。
等等!你当然会想“那倒是去像我们美好的过去一样,下载原生(Native)构建的软件、安装到自己的电脑上运行啊!”从开发者的角度来想,很容易观察到不同执行环境带来的额外适配成本、跨平台软件构建的开销复杂化了开发和分发,舍弃 Web 开发生态的诸多范式(例如,使用 Web 前端框架构建用户交互界面在跨平台场景下相较去和不同平台各自为政的 UI Kit 打交道,前者往往能更简便地实现统一的用户体验)更带来了许多头疼的问题;从用户的角度来想,能够通过网络分发的软件服务却要多执行一次和操作环境息息相关的安装过程,继而面临许多纷至沓来的问题——无论是使用一次还是使用多次、无论是在公用的设备还是私有的设备上......怎么想都很完蛋!Web 对软件开发及使用范式的改变如同打开的潘多拉魔盒,已经回不去了!
——Web 基础设施面对着诸多方面的需要,在各种各样的设计取舍中打转。面对这样多首的怪物,应该如何是好呢?
要是能结合 Web 和 Native 的优点就好了!
那么,要让 Web 生态、或者说一种雄心勃勃的软件运行环境的未来,受益于既有的‘原生’软件开发模式、技术栈和工具链,我们还缺少什么呢?
由 W3C 及 Mozilla、Microsoft、Google、Apple、Fastly、Intel、Red Hat 等企业提供的一种答案是,通过定义一套二进制指令格式(基本上可以说:一套字节码设计规范)和对应的基于栈式虚拟机的运行时设计规格、以及相关基础设施及生态工具链的设计协议来提供一种 Web 原生的解决方案。
这便是本项目(技术上)的主角——WebAssembly🎉!
开源社区、企业——尤其是以 Web 生态相关企业为中心的字节码联盟(Bytecode Alliance)进而根据这些方案去实现相关的工具链,包括提供运行时、开发工具链的实现、完成对上下游的适配等等。
**也许还是有些云里雾里!**简单来说,就是定义一套综合考虑多种编程语言设计的、较低层级的统一字节码,上游对接各类编程语言——实现一套编译器后端将各类语言编译为 WebAssembly(缩短一点,Wasm!)字节码,下游对接目前的 Web 运行时——实现浏览器引擎的 Wasm 执行能力、或者更激进一些实现脱离浏览器的独立 Wasm 运行时。这样的好处显而易见——我们可以在 Web 生态中整合多种编程语言、生态系统、开发工具和开发范式的优势了!
eg. C/C++ 语言和工具链能够适应某些更性能敏感任务的开发需要,那么我们就用 C/C++ 来实现,然后编译到 Wasm 并在既有的(主要基于 JS 的)代码库基础上通过调用 Wasm 运行时来使用它,进而得益于这种性能提升。
得益于一套统一的基础,对更多编程语言及其生态的支持也就得以花费相对小的成本进行。在这样一套新的基础设施上,开发人员能够跳出 JS 的性能泥淖,但又可以保持在既有的生态中,实现渐进的转移。由此,我们得到了 Web 崭新的能力底座!
哪怕是 JS 语言本身,也因此获得了发挥长处而不是继续不堪重负的好去处。让合适的语言去合适的地方吧!在合适的应用场景,如性能不敏感的程序逻辑描述、处理各程序模块交互的胶水代码等领域,开发人员依然可以充分利用 JS 的优势。
在本次的结对项目中, Wasm 将站在我们的舞台中心!
→ 📖 Q0.2(I) 【你可以在结对结束后另行补充。】作为本项目的调查:
请如实标注在开始项目之前对 Wasm 的熟悉程度分级,可以的话请细化具体的情况。(分别回答两人各自的情况)
I. 没有听说过;
II. 仅限于听说过相关名词;
III. 听说过,且有一定了解;
IV. 听说过,且使用 Wasm 实际进行过开发(即便是玩具项目的开发)。
请如实标注在开始项目之前对桌游花见小路的熟悉程度分级,可以的话请细化具体的情况。(分别回答两人各自的情况)
I. 不了解玩法和规则;
II. 听说过,且有一定了解;
接下来的任务需要你和你的搭档:
- 使用任意语言编程实现列出的需求;
- 使用相关工具链将代码编译到 Wasm;
- 使用相关工具链生成或手工编写 JS 胶水代码,导出封装好的 Wasm 中你实现的函数;
- 调试代码,确保你的实现能够对接课程组提供的测试用 JS 代码,这些代码将在 Node.js 的当前 LTS 版本 (now at v22.14.0) 运行时上运行。
由于目前支持 Wasm 作为编译目标的编程语言及工具链种类繁多,你可以参照 Wasm 官方文档 选择支持的语言进行尝试。**代码实现只要能通过课程组提供的提交测试 JS 代码,都可以接受作为提交。**然而,为了降低实践难度,课程组为以下两种编程语言提供额外的支持,为 C++ 提供更为有限的支持。你的选择对项目进行的影响如下表所示:
| 所选用的编程语言 | 课程组支持情况 | 对任务的影响 |
|---|---|---|
| AssemblyScript | 支持:提供资料,有限答疑(资料范围内) | 任务设计的难度基线 |
| Rust | 支持:提供资料,有限答疑(资料范围内) | 任务设计的难度基线 |
| C/C++ | 部分支持:提供更为有限的资料,不提供任何答疑 | 由于工具链、语言和运行时设计等原因,完成任务的难度相对会更高,请谨慎选择 |
| 能够完成需求的其他编程语言 | 不提供参考资料支持,需要自行了解和学习 | **挑战性的:**需要自行解决可能遇到的问题 |
请注意,对编程语言及工具链的选择,可能影响程序实现的性能表现。
课程组在课程社区提供了指引来帮助大家在短时间内入门 WebAssembly,请各位根据自己的情况选择合适的编程语言。相关代码已位于代码仓库 /G 内。
阅读完成 Guide 后——
→ 📖 Q0.3(P) 请记录下目前的时间。
结对项目:贾帐伎七人
繁华与风雅汇聚的大江户,「花见小路」。
少女们的相遇,
以及为离别而设的舞台,就此拉开序幕——
大正年间的某个春日。
在花见小路经营着没落料理亭「桔梗亭」老板,我,
过着努力兼顾揽客和算账,超级忙碌的每一天。
我每天的慰藉,便是驻足于自家庭院的垂枝下,静静眺望那条石板路上往来不息的艺者身影。
在这条任何一家料理亭都渴望着谁的垂怜的花见小路上,他一面为熟客安排座次,一面在茶屋之间小心周旋,勉强维持着家业的体面。
就在某一次前往茶屋赴约的夜路上,我发现了一把落在地上的樱花纹和纸伞。
在月光下拾起时,竟泛起幽幽萤光,随后,一位粉色头饰的少女在伞后现身。
或许是来自月亮的公主。
不忍将她丢在夜路上,我把少女带回了店,而少女在镜台前梳妆罢,转身面向你。
“嗨多摩(真是非常感谢)!请叫我爱酱就好!”
任性又聪慧的少女的身影出现在桔梗亭。
在爱酱的请求(任性)下,我决定帮助她在花见小路的演出活动。
我作为老板张罗座敷,而爱酱则以舞与歌声款待来客,
二人就这样一点一点地靠近彼此心中深藏的念想。
我们不知道的是,别有所图的身影,已经悄然藏匿在花见小路的灯火之下——
这是,一个前所未见的「花见小路」的故事。
本故事纯属虚构。提及的人物、地点、事件和现实存在的人物、地点、事件之间没有任何关系。
如有雷同请装作不知道。
⏲️ 本章内容的参考完成时间为 0~60 分钟。本章主要是规则讲解,没有需要完成的任务。你可以在阅读规则一节后直接跳到 Chapter.1。
本次结对项目改编自桌游花见小路,若下列规则与原桌游规则有出入,欢迎向助教反馈,但结对任务仍以此文档为准。
桌游《花见小路》,设计师为Kota Nakayama,游戏适合人数为2人,也就是必须要2人,是个双人博弈的博弈。游戏时间大约为15分钟,游戏机制包含了行动规划、区域控制和手牌管理等。桌游《花见小路》是个小盒的轻策游戏,在游戏里你是知名高级料理店的老板,为了争取七名最好的艺伎,你要与其他老板进行竞争。用她们喜欢的物品来吸引她们的芳心。率先赢得四名艺伎或者总魅力值达到十一的玩家,将获得游戏的胜利。游戏共有七名艺伎,分别喜欢七种不同的物品。(引自游卡桌游圈)
花见小路的核心资源是 7 枚得分标记和 21 张物品牌。每张牌初始时处于抽牌堆,在回合开始时进入手牌,在每回合的行动中进入桌面双方玩家的区域,或进入弃牌区。7 枚得分标记分别对应 7 名角色,我们将其记为 ABCDEFG。21 张牌分别是 ABC 各 2 张,DE 各 3 张,F 共 4 张,G 共 5 张。
一局游戏由数个小轮构成,最多进行三小轮,每一小轮执行内容如下:
- 发牌:将抽牌堆 1 张牌背面朝上弃掉,随后双方各抽 6 张牌。
- 行动(轮流执行). 抽 1 张牌,然后从下列 4 个行动中选一个执行:
- 密约:将 1 张牌背面朝上放在自己的区域,这张牌会在计分阶段翻开并进行计分;
- 取舍:将 2 张牌背面朝上弃掉,本回合不进入计分;
- 赠予:将 3 张牌展示给对手,对手选一张放在对手区域,剩下的放在自己的区域;
- 竞争:将 4 张牌分为两组(两张为一组),对手选一组放在对手区域,另一组放在自己区域。
每次只能执行不同行动,双方各自执行了 4 个动作后,抽牌堆、玩家的手牌恰好耗尽。小轮结束,进行结算。
- 计分(小轮结算):有 ABCDEFG 七个角色,其七个倾心标记(得分标记)初始放在中间(中立位置)。小轮结算时,亮出背面朝上放置于区域内的牌(密约牌)。如果你或对手区域内的 ABCDEFG 某一种牌多于对方,将其角色的倾心标记从对手区域或中立位置移动到你的区域。平手时倾心标记位置不变。
每个角色的倾心标记的分数和其对应发卡牌数量一致:ABC 两分,DE 三分,F 四分,G 五分。如果一方获得 11 分,立即赢得胜利。如果一方获得 4 个倾心标记,且对方没有 11 分,赢得胜利。如果这是第三小轮:分数更高的玩家赢得胜利,若分数一致,拥有最高分倾心标记(G>F>E=D>C=B=A)的玩家获胜。 - 更新:若尚无玩家获胜,收起每张卡牌,倾心标记位置保持不变,重新洗牌,交换先手,并继续下一小轮。
在非常非常罕见的情况下,第三小轮结束时无人达成胜利条件,游戏会平局。
这里是规则讲解视频
你也可以找课程组借用此桌游。
从那个少女踏入店门的那天起。
原本寂寥的「桔梗亭」,仿佛被施予了结缘的魔法。
——如何也无法预测的,热闹非凡的异变。
起初,只是偶尔在庭院深处回荡的、清脆如铃的歌声。
那是擅长用嗓音捕捉风之轨迹的阿包,仅仅凭一曲流萤,便留住了往来食客的脚步。
然而,事态很快便朝着“不可思议”的方向疾驰而去——
明明身负骇人的巨大镰刀,却意外地有着一颗温柔之心的死神,卡利俄珀。不知何时起,她竟将店内的阴影处当成了专属的避暑地。
而在后厨那满溢着甜香的储物间里,总能窥见少女黛安娜的身影,抱着刚摘的草莓,露出如梦似幻般的烂漫笑容。
每当我打开店门,都仿佛误入了一场盛大的幻梦。
“我可是,最最邪恶的存在哦!”
总是叉着腰、宣称要征服大江户的阿邪,其实只要轻轻抚摸她的发顶,便会瞬间卸下伪装,变成软绵绵的模样。
在那身侧敏捷跳跃着的白色身影,是坚称自己不是福瑞的菲利安。她总能以华丽的后空翻,接住那些摇摇欲坠的茶杯,惊起一阵阵欢呼。
至于那位驻足于水池边、静静凝视着游鱼的古拉……不必再提往事,此刻她眼中映出的,似乎只剩下店里新进的鲜鱼。
冰冷的算珠声被欢笑掩盖,
原本为了生计而日复一日精打细算的我,
如今,光是凝视着这七位少女在屋檐下交织的笑颜,心中便被某种从未触碰过的温热感所填满。
七色的缘分之丝,编织成了此处最喧嚣、却也最令人眷恋的归宿。
⏲️ 本章内容的参考完成时间为 30~60 分钟。
→ 📖 Q1.1(P) 请记录下目前的时间。
→ 📖 Q1.2(P) 请在完成任务的同时记录,并在完成任务后整理完善:
- 浏览任务要求,参照 附录A:基于 PSP 2.1 修改的 PSP 表格,估计任务预计耗时;
- 完成编程任务期间,依次做了什么(例如查阅了哪些资料、如何设计判定逻辑、如何设计测试样例、遇到了什么问题、如何解决)。
代码有进展即进行 commit,commit log 不合理的项目会被助教抽查询问项目细节。
请按照你所选择的编程语言,在 /T1 内新建文件夹 t1-{PL},其中分隔符和 {PL} 请替换为你所选择的编程语言名称,例如 /T1/t1-as,并在其中完成接下来的代码实现。
在前一章中,你已经了解了《花见小路》的基本规则;为了在最后的任务中,编写能够真正参与对局的策略程序。这一章里,你要先完成一个更小、也更明确的模块:根据当前倾心标记的分布,判断这一局游戏是否已经分出胜负。
这个模块不关心玩家是如何出牌的,也不关心本小轮内每张牌是如何分配的。你只需要站在“小轮结算已经完成”的时间点上,根据 7 枚倾心标记的位置和当前小轮数,判断:
- 是否已经有人获胜;
- 如果已经获胜,获胜者是谁;
- 如果尚未获胜,当前是否仍应继续游戏;
- 如果已经来到第三小轮,且仍然无法分出唯一胜者,是否应判为平局。
为避免重复,本节只保留与胜负判定直接相关的规则。完整的游戏背景、行动方式与小轮流程,请以前一章为准。
游戏中共有 7 枚倾心标记,分别对应角色 A B C D E F G。
每枚倾心标记都代表一位角色的当前倾向,其分值与该角色对应礼物牌的总数量一致:
A、B、C:各2分;D、E:各3分;F:4分;G:5分。
每枚倾心标记只会处于以下三种状态之一,我们这样记录:
1:该角色当前倾向于你;-1:该角色当前倾向于对手;0:该角色仍保持中立。
当某个小轮结算完成后,如果满足以下任意一种情况,则游戏立即结束:
- 某一方获得的倾心标记总分值达到或超过
11分; - 某一方获得的倾心标记数量达到或超过
4枚,且另一方没有达到11分。
如果上述条件都不满足,则:
- 当
round为1或2时,游戏尚未结束,应继续进入下一小轮; - 当
round为3时,需要进行第三小轮结束后的最终判定。
如果游戏进行到第三小轮结束时,仍然没有触发立即胜利条件,则继续按照以下顺序判定:
- 比较双方获得的倾心标记总分值,总分更高者获胜;
- 如果总分相同,则比较双方所拥有的最高分倾心标记,优先级为:
GFD / EA / B / C
- (按分数依次判定)若在某一档位上,有一方拥有该档位中的至少一枚倾心标记,则该方获胜;
- 如果双方在最高非空档位上仍然无法区分,则本局判为平局。
你要设计一个函数实现胜负判定。函数命名为 hanamikoji_judge()、HanamikojiJudge(),或参考你所用语言的命名风格选择合适的命名。
-
当前倾心标记状态
board- 7 个
int8类型元素的数组int8[7],按A B C D E F G的顺序给出 7 枚倾心标记的状态; board[i]的取值只会是1、0或-1;- 其中:
1表示对应角色倾向于你;-1表示对应角色倾向于对手;0表示对应角色保持中立。
例如:
[1, 1, 0, 0, 0, -1, 0]表示:你得到了
A、B两位角色的倾心;对手得到了F的倾心;其余角色保持中立。 - 7 个
-
当前小轮数
round- 一个
int8数值,取值为1、2或3; - 表示当前完成结算的是第几小轮。
- 一个
- 一个
int8数值,表示判定结果:1:你获胜;-1:对手获胜;0:当前尚未分出胜负,进入下一小轮;2:第三小轮结束后判定为平局。
- 本任务只负责“判定胜负”,不涉及其他行动;
- 本任务的输入已经是小轮结算后的倾心标记状态;你不需要根据双方区域内的牌数去更新这些标记。
- 你可以考虑设计辅助函数,例如:
- 统计一方当前获得的标记数量;
- 统计一方当前获得的总分;
- 提取一方拥有的最高档位倾心标记。
→ 📖 Q1.3(P) 请说明你们为这个判定模块设计了哪些中间量或辅助函数;如果没有额外设计,也请说明为什么认为直接实现已经足够清晰。
→ 📖 Q1.4(I) 请说明在这样一个规则判定类模块中,如何避免“漏判”“错判”或分支顺序错误等问题。
你和你的搭档需要为这个判定函数设计测试用例,并实现最基本的自动化测试或驱动代码。这是本章的必做内容。
至少应覆盖以下几类情况:
- 一方分值达到
11分而获胜; - 一方获得至少
4枚倾心标记而获胜; - 前两小轮结束时尚未满足胜利条件,应返回
0; - 第三小轮结束时,总分不同,由总分高者获胜;
- 第三小轮结束时,总分相同,由最高档位倾心标记判定胜负;
- 第三小轮结束时平局,应返回
2。
如果你的实现支持更多测试,建议进行补充,例如输入非法时的应对等。
→ 📖 Q1.5(P) 请说明你们设计了哪些测试用例,这些测试分别覆盖了哪一类规则或边界情况。
→ 📖 Q1.6(I) 请说明你对“先写测试再实现”与“先实现再补测试”两种方式的理解。
对于所有代码任务,课程组将主要从以下方面进行评价:
- 正确性:是否能够对输入返回正确结果,按测试点通过率计分;
- 代码质量:命名是否清晰,结构是否易读,仓库中的记录是否清晰合理,应当提交的、不应当提交的文件是否正确区分等。经检查确认存在问题的,将扣除一定比例的分数,10%起。包括但不限于:
- 提交记录和声明的缺失
- 没有上传完整代码、构建配置文件
- 在 ddl 前公开或泄露了代码仓库
- 其他让人看了虎躯一震的问题
本部分通过测试用例,对大家程序实现的正确性进行评价,按照通过的测试用例数占测试用例总数的比例给予相同比例的分数。
满分 30 分。
→ 📖 Q1.7(P) 请记录下目前的时间,并根据实际情况填写 附录A:基于 PSP 2.1 修改的 PSP 表格 的“实际耗时”栏目。
→ 📖 Q1.8(I) 请写下本部分的心得体会。
然而,繁华之下,总是涌动着名为「强欲」的暗流。
那是某个被残阳染红的黄昏。
通往码头的窄巷中,我捕捉到了一丝不寻常的悸动。
顺着那阵冰冷的低语望去。在被夕阳拉长的、如同枯爪般的阴影里,一个男人的轮廓逐渐清晰。
那是花见小路另一端、立于顶点的有名料亭的主人。
那与我有着相似出身的家伙的眼神咬住了我、瞳孔如猎手般闪闪发光。
此刻,他正拦在爱酱回店的归途之上。
在那张无懈可击的谄媚笑脸后,是堆积如山的极品丝绸、镶嵌着金箔的茶器,以及传世的和歌绘卷。
他正试图用这些炽热而冰冷之物,去买断少女们的双眸,将她们那纯粹的羽翼,化作自家店铺敛财的招牌。
我藏匿于暗处,感受着指尖传来的轻微战栗。
在这条花见小路上,虽无刀光剑影,却充斥着更为残酷的、关于“灵魂”的博弈。
「怎能将这份羁绊交给那种家伙......」
在炽热的晚风中,无声对决的帷幕悄然拉开。
⌛本章内容的参考完成时间为 120~180 分钟。
→ 📖 Q2.1(P) 请记录下目前的时间。
→ 📖 Q2.2(P) 请在完成任务的同时记录,并在完成任务后整理完善:
- 浏览任务要求,参照 附录A:基于 PSP 2.1 修改的 PSP 表格,估计任务预计耗时;
- 完成编程任务期间,依次做了什么(比如查阅了什么资料,随后如何进行了开发,遇到了什么问题,又通过什么方式解决);
在前两章中,你已经了解了游戏的基本规则,并且明确了如何判断游戏的胜负。现在,我们将模拟一小轮游戏的进行过程。
游戏将至多进行三小轮,每一小轮都将依次经历下面三个阶段:初始状态,对弈阶段,结算阶段。
- 初始状态下,倾心标记均保持中立;在后续轮次中,倾心标记由上一轮结束状态决定。
- 初始状态下,物品牌库将进行洗牌,并从牌库中随机暗置移除一张牌(本轮不使用),然后两位玩家从物品牌库中各抽 6张 牌。
- 每位玩家都有四个待执行的行动:1.密约、2.取舍、3.赠送、4.竞争,行动名前的数字也是该行动中消耗的卡牌数量。
双方交替行动,在当前玩家的回合内,将会按照以下几个步骤进行
- 摸牌:在回合开始时,当前玩家从牌库顶摸取一张牌,此时可以看到所有手牌。
- 回合内行动:当前玩家需要从尚未使用的行动(每种行动在单轮中仅能使用一次)中选择 1 种,并选择使用的卡牌。
- 响应:如果本回合选择了3.赠送(给出3张牌)或4.竞争(给出两组各2张牌),此时对手的程序将会被唤醒,对手需要做出“挑选其中一组”的决策。
- 轮次结算:当双方的 4 个行动全部用完(此时牌库抽空,双方手牌打空),本轮结束,亮出1.密约行动背面朝上暗置于区域内的牌,结算倾心标记,并根据
T1中的胜负判定规则结算本轮次的胜负。 - 决胜:如果本轮有任何一方符合胜利条件,游戏立即结束。
- 更新:如果本轮结束时无人达成获胜条件,且不是第三轮,游戏将进入下一轮,此时将交换行动先后手。保留当前的好感度标记位置,清空物品牌重新洗牌、发牌。
请根据以下要求及参考资料,完成任务。
本任务的全部源代码应该存放于
/T2。代码有进展即进行
commit,commit log不合理的项目会被助教抽查询问项目细节。
你需要设计一个函数来根据本轮对局记录 history 和本轮开始前的得分标记 board,计算出行动完的局面情况,函数名为 calc_current_state、calcCurrentState、或参考你所用语言命名风格的合适命名。
-
本小轮的对局记录
history:-
类型:一个字符串,这个字符串由 8 段字符串组成,各字符串之间用空格分割,代表本小轮双方玩家的行动记录,各条行动记录之间用空格分隔。
-
行动记录:行动类型号+使用的手牌id(A-G)。
-
行动类型号:取值范围是 1、 2、 3、 4,代表这次行动消耗的手牌数
-
使用的手牌id:一串大写字母,代表这次行动使用的手牌 ID,用
X表示未知的卡牌。除了进行竞争操作外,手牌 ID 的顺序是任意的。响应3赠予或4竞争的选择的记录用-连接附在行动后,不会单独形成一次行动。下表给出了所有可能的行动记录类型和含义。行动记录 含义 1E 将1张E背面朝上置于自己区域 1X 对方背面朝上放置了1张牌,你不知道是什么 2BC 背面朝上弃置BC2张牌 2XX 对方背面朝上弃置了2张牌,你不知道是什么 3BCC 对方提供了BCC,你可以选择其一 3BCC-C 某一方提供了BCC,另一方选择了C 4ACBD 对方提供了AC、BD两组可选牌(前两张一组,后两张一组) 4ACBD-BD 某一方提供了AC、BD,另一方选择了BD 需要注意的是,在 T2 中,输入的是游戏进行完整一小轮的对局记录,在游戏最后,所有玩家密约和取舍使用的牌都会公开,因此 T2 的输入中不会有形如
1X和2XX的行动记录,但是在 T3 中会出现。
-
-
一小轮牌局的记录与行动的示例如下表所示,最后的
2GG 2FF 1D 3EEG-E 3AFF-F 1C 4ABEG-AB 4BCDD-DD就是输入的history玩家 对局记录 history操作 P1 空 2GG (取舍) P2 2XX 2FF (取舍) P1 2GG 2XX 1D (密约) P2 2XX 2FF 1X 3EEG (赠送) P1 2GG 2XX 1D 3EEG -E (选择 E) P1 2GG 2XX 1D 3EEG-E 3AFF (赠送) P2 2XX 2FF 1X 3EEG-E 3AFF -F (选择 F) P2 2XX 2FF 1X 3EEG-E 3AFF-F 1C (密约) P1 2GG 2XX 1D 3EEG-E 3AFF-F 1X 4ABEG (竞争) P2 2XX 2FF 1X 3EEG-E 3AFF-F 1C 4ABEG -AB (选择 AB) P2 2XX 2FF 1X 3EEG-E 3AFF-F 1C 4ABEG-AB 4BCDD (竞争) P1 2GG 2XX 1D 3EEG-E 3AFF-F 1X 4ABEG-AB 4BCDD -DD (选择 DD) 一小轮结束
公开所有牌(不存在X)2GG 2FF 1D 3EEG-E 3AFF-F 1C 4ABEG-AB 4BCDD-DD
-
-
得分标记
board:- 类型:7 个
int8类型元素的数组int8[7]。 - 代表小轮开始前 A-G 共 7 种倾心标记的状态,数组每个元素有
-1,0,1三个取值,1表示倾向自己,-1表示倾向对手,0表示中立。 - 例如
[1, 1, 0, 0, 0, 0, -1]表示当前 A 和 B 倾向自己,G 倾向对手,其他的保持中立。
- 类型:7 个
- 返回:一个二维数组 int[3][7],代表行动完成后,当前的场面情况。第一行和第二行分别表示当前自己和对手场面上每张牌的张数,第三行表示小轮结算后的得分标记。
- 牌数:7 个
int8构成的数组,依次代表 A-G 这 7 种牌的张数。这里的场面上每张牌的张数指的是,通过历史行动,可以推断出的双方区域上的每张牌的张数。比如在回合开始,你选了3BCC(提供 1 张 B 和 2 张 C,让对手选一张),对手选择了3BCC-C(对手选择了一张 C),那么可以据此可以推断当前你有 1 张 B 和 1 张 C,对手有 1 张 C。 - 得分标记与上文中参数
board定义保持一致。 - 例如,下面的输出代表自己场上有 1 张 B 和 2 张 D,对手有 3 张 G,当前 A 和 B 倾向自己,G 倾向对手,其他的保持中立。
[ [0, 1, 0, 2, 0, 0, 0], [0, 0, 0, 0, 0, 0, 3], [1, 1, 0, 0, 0, 0, -1] ] - 牌数:7 个
→ 📖 Q2.3(P) 请说明针对该任务,你们对
🧑💻 T1中已实现的代码进行了哪些复用和修改。
→ 📖 Q2.4(I) 请说明在编码实现时,可以采取哪些设计思想、考虑哪些设计冗余,来提高既存代码适应需求变更的能力。
当你完成全部代码编写、测试任务后,请切换到目录 /T2;**参照注释,根据你选择的语言/实现方式修改 test.js 开头的相关引入代码,只允许修改和你的选择相关的那一行,其他代码不允许修改!**然后,执行以下命令来完成提交前的测试:
npm run submit-test如果测试输出:
🎉 You have passed all the tests provided.说明你的代码实现已能被接受作为有效提交。
满分 40 分。
本部分通过测试用例,对大家程序实现的正确性进行评价,按照通过的测试用例数占测试用例总数的比例给予相同比例的分数。
→ 📖 Q2.5(P) 头脑风暴环节:
我们终于快要开始让程序玩游戏了!请尝试分析:T2 中不带 X 的操作记录比起实际对局多出了多少信息?如果加上 X,也就是失去了这部分信息的话,如何处理对小轮结束后状态的估计?
→ 📖 Q2.6(P) 请记录下目前的时间,并根据实际情况填写 附录 A:基于 PSP 2.1 修改的 PSP 表格 的“实际耗时”栏目。
→ 📖 Q2.7(I) 请写下本部分的心得体会。
自那一晚后。
花见小路的空气中,开始弥补着某种令人窒息的冰冷的气息。
那料亭挂起了数倍于往常的长明灯,那耀眼的灯火,试图将「桔梗亭」门前最后的一抹月光也悉数吞噬。
原本熟悉的茶屋老板们,在石板路上与我擦肩而过时,眼神中多了一丝晦暗的闪躲。
那一封封印着对方家纹的邀请函,如同雪片般飞向每一位曾驻足于此的豪客。
对方正动用着积攒了数代的家底,在这条街上布下了一张密不透风的网。
深夜,我独自坐在账房内,指尖在算珠上迟疑。
原本为了生计而拨动的清脆声响,在寂静的夜里,一声声敲响。
那一叠叠厚重的账本背后,是无法数清与算尽的、属于少女们的笑声。
——若是失去了这一方净土,那七色的丝线,是否也会随之断裂?
「又或许,想守护这份繁华,本身就是一种傲慢吧……」
我推开了窗。
后院依然隐约传来不知是谁的细小鼾声,以及木屐偶尔敲击石阶的余音。
当第一缕晨曦刺破薄雾。
我整了整略显褶皱的羽织,将写满决意的名帖收入怀中。
⏲️ 本章内容的参考完成时间为 180~360 分钟。
→ 📖 Q3.1(P) 请记录下目前的时间。
→ 📖 Q3.2(P) 请在完成任务的同时记录,并在完成任务后整理完善:
1. 浏览任务要求,参照 附录A:基于 PSP 2.1 修改的 PSP 表格,估计任务预计耗时;
2. 完成编程任务期间,依次做了什么(比如查阅了什么资料,随后如何进行了开发,遇到了什么问题,又通过什么方式解决);
代码有进展即进行 commit,commit log 不合理的项目会被助教抽查询问项目细节。
请按照你所选择的编程语言,在 /T3 内新建文件夹 t3-{PL},其中分割符和 {PL} 请替换为你所选择的编程语言名称,例如 /T3/t3-as,并在其中完成接下来的代码实现。
你要设计一个函数实现棋手的出牌与决策。函数命名为hanamikoji_action()、HanamikojiAction() 或类似命名。
-
一条对局记录
history-
string类型,对任意一方玩家的对局记录由n(n为双方已进行的本轮回合数,即最大为8)段字符串组成,中间用空格分割。
-
行动记录:行动类型号+使用的手牌id**(A-G),响应3赠予或4竞争的选择**的记录用
-连接附在行动后,更详细的说明参考T2。
-
-
手牌
cards-
string类型,长度为n,表示所拥有的n张手牌。
例如:
AABCDG你拥有角色A礼物*2与B、C、D、G礼物各一
-
-
得分标记
board-
一个 int8[7]类型数组,表示每种倾心标记的当前状态(倾向自己:1,倾向对手:-1,中立:0)。
例如:
[1,1,0,0,0,-1,0]你得到了A B 两位的倾心,对手得到F的倾心,其余角色保持中立态度
-
-
你的行动
-
string类型
-
行动:数字代表消耗的卡牌数,随后是使用的手牌id (A-G)。如果发生了选择,用 '-'标记 被选中的牌。(手牌id的排列是任意的)
-
可能的输出举例:
1E:将1张E背面朝上置于自己区域
2BC:背面朝上弃置BC2张牌
3BCC:提供了BCC
-C:我方选择了C
4ACBD:提供了AC、BD两组可选牌
-BD:我方选择了BD
行动(一个字符串):你要打出或选择的每张卡牌编号,包括前缀的数字或横杠。横杠代表选择这些牌,选择后仍将是你的回合。
-
一次牌局的记录与行动如:
P1: 记录="" // P1行动=3FGG (赠送)
P2: 记录="3FGG" // P2行动=-G (选择)
P2: 记录="3FGG-G" // 行动=1F (密约)
P1: 记录="3FGG-G 1X" // 行动=4AEBE (竞争)
P2: 记录="3FGG-G 1F 4AEBE" // 行动=-BE (选择)
P2: 记录="3FGG-G 1F 4AEBE-BE" // 行动=...
→ 📖 Q3.3(P) 头脑风暴环节:
假设提供更充裕的时间和资源,这个游戏中你能找到的最优策略有可能是什么形式的?进行调研并总结分析。你还可以在任务结束后试着实现(不计分)。
→ 📖 Q3.4(P) 请说明针对该任务,你们采取了哪些策略来优化决策。具体而言,怎么选择行动类型?选牌如何更优?如何编程实现。
→ 📖 Q3.5(P) 请说明针对该任务,你们对 🧑💻 T2 中已实现的代码进行了哪些复用和修改。
→ 📖 Q3.6(I) 请说明在编码实现时,可以采取哪些设计思想、考虑哪些设计冗余,来提高既存代码适应需求变更的能力。
当你完成代码编写任务后,请切换到 /T3 目录下:
- 联系其他已完成 🧑💻 T3.2 的小组,请他们提供编译出的 Wasm 模块及 JS 胶水代码(不允许互相交换源代码!),将相关文件粘贴到新建文件夹
t3-hanamikoji-{num}-{PL}。如果实在联系不上其他小组,请将自己完成的代码,复制几份存放在新建文件夹t3-hanamikoji-{num}-{PL}下; - 参照注释,根据你的编程语言/实现方式修改
test.js开头的相关引入代码,以及测试的参数;- 引入双方的代码,并记住编号;
- 修改下面的测试参数,以适应你的测试;
- 然后,执行以下命令来完成提交前的测试:
npm run submit-test如果测试输出如下,说明你们的代码实现已能被接受作为有效提交。
=== FINAL RESULTS ===
Hanamikoji winner: X
board: A---B---C---D---E---F---G
1---2---1---1---2---2---1满分 40 分,基础得分包含此两部分:
- 正确性:能够与特定的对手完成对局,不考虑胜负(10)。
- 性能分:对全部结对小组的程序进行重复双循环赛,根据胜率排名获得性能分(30)。
- 牌堆:每个数据点中,将会以随机的方式生成三个牌堆,作为三小轮的牌堆。
- 双循环赛:对于每个数据点,任何两个小组都会相遇两次,作为首轮先手和后手各进行对局。
- 胜率的统计:至少重复五轮双循环赛后按照净胜场计算胜率。若条件允许,将尽可能重复比赛直到排名稳定。
- 每次回合行动中,你最多有 2 秒的时间进行决策。计时以评测时设备(会尽量找个性能好一点的)的计时为准,请保留足够的冗余。
- 如果在对局中发生超时或异常情况,本局将判负。如果超时或异常情况的出现频率和造成的副作用过大,严重影响评测,将不参与排名。
正确性的 10 分基础分根据测试点计算,性能分规则如下:
- 排名最高的队伍获得全部 30 分,第二名获得 29 分,第三名获得 28 分,以此类推直至 1 分,此后均为 1 分。无法参与排名的计 0 分。
- 以上规则无法决定部分队伍的名次时,相应队伍将平分分数。
代码质量的扣分将对本章节每个分数部分生效。
→ 📖 Q3.7(P) 请说明你们如何量度所实现的程序模块的有效性,例如:“如何说明我们的程序模块决策能力很强?”,尝试提出一些可能的定量分析方式或测试方式。
→ 📖 Q3.8(P) 请记录下目前的时间,并根据实际情况填写 附录A:基于 PSP 2.1 修改的 PSP 表格 的“实际耗时”栏目。
→ 📖 Q3.9(I) 请写下本部分的心得体会。
- 平等王:获得最多平局的队伍将获得课程组准备的神秘小礼物。
- 创意赛道:若实现了让人眼前一黑和眼前一亮的 T3 思路,或许会获得神秘小礼物。
(神秘小礼物的内容和发放方式以课程组通知为准)
宣告春日的终焉的,是一场盛大得有些寂寥的樱吹雪。
漫天飞舞的花瓣,将整个花见小路妆点成了离别的、亦是重逢的祭典。
在长街的尽头,那场赌上尊严的战斗终于落下了帷幕。
面对我所呈出的最后一份“诚意”,以及我身旁少女的身影……
那个男人的底牌,彻底粉碎了。
他引以为傲的财力与算计,在少女们那嬉笑怒骂间建立起的、坚不可摧的真心面前,显得如此苍白。
樱花飘零的间隙,落日余晖将他的影子拉得格外修长。
他定定地凝视着我们,良久,发出了一声如释重负般的叹息。
“……我输了啊。”
没有多余的辞令。他只是洒脱地挥了挥手,随即转身。
那个曾给予我无限压力的宿敌,就这样在众人的注视下宣布了投降,踏上了离开江户城的远行之路。
我没有追上去,只是静静地注视着那个背影消失在长街的转角。
风,渐渐停息了。
阿邪轻轻扯动我的衣角,卡利俄珀将镰刀扛在肩头、露出了罕见的柔和微笑,而古拉则发出一阵欢呼,奔向了飘出饭香的厨房。
我回过头,仰望着熠熠生辉的「桔梗亭」招牌。
深深地吸了一口,这属于大江户傍晚、微凉且清彻的空气。
残阳余晖中,由山峦连绵而下,绚烂樱花色。
→ 📖 Q4.1(P) 提供两人在讨论的结对图像资料。
→ 📖 Q4.2(P) 回顾结对的过程,反思有哪些可以提升和改进的地方。
→ 📖 Q4.3(I) 锐评一下你的搭档!并请至少列出三个优点和一个缺点。
→ 📖 Q4.4(I) 说明结对编程的优缺点、你对结对编程的理解。
→ 📖 Q4.5(P) 请提供你们完成代码实现的代码仓库链接。
| 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 | 合计 |
本项目由 2026 级北京航空航天大学计算机学院敏捷软工课程组设计。
项目设计:swx、rcx、lsm;审校:zyt;其他:感谢喜欢术力口的大哥哥kuma、red、以及其他朋友们对项目的关心和支持!