8.2 阈值触达的分布
把 14 道好感度阈值门(13 条选项门 + 1 条结局门)抽平,定义路径触达为「变量沿路径最大值 >= 门限」。实测 5000 条采样路径里 18 条(3‰)从未触达任何门,yan>=25 的触达率最高 858‰。B 层把每道门限 ±10 后重走路径:14 道门全部改变了触达率,只打印软告警、exit 0。
tag
把 14 道好感度阈值门(13 条选项门 + 1 条结局门)抽平,定义路径触达为「变量沿路径最大值 >= 门限」。实测 5000 条采样路径里 18 条(3‰)从未触达任何门,yan>=25 的触达率最高 858‰。B 层把每道门限 ±10 后重走路径:14 道门全部改变了触达率,只打印软告警、exit 0。
本章的张力来自叙事节拍结构,不是机制动力学。本节把这条边界跑成可核对的数:递归展开真源,叙事侧词元命中 143 次,机制侧词元 hp、damage、resource、turn 等命中 0 次——机制张力在这份规格上根本无法计算。分工不是一句口号,而是输入清单的差集。
一次可玩性测试如果没有假设,就只是点着玩。本节把《雾港夜航》这次测试要验的东西落成四条可证伪的命题——覆盖率、结局覆盖、跨宿主一致、分布熵,每条都给出可测整数、判据与证伪条件,并算出零假设基线 446‰。真人玩家测试需真实业务环境,命令行不可复现,标 X。
四条必须恒为 0 的残差:钳位(可达状态与转移后都在 [0,100] 内)、初值(每个声明角色的 init 落在自身 range 内)、累积口径(新值恒等于 clamp(旧值 + add),set 不写好感度)、门限可达性(15 条门限均可跨过)。四条残差合计 0;lin 与 hai 有 affinity 声明却不在 state.vars,按「声明未用=报告,用了未声明=FAIL」报告。
好感度的分段在规格里就是一串 [aff, 名, >=, 整数] 阈值门,长在选项 when、节点 line_when、结局 when 三处。本节把 15 条门限全抽出来,再逐条扰动 ±5,在同一采样口径下重算覆盖率与可达结局集合,找出 4 条临界门限;并如实说明为什么放宽 5 点不起作用而收紧 5 点才起作用——基线覆盖率已饱和在 1000‰。B 层只打印 WARN,退出码 0。
好感度是一台受钳位约束的整数状态机:状态 = 节点 + 4 个好感度整数 + 18 个 flag + 9 个物品。本节把《雾港夜航》的完整状态空间 BFS 一遍,实测四个变量的可达值域分别是 yan 21、hui 35、shen 12、mu 16,值域之积 141120,而真正可达的好感度四元组只有 87190——四个变量之间并不独立。P 层用同一批选项断言初值来源、add 累加与钳位。
选择序列是玩家按下的一串 choice_id,路径是引擎走出来的 node_id 序列。本节用同一趟推进给出双表示对照,实测 115 条选项去重后只有 56 条唯一跳转,因此选项数并不等于路径形状数,并用 Kahn 拓扑排序证明 48 个对话节点构成一张无环 DAG。
效果算子只有四个,每个只写一个命名空间:set 写 flag、add 写好感度、grant 与 revoke 写物品。本节把全规格 138 次写入摊平数一遍,校验写入域越界 0,并点出声明了却一次未用的 revoke;P 层用同一批选项断言命名空间互斥,并以把 add 放大到百万级仍被钳回证明钳位确实生效。
可满足性问的是:一个条件在有限状态域里到底有没有解,会不会恒假。本节用边界值枚举在「节点 × 4 个好感度 × 18 个 flag × 9 个物品」上判定,57 个条件位点里死条件 0 个;并处理一处真实特征——lin 与 hai 声明了好感度却不在状态域里,任何门限都结构性不可达。
条件在规格里是嵌套数组写成的表达式树,求值器就是把它递归地读成真假。本节给出六种形态的求值语义,用 Python 与 TypeScript 两条链把 story_spec.json 里 57 个条件位点全部求值一遍,并把声明域与使用域分开数:声明 6 种形态只用 5 种,声明 4 个比较算子只用 >= 一种。
装配检查表把本卷十五节交付的零件收成十二项可断言的检查:规格可解析、引用完整、写入域合法、整数不变量残差 0、规模对账、BFS 与基线一致、goto 全部存在、结局全部可达、跨语言锚点一致、W 层零外部依赖、覆盖率达目标、无卡死路径。S 层脚本逐项跑出 PASS/FAIL 表,任一项 FAIL 即非零退出。
属性测试不问「跑一遍对不对」,而问「一条命题是否对所有输入都成立」。本节把第 3 / 6 / 8 章的不变量写成六条属性:好感度恒在 [0,100]、set 类算子幂等、add 故意不幂等、静态死路 0、5 个结局全可达、每状态至少一个可用选项,并给每条属性配一个必须 fail 的注入式负例。
存档是状态的规范序序列化:键按字典序、值全是整数、无浮点、无时间戳、无随机。本节把一条 25 步路径的终局状态写成 795 字节的存档 JSON,验证字段与 canonical 串一一对应,并让序列化到反序列化再序列化两次字节完全相同;同时点明临时 UI 状态、派生量、绝对时间不能进存档。
条件是嵌套数组写成的表达式树:三种叶子(旗标 / 好感度比较 / 物品持有)与三种组合(and / or / not)构成完整文法。本节给出文法表,并用 node --test 逐条验证六种形式的正例与负例。实测全规格 58 个条件节点,实际用到 5 种形式——or 声明了却一次没用。
比例检验把「三段差不多长」「后面更紧张」这类印象换成整数:各幕拍数 5/6/5 折成千分比 312/375/312,张力均值 2000/3666/4000‰,峰值位置落在第 4、10、13/14 拍。本节给出可跑的 S 层脚本与纯 xml.etree 的各幕张力分布 SVG,并断言第三幕峰值不低于前两幕。
弧光在规格里就是两个数组:三个 stages 阶段名,加三个 beats 转折节拍号。本节把《雾港夜航》六个角色的弧光写成阶段表,并用 S 层脚本校验三阶段、三节拍号、严格递增、落在 1..16 的范围内;顺带暴露 b12 与 b16 被两个角色同时声明为转折点的结构拥挤。
设定冲突不是「感觉」,而是三种可判定的结构问题:立场失衡(带符号环的符号积为负)、地点解锁成环(互相依赖)、同一条规则被两个来源反证。本节用一个 S 层脚本各写一个检测器,在真规格上得到 0 死锁 / 0 失衡环 / 0 双源反证,再用三份注入式坏规格证明检测器真的会报错,而不是恒返回「无冲突」。
世界观在规格里是三组可校验的字段:4×4 的派系 stance 矩阵表达谁与谁对立,地点 unlock_when 表达空间解锁,rules 表达可检查的约束。本节用一个 S 层脚本把三者都读成可判定的量,逐对检验 stance 矩阵的反对称性,校验 14 个地点的解锁条件与提供者,并把 4 条自然语言规则整理成待绑定的清单。
装配检查表把前面十五节的判定收成十项可断言的检查:九段字段齐备、引用完整、写入域合法、整数不变量残差 0、规模对账、结局节点一致、死条件 0、覆盖率达目标、结局全部可达、卡死路径 0。S 层脚本逐项跑出 PASS/FAIL 表,十项全绿才算装配完成。
叙事策划与程序在规格上分工:策划写结构与条件,程序写内核与门禁,规格是两者唯一的接口。本节给出规格变更的机器可读 diff 与影响面评审(本机可复现),并把真实团队的评审会、冻结窗口与排期逐处标注为需真实业务环境、命令行不可复现,把能算与不能算的分开。