test(迭代35卡②T3 补口): V2 多入口与 V6 定位信息两条真实入口断言补位+死注释清扫——① node_map_templates 入口停点候选数改判「=参数档 entry_count」(L3 档实测 2)并加第二入口句柄显式进入断言(变异「候选写死单入口」的拦截面,原 >=1 写法拦不住);② adventure_feats 新增 _drive_to_pref 真实选路驱动(满配赢途中战→岔口按偏好点选),据此落三断言:奖励支路经停呈三选一候选在途、点选兑付受理、进战载荷 node_depth=节点深度且命中节点句柄≠深度(变异「enc 取句柄」拦截面〔v6〕);③ round_flow 模板时代孤儿注释删除

This commit is contained in:
DKChan committed 2026-10-24 19:09:04 +08:00
1 parent 9ee3bea194
commit 3849211621
3 files changed
+73 -47

No files matched your search

@@ -59,8 +59,14 @@ func _smoke_generated_map_determinism() -> void:
Kit.check("冒险模式起关受理(入口停点)", runner2.start_level_external(3,
{"hero_hp": 20, "gp": 0}, {}, Callable(self, "_smoke_rest_stub"), {}, true))
Kit.check("起关产图零计数器消费(同图不依赖取数号位)", rng_c.counter() == counter_before)
Kit.check("入口停点候选 = 生成图全部无入边节点(单入口时恰 1)",
(runner2.next_candidates() as Array).size() >= 1)
var params3: Dictionary = _db.level_gen_params_row(3)
var entry_count: int = int(params3.get("entry_count", -1))
Kit.check("入口停点候选数=参数档入口个数(L3 档 %d——多入口首跳即选路,禁写死单入口)" % entry_count,
entry_count >= 1 and (runner2.next_candidates() as Array).size() == entry_count)
if entry_count >= 2:
var eids: Array = runner2.next_candidate_ids()
Kit.check("第二入口经句柄显式进入(句柄 %d)" % int(eids[1]),
runner2.pick_next_node(int(eids[1])) and int(runner2.current_node_id()) == int(eids[1]))
# ④ 开局种子承载:新档(首局)种子 42 两次开局逐位同图,且与生成器直取一致
DirAccess.remove_absolute(_bp_path) # 测试隔离——上轮落盘残留先删,断点面从零档起
var sv_w = SaveManagerScript.new(_bp_path)
@@ -339,9 +339,6 @@ func _first_encounter_band(level_index: int, enemy_id: String) -> int:
return best
# 模板感知取节点表(row.templates 非空取模板 0=现行固定图,保持既有断言语义)
# 生成图取数面(图 = f(关卡行参数档, 种子 42)——与 runner 起关同图;节点键 id/type/depth/next)
func _level_active_nodes(level_index: int) -> Array:
var rng = load("res://core/rng_service.gd").new()
+65 -42
View File
@@ -163,54 +163,33 @@ func _smoke_adventure() -> void:
if rc >= 1:
reward_levels += 1
Kit.check("三关生成图各含 ≥1 奖励节点(节点三选一候选位在图)", reward_levels == 3)
# 行进侧接线证明走真实选路入口:满配打赢入口战→岔口点选奖励分支→三选一候选可取且停点在途
# (确定性评分策略恒选战斗/恢复分支,奖励位不在扫描路由上——旧「链必经」形状断言的替代形态)
var adr_rw = load("res://core/adventure_runner.gd").new(_db, _fresh_rng(42))
adr_rw.set_hero_override(900, 400)
Kit.check("奖励分支场景冒险启动(seed 42 满配)", adr_rw.start_adventure(42))
var r_rw = adr_rw.runner()
var rw_st := _adv_st()
var rw_cands: Array = []
var rw_guard := 30
while r_rw != null and rw_guard > 0:
rw_guard -= 1
if r_rw.awaiting_choice():
var rw_id := -1
for c in r_rw.next_candidates():
if String((c as Dictionary).get("type", "")) == "reward":
rw_id = int((c as Dictionary).get("id", -1))
break
if rw_id >= 0:
r_rw.pick_next_node(rw_id)
rw_cands = (adr_rw.node_curio_candidates() as Dictionary).get("candidates", []) as Array
break
var rw_ids: Array = r_rw.next_candidate_ids()
if rw_ids.is_empty():
break
r_rw.pick_next_node(int(rw_ids[0]))
continue
var b_rw = r_rw.current_battle()
if b_rw == null:
break
autoplay._autoplay_battle(r_rw, b_rw, rw_st, null)
var rw_pending_ok := false
if not rw_cands.is_empty():
adr_rw.present_node_curio(rw_cands)
rw_pending_ok = not (adr_rw._node_curio_pending as Dictionary).is_empty()
# 行进侧两断言走真实选路入口(满配赢下途中战→岔口按偏好显式点选):奖励支路经停即呈三选一、
# 进战载荷带节点深度——确定性评分策略恒选推进支路,奖励位不在扫描路由上,故显式点选验证接线
var rw: Dictionary = _drive_to_pref(autoplay, "reward")
Kit.check("奖励分支点选→三选一候选可取且停点在途(候选 %s 件)" % str((rw.get("cands", []) as Array).size()),
bool(rw.get("pending", false)) and (rw.get("cands", []) as Array).size() >= 1)
var rw_pc: Dictionary = {}
for c in rw_cands:
for c in (rw.get("cands", []) as Array):
var cd := c as Dictionary
if String(cd.get("kind", "")) != "feat_ticket":
rw_pc = cd
break
if rw_pc.is_empty() and not rw_cands.is_empty():
rw_pc = rw_cands[0] as Dictionary
if rw_pc.is_empty() and not (rw.get("cands", []) as Array).is_empty():
rw_pc = (rw.get("cands", []) as Array)[0] as Dictionary
var rw_pick_ok := false
if rw_pending_ok and not rw_pc.is_empty():
rw_pick_ok = adr_rw.apply_node_curio(String(rw_pc.get("kind", "")),
String(rw_pc.get("tier", "")), String(rw_pc.get("feat_id", "")))
Kit.check("奖励分支点选→三选一停点在途且兑付受理(候选 %d 件)" % rw_cands.size(),
rw_pending_ok and rw_pick_ok)
if not rw_pc.is_empty() and rw.get("adr") != null:
rw_pick_ok = rw["adr"].apply_node_curio(String(rw_pc.get("kind", "")), String(rw_pc.get("tier", "")), String(rw_pc.get("feat_id", "")))
Kit.check("奖励三选一点选兑付受理(件=%s)" % String(rw_pc.get("kind", "")), rw_pick_ok)
var enc_run: Dictionary = _drive_to_pref(autoplay, "battle")
var enc_probe: Dictionary = enc_run.get("enc", {}) as Dictionary
var node_probe: Dictionary = enc_run.get("node", {}) as Dictionary
# 定位信息两样(契约 2):战斗侧收到的 node_depth=节点深度,且命中节点句柄≠深度——
# 变异「enc 取句柄」必红(V6)
Kit.check("进战载荷两样=关卡序号+节点深度(实测 depth=%s 句柄=%s)〔v6〕" % [str(enc_probe.get("node_depth", -1)), str(node_probe.get("id", -1))],
not enc_probe.is_empty()
and int(enc_probe.get("node_depth", -1)) == int(node_probe.get("depth", -1))
and int(enc_probe.get("level_index", -1)) == 1
and int(node_probe.get("id", -1)) != int(node_probe.get("depth", -1)))
var node_resolves := 0
for r0 in resolves:
if String((r0 as Dictionary).get("context", "")) == "reward_node":
@@ -828,6 +807,50 @@ func _adv_drive_to_shop(a, st: Dictionary, autoplay) -> bool:
# 手工场景状态袋(_drive_level/_autoplay_battle 消费的最小字段族——run_adventure st 的同构子集)
# 真实选路驱动到偏好支路:赢下途中战斗,遇候选含目标类型即显式点选(reward=首个奖励节点、
# battle=首个句柄≠深度的战斗节点);返回 {node, enc, cands, pending, adr}
func _drive_to_pref(autoplay, prefer: String) -> Dictionary:
var adr = load("res://core/adventure_runner.gd").new(_db, _fresh_rng(42))
adr.set_hero_override(900, 400)
var out := {"node": {}, "enc": {}, "cands": [] as Array, "pending": false, "adr": adr}
if not adr.start_adventure(42):
return out
var r = adr.runner()
var st := _adv_st()
var guard := 30
while r != null and guard > 0:
guard -= 1
if r.awaiting_choice():
var hit: Dictionary = {}
for c in r.next_candidates():
var cd := c as Dictionary
if prefer == "reward" and String(cd.get("type", "")) == "reward":
hit = cd
break
if prefer == "battle" and String(cd.get("type", "")) == "battle" and int(cd.get("id", -1)) != int(cd.get("depth", -1)):
hit = cd
break
if not hit.is_empty():
r.pick_next_node(int(hit.get("id", -1)))
out["node"] = hit
if prefer == "battle":
out["enc"] = r.current_battle().encounter
else:
var cands: Array = (adr.node_curio_candidates() as Dictionary).get("candidates", []) as Array
adr.present_node_curio(cands)
out["cands"] = cands
out["pending"] = not (adr._node_curio_pending as Dictionary).is_empty()
return out
var ids: Array = r.next_candidate_ids()
if ids.is_empty():
return out
r.pick_next_node(int(ids[0]))
continue
var b = r.current_battle()
if b == null:
return out
autoplay._autoplay_battle(r, b, st, null)
return out
func _adv_st() -> Dictionary:
return {"result": "", "hp_left": 0, "battles": 0, "rounds": 0, "hero_dmg": 0,
"rest_healed": 0, "hp_before_rest": -1, "defeat_node_index": -1, "defeat_node_type": "",