iter49-卡1:效果端口与掷格分件批(轴戊两件建件):EffectPort六Callable路由+RewardRoller掷格面迁出+两业务类零引擎直连+effect_port_test V1-V3
This commit is contained in:
1 parent
926b05a61c
commit
11beb4fc59
31 files changed
+697
-586
No files matched your search
@@ -0,0 +1,129 @@
|
||||
# tests/unit/battle_settlement/effect_port_test.gd — 效果端口件验收宿主(效果端口与掷格分件批)
|
||||
# 断言面 = 三套(需求卡片 §9 对照表行 1/2/3 载体):
|
||||
# V1 端口单实例与真实发射路由(装配链同一实例+attack_commit 经真实战斗流命中可观测)
|
||||
# V2 业务类零引擎直连(源码契约锚——两业务文件全文零 EffectEngine/EffectContext token)
|
||||
# V3 伤害域事实面闭集(合成行阳性/阴性对照——载荷携带事实可达、宿主链事实不可达)
|
||||
# 协议照单元侧惯例:extends SceneTree+Kit.check 主通道;--script 模式无 autoload,
|
||||
# ConfigDB 显式实例化构造注入(各套自建 RngService——源套掷点隔离形态)。
|
||||
# 运行:godot --headless --path game --script res://tests/unit/battle_settlement/effect_port_test.gd
|
||||
extends SceneTree
|
||||
|
||||
const Kit := preload("res://tests/unit/test_kit.gd")
|
||||
const ConfigDBScript := preload("res://core/config_db.gd")
|
||||
const EffectEngineScript := preload("res://core/effect_engine.gd")
|
||||
|
||||
const TEST_SEED := 42 # 夹具种子(基建值非玩法数值——同种子两跑对比的隔离前提)
|
||||
const LAYER_COUNTER := "swap_charge_layers" # 整备军备蓄力层计数器(feats.tres charge_rotation 行)
|
||||
|
||||
var _db
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
_db = ConfigDBScript.new()
|
||||
var load_ok: bool = _db.load_tables("res://data/tables", true)
|
||||
Kit.check("前置:表加载零 error", load_ok and _db.diagnostics_count("error") == 0)
|
||||
if load_ok:
|
||||
_smoke_effect_port_wiring()
|
||||
_test_business_classes_engine_free()
|
||||
_test_biz_fact_face_closed()
|
||||
quit(Kit.finish("effect_port_test"))
|
||||
|
||||
|
||||
# ---- 夹具 ----
|
||||
|
||||
func _enc(feats: Array) -> Dictionary:
|
||||
return {"level_index": 1, "node_depth": 0, "hero_hp": int(_db.combat.hero.max_hp),
|
||||
"hero_armor_class": 10, "hero_ap_bonus": 0, "attunement": [], "feats": feats, "tree_rules": [],
|
||||
"battle_index": 1, "voucher_rap": 0, "voucher_time_sand": 0, "node_type": "boss", "adv_bucket": {}}
|
||||
|
||||
|
||||
func _orch(seed_v: int):
|
||||
var rng = load("res://core/rng_service.gd").new()
|
||||
rng.set_master_seed(seed_v)
|
||||
return load("res://core/battle_orchestrator.gd").new(_db, rng)
|
||||
|
||||
|
||||
func _hero_attack_value(rows: Array) -> int:
|
||||
for r in rows:
|
||||
if String(r.get("type", "")) == "damage" and String(r.get("source_class", "")) == "hero_attack":
|
||||
return int(r.get("value", 0))
|
||||
return -1
|
||||
|
||||
|
||||
# ---- V1 端口单实例与真实发射路由 ----
|
||||
|
||||
func _smoke_effect_port_wiring() -> void:
|
||||
print("--- 效果端口:装配链单实例+真实发射路由 ---")
|
||||
# 充能跑(整备军备在册——attack_commit 订阅行 attack_bonus per 蓄力层计数器)
|
||||
var orch_c = _orch(TEST_SEED)
|
||||
var rows_c: Array = []
|
||||
orch_c.step_row.connect(func(row): rows_c.append(row))
|
||||
Kit.check("V1 begin_battle 受理", orch_c.begin_battle(_enc(["charge_rotation"])))
|
||||
# 端口同一性(内部字段直查=受控例外:端口同一性无公开查询面,对照表行 1 注明理由)
|
||||
var port_d = orch_c.fx_host().damage._port
|
||||
var port_a = orch_c.action_economy._port
|
||||
Kit.check("V1 端口在座(damage/action_economy 双绑)", port_d != null and port_a != null)
|
||||
Kit.check("V1 端口单实例(两业务类持同一 EffectPort)", port_d == port_a)
|
||||
# 蓄力层预置 3(调试注入两层·数据面粒度——计数器账面直写,与 refresh_feats_test V8 同法)
|
||||
EffectEngineScript.counter_add(orch_c.fx_host().counters_ctx, LAYER_COUNTER, 3)
|
||||
# 最小回合:停手即轮末结算(主角先手攻击在战斗段同步兑付——零 await 同步链)
|
||||
orch_c.minesweeper.settle_batch([{"action": "stop"}])
|
||||
var v_c := _hero_attack_value(rows_c)
|
||||
Kit.check("V1 attack_commit 命中批可观测:主角攻击行在流", v_c >= 0)
|
||||
Kit.check("V1 命中批经端口路由全处理(attack_bonus 消费行+counter_add set-0 行——层账 3→0)",
|
||||
EffectEngineScript.counter(orch_c.fx_host().counters_ctx, LAYER_COUNTER) == 0)
|
||||
# 对照跑(同种子同 enc 零预置——attack_commit 订阅行 when 不通过=零命中零扰动)
|
||||
var orch_0 = _orch(TEST_SEED)
|
||||
var rows_0: Array = []
|
||||
orch_0.step_row.connect(func(row): rows_0.append(row))
|
||||
orch_0.begin_battle(_enc(["charge_rotation"]))
|
||||
orch_0.minesweeper.settle_batch([{"action": "stop"}])
|
||||
var v_0 := _hero_attack_value(rows_0)
|
||||
Kit.check("V1 对照跑攻击行在流", v_0 >= 0)
|
||||
Kit.check("V1 delta 读数非零(蓄力层加算进入主角攻击基值)", v_c - v_0 >= 1)
|
||||
|
||||
|
||||
# ---- V2 业务类零引擎直连(源码契约锚——theme_projection_test 源码锚惯例)----
|
||||
|
||||
func _test_business_classes_engine_free() -> void:
|
||||
print("--- 效果端口:业务类零引擎直连源码锚 ---")
|
||||
for path in ["res://core/damage_controller.gd", "res://core/action_economy.gd"]:
|
||||
var src := FileAccess.get_file_as_string(path)
|
||||
Kit.check("V2 扫描面非空前置:%s" % path, src.length() > 0)
|
||||
Kit.check("V2 全文零引擎直连 token(EffectEngine/EffectContext):%s" % path,
|
||||
not src.contains("EffectEngine") and not src.contains("EffectContext"))
|
||||
|
||||
|
||||
# ---- V3 伤害域事实面闭集(合成行两对照——damage_taken 真实单据流)----
|
||||
|
||||
func _test_biz_fact_face_closed() -> void:
|
||||
print("--- 效果端口:业务发射事实面闭集 ---")
|
||||
var orch = _orch(TEST_SEED)
|
||||
Kit.check("V3 begin_battle 受理", orch.begin_battle(_enc([])))
|
||||
# 合成行直改数据面(调试注入两层·数据面粒度):effect 段逐字抄现役铁壁 shield 表行(kind/value 零推表);
|
||||
# rebuild 走持有 id 过滤真实表行——合成行经直改表行进入装载域(订阅面换代为合成行独占,确定性)
|
||||
var pos_id := "probe_shield_pos"
|
||||
var neg_id := "probe_shield_neg"
|
||||
_db.crossrun.feats.rows.append({"feat_id": pos_id, "display_name": "探针盾甲甲", "rarity": "common",
|
||||
"desc": "V3 阳性对照(hp_ratio 载荷携带)", "triggers": [{"on": "damage_taken",
|
||||
"when": [{"fact": "hp_ratio", "op": "lte", "value": 100}], "do": {"kind": "shield", "value": 1}}]})
|
||||
_db.crossrun.feats.rows.append({"feat_id": neg_id, "display_name": "探针盾甲乙", "rarity": "common",
|
||||
"desc": "V3 阴性对照(battle_index 闭面外)", "triggers": [{"on": "damage_taken",
|
||||
"when": [{"fact": "battle_index", "op": "eq", "value": 1}], "do": {"kind": "shield", "value": 1}}]})
|
||||
var ctrl = orch.fx_host().damage
|
||||
# 阳性对照(载荷携带事实——harness 能见命中):合成行独占订阅面(rebuild 持有 id 过滤——
|
||||
# 只挂阳性行),board_flat_hero 真实单据流,base_value 足量避开伤害下限保底
|
||||
orch.fx_host().rebuild_subscriptions([pos_id], [], [])
|
||||
var hp0 := int(ctrl.units.hero_unit().get("hp", 0))
|
||||
var t_a: Dictionary = ctrl.open_ticket(ctrl.KIND_BOARD_FLAT_HERO, "probe_port", "闭面探测甲",
|
||||
{"channel": "board", "base_value": 10, "target_rule": "hero", "ri": 1, "origin_cell": orch.minesweeper.hero_pos()})
|
||||
ctrl.flush_submit(t_a)
|
||||
var hp1 := int(ctrl.units.hero_unit().get("hp", 0))
|
||||
Kit.check("V3 阳性对照:hp_ratio 载荷 when 通过——承伤减量恰 1(10→9)", hp0 - hp1 == 9)
|
||||
# 阴性对照(闭面外事实——宿主链答而业务面不答):订阅面换代为阴性合成行独占,同路径零减量=F10 现状形态守卫
|
||||
orch.fx_host().rebuild_subscriptions([neg_id], [], [])
|
||||
var t_b: Dictionary = ctrl.open_ticket(ctrl.KIND_BOARD_FLAT_HERO, "probe_port", "闭面探测乙",
|
||||
{"channel": "board", "base_value": 10, "target_rule": "hero", "ri": 1, "origin_cell": orch.minesweeper.hero_pos()})
|
||||
ctrl.flush_submit(t_b)
|
||||
var hp2 := int(ctrl.units.hero_unit().get("hp", 0))
|
||||
Kit.check("V3 阴性对照:battle_index 不答——零减量(10→10,事实面未扩)", hp1 - hp2 == 10)
|
||||
@@ -0,0 +1 @@
|
||||
uid://b11nytp21w58b
|
||||
@@ -221,27 +221,27 @@ func _smoke_global_variant() -> void:
|
||||
Kit.check("确定性:同种子 Boss 图两遍 board_variants/变异格/取号逐位相同(region 布置路径)", _arr_str_eq(bb2.board_variant_ids(), boss1_ids) and bb2.variants == boss1_variants and crng2.variant_calls == boss1_calls)
|
||||
# ④ 资源面偏斜投影(RewardEconomy 纯函数面——守恒/白名单/构造例/合成序)
|
||||
var eco = load("res://core/reward_economy.gd").new(_db, load("res://core/rng_service.gd").new())
|
||||
var base_rows: Array = eco._rollable_rows()
|
||||
var base_sum: int = eco._pool_weight(base_rows)
|
||||
var base_rows: Array = eco.roller._rollable_rows()
|
||||
var base_sum: int = eco.roller._pool_weight(base_rows)
|
||||
Kit.check("偏斜基线:正向池现行合计 77(挖掘风险批口径)", base_sum == 77)
|
||||
eco.board_skew = 0
|
||||
var off_rows: Array = eco._biased_cell_rows(0) # 内容数据驱动扩展批 T4:偏运权重改 reward_roll 订阅命中入参(luck_bias 注入位退役)
|
||||
eco.roller.board_skew = 0
|
||||
var off_rows: Array = eco.roller._biased_cell_rows(0) # 内容数据驱动扩展批 T4:偏运权重改 reward_roll 订阅命中入参(luck_bias 注入位退役)
|
||||
Kit.check("off 态零扰动:board_skew=0 行集与基线逐位相同(不进投影域)", off_rows.size() == base_rows.size() and _rows_weights_equal(off_rows, base_rows))
|
||||
eco.board_skew = 30
|
||||
var on_rows: Array = eco._biased_cell_rows(0)
|
||||
Kit.check("合计守恒:偏斜后正向池合计 = 77(整数最大余数重归一)", eco._pool_weight(on_rows) == 77)
|
||||
eco.roller.board_skew = 30
|
||||
var on_rows: Array = eco.roller._biased_cell_rows(0)
|
||||
Kit.check("合计守恒:偏斜后正向池合计 = 77(整数最大余数重归一)", eco.roller._pool_weight(on_rows) == 77)
|
||||
var wmap := _rows_weight_map(on_rows)
|
||||
Kit.check("白名单豁免:weaken/gp/action 三行偏斜后逐位不动", wmap.get("weaken", -1) == 8 and wmap.get("ap_up", -1) == 6 and wmap.get("gp_coin", -1) == 2)
|
||||
Kit.check("生存族下调:heal 4→3 / defense 11→7(v=30 构造例)", wmap.get("heal_minor", -1) == 3 and wmap.get("ac_up", -1) == 7)
|
||||
Kit.check("进攻族上浮:attack 14→15 / blast 16→18 / pierce 8→9 / magic 8→9(v=30 构造例,等比上浮守恒)", wmap.get("attack_up", -1) == 15 and wmap.get("spark_aoe", -1) == 18 and wmap.get("pierce_strike", -1) == 9 and wmap.get("magic_bolt", -1) == 9)
|
||||
eco.board_skew = 30
|
||||
var combo_rows: Array = eco._biased_cell_rows(2) # 件级加算 2=偏运硬币行 value(reward_roll bias_rewards 命中面同值)
|
||||
eco.roller.board_skew = 30
|
||||
var combo_rows: Array = eco.roller._biased_cell_rows(2) # 件级加算 2=偏运硬币行 value(reward_roll bias_rewards 命中面同值)
|
||||
var cw := _rows_weight_map(combo_rows)
|
||||
Kit.check("合成序:图级偏斜先(乘法面)件级 luck_bias 后(加法 +2×四稀有类)——合计 77+8", eco._pool_weight(combo_rows) == 77 + 4 * 2)
|
||||
Kit.check("合成序:图级偏斜先(乘法面)件级 luck_bias 后(加法 +2×四稀有类)——合计 77+8", eco.roller._pool_weight(combo_rows) == 77 + 4 * 2)
|
||||
Kit.check("合成序构造例:skew(heal 3)+2=5 / gp 2+2=4 / pierce 9+2=11 / magic 9+2=11(图级定型后玩家构筑叠加)", cw.get("heal_minor", -1) == 5 and cw.get("gp_coin", -1) == 4 and cw.get("pierce_strike", -1) == 11 and cw.get("magic_bolt", -1) == 11)
|
||||
eco.board_skew = 50
|
||||
var v50: Dictionary = _rows_weight_map(eco._biased_cell_rows(0))
|
||||
Kit.check("域上界 v=50:守恒成立且逐行权重恒正(域 [0,50] 安全)", eco._pool_weight(eco._biased_cell_rows(0)) == 77 and int(v50.get("heal_minor", -1)) > 0)
|
||||
eco.roller.board_skew = 50
|
||||
var v50: Dictionary = _rows_weight_map(eco.roller._biased_cell_rows(0))
|
||||
Kit.check("域上界 v=50:守恒成立且逐行权重恒正(域 [0,50] 安全)", eco.roller._pool_weight(eco.roller._biased_cell_rows(0)) == 77 and int(v50.get("heal_minor", -1)) > 0)
|
||||
# wr10 偏运判定收口+注入接线(p5 缝拆)——判定面夹具白盒装载(现网 game/data/tables/ 无产出
|
||||
# bias_rewards 的数据行,先例=tree_rules_v11_test 读 _triggers);命中数前置写在消息里防空扫描面假绿。
|
||||
var wrng = load("res://core/rng_service.gd").new()
|
||||
@@ -256,13 +256,13 @@ func _smoke_global_variant() -> void:
|
||||
Kit.check("wr10①判定面:空订阅=%d / 装载后命中 %d 行 → luck_bias_for(4)=%d(期望 0/1/2;命中 0 即判据空转)" % [empty_bias, hit_rows, loaded_bias],
|
||||
empty_bias == 0 and hit_rows == 1 and loaded_bias == 2)
|
||||
Kit.check("wr10②接线面:组装点注入的查询口有效且与宿主查询口同源",
|
||||
woc._reward_economy.luck_bias_query.is_valid() and woc._reward_economy.luck_bias_query == wfx.luck_bias_for)
|
||||
woc._reward_economy.board_skew = 0
|
||||
woc._reward_economy.roller.luck_bias_query.is_valid() and woc._reward_economy.roller.luck_bias_query == wfx.luck_bias_for)
|
||||
woc._reward_economy.roller.board_skew = 0
|
||||
# 取值面防御:未接线时按「取不到偏运」计红而非崩在空回调上(撤销证明⑤态下本套件其余断言仍须跑完)
|
||||
var qb: Callable = woc._reward_economy.luck_bias_query
|
||||
var qb: Callable = woc._reward_economy.roller.luck_bias_query
|
||||
var wbias: int = int(qb.call(4)) if qb.is_valid() else -1
|
||||
Kit.check("wr10③权重生效面:注入回调取回 %d → 偏运池合计 77+4*2(实测 %d)" % [wbias, woc._reward_economy._pool_weight(woc._reward_economy._biased_cell_rows(wbias))],
|
||||
wbias == 2 and woc._reward_economy._pool_weight(woc._reward_economy._biased_cell_rows(wbias)) == 77 + 4 * 2)
|
||||
Kit.check("wr10③权重生效面:注入回调取回 %d → 偏运池合计 77+4*2(实测 %d)" % [wbias, woc._reward_economy.roller._pool_weight(woc._reward_economy.roller._biased_cell_rows(wbias))],
|
||||
wbias == 2 and woc._reward_economy.roller._pool_weight(woc._reward_economy.roller._biased_cell_rows(wbias)) == 77 + 4 * 2)
|
||||
# 普通图摇号全链路确定性(off 路径)——同种子普通图资源映射逐位相同(golden 对拍另有批前快照)
|
||||
var nrng = load("res://core/rng_service.gd").new()
|
||||
nrng.set_master_seed(42)
|
||||
|
||||
Reference in new issue
Block a user