diff --git a/CLAUDE.md b/CLAUDE.md index f3f524d..047a946 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -99,6 +99,15 @@ godot --headless --path . -s res://src/tools/import_main.gd -- --validate-only **验证手册**:`docs/engineering/2026-10-19-w3-verification-manual.md`(文档区,不入库)。 W3 的 GDScript 从未被引擎执行过(本机无 Godot)——**跑通第 1 节 headless 单测才算 W3 验过**。 +## 文档落点规则(架构 §9.7,W5 起) + +**判据一句话**:记录"这个仓库已发生了什么"的工程事实 → 跟项目仓走(`docs/engineering/`); +规定"这个项目应该怎样"的设计规范 → 留 workspace 设计区(C 盘 `design/` + `docs/architecture/`)。 +自检:"换一台电脑 clone 仓库后,这份文档还找得到、还需要吗?" +- 工程周报/验证报告/sim 对比/look-dev 冻结记录 → 本项目仓 `docs/engineering/<日期>-<名称>.md`; +- 设计母本(GDD / Balance Bible / 架构文档 / 裁定报告)→ workspace 设计区,多轮迭代母本,不随单次 commit 失效; +- W1-W4 工程报告仍留 C 盘原处(交叉引用不断链,不迁移);W5 起新工程报告一律按本规则落项目仓。 + ## 纪律提醒 - 写代码前**先写测试**(GUT W3 起);每个 Story 留测试证据路径; diff --git a/data/build/balance_tuning.json b/data/build/balance_tuning.json index a2df155..a9695b1 100644 --- a/data/build/balance_tuning.json +++ b/data/build/balance_tuning.json @@ -174,8 +174,8 @@ "id": "tune_restart_curve", "kind": "text", "num": 0, - "text": "1:0.6:0.4:0.3", - "desc_cn": "[B§4.3] 重开曲线目标(第 5 轮触底 ≥0.25×);模拟器门 #13 断言" + "text": "1:0.48:0.42:0.40", + "desc_cn": "[B§4.3] 重开曲线目标(第 5 轮触底 ≥0.25×);模拟器门 #13 断言。[v1.0→v1.1] 用户拍板方案 A:二轮 sim 实测校准值(旧 1:0.6:0.4:0.3 为一轮解析预估,未计阻尼)" }, { "id": "tune_event_timeout_default", diff --git a/data/sheets/balance_tuning.csv b/data/sheets/balance_tuning.csv index fb6dfe9..a6ffc99 100644 --- a/data/sheets/balance_tuning.csv +++ b/data/sheets/balance_tuning.csv @@ -22,7 +22,7 @@ tune_skip_gens,number,3,,[B§4.2] 跳过机制覆盖前 3 档班底 tune_skip_levels,number,25,,[B§4.2] 跳过机制每档 +25 级 tune_skip_level_cap_per_gen,number,50,,"[B§4.2] W5 裁定 P-27(F-2):跳过每档获赠 = min(floor(宿慧/5)×25, 本值)(原无封顶 → R2 开盘白送 2850 级)" tune_enthrone_rebirths,number,2,,[B§4.4] 阴司敕封触发:转世 ≥2(约第 2-3 轮,累计 8-12h 为其 pacing 后果) -tune_restart_curve,text,0,1:0.6:0.4:0.3,[B§4.3] 重开曲线目标(第 5 轮触底 ≥0.25×);模拟器门 #13 断言 +tune_restart_curve,text,0,1:0.48:0.42:0.40,[B§4.3] 重开曲线目标(第 5 轮触底 ≥0.25×);模拟器门 #13 断言。[v1.0→v1.1] 用户拍板方案 A:二轮 sim 实测校准值(旧 1:0.6:0.4:0.3 为一轮解析预估,未计阻尼) tune_event_timeout_default,text,0,opt_a,[B§7] 冤魂申诉超时默认 = 超度(opt_a) tune_souls_div,number,10000,,"[B§6] 超度亡魂 = floor(本轮累计阴德 ÷ 10,000)(叙事计数器,不设闸门;P-14)" tune_enthrone_suihui_req,number,60,,[G§3.4] 阴司敕封解锁条件:宿慧 ≥60([B§4.4] 仅给 8-12h 体感,采信 GDD 数值) diff --git a/data/sheets/balance_tuning.desc.translation b/data/sheets/balance_tuning.desc.translation index ea8a673..4ddd735 100644 Binary files a/data/sheets/balance_tuning.desc.translation and b/data/sheets/balance_tuning.desc.translation differ diff --git a/data/sheets/balance_tuning.text.translation b/data/sheets/balance_tuning.text.translation index aaf1a3d..de9e211 100644 Binary files a/data/sheets/balance_tuning.text.translation and b/data/sheets/balance_tuning.text.translation differ diff --git a/docs/engineering/2026-10-20-f-sim-round2-comparison.md b/docs/engineering/2026-10-20-f-sim-round2-comparison.md index 0cc3a65..d7de334 100644 --- a/docs/engineering/2026-10-20-f-sim-round2-comparison.md +++ b/docs/engineering/2026-10-20-f-sim-round2-comparison.md @@ -7,6 +7,20 @@ --- +## 后记(2026-10-21 · #13 方案 A 落定,本报告 §0-§7 为二轮当时记录) + +**用户已对 #13 拍板方案 A——接受实测曲线为校准则。** 文策渊产出 Balance Bible v1.1 +(`design/gdd/2026-10-21-F-balance-bible-v1.1.md` §4.3):#13 目标曲线 `1:0.6:0.4:0.3` → +**`1:0.48:0.42:0.40`**(标注为二轮 sim 实测校准值,归因 = 一轮预估的「182min÷倍率」线性模型未计 +阻尼),R4 目标带随曲线改为 [0.30,0.50],**R4=0.396 入带,新口径下 R1-R4 全 PASS**;R5 起 ≥0.25× 不变。 + +工程侧同步落地(W5 收尾,commit 待主理人代提):`PrestigeModel.RESTART_TARGETS` → `[1.0, 0.48, 0.42, 0.40]; +`tune_restart_curve`(balance_tuning.csv,经 gen 脚本)→ `1:0.48:0.42:0.40`;`gates.gd` 文档注释同步; +`test_economy_formulas` / `test_balance_data` 断言更新。**sim 复跑验证 #13 新口径 PASS**(见 §6 补记)。 +本节之下 §0-§7 的"#13 残留 FAIL"表述均为**旧曲线口径下的二轮当时判定**,保留作校准历史,不需回改。 + +--- + ## 0. 结论速览 | 项 | 结果 | @@ -179,6 +193,16 @@ **确定性备注**:sim 无时钟依赖(RandomStrategy 种子 7、固定步长),二轮报告重跑逐值一致;`report-2026-10-21.json` 为可复现产物。 +**§6 补记(2026-10-21 · W5 收尾:#13 方案 A 落地后复跑)**: + +| 步骤 | 结果 | +|---|---| +| gen 脚本重跑 | exit=0;漂移哈希前后**仅 2 文件变化**(`balance_tuning.csv` + 其 build JSON——`tune_restart_curve` 一行 1:0.6:0.4:0.3 → 1:0.48:0.42:0.40),其余 95 文件字节不变 | +| import_all | PASS(10 表/238 行/0 违规,差异化「~0 变更」= importer 与 gen 产物逐字节一致) | +| test_runner | **PASS(752 检查全过)**——economy_formulas 160→162(新增 R2=0.48/R3=0.42 断言);balance_data 的重开曲线文本断言同步 v1.1 | +| sim | exit=0;**`restart.pass=true`**:「R1=1.00(目标 1.00);R2=0.48(目标 0.48);R3=0.42(目标 0.42);R4=0.40(目标 0.40)」——新曲线下 R1-R4 全入带;主口径未过门 = [];硬门 5/10 双 true;6 个 `pass:false` 全部为非主口径策略 cross 观测(greedy#2、novice#3/#14、random#2/#3/#14,主理人已核) | +| 报告 | `tests/sim_reports/report-2026-10-21.json` 已按新代码重生成(同日覆盖;FAIL 旧版存于 commit `c0a5c52` 历史) | + --- ## 7. 待办与阻塞 @@ -198,16 +222,18 @@ ### 7.2 待裁定(按优先级) -1. **#13 R4 残留**:选项 A/B/C/D(§3.3),工程建议 A 或 D;若选 C,工程立即可跑变体 sim 定量。 -2. **devour 后 H10 越红线**(§4.3):是否纳入 #10 断言口径 + 设计侧处理选项,建议 W6 敕封校准项一并处理。 -3. **设计侧文档补登**:[B§12] 表清单补登 `wrath_layers`(架构文档在 C 盘设计仓,属文策渊);若采选项 A,需同步 [B§4.3] 重开目标曲线修订条。 +1. ~~**#13 R4 残留**~~ → **已定**:用户拍板方案 A,Balance Bible v1.1 §4.3 曲线改 1:0.48:0.42:0.40,代码/数据/测试已同步(见文首后记)。 +2. **devour 后 H10 越红线(当前唯一已知的红线口径缺口)**(§4.3):门 #10 的"单轮峰值"是否取 max-over-runs 待决——文策渊已确认 devour 轮后 H10 峰值 2.498e14 越红线 2.5×(当前 #10 双断言只含 greedy 单轮 11.831B + restart lifetime 7.007B,devour 轮不在断言内)。**W6 敕封校准时决策;在此之前不扩断言范围**(扩了会让 #10 变 FAIL,阻塞当前全绿状态)。 +3. **设计侧文档补登**:[B§12] 表清单补登 `wrath_layers`(架构文档目录树已由文策渊补登;架构文档 §4.2 MultiplierSet 注释与目录树 meta_upgrades.csv 行本收尾轮已同步)。 ### 7.3 W6 输入备忘 +- **红线口径缺口(唯一已知)**:门 #10"单轮峰值"是否取 max-over-runs 待决——devour 轮后 H10 峰值 2.498e14 越红线 2.5×(文策渊已确认)。W6 敕封校定时决策;**当前不扩断言范围**(保全绿状态)。若取 max-over-runs,需先设计侧处理后 H10 膨胀(gen12 rate 下调或 lifetime 显式软上限,[裁] §2.5 已列选项)。 - M-4:门 #9-b 的 β 口径标定(离线 8h 推进比)仍缺数据; - M-5:门 #11(BossBattle 接入后)与 P-10/P-15 的 #11-d 触发条件; -- #13 触底支路(R5 起 ≥0.25×)本轮未覆盖——8h 预算只完成 R1-R4;如需验证触底支路,restart 预算提至 10-12h; -- 任务 #22/#27(W3/W4 批次提交)同受 git 缺失阻塞,可一并处理。 +- #13 触底支路(R5 起 ≥0.25×)本轮未覆盖——8h 预算只完成 R1-R4;新曲线下复跑时 restart 预算提至 10-12h; +- m04 P2 备选("跳过每档封顶 +25 级")是否转正——依赖 #13 曲线数据,v1.1 已列 P2 维持待定; +- 任务 #22/#27/#44(W3/W4/W5 批次提交)同受 git 缺失阻塞,可一并处理。 --- diff --git a/src/core/economy/prestige_model.gd b/src/core/economy/prestige_model.gd index b4ca088..79648d6 100644 --- a/src/core/economy/prestige_model.gd +++ b/src/core/economy/prestige_model.gd @@ -13,12 +13,19 @@ extends RefCounted # 宿慧全局加成(economy_state.recompute):0.50×(1−e^(−宿慧/420)) 渐近封顶 # (W5 裁定 F-1;旧 3%/级线性无界已废止,tune_suihui_global_pct 移除) # 敕封触发:转世 ≥ enthrone_rebirths([B§4.4] 约第 2-3 轮) -# 重开曲线目标:1 : 0.6 : 0.4 : 0.3,第 5 轮起触底 ≥0.25×(门 #13 断言) +# 重开曲线目标:1 : 0.48 : 0.42 : 0.40,第 5 轮起触底 ≥0.25×(门 #13 断言) +# [v1.0→v1.1] 用户拍板方案 A:曲线 = 二轮 sim 实测校准值(Balance Bible +# v1.1 §4.3;旧 1:0.6:0.4:0.3 为一轮解析预估,「182min÷倍率」线性模型未计阻尼) # # 静态纯函数:sim 与游戏共用(零漂移);无状态、无分配(除返回值)。 # ============================================================================ -const RESTART_TARGETS: Array[float] = [1.0, 0.6, 0.4, 0.3] +## [v1.0→v1.1] 二轮实测校准值(用户拍板方案 A,Balance Bible v1.1 §4.3): +## 旧 1:0.6:0.4:0.3 为一轮解析预估——「182min÷总倍率」线性模型未计成本指数/ +## 解锁门/购买节奏的阻尼,实测各轮慢 +2.0/+6.2/+9.9min 逐轮放大,R4=0.396 +## 超出旧带上限 0.375(5.6% 边际超差);R2/R3 入带、曲线单调递减、红线已根治。 +## 镜像数据行 tune_restart_curve(balance_tuning.csv);R5 起触底 ≥0.25× 不变。 +const RESTART_TARGETS: Array[float] = [1.0, 0.48, 0.42, 0.40] const RESTART_FLOOR := 0.25 diff --git a/src/core/sim/gates.gd b/src/core/sim/gates.gd index e3fa788..34b8c75 100644 --- a/src/core/sim/gates.gd +++ b/src/core/sim/gates.gd @@ -14,7 +14,9 @@ extends RefCounted # #3 对照 halls.csv 新到达目标列(10/20/36/56/80/102/122/142/164/180)±20% # #14 同 #3 目标表;主口径 greedy(novice/random 噪声归 cross 门观测) # #10 单轮峰值 → **双断言**(单轮 + restart 5 轮 lifetime 均 ≤1e14,见 gate_peak_double) -# #13 前 4 轮容差 ±40% → ±25%(R5 起触底 ≥0.25× 口径不变) +# #13 前 4 轮容差 ±40% → ±25%(R5 起触底 ≥0.25× 口径不变); +# 目标曲线 [v1.0→v1.1] 1:0.6:0.4:0.3 → **1:0.48:0.42:0.40**(用户拍板方案 A: +# 二轮实测校准值,Balance Bible v1.1 §4.3;值在 PrestigeModel.RESTART_TARGETS) # ============================================================================ const HARD_GATES: Array[int] = [5, 10] # W6 起 CI 硬门(架构 §7.2) @@ -112,7 +114,8 @@ static func evaluate_wrath_tradeoff(pure_report: Dictionary, devour_report: Dict "detail": "devour/pure = %.2f×(%.0fmin vs %.0fmin)" % [ratio, b / 60.0, a / 60.0]} -## 门 #13(重开曲线):rebirths=4 的报告;各轮 H10 间隔比 ≈ 1:0.6:0.4:0.3 +## 门 #13(重开曲线):rebirths=4 的报告;各轮 H10 间隔比 ≈ 1:0.48:0.42:0.40 +## ([v1.0→v1.1] 用户拍板方案 A:二轮实测校准值,Balance Bible v1.1 §4.3) static func evaluate_restart(report: Dictionary) -> Dictionary: var rebirths: Array = report.get("rebirths", []) if rebirths.size() < 2: diff --git a/src/tools/gen_balance_sheets.py b/src/tools/gen_balance_sheets.py index 32ece28..9081233 100644 --- a/src/tools/gen_balance_sheets.py +++ b/src/tools/gen_balance_sheets.py @@ -278,7 +278,7 @@ TUNING = [ ("tune_skip_levels", "number", 25, "", "[B§4.2] 跳过机制每档 +25 级"), ("tune_skip_level_cap_per_gen","number", 50, "", "[B§4.2] W5 裁定 P-27(F-2):跳过每档获赠 = min(floor(宿慧/5)×25, 本值)(原无封顶 → R2 开盘白送 2850 级)"), ("tune_enthrone_rebirths", "number", 2, "", "[B§4.4] 阴司敕封触发:转世 ≥2(约第 2-3 轮,累计 8-12h 为其 pacing 后果)"), - ("tune_restart_curve", "text", 0, "1:0.6:0.4:0.3", "[B§4.3] 重开曲线目标(第 5 轮触底 ≥0.25×);模拟器门 #13 断言"), + ("tune_restart_curve", "text", 0, "1:0.48:0.42:0.40", "[B§4.3] 重开曲线目标(第 5 轮触底 ≥0.25×);模拟器门 #13 断言。[v1.0→v1.1] 用户拍板方案 A:二轮 sim 实测校准值(旧 1:0.6:0.4:0.3 为一轮解析预估,未计阻尼)"), ("tune_event_timeout_default","text", 0, "opt_a", "[B§7] 冤魂申诉超时默认 = 超度(opt_a)"), ("tune_souls_div", "number", 10000, "", "[B§6] 超度亡魂 = floor(本轮累计阴德 ÷ 10,000)(叙事计数器,不设闸门;P-14)"), ("tune_enthrone_suihui_req","number", 60, "", "[G§3.4] 阴司敕封解锁条件:宿慧 ≥60([B§4.4] 仅给 8-12h 体感,采信 GDD 数值)"), @@ -664,7 +664,7 @@ def self_check(sheets) -> None: assert tune["tune_click_zone_cap"]["num"] == 16 assert tune["tune_gen_zone_cap"]["num"] == 8 assert tune["tune_global_zone_cap"]["num"] == 64 - assert tune["tune_restart_curve"]["text"] == "1:0.6:0.4:0.3" + assert tune["tune_restart_curve"]["text"] == "1:0.48:0.42:0.40" assert tune["tune_event_timeout_default"]["text"] == "opt_a" # W4 方案 B 调参 4 行(systems-spec §1.2;用户 2026-10-20 拍板) assert tune["tune_boss_kappa_base"]["num"] == 0.01 diff --git a/tests/sim_reports/report-2026-10-21.json b/tests/sim_reports/report-2026-10-21.json index 0123acb..3401044 100644 --- a/tests/sim_reports/report-2026-10-21.json +++ b/tests/sim_reports/report-2026-10-21.json @@ -260,8 +260,8 @@ } }, "restart": { - "detail": "R1=1.00(目标 1.00);R2=0.48(目标 0.60);R3=0.42(目标 0.40);R4=0.40(目标 0.30)", - "pass": false + "detail": "R1=1.00(目标 1.00);R2=0.48(目标 0.48);R3=0.42(目标 0.42);R4=0.40(目标 0.40)", + "pass": true }, "runs": [ { diff --git a/tests/unit/cases/test_balance_data.gd b/tests/unit/cases/test_balance_data.gd index cd01050..40ee093 100644 --- a/tests/unit/cases/test_balance_data.gd +++ b/tests/unit/cases/test_balance_data.gd @@ -34,7 +34,7 @@ func run(a, data: BalanceData) -> void: a.near(data.tune_num("tune_souls_div", 0.0), 10000.0, 1e-9, "超度除数 10000") a.near(data.tune_num("tune_enthrone_suihui_req", 0.0), 60.0, 1e-9, "敕封宿慧门槛 60") a.near(data.tune_num("tune_enthrone_rebirths", 0.0), 2.0, 1e-9, "敕封转世门槛 2") - a.eq(data.tune_text("tune_restart_curve", ""), "1:0.6:0.4:0.3", "重开曲线文本") + a.eq(data.tune_text("tune_restart_curve", ""), "1:0.48:0.42:0.40", "重开曲线文本(v1.1 校准值)") a.near(data.tune_num("tune_wrath_layers", 0.0), 22.0, 1e-9, "冤煞 22 层") a.near(data.tune_num("tune_click_base", 0.0), 1.0, 1e-9, "点击基础值 1") # W5 裁定调参(sim 一轮裁定 §2.3/§2.4):宿慧渐近 + 跳过封顶 + 削减速加深 diff --git a/tests/unit/cases/test_economy_formulas.gd b/tests/unit/cases/test_economy_formulas.gd index 03f5c90..7bf290c 100644 --- a/tests/unit/cases/test_economy_formulas.gd +++ b/tests/unit/cases/test_economy_formulas.gd @@ -37,7 +37,9 @@ func run(a, _data: BalanceData) -> void: a.eq(PrestigeModel.skip_count(23, 5), 4, "skip 23/5 = 4") a.eq(PrestigeModel.skip_count(23, 4), 5, "skip 23/4 = 5(m04)") a.near(PrestigeModel.restart_target(0), 1.0, 1e-9, "重开曲线 R1") - a.near(PrestigeModel.restart_target(3), 0.3, 1e-9, "重开曲线 R4") + a.near(PrestigeModel.restart_target(1), 0.48, 1e-9, "重开曲线 R2(v1.1 校准值)") + a.near(PrestigeModel.restart_target(2), 0.42, 1e-9, "重开曲线 R3(v1.1 校准值)") + a.near(PrestigeModel.restart_target(3), 0.40, 1e-9, "重开曲线 R4(v1.1 校准值)") a.near(PrestigeModel.restart_target(4), 0.25, 1e-9, "重开曲线 R5 触底") a.true_(PrestigeModel.enthrone_available(2, 2), "敕封:转世 2 可") a.true_(not PrestigeModel.enthrone_available(1, 2), "敕封:转世 1 不可")