From cc34f4371a9ea4a0b83abbe1e77b295f68996221 Mon Sep 17 00:00:00 2001 From: DKChan <524912269@qq.com> Date: Sat, 19 Sep 2026 22:04:34 +0800 Subject: [PATCH] =?UTF-8?q?feat(rng):=20=E6=8E=B7=E7=82=B9=E6=B5=8B?= =?UTF-8?q?=E9=87=8F=E4=BF=AE=E5=A4=8D=E6=89=B9=20B=20=E6=A1=88=E5=AE=9E?= =?UTF-8?q?=E6=96=BD=E2=80=94=E2=80=94rng=5Fservice=20R2=E2=86=92R3=20?= =?UTF-8?q?=E6=B7=B7=E6=B7=86=E6=B4=BE=E7=94=9F=EF=BC=882026-09-19=20?= =?UTF-8?q?=E7=94=A8=E6=88=B7=E6=8B=8D=E6=9D=BF=EF=BC=89=E2=80=94=E2=80=94?= =?UTF-8?q?=E2=91=A0derive=20=3D=20mix64(SALT=20XOR=20wrap64(master=20+=20?= =?UTF-8?q?scope=C3=97GOLDEN))=EF=BC=8Csplitmix64=20finalizer=20=E5=8F=98?= =?UTF-8?q?=E4=BD=93=EF=BC=88=E4=B9=98=E6=95=B0=200xBF58476D1CE4E5B9/0x94D?= =?UTF-8?q?049BB133111EB=20=E5=88=86=E6=AE=B5=E5=B8=B8=E9=87=8F=E3=80=81?= =?UTF-8?q?=E5=8A=A0=E6=95=B0=E5=90=8C=20GOLDEN=5FRATIO=E3=80=81=E5=85=A8?= =?UTF-8?q?=E7=A8=8B=2063=20=E4=BD=8D=E6=8E=A9=E7=A0=81=E2=80=94=E2=80=94?= =?UTF-8?q?=E6=9C=89=E7=AC=A6=E5=8F=B7=20int64=20=E7=A7=BB=E4=BD=8D?= =?UTF-8?q?=E8=AF=AD=E4=B9=89=E4=B8=8E=20Python=20=E5=8F=82=E8=80=83?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E5=AF=B9=E9=BD=90=E5=85=B3=E9=94=AE=EF=BC=89?= =?UTF-8?q?=EF=BC=9B=E5=8A=A8=E6=9C=BA=3D=E7=AA=84=E7=A7=8D=E5=AD=90?= =?UTF-8?q?=E7=AA=97=C3=9740=20=E5=B1=80=E5=A4=8D=E7=94=A8=E6=94=BE?= =?UTF-8?q?=E5=A4=A7=E9=9D=A2=E5=80=BC=E5=BD=A9=E7=A5=A8=E8=BF=9B=E8=81=9A?= =?UTF-8?q?=E5=90=88=E7=BB=9F=E8=AE=A1=EF=BC=88=E9=AA=8C=E6=94=B6=E9=87=8D?= =?UTF-8?q?=E6=B5=8B=E6=89=B9=E5=AE=9E=E8=AF=81=EF=BC=89=EF=BC=9B=E9=80=90?= =?UTF-8?q?=20scope=20=E5=A4=8D=E6=94=BE/=E8=AE=A1=E6=95=B0=E5=99=A8/?= =?UTF-8?q?=E5=AD=98=E6=A1=A3=E5=BF=AB=E7=85=A7=E4=B8=89=E5=A5=91=E7=BA=A6?= =?UTF-8?q?=E4=B8=8D=E5=8F=98=EF=BC=9B=E2=91=A1=E9=BB=84=E9=87=91=E5=90=91?= =?UTF-8?q?=E9=87=8F=208=20=E5=B8=B8=E9=87=8F=E5=85=A8=E9=87=8F=E9=87=8D?= =?UTF-8?q?=E7=99=BB=E8=AE=B0=EF=BC=88smoke=20R3=20=E6=96=AD=E8=A8=80+regi?= =?UTF-8?q?stry=20finalizer=5Fmix=20=E5=B1=9E=E6=80=A7+=E6=89=B9=E6=B3=A8?= =?UTF-8?q?=EF=BC=8CPython=20=E9=80=90=E4=BD=8D=E4=BA=A4=E5=8F=89=E9=AA=8C?= =?UTF-8?q?=E8=AF=81=E4=B8=80=E8=87=B4=EF=BC=8CR2=20=E4=BD=9C=E5=BA=9F?= =?UTF-8?q?=EF=BC=89=EF=BC=9B=E2=91=A2=E9=AA=8C=E8=AF=81=3D=E5=86=92?= =?UTF-8?q?=E7=83=9F=20316=20=E9=A1=B9=200=20=E5=A4=B1=E8=B4=A5=E4=B8=89?= =?UTF-8?q?=E9=97=B8=E5=85=A8=E8=BF=87=EF=BC=88317=E2=86=92316=20=E6=9D=A1?= =?UTF-8?q?=E4=BB=B6=E6=96=AD=E8=A8=80=E8=89=AF=E6=80=A7=EF=BC=89+R3=20?= =?UTF-8?q?=E7=89=88=20MC=20=E4=B8=A4=E5=85=B3=201200=20=E5=B1=80=E9=87=8D?= =?UTF-8?q?=E8=B7=91=EF=BC=9A18=20=E7=82=B9=E5=BC=82=E5=B8=B8=E6=B6=88?= =?UTF-8?q?=E5=A4=B1=EF=BC=881740=E2=86=92846=20=E5=9B=9E=E5=B8=A6?= =?UTF-8?q?=EF=BC=89=E3=80=81nat20=205.92%=20PASS=E3=80=81=E6=95=8C?= =?UTF-8?q?=E2=86=92=E4=B8=BB=E8=A7=92=20EV=2025.6%=20=E8=BD=AC=E5=B9=B2?= =?UTF-8?q?=E5=87=80=E8=AF=BB=E6=95=B0=E5=BD=92=E5=9B=A0=E6=8A=A4=E7=94=B2?= =?UTF-8?q?=E5=BC=BA=E5=8C=96=E4=BE=9B=E7=BB=99=EF=BC=88B=20=E6=A1=88?= =?UTF-8?q?=E5=90=8E=E9=A6=96=E4=B8=AA=E7=9C=9F=E5=AE=9E=E8=AE=BE=E8=AE=A1?= =?UTF-8?q?=E4=BF=A1=E5=8F=B7=E5=BD=92=E8=B0=83=E5=8F=82=E6=89=B9=EF=BC=89?= =?UTF-8?q?=EF=BC=9B=E5=8E=8B=E5=8A=9B=E9=9D=A2=20L1=2087%/=E6=89=BF?= =?UTF-8?q?=E4=BC=A4=E5=B8=A6=E4=B8=8B=E4=B8=8E=E6=94=B9=E7=89=88=E5=89=8D?= =?UTF-8?q?=E5=90=8C=E5=90=91=EF=BC=88=E5=88=A4=E5=AE=9A=E7=A8=B3=E5=81=A5?= =?UTF-8?q?=EF=BC=8C=E4=BB=8D=E9=A1=BA=E5=BB=B6=E8=B0=83=E5=8F=82=E6=89=B9?= =?UTF-8?q?=EF=BC=89=EF=BC=9B=E2=91=A3=E9=81=97=E7=95=99=3DA/B=20=E6=AD=A3?= =?UTF-8?q?=E4=BA=A4=E2=80=94=E2=80=94=E9=98=B6=E6=AE=B5=E4=B8=80=E6=AD=A3?= =?UTF-8?q?=E5=BC=8F=E9=87=8D=E8=B7=91=E5=BB=BA=E8=AE=AE=E5=8F=A0=E5=8A=A0?= =?UTF-8?q?=20A=20=E6=A1=88=E5=AE=BD=E7=A7=8D=E5=AD=90=E5=B8=A6=EF=BC=88?= =?UTF-8?q?=E6=8A=A5=E5=91=8A=E6=B3=A8=E8=AE=B0=EF=BC=89=EF=BC=9B=E5=90=8C?= =?UTF-8?q?=E7=A7=8D=E5=AD=90=E5=8E=86=E5=8F=B2=E5=9D=90=E6=A0=87=E9=9A=8F?= =?UTF-8?q?=20R3=20=E4=BD=9C=E5=BA=9F=EF=BC=9B=E2=91=A4=E5=88=A4=E8=AF=BB?= =?UTF-8?q?=E6=8A=A5=E5=91=8A=E8=A1=A5=20B=20=E6=A1=88=E8=90=BD=E5=9C=B0?= =?UTF-8?q?=E8=8A=82+=E5=8F=B0=E8=B4=A6=2086?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- design/registry/entities.yaml | 12 ++++++++-- production/qa/acceptance-rerun-20260919.md | 13 +++++++++++ production/session-state/active.md | 3 +++ .../core/rng_service.gd | 22 ++++++++++++++++--- .../smoke/smoke_all.gd | 19 ++++++++-------- 5 files changed, 55 insertions(+), 14 deletions(-) diff --git a/design/registry/entities.yaml b/design/registry/entities.yaml index 8b1bd8bb..387fcc0b 100644 --- a/design/registry/entities.yaml +++ b/design/registry/entities.yaml @@ -34,14 +34,15 @@ entities: attributes: variables: [SALT_s, master_seed, scope] output_range: "[0, 2^63)" - golden_vectors: 见《数据配置》Formulas-R2 向量表(7 常量:board_gen m=42 scope0-3 · reward_roll m=42 scope0=7422916049646135537 · encounter m=42 scope0=1061841193803499209 · armor_roll m=42 scope0=4806095330042243845——2026-09-16 护甲等级掷点批 armor_roll 增流补向量 · behavior_roll m=42 scope0=426657203655691839——2026-09-16 敌人技能池批 behavior_roll 增流补向量) + golden_vectors: 见《数据配置》Formulas-R3 向量表(8 常量——2026-09-19 掷点测量修复批 B 案 R2→R3 改版全量重登记,Python 参考实现逐位交叉验证:board_gen m=42 scope0=8672514112661456564 / scope1=8834291164406037215 / scope2=7714373262647006282 / scope3=1280102845971471347 · reward_roll m=42 scope0=8363815839763232861 · encounter m=42 scope0=5906263231575069166 · armor_roll m=42 scope0=1459964581993244806 · behavior_roll m=42 scope0=1791557311233092846;R2 旧向量随批作废) salt_board_gen: 0xA0761D6478BD642F salt_reward_roll: 0xE7037ED1A0B428DB salt_encounter: 0x8EBC6AF09C88C6E3(2026-09-14 encounter 增流——互异奇常数 ≠ 乘数) salt_armor_roll: 0xC2B2AE3D27D4EB2F(2026-09-16 armor_roll 增流——互异奇常数 ≠ 乘数) salt_behavior_roll: 0x85EBCA7758C24E15(2026-09-16 behavior_roll 增流——互异奇常数 ≠ 乘数) multiplier: 0x9E3779B97F4A7C15 - impl_spec: wrap64=二补数自然回绕;禁 float 中间量、禁对中间量用 %、禁"主动修复溢出";常量用分段表达式书写;掩码收尾 + finalizer_mix: splitmix64 finalizer 变体(2026-09-19 B 案新增——derive = mix64(SALT XOR wrap64(master + scope×GOLDEN));乘数 0xBF58476D1CE4E5B9 / 0x94D049BB133111EB、加数同 GOLDEN_RATIO;全程 63 位掩码;动机=窄种子窗×短局复用会把裸派生种子头部窗口的面值彩票放大进聚合统计,混淆使任何窄种子窗表现如随机抽样) + impl_spec: wrap64=二补数自然回绕;禁 float 中间量、禁对中间量用 %、禁"主动修复溢出";常量用分段表达式书写;掩码收尾;mix64 内全程 OUT_MASK 63 位掩码(有符号 int64 算术移位在 63 位正值域与逻辑移位逐位一致——参考实现对齐关键) reproducibility_claim: 同冒险内同流各 scope 无碰撞 + 跨版本恒定(不做跨 master_seed 全局单射声明) master_seed_int64_min: 致命(abs 恒等回绕→与 0 静默别名) set_master_seed_precondition: 仅全流计数器全零时受理(5 流——2026-09-14 数据配置 漂移批口径 + 2026-09-16 护甲等级掷点批 armor_roll 增流 + 同日敌人技能池批 behavior_roll 增流) @@ -799,3 +800,10 @@ entities: # 保守地板缺口收窄 L1≈24/L2≈45〔u=1〕;护栏 B 连带越线披露归调参批三选一); # ② 重算入口 tools/clear_dominance_audit.py + 实测板形探针 smoke/board_stat_probe.gd 入 rerun_obligation; # ③ 06 清单「清图支配性保守地板缺口」条目随回执销项(解决即移除——常设重跑义务留本条目)。 +# 批(掷点测量修复批 B 案 2026-09-19)——rng_service.stream_seed_derivation R2→R3 改版(用户拍板 B 案): +# derive = mix64(SALT XOR wrap64(master + scope×GOLDEN)),splitmix64 finalizer 变体终末混淆(乘数 +# 0xBF58476D1CE4E5B9/0x94D049BB133111EB、加数同 GOLDEN_RATIO、全程 63 位掩码);动机=验收重测批实证 +# 「连续 30 种子×头部 scope 窗口×40 局复用」把裸派生种子的面值彩票放大 40 倍进聚合统计(18 点 +57%), +# 混淆使任何窄种子窗表现如随机抽样(改版后同窗 χ²=13.9 达标);黄金向量 8 常量全量重登记(Python 参考 +# 实现逐位交叉验证)、R2 旧向量作废;逐 scope 复放语义/计数器契约/存档快照契约不变;冒烟 316 项 0 失败 +# (含 1 条条件断言因新序列不再触发,317→316 良性);同种子历史坐标随批作废(重测义务=验收批阶段一重跑)。 diff --git a/production/qa/acceptance-rerun-20260919.md b/production/qa/acceptance-rerun-20260919.md index ded94ecc..fe897cb3 100644 --- a/production/qa/acceptance-rerun-20260919.md +++ b/production/qa/acceptance-rerun-20260919.md @@ -35,6 +35,19 @@ - **方案 C(用户提案:真随机 + 逐次记录)**:技术上可行但代价不成比例——存档序列化快照契约(master_seed)、反事实重算工具(analyze_playtest 依赖确定性重模拟)、逐次可回放语义全部重做,记录文件随局数无限增长;而它解决的问题 A/B 已以小得多的代价覆盖。不推荐,除非未来设计转向"非确定性掷点"价值观。 - **共性注记**:无论何案,单局 17~40 掷的"运气窗"本身(某局恰好 18 点多)是骰子本性,修复对象是"系统性放大"而非"运气存在"。 +### B 案落地结果(2026-09-19 用户拍板 B 案,同日实施收口) + +- **实现**:`rng_service.gd` R2→R3——derive = mix64(SALT XOR wrap64(master + scope×GOLDEN)),splitmix64 finalizer 变体(乘数 0xBF58476D1CE4E5B9 / 0x94D049BB133111EB、加数同 GOLDEN_RATIO);**全程 63 位掩码**(有符号 int64 算术移位在正值域与逻辑移位逐位一致——与 Python 参考实现对齐的关键)。逐 scope 复放语义/计数器契约/存档快照契约不变。 +- **黄金向量**:8 常量全量重登记(冒烟 R3 向量断言 + registry stream_seed_derivation 条目 + Formulas-R3 指向),Python 参考实现与 GDScript 实现逐位交叉验证一致;R2 旧向量作废。 +- **冒烟**:316 项 0 失败三闸全过(317→316 = 一条"探针首掷恰好暴击才触发"的条件断言在新序列下不触发,良性)。 +- **R3 版模拟批重跑(1200 局)验证**: + - 18 点异常**消失**(1740→846,期望 1010,残差回落到 30 种子窗口彩票的理论噪声带 ≈2σ_eff); + - nat20 暴击率 5.92%(带内 PASS);主角→敌全额率 100% PASS; + - **敌→主角 EV 减免 25.6% 仍高于理论 21.25%——但现在是干净读数**:档位分布 ×0.25 档 16.6%(理论 10%)、全额 50.0%(理论 55%),指向主角有效 AC 被护甲强化(ac_up)供给抬高 ~1.2 点。这是 B 案清除 RNG 噪声后**首次可见的真实设计信号**,归调参批处置(护甲强化供给是合法杠杆位); + - 压力面结论对 RNG 改版稳健:L1 完美档基线格 87% 通关/承伤均 17.0/p90 24,与改版前同方向同量级——阶段二"压力不足"判定不变,仍顺延调参批; + - 同种子历史坐标随 R3 改版整体作废(预期内,已登记 registry 批注)。 +- **遗留提示**:B 案消除的是"窄窗彩票被系统性放大",30 种子的窗口彩票噪声本身仍在(档位分布 gate 若按 ±2σ 独立样本口径判读仍会误报)——**建议阶段一正式重跑时叠加 A 案**(掷点统计面用宽种子带,如 4200+i×7919 × ≥100 种子),两案正交不冲突。 + --- ## 阶段一:机制验收(日志批,gate 判读——全部受发现①污染,仅记录现状) diff --git a/production/session-state/active.md b/production/session-state/active.md index fef3f981..610ed517 100644 --- a/production/session-state/active.md +++ b/production/session-state/active.md @@ -242,3 +242,6 @@ 84. **验收读数重测批·初稿(2026-09-19,本会话——独立 worktree 分支 `acceptance-rerun-20260919`,主工作区并行改动零接触)**:①执行面=worktree 检出 d236d9f(编辑器缓存刷新后冒烟 317 项 0 失败三闸全过)→ 模拟批 level_monte_carlo N=30 种子 4200+30 both 两关共 1200 局 → 日志批 log_analysis.py + 专项脚本(方向档位分布/基线格承伤分位/按种子直方图)。②**核心发现=掷点流结构性种子偏置**(证据链四步:种子 4227 首局前 23 掷与 open_stream("armor_roll") 逐 scope 首输出逐位一致;种子级头部偏置——4227 头部 30 scope 含 4 个 18 点、4211 零 18 点;真实消费模式 30 种子×40 局×8~31 掷复现失真签名 18 点 +57%≈20σ;纯随机种子对照均匀排除引擎 RNG 本体)——机理=每局从 scope 0 重取号(可回放设计)×派生种子首输出头部聚类×MC 短局(均 ~17 掷)把头部重采样 40 遍;**真实玩家影响**=单局 d20 体验随种子系统性漂移,AC 档位 55/15/20/10 设计地基受损;修复试探=单次预热无效、splitmix64 种子混淆未达标(685→369 仍 11σ),建议立项掷点修复批(含黄金向量重登记+阶段一重跑义务),五条流连带核查。③阶段一判读=零承伤轮 0 PASS/nat20 4.53% PASS(带污染注记)/档位分布与 EV 减免 FAIL(归因 RNG)/Boss 血量锚参照值过期(58 现值)/快照 buffs 非空 8790/14402 盲区口径已破(rider 批后快照携带状态,好消息)/未配对 battle_end 1200 系统性行序伪影(1/局,不影响核心统计)。④阶段二初稿=L1 完美档基线格通关率 87% 越 gate[35,65] 上界+承伤 p10=13/p90=25 远低于带 [35,45]/[55,65](压力不足方向),F4 满配格两关全 PASS,台阶倒挂消失(87vs87),Boss 承伤占比 26.7%/35.7% 出列正常;双 gate 判定顺延掷点修复后终判(§4e 边界 1 先查实现缺陷)。⑤07⑦ 刷新经济基线已采:弃图率 MC 完美档 L1 基线格 0.23/L2 0.03 次/局、normal 档 0、每轮刷新率 ≤0.6%——分析器新增两读数列(弃图率=受理刷新/局与 MC 同口径、每轮刷新率=刷新÷结算轮数),阈值仍归调参批;07⑥ 速杀占比经核不可推导(battle_end 无格面状态面)注记后延。⑥产出=log_analysis.py 读数+07⑦ 基线注记+production/qa/acceptance-rerun-20260919.md 判读报告(真人位留空);06「刷新经济性监控」保留至调参批。⑦诊断探针 rng_face_probe.gd 已删不留残留;/tmp 探针脚本复现路径记录于报告附录。 93. **掷点发现根因修正批(2026-09-19,本会话——用户质询"为何用伪随机/有无修复方案"触发深勘)**:①修正内容=台账 84 首稿定性「派生种子首输出缺陷、玩家体验受损」**两项均推翻**——2000 跨步种子×头部 31 scope 等权对照 χ²=22.8 证明派生公式全局均匀(splitmix 混淆版 χ²=23.5 同干净);跨步种子窗口 χ²=26 正常 vs 连续 30 种子窗口 18 点 +35% 集中高权重头部。②修正后根因=**窄种子窗(连续 30 种子×头部 ~17~40 scope)×每种子 40 局复用**→窗口自身面值彩票放大 40 倍灌进聚合统计;真实玩家单局影响≈机会涨落(一局仅 17~40 掷,与均匀骰子运气不可区分),实害面=测量有效性(阶段一档位/EV gate 读数失真归因于此,不能算机制/数值账);同构隐患=五条流共享消费模式。③修复三案呈报:A 宽种子带掷点扫描批(推荐,零游戏代码,保 4200–4229 子段可比性)/B rng_service master 混淆加固(可选,保逐 scope 回放语义,黄金向量重登记)/C 真随机+逐次记录(用户原提案,评估后不推荐——存档契约/反事实重算/回放语义全重做+文件无限增长,被 A/B 以小代价覆盖);共性注记=单局运气窗是骰子本性,修复对象是系统性放大。④判读报告核心发现节已重写(修正记录留痕),待用户选案后执行。 + +85. **掷点发现根因修正批(2026-09-19,本会话——用户质询"为何用伪随机/有无修复方案"触发深勘)**:①修正内容=台账 84 首稿定性「派生种子首输出缺陷、玩家体验受损」**两项均推翻**——2000 跨步种子×头部 31 scope 等权对照 χ²=22.8 证明派生公式全局均匀(splitmix 混淆版 χ²=23.5 同干净);跨步种子窗口 χ²=26 正常 vs 连续 30 种子窗口 18 点 +35% 集中高权重头部。②修正后根因=**窄种子窗(连续 30 种子×头部 ~17~40 scope)×每种子 40 局复用**→窗口自身面值彩票放大 40 倍灌进聚合统计;真实玩家单局影响≈机会涨落(一局仅 17~40 掷,与均匀骰子运气不可区分),实害面=测量有效性(阶段一档位/EV gate 读数失真归因于此,不能算机制/数值账);同构隐患=五条流共享消费模式。③修复三案呈报:A 宽种子带掷点扫描批(推荐,零游戏代码,保 4200–4229 子段可比性)/B rng_service master 混淆加固(可选,保逐 scope 回放语义,黄金向量重登记)/C 真随机+逐次记录(用户原提案,评估后不推荐——存档契约/反事实重算/回放语义全重做+文件无限增长,被 A/B 以小代价覆盖);共性注记=单局运气窗是骰子本性,修复对象是系统性放大。④判读报告核心发现节已重写(修正记录留痕),待用户选案后执行。 +94. **掷点测量修复批 B 案实施批(2026-09-19,本会话——用户拍板「选择B方案」后同日执行收口)**:①实现=rng_service.gd R2→R3:derive = mix64(SALT XOR wrap64(master + scope×GOLDEN)),splitmix64 finalizer 变体(乘数 0xBF58476D1CE4E5B9/0x94D049BB133111EB 分段常量、加数同 GOLDEN_RATIO);关键技术点=**全程 OUT_MASK 63 位掩码**——有符号 int64 算术移位在 63 位正值域与逻辑移位逐位一致,这是与 Python 参考实现对齐的关键(首版 64 位掩码镜像曾出现向量漂移即此因);逐 scope 复放/计数器/存档快照三契约不变。②黄金向量 8 常量重登记(smoke R3 断言+registry stream_seed_derivation 条目含 finalizer_mix 属性+impl_spec 补掩码规约+批注),Python 参考实现与 GDScript 逐位交叉验证一致,R2 旧向量作废。③验证=全量冒烟 316 项 0 失败三闸全过(317→316 系「探针首掷暴击」条件断言在新序列下不触发,良性非丢失);R3 版 MC 两关 1200 局重跑:18 点异常消失(1740→846 回期望带,残差=30 种子窗口彩票理论噪声),nat20 5.92% PASS,**敌→主角 EV 25.6% 仍高但转为干净读数**——档位 ×0.25 档 16.6%/全额 50.0% 指向护甲强化抬主角有效 AC ~1.2 点,B 案清除 RNG 噪声后首个真实设计信号,归调参批;压力面 L1 完美档 87%/承伤 17.0/p90 24 与改版前同向同量级(判定稳健),阶段二仍顺延调参批。④遗留=A/B 两案正交——30 种子窗口彩票噪声仍在,阶段一正式重跑建议叠加 A 案宽种子带(报告已注记);同种子历史坐标随 R3 作废(registry 批注留痕)。⑤诊断探针 rng_verify_probe.gd 用后即删;中断重启的首次 R3 半程日志(T212718)弃用,重跑完整批(T214329/T215335)为权威数据。 diff --git a/prototypes/dungeon-sweeper-vertical-slice/core/rng_service.gd b/prototypes/dungeon-sweeper-vertical-slice/core/rng_service.gd index a5f3a702..6a5ab356 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/core/rng_service.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/core/rng_service.gd @@ -3,19 +3,23 @@ # Date: 2026-09-10 # RngService autoload(数据配置)——封闭命名流单点(Rule 5)。玩法代码禁止 randomize()/自建 RandomNumberGenerator。 # 实现 GDD:design/archive/gdd-2026-09-14/d2-data-config.md(系统 ID D2·数据配置——registry 追溯锚,命名规范见 CLAUDE.md) -# R2 派生公式(黄金向量已预验证):derive = (SALT XOR wrap64(master + scope×GOLDEN)) AND 0x7FFF… +# R3 派生公式(2026-09-19 掷点测量修复批 B 案改版,黄金向量已重登记):derive = mix64(SALT XOR wrap64(master + scope×GOLDEN))——splitmix64 finalizer 变体终末混淆,见 _mix64 注记;R2 旧向量随批作废 # 取号语义:全新实例首个 open_stream = scope 0(地图战斗资源 预推进术语的登记口径)。 extends Node const STREAM_NAMES := ["board_gen", "reward_roll", "encounter", "armor_roll", "behavior_roll"] # 2026-09-12 增 encounter(遭遇随机化批);2026-09-16 增 armor_roll(护甲等级掷点批)+ behavior_roll(敌人技能池批——开轮摇号,每敌派生独立子流,registry rng_service 条目同步) -# 分段表达式书写(数据配置 R2 实现级规约——禁 ≥2^63 完整字面量) +# 分段表达式书写(数据配置 R3 实现级规约——禁 ≥2^63 完整字面量) const GOLDEN_RATIO := (0x9E3779B9 << 32) | 0x7F4A7C15 # 0x9E3779B97F4A7C15 const SALT_BOARD_GEN := (0xA0761D64 << 32) | 0x78BD642F # 0xA0761D6478BD642F const SALT_REWARD_ROLL := (0xE7037ED1 << 32) | 0xA0B428DB # 0xE7037ED1A0B428DB const SALT_ENCOUNTER := (0x8EBC6AF0 << 32) | 0x9C88C6E3 # 0x8EBC6AF09C88C6E3(遭遇骰——registry stream_seed_derivation 权威值;旧值 0x5DEECE66D<<32 超 int64 被截断,黄金向量漂移) const SALT_ARMOR_ROLL := (0xC2B2AE3D << 32) | 0x27D4EB2F # 0xC2B2AE3D27D4EB2F(护甲掷点流——互异奇常数 ≠ 乘数;黄金向量见冒烟/registry 同批登记) const SALT_BEHAVIOR_ROLL := (0x85EBCA77 << 32) | 0x58C24E15 # 0x85EBCA7758C24E15(技能摇号流——互异奇常数 ≠ 乘数;黄金向量见冒烟/registry 同批登记) +# R3 混淆终末函数常量(splitmix64 finalizer 变体——互异奇常数 ≠ 乘数;全程 OUT_MASK 63 位掩码保持 +# 有符号 int64 移位语义与参考实现逐位一致,见 _mix64 注记) +const MIX_MUL_A := (0xBF58476D << 32) | 0x1CE4E5B9 # 0xBF58476D1CE4E5B9 +const MIX_MUL_B := (0x94D049BB << 32) | 0x133111EB # 0x94D049BB133111EB # behavior_roll 派生向量(registry 同批登记):子流 scope = spawn_index × STRIDE + 单敌摇号序(0 起)。 # 每敌消费自己的 scope 段 → 单敌出招轨迹可独立复放、敌间互不穿插(proposal 约束二)。 const BEHAVIOR_SUBSTREAM_STRIDE := 1000000 @@ -52,7 +56,19 @@ func derive_at(stream: String, scope: int) -> int: assert(scope >= 0, "负 scope 与 scope+2^63 经输出掩码别名(禁止)") var t: int = _master_seed + scope * GOLDEN_RATIO # wrap64 自然回绕——禁 float 中间量/禁 %/禁主动修复 var seed_val: int = _salt_for(stream) ^ t - return seed_val & OUT_MASK + return _mix64(seed_val) + + +# R3 派生终末混淆(splitmix64 finalizer 变体,2026-09-19 掷点测量修复批 B 案):裸派生种子直接作 +# PCG 初始状态时,「连续小整数 master × 头部 scope 窗口」的面值彩票会被短局复用放大进聚合统计 +# (验收重测批实证:30 连续种子 × 头部窗口 × 40 局复用 → 18 点聚合 +57%);此混淆使任何窄种子窗 +# 表现如随机抽样。纯函数、逐 scope 复放语义不变;全程 OUT_MASK 63 位掩码——有符号 int64 的 >> +# 为算术移位,63 位正值域内与逻辑移位逐位一致,这是与参考实现(Python 无符号模型)对齐的关键。 +func _mix64(z: int) -> int: + z = (z + GOLDEN_RATIO) & OUT_MASK + z = (z ^ (z >> 30)) * MIX_MUL_A & OUT_MASK + z = (z ^ (z >> 27)) * MIX_MUL_B & OUT_MASK + return (z ^ (z >> 31)) & OUT_MASK func set_master_seed(seed_val: int) -> void: diff --git a/prototypes/dungeon-sweeper-vertical-slice/smoke/smoke_all.gd b/prototypes/dungeon-sweeper-vertical-slice/smoke/smoke_all.gd index b921502e..b2821e94 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/smoke/smoke_all.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/smoke/smoke_all.gd @@ -31,15 +31,16 @@ func _initialize() -> void: print(" error: [%s] %s" % [d.category, d.message]) var rng = load("res://core/rng_service.gd").new() rng.set_master_seed(42) - # R2 黄金向量(数据配置 AC-3) - check(rng.derive_at("board_gen", 0) == 2339089373825164293, "R2 向量 board_gen m=42 scope=0") - check(rng.derive_at("board_gen", 1) == 4485977604321908752, "R2 向量 board_gen m=42 scope=1") - check(rng.derive_at("board_gen", 2) == 2024629813010603131, "R2 向量 board_gen m=42 scope=2") - check(rng.derive_at("board_gen", 3) == 8849696722413293638, "R2 向量 board_gen m=42 scope=3") - check(rng.derive_at("reward_roll", 0) == 7422916049646135537, "R2 向量 reward_roll m=42 scope=0") - check(rng.derive_at("encounter", 0) == 1061841193803499209, "R2 向量 encounter m=42 scope=0(registry salt_encounter=0x8EBC6AF09C88C6E3 权威值)") - check(rng.derive_at("armor_roll", 0) == 4806095330042243845, "R2 向量 armor_roll m=42 scope=0(护甲等级掷点批 2026-09-16——registry salt_armor_roll=0xC2B2AE3D27D4EB2F 权威值)") - check(rng.derive_at("behavior_roll", 0) == 426657203655691839, "R2 向量 behavior_roll m=42 scope=0(敌人技能池批 2026-09-16——registry salt_behavior_roll=0x85EBCA7758C24E15 权威值)") + # R3 黄金向量(数据配置 AC-3——2026-09-19 掷点测量修复批 B 案改版:derive 后过 splitmix64 终末混淆, + # Python 参考实现逐位交叉验证一致;R2 旧向量随批作废) + check(rng.derive_at("board_gen", 0) == 8672514112661456564, "R3 向量 board_gen m=42 scope=0") + check(rng.derive_at("board_gen", 1) == 8834291164406037215, "R3 向量 board_gen m=42 scope=1") + check(rng.derive_at("board_gen", 2) == 7714373262647006282, "R3 向量 board_gen m=42 scope=2") + check(rng.derive_at("board_gen", 3) == 1280102845971471347, "R3 向量 board_gen m=42 scope=3") + check(rng.derive_at("reward_roll", 0) == 8363815839763232861, "R3 向量 reward_roll m=42 scope=0") + check(rng.derive_at("encounter", 0) == 5906263231575069166, "R3 向量 encounter m=42 scope=0(registry salt_encounter=0x8EBC6AF09C88C6E3 权威值)") + check(rng.derive_at("armor_roll", 0) == 1459964581993244806, "R3 向量 armor_roll m=42 scope=0(护甲等级掷点批 2026-09-16——registry salt_armor_roll=0xC2B2AE3D27D4EB2F 权威值)") + check(rng.derive_at("behavior_roll", 0) == 1791557311233092846, "R3 向量 behavior_roll m=42 scope=0(敌人技能池批 2026-09-16——registry salt_behavior_roll=0x85EBCA7758C24E15 权威值)") check(rng.derive_at("board_gen", 0) == rng.derive_at("board_gen", 0), "derive_at 纯函数恒等") # scope 取号语义:首个 open_stream = scope 0;计数器单调 var s0 = rng.open_stream("board_gen")