交互案例 · 规则级复盘
一款四人社交推理游戏可以在引擎层完全自洽,却依然不具备上线条件。在 v2.0.0 的这次复核里,引擎回归是绿的——11/11 条断言全部通过——但玩家看到的 规则书、操作控件和引擎实际执行的是三套不同的游戏。两处错位是玩家可见的阻断 项,一处是精度问题,一处是尚未定论的措辞问题。真正值得记录的不是崩溃,而是 静默的信息差:每一层单独看都合理,所以当玩家围绕一个引擎永远不会支付的 数字做规划时,系统不会抛出任何异常。
《罪恶摇摆:白垩与黑铁》是一款 H5 社交推理游戏:1 名真人 + 3 名 AI,四张面具 无放回分配,五轮“公开发言 + 暗中行动”,黑白天平随每次选择摇摆。嫌疑值 clamp 在 [0, 100],达到 100 即被处刑;只有存活者参与最终排名。
上线材料包含四份文档:
| 文档 | 它定义的真相层 |
|---|---|
01_角色与规则设定.md | 玩家在角色揭示、规则教学和商店页读到的内容 |
02_核心数值与结算引擎.md | 引擎实际计算的内容 |
03_三剧本回归测试记录.md | 自动回归覆盖了什么,以及明确不覆盖什么 |
04_发行说明.md | v2.0.0 改了什么,以及哪些事项被声明为就绪 |
src/App.jsx 里还有一份引擎与玩家可见角色文案的内联副本。回归记录明确写了: 这份内联副本不与 src/gameEngine.js 做一致性比较。这个缺口后面会变成关键证据。
玩家和引擎对同四条规则持有不同模型。四处差异都不会产生错误提示。
| 规则 | 玩家可见文案 | 引擎 / 源码 | 后果 |
|---|---|---|---|
| 告密修女隐藏胜利 | 刺杀得分 +500 | assassinationBonus = 50;回归断言也是 50 | 玩家把一次处刑的价值高估了 10 倍 |
| 无面公爵伪装 | 可选勾选框;不勾选就不伪装 | 首次 EXECUTE_BLACK 自动伪装;context.disguiseRequests 从不被读取 | 玩家无法选择“不伪装” |
| 摄政王背叛加成 | “背叛动作 +75%” | +75% 绑定 EXECUTE_BLACK,而非 SABOTAGE_BOTH;术语表却把“背叛”定义为 SABOTAGE_BOTH | 按字面推演 0 × 1.75 = 0;意图需要设计确认 |
| 公爵平衡加成 | “差值 ≤10 达到上限 +50” | max(0, round(50 − diff));差值 10 → 40 | 在文案宣称的上限边界上出现 10 分预期差 |
前两项是阻断项,因为玩家能看见或操作错误的东西。第三项是未决项,因为两种读法 都成立,材料没有说明设计意图。第四项是精度缺陷:它改变了玩家看到的收益,但在 本次审计的剧本里不改变“完美平衡”评级,也不改变冠军归属。
这是一次规则级复盘,不是录制下来的玩家对局。下表中的每个数字都能从上述材料 复现;重点在于展示这些缺口如何在多轮中叠加。
| 轮次 | 玩家如何规划 | 引擎实际做什么 | 玩家观察到什么 |
|---|---|---|---|
| 1 | 抽到告密修女;因为隐藏条件写着 +500,主动推动一次处刑 | 结算 +50 | 一个看起来合理、实际错误的得分明细;没有警告 |
| 2 | 抽到无面公爵;选择 EXECUTE_BLACK,且不勾选伪装 | 首次黑王座行动自动伪装,并把 dukeDisguiseUsed 置为 true | 桌面被告知这是白圣堂行动;“不伪装”的计划被静默覆盖 |
| 3 | 抽到摄政王;按“背叛动作”期待 +75% | 只对 EXECUTE_BLACK 加成;SABOTAGE_BOTH 的信用收益为 0,无法被放大 | 取决于实际选择,可能没有加成,也可能出现意料之外的加成 |
| 4 | 用公爵把信用差控制在 10,期待吃到 +50 上限 | 计算 max(0, round(50 − 10)) = 40 | 在文案宣称的边界上少了 10 分 |
| 5 | 按规则书重建最终排名 | 只对存活者按引擎值排名 | 叙事与记分板不一致,但两者都“合法” |
真正的问题是叠加效应。单处错位可以被当成显示 bug;五轮里出现四处错位,玩家就 无法建立可靠的心智模型——而每个子系统都在报告自己成功。
回归记录对自己的边界是诚实的:
本次回归仅覆盖
src/gameEngine.js的引擎数值与台词生成,不覆盖玩家可见文案、 UI 控件到引擎入参的传递,也不覆盖两份引擎副本之间的一致性。
而发布失败恰好就住在这三层里。断言 #5 检查的是 assassinationBonus = 50——引擎 常量——所以它会通过,而玩家可见的隐藏条件仍然写着 +500。这套回归从未打开角色 揭示弹窗,从未点击伪装勾选框,也从未把 App.jsx 的内联副本与 gameEngine.js 做 diff。
对发布包逐条审计得到 20 个检查项:
| 处置 | 数量 | 例子 |
|---|---|---|
| 通过 | 15 | 四动作基础表、clamp 范围、天平阈值、处刑顺序、存活者排名 |
| 阻断 | 2 | 修女 +500 vs +50;伪装勾选框未接线 |
| 非阻断 | 1 | 差值 10 时公爵上限 +50 vs +40 |
| 未决 | 1 | 摄政王“背叛”措辞与实现不一致 |
| 待核 | 1 | 契约测试、文案快照、双副本 diff 均未编写 |
App.jsx 内联引擎与 src/gameEngine.js。在被证明完全一致之前, 重复引擎就是第二个真相源。| 检查项 | 结果 |
|---|---|
2026-10-22 重跑 npm run sim | 11/11 PASS |
| 发布包检查项 | 20 |
| 玩家可见阻断 | 2 |
| 非阻断精度缺陷 | 1 |
| 未决措辞问题 | 1 |
| 伪装勾选框契约测试 | 0 |
| 文案快照测试 | 0 |
| 双引擎 diff 测试 | 0 |
| 无来源支撑的结论 | 0 |
内联 UI 副本不是假设:App.jsx 自带一份 HIDDEN_WIN_CONDITIONS.NUN 字符串, 里面仍是 +500;它的 processTurn 也会在未读取 useDisguise 状态的情况下 自动把 dukeDisguiseUsed 置为 true。引擎文件和 UI 文件都可以相对自己的测试 “正确”,却依然互相矛盾。
未通过。 引擎足以通过它自己的回归,但发布不够。两个玩家可见的阻断项仍未 关闭:修女隐藏条件写错了支付数字,公爵伪装控件并没有控制任何东西。摄政王措辞 仍需要设计决策,公爵上限需要修文案。
绿色回归只证明它覆盖的那一层,不是发布证书。可复用的规则是: 测试层与层之间的契约,而不只是碰巧有测试的那一层。
| 事项 | 责任角色 | 关闭条件 |
|---|---|---|
修女隐藏条件 +500 → +50(或改引擎为 500 并重新平衡) | 玩法/逻辑工程师 | 角色弹窗截图显示 +50;回归仍通过 |
把勾选状态写入 context.disguiseRequests;引擎只在请求时伪装 | 客户端 + 玩法工程师 | 勾选/不勾选/重复请求三条契约测试通过 |
| 澄清摄政王“背叛”或改绑加成 | 设计 + 玩法工程师 | 设计确认单 + 最终文案 |
| 修正公爵上限文案(差值 0 → +50,每增加 1 点减 1) | 玩法工程师 | 文案快照通过 |
| 增加文案快照与双引擎 diff | QA | 未来任何漂移都会让 CI 失败 |