diff --git a/game/tests/bp_baseline_dump.gd b/game/tests/bp_baseline_dump.gd deleted file mode 100644 index 0501720a..00000000 --- a/game/tests/bp_baseline_dump.gd +++ /dev/null @@ -1,177 +0,0 @@ -# VERTICAL SLICE - NOT FOR PRODUCTION -# 断点基线对拍采集脚本(迭代 39 拆件批 T0 载体,批末随中间产物退役——非长期测试件)。 -# 固定种子集驱动完整冒险至各停点(节点入口/选路/三选一在途/兑付后待推进),逐停点 -# dump 断点 Dictionary 全键值(直调 _bp_parts 写侧取值面)→ JSON 落盘,供改前/改后 -# 逐键逐位比对(bp1:同种子改前改后 27 键逐位一致)。 -# 运行:BP_DUMP_OUT= godot --headless --path . --script res://tests/bp_baseline_dump.gd -# 缺省输出 user://bp_baseline_dump.json。 -# 驱动与 sim/level_autoplay 同构(完美档战斗驱动 + 确定性选件/选路)——零随机流自开, -# 同种子逐位可复放;dump 时点由驱动循环状态判定,确定性时点。 -extends SceneTree - -const AdventureRunnerScript := preload("res://core/adventure_runner.gd") -const Autoplay := preload("res://sim/level_autoplay.gd") - -const SEEDS: Array[int] = [1234, 4321, 910042, 20260101, 20260102, 20260103] -const PARAMS := { - "hero_override": {}, - "treasure_unlocked": {"wide_scout_band": true}, - "attunement": ["sky_eye", "incense_stick", "refresh_practice"], # 充能账非零形态+刷新存量加仓(attunement_charges/refresh_stock 键覆盖) - "attune_refresh_y": 2, # Y 池非零形态(attune_refresh_y 键覆盖) - "shop_mode": "pool", # 共享池货架面(shop_shelf/shop_visit_buys/shop_purchased 覆盖) - "relics_seed": ["twin_pick"], # 特性持有集+adventure_counters 非空形态(adv_twin_digs 计数器用户) -} - - -func _init() -> void: - var db = load("res://core/config_db.gd").new() - if not db.load_tables("res://data/tables", false): - print("[bp_baseline_dump] FAIL — 数值表加载失败") - quit(1) - return - var runs: Array = [] - for seed_val in SEEDS: - var run := _drive_dump_run(db, int(seed_val)) - runs.append({"seed": int(seed_val), "result": String(run["result"]), "dumps": run["dumps"]}) - print("[bp_baseline_dump] seed=%d result=%s dumps=%d" % [int(seed_val), String(run["result"]), (run["dumps"] as Array).size()]) - var out_path := OS.get_environment("BP_DUMP_OUT") - if out_path.is_empty(): - out_path = "user://bp_baseline_dump.json" - var f := FileAccess.open(out_path, FileAccess.WRITE) - if f == null: - print("[bp_baseline_dump] FAIL — 输出文件打不开: %s" % out_path) - quit(1) - return - f.store_string(JSON.stringify({"generated_by": "bp_baseline_dump", "seeds": SEEDS, "runs": runs}, " ")) - f.close() - print("[bp_baseline_dump] DONE — %s" % out_path) - quit(0) - - -# 单局驱动+停点 dump(返回 {result, dumps};dumps 元素 = {seq, tag, level_index, node_id, bp}) -func _drive_dump_run(db, seed_val: int) -> Dictionary: - var rng = load("res://core/rng_service.gd").new() - rng.set_master_seed(seed_val) - var adr = AdventureRunnerScript.new(db, rng) - var ctx := {"result": "", "dumps": [] as Array, "seq": 0, "adr": adr, "db": db} - # 信号直连(拆件批 T3——转发族退役):runner 信号在 level_started 重取点重连(同关守卫恰一次); - # 停点件信号面订 stop()(每冒险一次) - adr.level_started.connect(_on_dump_level_started.bind(ctx)) - adr.stop().curio_resolved.connect(_on_curio_resolved.bind(ctx)) - if not adr.start_adventure(seed_val, PARAMS): - ctx["result"] = "start_failed" - return ctx - var guard := 2000 - while String(ctx["result"]) == "" and guard > 0: - guard -= 1 - if not (adr.stop()._node_pending as Dictionary).is_empty(): - _dump(ctx, "node_curio_pending") - var ncands: Array = (adr.stop()._node_pending.get("candidates", []) as Array) - var npick: Dictionary = Autoplay._pick_token(ncands, "off") - if not adr.stop().apply_node_curio(String(npick.get("kind", "")), String(npick.get("tier", "")), String(npick.get("feat_id", ""))): - ctx["result"] = "node_advance_failed" - continue - if not (adr.stop()._pending as Dictionary).is_empty(): - _dump(ctx, "curio_pending") - var cands: Array = (adr.stop()._pending.get("candidates", []) as Array) - var pick: Dictionary = Autoplay._pick_token(cands, "off") - if not adr.stop().pick_curio(String(pick.get("kind", "")), String(pick.get("tier", "")), String(pick.get("feat_id", ""))): - ctx["result"] = "advance_failed" - continue - if bool(adr._pending_next_level): - _dump(ctx, "resolved_wait_continue") - if not adr.continue_to_next_level(): - ctx["result"] = "advance_failed" - continue - var runner = adr.runner() - if runner == null: - break # 冒险已收束或停点态未覆盖(防御) - if runner.awaiting_choice(): - _dump(ctx, "route_choice") - var ids: Array = runner.next_candidate_ids() - if ids.is_empty(): - ctx["result"] = "no_candidates" - break - if not runner.pick_next_node(int(ids[0])): - ctx["result"] = "route_failed" - continue - var settlement = runner.current_battle() - if settlement != null and String(settlement.battle_result) == "": - _drive_battle(ctx, runner, settlement) - continue - # 收束态分派:结算受理后(state=idle/settling)按 summary 定局;其余态无战无停点无选路 = 死循环防护 - if String(ctx["result"]) == "": - var sum: Dictionary = adr.summary if adr.summary != null else {} - if not sum.is_empty(): - ctx["result"] = String(sum.get("result", "settled")) - break - if String(adr.state) in ["idle", "settling"]: - ctx["result"] = "settled_no_summary" - break - if guard == 1: - ctx["result"] = "timeout" - return ctx - - -# 完美档战斗驱动(复用 sim 同款决策——上界策略;st 键集满足 _autoplay_battle 消费面) -func _drive_battle(ctx: Dictionary, runner, settlement) -> void: - var st := {"result": "", "rounds": 0, "refreshes": 0, "seal_placed": 0, - "policy": "perfect", "adv": false} - Autoplay._autoplay_battle(runner, settlement, st, null) - - -# 停点 dump(写侧取值面直调;_runner 为 null 的停点态节点域字段按缺省落档——与真机同构) -static func _dump(ctx: Dictionary, tag: String) -> void: - var adr = ctx["adr"] - var bp: Dictionary = adr._bp_parts() - var runner = adr.runner() - var dumps: Array = ctx["dumps"] - ctx["seq"] = int(ctx["seq"]) + 1 - dumps.append({ - "seq": int(ctx["seq"]), - "tag": String(tag), - "level_index": int(adr.level_index), - "node_id": int(runner.current_node_id()) if runner != null else -1, - "bp": bp, - }) - - -# 每关开始(dump 直连面):重取 runner 重连 node_entered(同关守卫——level_started 每关恰一次发射) -static func _on_dump_level_started(_li: int, _dn: String, ctx: Dictionary) -> void: - var adr = ctx["adr"] - if String(ctx.get("wired_level", "")) == "%s|%d" % [str(adr.master_seed), int(adr.level_index)]: - return - ctx["wired_level"] = "%s|%d" % [str(adr.master_seed), int(adr.level_index)] - var r = adr.runner() - if r == null: - return - r.node_entered.connect(_on_node_entered.bind(ctx)) - - -# 节点入口驱动(信号时点):入口 dump 覆盖节点域字段形态;rest/shop 直通继续,reward 呈现三选一 -static func _on_node_entered(node: Dictionary, _info: Dictionary, ctx: Dictionary) -> void: - var adr = ctx["adr"] - var runner = adr.runner() - if runner == null: - return - var t := String(node.get("type", "")) - _dump(ctx, "node_" + t) - if t == "rest" or t == "shop": - runner.continue_after_node() - elif t == "reward": - var ndraw: Dictionary = adr.stop().node_curio_candidates() - var ncands: Array = ndraw.get("candidates", []) as Array - if ncands.is_empty(): - runner.continue_after_node() # 牌库空合法态直通(池耗即枯竭) - else: - adr.stop().present_node_curio(ncands) - - -# 节点三选一兑付完成回图(context=reward_node → continue_after_node 回选路) -static func _on_curio_resolved(result: Dictionary, ctx: Dictionary) -> void: - if String(result.get("context", "")) != "reward_node": - return - var adr = ctx["adr"] - var runner = adr.runner() - if runner != null and not runner.continue_after_node(): - ctx["result"] = "node_advance_failed" diff --git a/game/tests/bp_baseline_dump.gd.uid b/game/tests/bp_baseline_dump.gd.uid deleted file mode 100644 index d16de9c6..00000000 --- a/game/tests/bp_baseline_dump.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://byleq6f7m8qm6