iter44-卡3:卡③(测速循环批v1.1): 步进状态机改造(循环内await消除·收尾四语句同帧同相位迁入·_run_one_level零触碰)+双实现对拍套件(五面·n=3+n=0)·wd8与探针零改动保持绿·裸键从gdstyle命中消失·含S9参数化修复·并master·三卡产出物(实现报告/反哺点/验收审计通过)

This commit is contained in:
DKChan committed 2026-10-30 07:11:41 +08:00
1 parent 07607f45a7
commit 3b761623de
21 files changed
+655 -137

No files matched your search

@@ -339,7 +339,7 @@ func _meta_settle_and_workshop() -> void:
adr_res.resume_adventure(bp_min, p_resume))
Kit.check("继续上局受理面:tree_rules 随参数面进 runner._tree_rules(消费面 set_tree_rules 下传现成)",
adr_res.runner() != null and adr_res.runner().pick_next_node(int(adr_res.runner().next_candidate_ids()[0]))
and ((adr_res.runner().current_battle().encounter.get("tree_rules", []) as Array) == rules_e))
and ((adr_res.runner().current_battle()._facts.encounter.get("tree_rules", []) as Array) == rules_e))
_meta_smoke_cleanup()
@@ -746,7 +746,7 @@ func _smoke_tree_rules() -> void:
"feats": [], "tree_rules": [], "battle_index": 1,
"bonus_ref": {"atk": 0, "max_hp": 0, "armor_class": 0}, "adventure_counters": {},
"voucher_rap": 0, "voucher_time_sand": 0, "node_type": "battle"}))
orch3.encounter["tree_rules"] = []
orch3._facts.encounter["tree_rules"] = []
var gp_base: int = orch3._roll_gp_drop()
var orch4 = load("res://core/battle_orchestrator.gd").new(_db, _fresh_rng(777005))
Kit.check("聚 rig:增益编排启动(tree_rules=economy_hoard)",
@@ -755,7 +755,7 @@ func _smoke_tree_rules() -> void:
"feats": [], "tree_rules": ["economy_hoard"], "battle_index": 1,
"bonus_ref": {"atk": 0, "max_hp": 0, "armor_class": 0}, "adventure_counters": {},
"voucher_rap": 0, "voucher_time_sand": 0, "node_type": "battle"}))
orch4.encounter["tree_rules"] = ["economy_hoard"]
orch4._facts.encounter["tree_rules"] = ["economy_hoard"]
var gp_hoard: int = orch4._roll_gp_drop()
Kit.check("聚:战斗掉落乘算 ×1.5(provisional——同种子配对 %d→%d,入账前=结算框显示实得)",
gp_hoard == int(round(float(gp_base) * 1.5)) and gp_hoard >= gp_base)
+1 -1
View File
@@ -72,7 +72,7 @@ func _seal_propagation(db) -> bool:
rng_a.set_master_seed(5)
var oc_bg = OrchestratorScript.new(db, rng_a)
var bi_bg = BoardInstallerScript.new({"db": db, "rng": rng_a, "minesweeper": oc_bg.minesweeper,
"reward_economy": oc_bg._reward_economy, "encounter": oc_bg.encounter, "fatal_cb": oc_bg._fatal})
"reward_economy": oc_bg._reward_economy, "encounter": oc_bg._facts.encounter, "fatal_cb": oc_bg._fatal})
bi_bg.generator._fatal("测试违约①棋盘生成")
if oc_bg.battle_result != "fatal_error":
push_error("[run_time_fatal_test/wr5] 棋盘生成违约未传导封印(battle_result=%s)" % str(oc_bg.battle_result))
@@ -0,0 +1,245 @@
# 双实现对拍配对套件(注册面批同代测速循环批 v1.1——帧相位等价的执行化证据):
# 产品码(process_frame 信号步进状态机)vs 夹具(现行 await 形态逐字副本——循环内 await+收尾四语句在 run() 尾)。
# 五面对拍(帧索引序列/运行态序列/进度串序列/stats 六键/报告全文)逐位相等=
# 每步恰一帧+相位同点+输出逐字的当轮真实执行证据(测量语义逐点等价四判据的落码面)。
# 拓扑钉死:测试宿主先连 process_frame 帧计数、后启动两形态——两形态下「计数先于步进体」相对次序同构,序列可比。
# n=3 常规案+n=0 退化案(同步收尾零帧——_mean/_p90 空守卫渲染面)。
# 运行:godot --headless --path game --script res://tests/unit/presentation/tune_sim_batch_parity_test.gd(expect 0)
extends SceneTree
const Kit := preload("res://tests/unit/test_kit.gd")
const TuneSimScript := preload("res://presentation/tune_sim_controller.gd")
# 假调试面板(wd8 同形 setter 面+进度/运行态/报告三记录数组供序列对拍)
class FakePanel extends Node:
var running := false
var progress := ""
var report := ""
var atk := 0
var hp := 0
var progress_log: Array[String] = []
var report_log: Array[String] = []
var running_log: Array[bool] = []
func atk_pct() -> int:
return atk
func hp_pct() -> int:
return hp
func set_sim_running(r: bool) -> void:
running = r
running_log.append(r)
func set_sim_progress(t: String) -> void:
progress = t
progress_log.append(t)
func set_sim_report(t: String) -> void:
report = t
report_log.append(t)
# 假自动策略(固定返回值两档 victory/defeat 交替——wd8 :53-55 同款取值,零随机;
# 逐局记录帧索引与运行态读数——帧索引经宿主注入的计数 Callable 读取)
class FakeDriver extends RefCounted:
var ctrl
var frame_counter: Callable # 宿主帧计数(0 参 Callable——拓扑钉死:计数先于步进体)
var seen: Array[bool] = []
var frames: Array[int] = []
var calls := 0
func run_level(_db: Variant, _seed: int, _level: int, _atk: int, _hp: int, _other: bool, _logger: Variant) -> Dictionary:
calls += 1
seen.append(ctrl.is_running())
frames.append(int(frame_counter.call()))
if calls % 2 == 1:
return {"hero_dmg": 12.0, "rounds": 4, "result": "victory", "hp_left": 3.0, "hp_at_boss": 5.0}
return {"hero_dmg": 6.0, "rounds": 2, "result": "defeat",
"defeat_node_index": 3, "defeat_node_type": "battle", "hp_left": 0.0, "hp_at_boss": 0.0}
# 假配置库(TuneSimController 只读 db.run.action_economy.ap_per_round——wd8 同款最小域对象图)
class FakeActionEconomy:
var ap_per_round := 2
class FakeRun:
var action_economy := FakeActionEconomy.new()
class FakeDB:
var run := FakeRun.new()
# 现行 await 形态逐字副本(tune_sim_controller.gd run(n) 改形前形态——循环内 await+收尾四语句在 run() 尾;
# 全仓唯一在测夹具——产品码禁留第二实现,tests/ 不在 S13/S8/S9/S10 扫描面、夹具内循环 await 合法在册)。
# 与产品码新形态的申报差异仅一处:批末把 stats 六键束存 out_stats 成员供对拍读取
# (产品码 stats 经报告公开面观测——内部字段直查受控例外申报在卡 §17)。
class AwaitLoopRunner extends RefCounted:
const SEED_BASE := 4200
const ParityLogger := preload("res://presentation/playtest_logger.gd")
var db
var panel
var _base_hp := 0
var _driver = null
var _running := false
var out_stats: Dictionary = {}
func _init(db_ref, panel_ref, base_hp: int, driver = null) -> void:
db = db_ref
panel = panel_ref
_base_hp = base_hp
_driver = driver
func is_running() -> bool:
return _running
func run(n: int) -> void:
_running = true
var logger = ParityLogger.new()
logger.ap_per_round = int(db.run.action_economy.ap_per_round)
logger.start_session(SEED_BASE)
panel.set_sim_running(true)
panel.set_sim_progress("模拟中:0/%d …" % n)
var stats := {"wins": 0, "dmg_all": [], "hp_left_win": [], "hp_boss": [], "rounds_all": [], "death_nodes": {}}
var autoplay = _driver
for i in range(n):
_run_one_level(autoplay, stats, i, panel, logger)
panel.set_sim_progress("模拟中:%d/%d …" % [i + 1, n])
await panel.get_tree().process_frame
logger.close_session()
panel.set_sim_report(HudPresenter.tune_sim_report(n, panel.atk_pct(), panel.hp_pct(), stats, _base_hp))
panel.set_sim_running(false)
_running = false
out_stats = stats
func _run_one_level(autoplay, stats: Dictionary, i: int, panel, logger) -> void: # type-hint: ignore(驱动/面板/日志件鸭子型注入——同产品码构造缝)
var st = autoplay.run_level(db, SEED_BASE + i, 1, panel.atk_pct(), panel.hp_pct(), false, logger)
(stats["dmg_all"] as Array).append(st.hero_dmg)
(stats["rounds_all"] as Array).append(st.rounds)
if st.result == "victory":
stats["wins"] = int(stats["wins"]) + 1
(stats["hp_left_win"] as Array).append(st.hp_left)
(stats["hp_boss"] as Array).append(st.hp_at_boss)
elif st.result == "defeat":
var key := "%d.%s" % [st.defeat_node_index, st.defeat_node_type]
stats["death_nodes"][key] = int(stats["death_nodes"].get(key, 0)) + 1
func _init() -> void:
var frame_idx := {"i": -1} # 帧计数(宿主最先连——拓扑钉死「计数先于步进体」)
process_frame.connect(func() -> void: frame_idx["i"] += 1)
await process_frame # 树初始化就绪帧(SceneTree._init 时点 root 未挂树——面板 get_tree() 首帧后才有效;wd8 同款先例:panel 段前有 await 过帧)
var r3 := await _run_pair(3, frame_idx)
_check_n3(r3)
var r0 := await _run_pair(0, frame_idx)
_check_n0(r0)
quit(Kit.finish("tune_sim_batch_parity"))
# 对拍驱动:两形态各经 run(n) 真实入口(同参假面板/假驱动各自实例);先启动两形态、后分别 await——
# 两形态在调用点同一同步段内先后挂起(并行于同一帧时钟),驱动协程返回 int 承接 void run 的可等待面
func _run_pair(n: int, frame_idx: Dictionary) -> Dictionary:
# 两形态串行驱动(各经 run(n) 真实入口 await 调用——GDScript 协程动态调用必须 await,
# 无并行启动形态):帧序列各自记录、断言按相对首帧偏移归一后逐位比对——拓扑不变式
# 「计数先连、启动后于计数、计数先于步进体」在两形态下相对次序同构,等价证据结构不变。
var panel_a := FakePanel.new()
root.add_child(panel_a)
var drv_a := FakeDriver.new()
var a: Variant = TuneSimScript.new(FakeDB.new(), panel_a, 100, drv_a)
drv_a.ctrl = a
drv_a.frame_counter = func() -> int: return frame_idx["i"]
await a.run(n)
var panel_b := FakePanel.new()
root.add_child(panel_b)
var drv_b := FakeDriver.new()
var b: Variant = AwaitLoopRunner.new(FakeDB.new(), panel_b, 100, drv_b)
drv_b.ctrl = b
drv_b.frame_counter = func() -> int: return frame_idx["i"]
await b.run(n)
return {"a": a, "b": b, "pa": panel_a, "pb": panel_b, "da": drv_a, "db": drv_b}
func _check_n3(r: Dictionary) -> void:
var pa: FakePanel = r["pa"]
var pb: FakePanel = r["pb"]
var da: FakeDriver = r["da"]
var drv_b: FakeDriver = r["db"]
var a = r["a"]
var b = r["b"]
Kit.check("n=3 帧序列相对偏移逐位相等(%s vs %s)+相邻差恒一(每步恰一帧)〔测速 V1〕",
str(_norm(da.frames)) == str(_norm(drv_b.frames)) and da.frames.size() == 3
and _consecutive(da.frames) and _consecutive(drv_b.frames))
Kit.check("n=3 运行态读数序列逐位相等+收尾双运行态假+面板置假序列同构〔测速 V2〕",
str(da.seen) == str(drv_b.seen) and da.seen == [true, true, true]
and not a.is_running() and not b.is_running()
and not pa.running and not pb.running
and str(pa.running_log) == str(pb.running_log))
Kit.check("n=3 进度串序列逐位相等(%d 段)〔测速 V3〕",
str(pa.progress_log) == str(pb.progress_log) and pa.progress_log.size() == 4)
var got: Dictionary = r["b"].out_stats
var deaths: Dictionary = got.get("death_nodes", {})
Kit.check("n=3 夹具 stats 六键与按假驱动固定返回值重算的期望逐键相等〔测速 V4〕",
int(got.get("wins", -1)) == 2
and _arr_eq(got.get("dmg_all"), [12.0, 6.0, 12.0])
and _arr_eq(got.get("hp_left_win"), [3.0, 3.0])
and _arr_eq(got.get("hp_boss"), [5.0, 5.0])
and _arr_eq(got.get("rounds_all"), [4, 2, 4])
and deaths.size() == 1 and int(deaths.get("3.battle", -1)) == 1)
Kit.check("n=3 报告全文逐字相等且含渲染面四关键串〔测速 V5〕",
pa.report == pb.report and pa.report != ""
and pa.report.contains("关卡完成率") and pa.report.contains("关卡完成剩余 HP 均")
and pa.report.contains("败北节点") and pa.report.contains("N=3"))
func _check_n0(r: Dictionary) -> void:
var pa: FakePanel = r["pa"]
var pb: FakePanel = r["pb"]
var da: FakeDriver = r["da"]
var drv_b: FakeDriver = r["db"]
var a = r["a"]
var b = r["b"]
Kit.check("n=0 退化:两形态驱动零回调、帧索引序列空(同步收尾零帧)〔测速 V1〕",
da.frames.is_empty() and drv_b.frames.is_empty() and da.calls == 0 and drv_b.calls == 0)
Kit.check("n=0 运行态置假序列 [true, false] 双形态同构+收尾双假〔测速 V2〕",
str(pa.running_log) == str(pb.running_log) and pa.running_log == [true, false]
and not a.is_running() and not b.is_running())
Kit.check("n=0 进度串单段逐字相等〔测速 V3〕",
str(pa.progress_log) == str(pb.progress_log) and pa.progress_log == ["模拟中:0/0 …"])
Kit.check("n=0 夹具 stats 六键零计〔测速 V4〕",
int((r["b"].out_stats as Dictionary).get("wins", -1)) == 0
and ((r["b"].out_stats as Dictionary).get("dmg_all") as Array).is_empty()
and ((r["b"].out_stats as Dictionary).get("hp_left_win") as Array).is_empty()
and ((r["b"].out_stats as Dictionary).get("hp_boss") as Array).is_empty()
and ((r["b"].out_stats as Dictionary).get("rounds_all") as Array).is_empty()
and ((r["b"].out_stats as Dictionary).get("death_nodes") as Dictionary).is_empty())
Kit.check("n=0 报告全文逐字相等且非空(空 stats 渲染面)〔测速 V5〕",
pa.report == pb.report and pa.report != "")
# 帧序列归一(相对首帧偏移——串行驱动下两形态帧段错位、结构可比性归一承载)
func _norm(fr: Array) -> Array:
var out: Array = []
var base := int(fr[0]) if fr.size() > 0 else 0
for i in fr.size():
out.append(int(fr[i]) - base)
return out
# 相邻差恒一(每步恰占一帧——判据①的序列面证据)
func _consecutive(fr: Array) -> bool:
for i in range(1, fr.size()):
if int(fr[i]) - int(fr[i - 1]) != 1:
return false
return true
# 数组逐元素浮点比对(stats 桶重算期望——类型化比较,消 str 形态歧义)
func _arr_eq(a, b: Array) -> bool:
var aa := a as Array
if aa == null or aa.size() != b.size():
return false
for i in b.size():
if absf(float(aa[i]) - float(b[i])) > 0.0001:
return false
return true