# VERTICAL SLICE - NOT FOR PRODUCTION # 轮间结算节奏化断言(逻辑先行断言面): # ① 演出队列排空前后核心逻辑状态逐位一致(演出关停不产生分叉——排播队列零核心触碰的结构证明); # ② 零丢行:step_row 信号总数 = 明细带实收行数(排播/排空两路都不吞行); # ③ 段间延迟取数面双态(常态 >0 / 减动效 =0 归零不跳过——pace_delay_for 直读)。 # 载体:main.tscn 实例(同 ui_click_probe 形态——组合根全链在座),真实冒险驱动至首战结算后断言。 # 运行:godot --headless --path game --script res://tests/unit/presentation/pacing_queue_test.gd(expect 0) extends SceneTree var _problems: Array = [] func check(cond: bool, msg: String) -> void: if not cond: _problems.append(msg) func _init() -> void: var main: Node = load("res://presentation/main.tscn").instantiate() root.add_child(main) await process_frame # 隔离换注(迭代 27 T1——SaveManager 升格 autoload 后改原地 attach:先删后建防陈旧档泄入, # 读盘在 attach 内完成;meta 经 save 引用透传零重建) for p in ["user://test_pacing_queue_save.json", "user://test_pacing_queue_save.json.tmp"]: if FileAccess.file_exists(p): DirAccess.remove_absolute(p) var sm = main.get_node("/root/SaveManager") sm.attach_path("user://test_pacing_queue_save.json") sm.mark_first_adventure_done() main._refresh_resume_button() # 行信号计数器(真实信号路径旁挂计数——零触碰既有接线) var rows_seen: Array = [] main._start_adventure_from_menu() await process_frame # 入口停点选路进首战(真实停点链) if not main.level_screen.visible: _problems.append("入口停点未呈现(前置失守)") _finish() return main.run_scope.runner.pick_next_node(int(main.run_scope.runner.next_candidate_ids()[0])) await process_frame # 等首战开打(board_changed 同步链——两帧余量) await process_frame var battle = main.run_scope.runner.current_battle() if battle == null: _problems.append("首战未开打(排播断言无载体)") _finish() return battle.step_row.connect(_count_row.bind(rows_seen)) # 理性策略挖到触发结算(≥1 行入队即达断言前提——AP 一次消耗;挖格须邻英雄——邻接优先排序) var ms = battle.minesweeper # 理性策略驱动(auto_player 单一实现复用——damage_probe 同款两步消费:先 move 到 hero_cell 再 dig) var ap = load("res://smoke/auto_player.gd") var acted := false for guard in range(40): var plan: Dictionary = ap.choose_dig(ms) if plan.is_empty(): break if plan.hero_cell != ms.hero_pos(): ms.settle_batch([{"action": "move", "cell": plan.hero_cell}]) if battle.action_economy.phase != "Active": break var outcomes: Array = ms.settle_batch([{"action": "dig", "cell": plan.dig_cell}]) acted = not outcomes.is_empty() and bool(outcomes[0].accepted) if acted: break # 轮末结算触发(step_row 行族只在轮末 emit——停手即结,damage_probe 同款) if battle.action_economy.phase == "Active": ms.settle_batch([{"action": "stop"}]) if not acted: _problems.append("首战无受理动作(队列断言无行)") _finish() return await process_frame # ---- 断言①:排空前后核心状态逐位一致(演出关停零分叉)---- var snap_before: String = JSON.stringify(battle.diagnostic_snapshot()) var hp_before: int = int(battle.action_economy.ap) + int(battle.action_economy.rap) * 0 # 观测面双录 main._flush_row_queue() var snap_after: String = JSON.stringify(battle.diagnostic_snapshot()) check(snap_before == snap_after, "排空前后核心诊断快照逐位一致(AC⑤ 演出关停零分叉)") # ---- 断言②:零丢行(结构判据:排空后队列恒空 + 末信号行原文在带——append_row 直落不吞行)---- check(rows_seen.size() > 0, "结算行已产生(断言前提)") check(main._row_queue.is_empty(), "排空后队列恒空(flush 完整性)") var last_text := "" for i in range(rows_seen.size() - 1, -1, -1): var cand_row: Dictionary = rows_seen[i] last_text = String(cand_row.get("text", "")) if last_text == "": last_text = String(cand_row.get("display_name", "")) if last_text != "": break check(last_text != "" and main.log_view.parsed_text().contains(last_text), "末信号行原文在明细带(排空不吞行:『%s』)" % last_text) # ---- 断言③:段间延迟取数面双态 ---- UiKit.motion_scale = 1.0 var d_on: float = main.pace_delay_for("damage", "heal") var d_same: float = main.pace_delay_for("damage", "damage") check(d_on > 0.0 and d_on >= d_same, "常态段间延迟 >0 且 ≥ 段内(%f/%f)" % [d_on, d_same]) UiKit.motion_scale = 0.5 check(main.pace_delay_for("damage", "heal") == 0.0, "减动效段间延迟归零(行不跳过——同帧直落快路)") UiKit.motion_scale = 1.0 _finish() func _count_row(row: Dictionary, arr: Array) -> void: arr.append(row.duplicate(true)) func _finish() -> void: for p in ["user://test_pacing_queue_save.json", "user://test_pacing_queue_save.json.tmp"]: if FileAccess.file_exists(p): DirAccess.remove_absolute(p) if _problems.is_empty(): print("[pacing_queue_test] PASS — 排空零分叉/零丢行/延迟双态 全绿") quit(0) else: for p in _problems: push_error("[pacing_queue_test] FAIL — " + p) print("[pacing_queue_test] FAIL — " + p) quit(1)