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:
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()
|
||||
|
||||
@@ -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": "",
|
||||
|
||||
Reference in new issue
Block a user