From 33e1f1105783589cb86f14d66e25affa0f220155 Mon Sep 17 00:00:00 2001 From: DKChan <524912269@qq.com> Date: Wed, 21 Oct 2026 02:45:57 +0800 Subject: [PATCH] =?UTF-8?q?test(=E5=8D=A1=E2=91=A1=20T7=E8=A1=A5=E6=BC=8F)?= =?UTF-8?q?:=20=E8=A1=A5=E9=BD=90=20=C2=A79=20=E6=AD=A3=E5=90=91=E9=AA=8C?= =?UTF-8?q?=E6=94=B6=E4=B8=BB=E5=8A=9B=20V1/V2/V6=20=E4=B8=8E=E5=85=A8=20V?= =?UTF-8?q?n=20=E6=A0=87=E6=B3=A8=20+=20V1/V4=20=E5=8F=98=E5=BC=82?= =?UTF-8?q?=E9=AA=8C=E8=AF=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 前批合回时漏建卡② §9 逐需求点「正向验收主力」断言(四桶机制),仅靠迁移旧测+V12 兜底—— V12 formed/rules 输出面不作判据,adventure 桶跨战持久若坏不会红,核心缺陷消解无回归网。本批补: - V1 冒烟新段:同一 RunLedger 两 begin_battle 各真实 request_dig,断 adv_twin_digs 经 ctx 就地写宿主字典跨战累积=2 - V2:start_adventure 后 adventure 桶置空(feat_vouchers) - V6 冒烟新段:次战新编排器 fx_counters_battle 天然空、battle 桶 insurance_used 读 0 - V3/V4/V5/V7/V8/V9/V10 补〔无状态化 Vn〕尾缀使 §9 可追溯 - 变异验证:V1 断 adventure 桶引用改新建空字典→3 条红;V4 断 level 桶不共享→红(证回归网真拦得住) --- game/smoke/smoke_all.gd | 56 ++++++++++++++++++- game/tests/static_scan.sh | 2 +- game/tests/unit/core/effect_words_t1_test.gd | 2 +- game/tests/unit/core/feat_vouchers_test.gd | 7 ++- .../data_config/counter_prefix_fatal_test.gd | 2 +- 5 files changed, 62 insertions(+), 7 deletions(-) diff --git a/game/smoke/smoke_all.gd b/game/smoke/smoke_all.gd index dd8c2b10..7f8cf2e8 100644 --- a/game/smoke/smoke_all.gd +++ b/game/smoke/smoke_all.gd @@ -697,6 +697,7 @@ func _run_core_smoke(db, rng) -> void: _smoke_meta() _smoke_tree() _smoke_tree_rules() + _smoke_counter_buckets() _smoke_node_map_templates() @@ -6049,6 +6050,57 @@ func _smoke_tree() -> void: # ---- 天赋树六键行为冒烟(V10:开局姿态窗/危险直觉限次/财富之道聚散/守侧失误补偿; # 引擎直构 + 编排授予段直注(_grant_seg._judge_seal=结算授予段封印判定单口))---- +func _smoke_counter_buckets() -> void: + print("--- 效果引擎无状态化四桶正向验收(卡② §9 V1/V6)---") + # V1——adventure 桶跨战持久:同一 RunLedger 下两战各真实 request_dig 一次, + # 断付费计数经 ctx 就地写入宿主字典并跨战累积(副本搬运态:读不到上一战值→第 2 战仍 1,此断言会红)。 + var led = load("res://core/run_ledger.gd").new() + var RigS = load("res://core/battle_orchestrator.gd") + var RngS = load("res://core/rng_service.gd") + var enc1 = {"level_index": 1, "node_depth": 0, "hero_hp": 40, "hero_atk": 8, "hero_max_hp": 40, + "hero_armor_class": 10, "hero_ap_bonus": 0, "attunement": [], "attune_charges": {}, + "feats": ["twin_pick"], "tree_rules": [], "battle_index": 1, + "bonus_ref": {"atk": 0, "max_hp": 0, "armor_class": 0}, "voucher_rap": 0, "voucher_time_sand": 0, + "node_type": "battle", "ledger": led} + var o1 = RigS.new(_db, _fresh_rng(779301)) + o1.begin_battle(enc1) + var dig1: bool = o1.action_economy.request_dig(0, false) + check(dig1, "V1 第1战真实 request_dig 受理(begin_battle 后 ACTIVE 相位)〔无状态化 V1〕") + check(int(led.adventure_counters.get("adv_twin_digs", 0)) == 1, + "V1 第1战付费挖格经 ctx 就地写 adventure 桶=1(无 export 搬运时点)〔无状态化 V1〕") + var enc2 = enc1.duplicate(true) + enc2["battle_index"] = 2 + var o2 = RigS.new(_db, _fresh_rng(779302)) + o2.begin_battle(enc2) + check(EffectEngine.counter(o2.counters_ctx, "adv_twin_digs") == 1, + "V1 第2战 begin_battle 经 ctx 读到上一战持久值=1(读路径走宿主字典引用,非副本种子回灌)〔无状态化 V1〕") + o2.action_economy.request_dig(0, false) + check(int(led.adventure_counters.get("adv_twin_digs", 0)) == 2, + "V1 第2战挖后 adventure 桶跨战累积=2(run_ledger:42 判空早退缺陷消解的回归钉面——旧副本态恒 1)〔无状态化 V1〕") + # V6——battle 桶随宿主销毁:第1战标错落 insurance_used(battle 桶无前缀),次战新编排器读 0。 + var encA = {"level_index": 1, "node_depth": 0, "hero_hp": 40, "hero_atk": 8, "hero_max_hp": 40, + "hero_armor_class": 10, "hero_ap_bonus": 0, "attunement": [], "attune_charges": {}, + "feats": ["perfect_insurance"], "tree_rules": [], "battle_index": 1, + "bonus_ref": {"atk": 0, "max_hp": 0, "armor_class": 0}, "voucher_rap": 0, "voucher_time_sand": 0, + "node_type": "battle", "ledger": load("res://core/run_ledger.gd").new()} + var oA = RigS.new(_db, _fresh_rng(779401)) + oA.begin_battle(encA) + var safe_a: Variant = null + for ca in oA.minesweeper.current_board().all_cells(): + if not oA.minesweeper.current_board().is_mine(ca): + safe_a = ca + break + oA._grant_seg._judge_seal({"cell": safe_a, "hit": false}, 1) + check(EffectEngine.counter(oA.counters_ctx, "insurance_used") == 1, + "V6 第1战标错落 battle 桶 insurance_used=1(_apply_counter_hit 静态写口经 ctx)〔无状态化 V6〕") + var encB = encA.duplicate(true) + encB["battle_index"] = 2 + var oB = RigS.new(_db, _fresh_rng(779402)) + oB.begin_battle(encB) + check(EffectEngine.counter(oB.counters_ctx, "insurance_used") == 0, + "V6 battle 桶随宿主销毁:次战新编排器 fx_counters_battle 天然空,读 insurance_used=0〔无状态化 V6〕") + + func _smoke_tree_rules() -> void: print("--- 天赋树六键行为(V10:窗/限次/聚散/守侧)---") # 开局姿态组:每关首轮窗(battle_index 事实——引擎直构 + 自建 ctx) @@ -6078,13 +6130,13 @@ func _smoke_tree_rules() -> void: got_add = true check(got_mult and got_add, "锋:踩雷减半命中+限次记账(counter 0<2 闸开——damage_mult+counter_add 双行同预评估态)") EffectEngine.counter_add(ctx_fz, "lvl_tree_sense_sharp_used", 2) # 直调置数(V10 注入口径——置 level 桶计数后走事件路径) - check((fz.dispatch(ctx_fz, "mine_hit", {}, Callable()) as Array).is_empty(), "锋:限次耗尽全额(counter 2 不<2 闸关——每关限次 provisional 2)") + check((fz.dispatch(ctx_fz, "mine_hit", {}, Callable()) as Array).is_empty(), "锋:限次耗尽全额(counter 2 不<2 闸关——每关限次 provisional 2)〔无状态化 V5〕") var fz2 = load("res://core/effect_engine.gd").new(null) fz2.rebuild([], [], ["sense_sharp"]) var ctx_fz2 := EffectContext.new() ctx_fz2.level_bucket = lvl_shared # 挂同一 level 桶引用(新形态跨战持久:无 export/seed,同字典即持久) check((fz2.dispatch(ctx_fz2, "mine_hit", {}, Callable()) as Array).is_empty(), - "锋:限次跨战持久(level 桶共享引用——替代旧副本搬运链)") + "锋:限次跨战持久(level 桶共享引用——替代旧副本搬运链)〔无状态化 V4〕") # 守侧五场景(契约 12):编排 rig——begin_battle 携 sense_guard,授予段 _grant_seg._judge_seal 直注 var orch = load("res://core/battle_orchestrator.gd").new(_db, _fresh_rng(777001)) check(orch.begin_battle({"level_index": 1, "node_depth": 0, "hero_hp": 40, "hero_atk": 8, "hero_max_hp": 40, diff --git a/game/tests/static_scan.sh b/game/tests/static_scan.sh index bdabdc7b..42bbdb91 100644 --- a/game/tests/static_scan.sh +++ b/game/tests/static_scan.sh @@ -228,7 +228,7 @@ else echo "$S12_FACE" | tr '\n' '\0' | xargs -0 grep -En "$S12_SYM" 2>/dev/null fail=1 else - echo "[static_scan] S12 ok(扫描面 $S12_FC 个 .gd/.tres,旧件零残留)" + echo "[static_scan] S12 ok(扫描面 $S12_FC 个 .gd/.tres,旧件零残留)〔无状态化 V10〕" fi fi fi diff --git a/game/tests/unit/core/effect_words_t1_test.gd b/game/tests/unit/core/effect_words_t1_test.gd index d0c44dc2..0979ef5d 100644 --- a/game/tests/unit/core/effect_words_t1_test.gd +++ b/game/tests/unit/core/effect_words_t1_test.gd @@ -113,7 +113,7 @@ func _init() -> void: "V4:counter_spend 静态写口消耗(用后清零语义承载面)〔专长批 V4〕") fx5.dispatch(ctx5, "round_start", {"round": 2}) Kit.check_quiet(EffectEngine.counter(ctx5, "round_mines") == 0, - "V4:round_ 前缀计数器随 round_start 整桶清空(旧 reset_at 名单声明制换代为前缀归桶)〔专长批 V4〕") + "V4:round_ 前缀计数器随 round_start 整桶清空(旧 reset_at 名单声明制换代为前缀归桶)〔无状态化 V8〕") # battle 桶对照:无前缀 helm_layers 不受 round_start 影响(跨 round_start 存活) var fx6 = EffectEngineScript.new(null) var ctx6 := EffectContext.new() diff --git a/game/tests/unit/core/feat_vouchers_test.gd b/game/tests/unit/core/feat_vouchers_test.gd index 2bece985..0179721c 100644 --- a/game/tests/unit/core/feat_vouchers_test.gd +++ b/game/tests/unit/core/feat_vouchers_test.gd @@ -26,6 +26,9 @@ func _init() -> void: Kit.check_quiet(false, "V9 前置:冒险启动") quit(Kit.finish("feat_vouchers")) return + # V2——adventure 桶新冒险置空:start_adventure 后宿主字典为空(寿命随 RunLedger,无残留) + Kit.check_quiet(a.run_ledger.adventure_counters.is_empty(), + "V2:新冒险 adventure 桶置空(start_adventure 后 run_ledger.adventure_counters 为空)〔无状态化 V2〕") # V9-1 刷新券:入存量账(冒险级)——反例=写进 _attune_refresh_x(X 每停点重置会吞存量) a._reset_refresh_x() # X 归零基线(模拟新停点) @@ -136,7 +139,7 @@ func _init() -> void: Kit.check_quiet(b4.resume_adventure(bp_legacy2, {}), "V13:旧档 adventure_counters 含改名前旧键续玩受理零崩溃(无迁移——旧键留桶成死键)〔卡② V13/H1〕") Kit.check_quiet(int(b2.run_ledger.adventure_counters.get("pain_layers", -1)) == 3 and b2.refresh_stock() == 2, - "V16:续玩回填逐项一致(计数恢复——跨战账不丢)〔专长批 V16〕") + "V16:续玩回填逐项一致(adventure 桶随断点往返·计数恢复)〔无状态化 V3〕") # V25 完美保险专长验收组(R2 全项:装载/限次一次/计数纯净/两账相减/跨战清零/事件不吞) var fi: Dictionary = db.feats.row_by_id("perfect_insurance") Kit.check_quiet(not fi.is_empty() and String(fi.get("rarity", "")) == "rare" @@ -178,7 +181,7 @@ func _init() -> void: orch._perfect_hit_false = 1 orch._perfect_hit_true = 1 Kit.check_quiet(orch.perfect_sweep_earned(), - "判定相减:误标 1 − 豁免 1 = 有效 0 且标中≥1 → 完美成立〔卡② R2 真值表〕") + "判定相减:误标1−豁免1=有效0且标中≥1→完美成立(保险每战限次一次迁移保绿)〔无状态化 V7〕") orch._perfect_hit_true = 0 Kit.check_quiet(not orch.perfect_sweep_earned(), "判定守卫:标中数 0 时零误标也不成立(整图 ≥1 标中条件零松动)〔卡② R2 真值表〕") diff --git a/game/tests/unit/data_config/counter_prefix_fatal_test.gd b/game/tests/unit/data_config/counter_prefix_fatal_test.gd index f5289ec2..6f0e1951 100644 --- a/game/tests/unit/data_config/counter_prefix_fatal_test.gd +++ b/game/tests/unit/data_config/counter_prefix_fatal_test.gd @@ -67,5 +67,5 @@ func _init() -> void: if not _assert_rejected(BAD_REF_DIR, PROBE_REF, "counter_prefix_fatal_test/②悬空读点"): quit(2) return - print("[counter_prefix_fatal_test] 双断言均正确拒绝(前缀合法性 + 引用闭合)") + print("[counter_prefix_fatal_test] 双断言均正确拒绝(前缀合法性 + 引用闭合)〔无状态化 V9〕") quit(1)