Files
game-code-4/tests/unit/cases/test_economy_formulas.gd
T
程基岩(engineering-lead) c534e95458 fix: 首跑错误批次(Godot 4.7.2 引擎实证修复 R1-R14)
本机首次具备引擎自验证能力后的首跑修复。四条命令 + 主场景 180 帧全绿:
- --import exit=0(漂移门 git diff data/build 空)
- --validate-only exit=0 RESULT: PASS
- test_runner exit=0 PASS(584 检查,8/8 用例)
- sim_main exit=0(报告落 tests/sim_reports/report-2026-10-20.json)
- 主场景 --quit-after 180 exit=0 零 ERROR/WARNING(F5 可玩)

预定位错误(team-lead 清单):
- R1 economy_state.gd:596 enthone_ready -> enthrone_ready(单点故障连锁)
- R2 hud_root.gd:142,160 HorizontalAlignment.LEFT -> HORIZONTAL_ALIGNMENT_LEFT
- R3 boss_arena.gd 补 var _pending_hall_index := -1
- R4 hall_band_world.gd:95 删 "_ = ext"(非法语句;--check-only 定位)
- R5 sheet_importer._type_ok 补 object->Dictionary / array->Array(9 条假违规)
- R6 game_state.gd econ==null 防护(防 autoload 崩每帧刷错打满管道挂死)

引擎新暴露错误:
- R7 prestige_model floor() Variant 推断(4.7.2 按错误)连锁编译失败
- R8 gates.gd var pass 保留字;R9 %g/%.*f 格式符 ×6 处
- R10 greedy.gd Dictionary.get() 推断;R11 probe_p1 条件表达式推断
- R12 测试用例 data 参数统一标 BalanceData(无类型参数成员调用返 Variant)
- R13 boss_battle 用例 Dictionary 下标运算显式 float 化
- R14 band_world_root.compute_ranges 返回 Array[Vector2](类型化数组不隐式转换)

测试侧数值修正(根因已核实,实现符合设计):
- gen3 可购断言 merit 1000->2000(800 文书 + 1200 首购)
- 开场伤害断言改"循环至 ACTIVE 即停"(INTRO 结束 tick 不计军列伤)
- 容量断言预算 30->34s(战斗真实给 intro 3s + active 30s)

工程加固:
- EconomyState 红线守卫首次越线才报(sim 长跑日志 6.5MB->1KB)
- stage_world.gd stretch=false(SubViewport 手动尺寸驱动生效)

知识缺口登记:docs/engineering/2026-10-20-f-engine-first-run-notes.md
sim 首轮数据:主口径 11/14 过(#2/#3/#12/#13/#14 FAIL 待文策渊裁定)
2026-10-20 18:47:07 +08:00

81 lines
5.3 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
extends RefCounted
# 用例:核心公式(MultiplierSet / PrestigeModel / OfflineModel)
func run(a, _data: BalanceData) -> void:
# ---- MultiplierSet 三乘区 + 软上限 ----
var m := MultiplierSet.new(16.0, 8.0, 64.0)
m.set_click_levels(4, 0.0, 0.01)
a.near(m.click_mult(), 16.0, 1e-9, "点击区 4 级 = ×16")
m.set_click_levels(5, 0.0, 0.01)
a.near(m.click_mult(), 16.0, 1e-9, "点击区 5 级仍钳 ×16")
m.set_gen_eff("gen_01_youhun", 6)
a.near(m.gen_mult("gen_01_youhun"), 7.0, 1e-9, "班底区 6 级 = ×7")
m.set_gen_eff("gen_01_youhun", 99)
a.near(m.gen_mult("gen_01_youhun"), 8.0, 1e-9, "班底区钳 ×8")
m.set_global_add(63.0)
a.near(m.global_mult(), 64.0, 1e-9, "全局区 ×64")
m.set_global_add(100.0)
a.near(m.global_mult(), 64.0, 1e-9, "全局区钳 ×64")
m.set_click_levels(4, 8.0, 0.01)
a.near(m.click_value(1.0, 1000.0), 96.0, 1e-6, "click_value = 1×16 + 1000×8%")
# ---- PrestigeModel ----
a.eq(PrestigeModel.suihui_gain(1.5e9, 1e6, 5.0, 0.0, 0.0), 193, "宿慧 1.5B → 193")
a.eq(PrestigeModel.suihui_gain(1.25e7, 1e6, 5.0, 0.0, 0.0), 17, "宿慧 12.5M → 17")
a.eq(PrestigeModel.suihui_gain(1.5e9, 1e6, 5.0, 0.0, 0.3), 135, "宿慧 30% 削减 → 135")
a.eq(PrestigeModel.suihui_gain(1.5e9, 1e6, 5.0, 0.45, 0.0), 279, "宿慧 m03×3 → 279")
a.eq(PrestigeModel.suihui_gain(0.0, 1e6, 5.0, 0.0, 0.0), 0, "零累计 → 0 宿慧")
a.eq(PrestigeModel.wrath_layer_cost(1, 8.0, 1.8), 8, "怨煞层 1 成本 8")
a.eq(PrestigeModel.wrath_layer_cost(2, 8.0, 1.8), 28, "怨煞层 2 成本 28")
a.eq(PrestigeModel.wrath_layer_cost(22, 8.0, 1.8), 2087, "怨煞层 22 成本 2087")
a.near(PrestigeModel.wrath_penalty(10, 0.03, 0.3, 0.0), 0.3, 1e-9, "冤气削减封顶 30%")
a.near(PrestigeModel.wrath_penalty(10, 0.03, 0.3, 0.15), 0.15, 1e-9, "m07 改 15%")
a.near(PrestigeModel.wrath_penalty(5, 0.03, 0.3, 0.0), 0.15, 1e-9, "5 层 = 15%")
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(4), 0.25, 1e-9, "重开曲线 R5 触底")
a.true_(PrestigeModel.enthrone_available(2, 2), "敕封:转世 2 可")
a.true_(not PrestigeModel.enthrone_available(1, 2), "敕封:转世 1 不可")
# ---- OfflineModel 分带 + 72h 封顶 + 时钟回拨 + 长明灯加法 + 硬顶 ----
# W4 契约 v2(systems-spec §2.4/§2.5):第三参数由 offline_mult(乘性)改为
# offline_add(加法,直接加在各档速率上,各档硬顶 100%)。
var r1: Dictionary = OfflineModel.settle(3600.0, 10.0, 0.0, 2.0, 1.0, 8.0, 0.7, 24.0, 0.4, 0.1, 72.0)
a.near(r1["effective_hours"], 1.0, 1e-9, "离线 1h = 1h 等效")
a.near(r1["merit"], 36000.0, 1e-6, "离线 1h × 10/s")
var r8: Dictionary = OfflineModel.settle(8.0 * 3600.0, 10.0, 0.0, 2.0, 1.0, 8.0, 0.7, 24.0, 0.4, 0.1, 72.0)
a.near(r8["effective_hours"], 6.2, 1e-9, "离线 8h = 6.2h 等效(契约 v2:77.5%)")
a.near(r8["merit"], 223200.0, 1e-6, "离线 8h 收益")
var r24: Dictionary = OfflineModel.settle(24.0 * 3600.0, 10.0, 0.0, 2.0, 1.0, 8.0, 0.7, 24.0, 0.4, 0.1, 72.0)
a.near(r24["effective_hours"], 12.6, 1e-9, "离线 24h = 12.6h 等效(契约 v2)")
var r100: Dictionary = OfflineModel.settle(100.0 * 3600.0, 10.0, 0.0, 2.0, 1.0, 8.0, 0.7, 24.0, 0.4, 0.1, 72.0)
a.near(r100["effective_hours"], 17.4, 1e-9, "离线 100h → 72h 封顶 = 17.4h 等效(24.2%)")
var rneg: Dictionary = OfflineModel.settle(-5.0, 10.0, 0.0, 2.0, 1.0, 8.0, 0.7, 24.0, 0.4, 0.1, 72.0)
a.near(rneg["merit"], 0.0, 1e-9, "时钟回拨 → 0")
a.near(OfflineModel.ratio_8h_over_1h(2.0, 1.0, 8.0, 0.7, 24.0, 0.4, 0.1), 6.2, 1e-9,
"历史口径比率 6.2(契约 v2 起门 #9 改判等效小时,见 SimGates.gate_offline_v2)")
# 长明灯加法:+30%(6 级)⇒ 2-8h 档 70%+30% = 100%(硬顶)⇒ 8h = 8.0h(效率 100%)
var r8l: Dictionary = OfflineModel.settle(8.0 * 3600.0, 10.0, 0.30, 2.0, 1.0, 8.0, 0.7, 24.0, 0.4, 0.1, 72.0)
a.near(r8l["effective_hours"], 8.0, 1e-9, "长明灯满级 8h = 8.0h 等效(硬顶 100%)")
a.near(r8l["merit"], 288000.0, 1e-6, "长明灯满级 8h 收益")
# 加法不越过硬顶:+55%(6 级 + m05)仍 = 8.0h;8-24h 档 40%+30% = 70%
var r8m: Dictionary = OfflineModel.settle(8.0 * 3600.0, 10.0, 0.55, 2.0, 1.0, 8.0, 0.7, 24.0, 0.4, 0.1, 72.0)
a.near(r8m["effective_hours"], 8.0, 1e-9, "加法 +55% 仍钳 8.0h(0-2h/2-8h 双档顶格)")
var r24l: Dictionary = OfflineModel.settle(24.0 * 3600.0, 10.0, 0.30, 2.0, 1.0, 8.0, 0.7, 24.0, 0.4, 0.1, 72.0)
a.near(r24l["effective_hours"], 2.0 + 6.0 + 16.0 * 0.7, 1e-9, "长明灯满级 24h = 19.2h(8-24h 档 70%)")
# 效率硬顶:任意加成下等效 ≤ 实际(离线不得优于在线)
for dur_h in [1.0, 8.0, 24.0, 72.0]:
for add in [0.0, 0.30, 0.55, 5.0]:
var rr: Dictionary = OfflineModel.settle(dur_h * 3600.0, 10.0, add,
2.0, 1.0, 8.0, 0.7, 24.0, 0.4, 0.1, 72.0)
a.true_(float(rr["effective_hours"]) <= dur_h + 1e-9,
"效率硬顶:%s h 离线 加法 +%s ⇒ 等效 %.2f h ≤ %s h" % [
str(dur_h), str(add), float(rr["effective_hours"]), str(dur_h)])
# ---- BigNumber 红线 ----
a.true_(BigNumber.assert_within(1e13, "test"), "1e13 不过红线")
a.true_(not BigNumber.assert_within(1e15, "test"), "1e15 触红线")