iter41-卡1:核心数据面结构收敛批:85键全清——三族拆域+config_db归位容器+存档拆件+meta瘦身+adventure拆段+spawn束化;行为零变化,8新件在阈
This commit is contained in:
1 parent
bda377eb16
commit
a613a3cb90
157 files changed
+7453
-4288
No files matched your search
@@ -78,7 +78,7 @@ func _initialize() -> void:
|
||||
var slots: int = 3
|
||||
var passives: Array = []
|
||||
var actives: Array = []
|
||||
for r in db.treasure_attunement_rows():
|
||||
for r in db.crossrun.treasure_items.attunement_rows():
|
||||
if String(r.get("form", "")) == "passive":
|
||||
passives.append(String(r.get("item_id", "")))
|
||||
elif String(r.get("form", "")) == "active_charged":
|
||||
@@ -102,7 +102,7 @@ func _initialize() -> void:
|
||||
for policy in policies:
|
||||
# 每策略一个共享日志会话(全量 JSONL 体量随局数增长,分文件便于定位)
|
||||
var logger = load("res://presentation/playtest_logger.gd").new()
|
||||
logger.ap_per_round = int(db.action_economy.ap_per_round)
|
||||
logger.ap_per_round = int(db.run.action_economy.ap_per_round)
|
||||
var log_path: String = logger.start_session(seed0)
|
||||
var tally := {}
|
||||
for i in range(n):
|
||||
|
||||
@@ -24,7 +24,7 @@ func _initialize() -> void:
|
||||
var gen = load("res://core/board_generator.gd").new()
|
||||
print("=== 板形统计:N=%d/行 · 种子基 %d ===" % [n, seed0])
|
||||
print("%-4s %-4s %-8s %-8s %-10s %-10s %-10s %-10s %-10s %-10s" % ["L", "D", "宽x高", "类型", "|M|均", "|M|min", "|M|max", "|S|均", "雷密度实测", "达标率"])
|
||||
for row in db.board_gen.rows:
|
||||
for row in db.combat.board_gen.rows:
|
||||
var L: int = int(row.get("level_index", 0))
|
||||
var D: int = int(row.get("node_depth", -1))
|
||||
var w: int = int(row.get("board_width", 0))
|
||||
|
||||
@@ -9,9 +9,13 @@ extends SceneTree
|
||||
|
||||
|
||||
class FakeDB:
|
||||
var row: Dictionary
|
||||
func resolve_board_gen(_L: int, _D: int) -> Dictionary:
|
||||
return row
|
||||
class _FakeBoardGen:
|
||||
var row: Dictionary
|
||||
func resolve_row(_L: int, _D: int) -> Dictionary:
|
||||
return row
|
||||
class _FakeCombat:
|
||||
var board_gen := _FakeBoardGen.new()
|
||||
var combat := _FakeCombat.new()
|
||||
func status_effect_row(_id: String) -> Dictionary:
|
||||
return {}
|
||||
|
||||
@@ -28,7 +32,7 @@ func _initialize() -> void:
|
||||
return
|
||||
var gen = load("res://core/board_generator.gd").new()
|
||||
print("level,depth,w,h,n,mines_mean,mines_med,safe_mean,safe_med,r0_mean,r0_sd,r0_p90,r0_max,inf_mean,cap_rej_mean")
|
||||
for row in db.board_gen.rows:
|
||||
for row in db.combat.board_gen.rows:
|
||||
var rd := {
|
||||
"level_index": int(row.level_index), "node_depth": int(row.node_depth),
|
||||
"board_width": int(row.board_width), "board_height": int(row.board_height),
|
||||
|
||||
+16
-16
@@ -53,17 +53,17 @@ func _initialize() -> void:
|
||||
|
||||
func _probe_boss(db, seed_val: int) -> void:
|
||||
var math = load("res://core/damage_math.gd")
|
||||
var clamp_lo: int = int(db.settlement.core.pool_clamp_lo)
|
||||
var clamp_hi: int = int(db.settlement.core.pool_clamp_hi)
|
||||
var clamp_lo: int = int(db.combat.settlement.core.pool_clamp_lo)
|
||||
var clamp_hi: int = int(db.combat.settlement.core.pool_clamp_hi)
|
||||
var hero_def_negative := 0
|
||||
for eff_id in db.settlement.core.effect_pool_map.keys():
|
||||
if String(db.settlement.core.effect_pool_map[eff_id]) != "hero_defense":
|
||||
for eff_id in db.combat.settlement.core.effect_pool_map.keys():
|
||||
if String(db.combat.settlement.core.effect_pool_map[eff_id]) != "hero_defense":
|
||||
continue
|
||||
var row: Dictionary = db.status_effect_row(eff_id)
|
||||
var row: Dictionary = db.combat.status_effects.row_by_id(eff_id)
|
||||
if not row.is_empty() and String(row.get("unit", "")) == "percent":
|
||||
hero_def_negative = mini(hero_def_negative, int(row.get("magnitude", 0)))
|
||||
print("表值面:hero_defense 池最负 percent 行 %d · pool_clamp [%d, %d] · damage_floor %d · AC 基准 %d(+ac_up×%d 层)" % [
|
||||
hero_def_negative, clamp_lo, clamp_hi, int(db.settlement.damage_roll.damage_floor), int(db.hero.armor_class), int(db.status_effect_row("ac_up").get("max_stack", 0))])
|
||||
hero_def_negative, clamp_lo, clamp_hi, int(db.combat.settlement.damage_roll.damage_floor), int(db.combat.hero.armor_class), int(db.combat.status_effects.row_by_id("ac_up").get("max_stack", 0))])
|
||||
|
||||
print("--- Run A:fresh 池(零挖掘停手轮——无授予,攻击链裸验)---")
|
||||
var run_a := _run_boss(db, seed_val, false)
|
||||
@@ -92,7 +92,7 @@ func _run_boss(db, seed_val: int, dig: bool) -> Dictionary:
|
||||
settlement.step_row.connect(func(row): cap["rows"].append(row))
|
||||
settlement.settlement_snapshot.connect(func(s): cap["snaps"].append(s))
|
||||
settlement.battle_finished.connect(func(p): cap["result"] = String(p.get("result", "")))
|
||||
if not settlement.begin_battle({"level_index": 1, "node_depth": 4, "hero_hp": int(db.hero.max_hp), "node_type": "boss"}):
|
||||
if not settlement.begin_battle({"level_index": 1, "node_depth": 4, "hero_hp": int(db.combat.hero.max_hp), "node_type": "boss"}):
|
||||
cap["result"] = "begin_failed"
|
||||
return cap
|
||||
var rounds := 0
|
||||
@@ -175,11 +175,11 @@ func _analyze_round(db, math, rows: Array, beats: Dictionary) -> Dictionary:
|
||||
# 池归属表驱动(settlement.effect_pool_map),percent 池加算后 clamp(战斗结算 F2 同口径)
|
||||
var pools := {"hero_defense": 0, "enemy_output": 0, "hero_offense": 0, "flat_attach": 0}
|
||||
for eid in last_pool:
|
||||
var pool_name: String = String(db.settlement.core.effect_pool_map.get(eid, ""))
|
||||
var pool_name: String = String(db.combat.settlement.core.effect_pool_map.get(eid, ""))
|
||||
if pools.has(pool_name):
|
||||
pools[pool_name] += int(last_pool[eid])
|
||||
var clamp_lo: int = int(db.settlement.core.pool_clamp_lo)
|
||||
var clamp_hi: int = int(db.settlement.core.pool_clamp_hi)
|
||||
var clamp_lo: int = int(db.combat.settlement.core.pool_clamp_lo)
|
||||
var clamp_hi: int = int(db.combat.settlement.core.pool_clamp_hi)
|
||||
var pd: int = clampi(int(pools["hero_defense"]), clamp_lo, clamp_hi)
|
||||
var pe: int = clampi(int(pools["enemy_output"]), clamp_lo, clamp_hi)
|
||||
var lines: Array = []
|
||||
@@ -194,11 +194,11 @@ func _analyze_round(db, math, rows: Array, beats: Dictionary) -> Dictionary:
|
||||
# 掷点口径重建:基线按行留档档位打折(暴击 ×倍率),附加必中
|
||||
var tier: int = int(row.get("tier", 100))
|
||||
var crit: bool = bool(row.get("crit", false))
|
||||
var base_factor: float = float(tier) * (float(db.settlement.damage_roll.crit_multiplier) / 100.0 if crit else 1.0)
|
||||
var base_factor: float = float(tier) * (float(db.combat.settlement.damage_roll.crit_multiplier) / 100.0 if crit else 1.0)
|
||||
var rolled_base := int(floor(float(baseline) * base_factor / 100.0))
|
||||
var raw: int = rolled_base + attach
|
||||
var tick: int = int(row.get("value", 0))
|
||||
var expected: int = maxi(int(db.settlement.damage_roll.damage_floor), math.pool_damage(raw, pd, pe))
|
||||
var expected: int = maxi(int(db.combat.settlement.damage_roll.damage_floor), math.pool_damage(raw, pd, pe))
|
||||
var roll_txt := "掷%s" % str(int(row.get("roll", -1)))
|
||||
var zeroed := "✓" if tick == expected else "✗ 算术不符!"
|
||||
lines.append("拍=%s %s/档%d%s 基线%d→掷后%d 附加%d hero_def=%d enemy_out=%d d_tick=%d 期望=%d %s" % [
|
||||
@@ -266,7 +266,7 @@ func _encounter_prediction(db, seed_val: int) -> Dictionary:
|
||||
if t != "battle" and t != "boss":
|
||||
continue
|
||||
var depth := int(node.get("node_depth", -1))
|
||||
var row: Dictionary = db.resolve_enemy_rowset(1, depth, t)[0] # 行集首行(row_kind 随节点类型)
|
||||
var row: Dictionary = db.combat.enemy.resolve_rowset(1, depth, t)[0] # 行集首行(row_kind 随节点类型)
|
||||
var lo := int(row.get("unit_count", 1))
|
||||
var hi := int(row.get("unit_count_max", lo))
|
||||
var rolled := lo
|
||||
@@ -280,7 +280,7 @@ func _encounter_prediction(db, seed_val: int) -> Dictionary:
|
||||
|
||||
|
||||
func _level_row(db, level_index: int) -> Dictionary:
|
||||
for row in db.level.rows:
|
||||
for row in db.run.level.rows:
|
||||
if int(row.level_index) == level_index:
|
||||
return row
|
||||
return {}
|
||||
@@ -415,11 +415,11 @@ func _scan_spawns(db, seed0: int, n_seeds: int) -> Dictionary:
|
||||
var kind: String = String(dep.get("kind", "battle"))
|
||||
total += 1
|
||||
var settlement = load("res://core/battle_orchestrator.gd").new(db, rng)
|
||||
if not settlement.begin_battle({"level_index": 1, "node_depth": depth, "node_type": kind, "hero_hp": int(db.hero.max_hp)}):
|
||||
if not settlement.begin_battle({"level_index": 1, "node_depth": depth, "node_type": kind, "hero_hp": int(db.combat.hero.max_hp)}):
|
||||
bad += 1
|
||||
continue
|
||||
var n: int = settlement.presentation_snapshot().enemies.size()
|
||||
var row: Dictionary = db.resolve_enemy_rowset(1, depth, kind)[0] # 行集首行(同上,row_kind 随节点类型)
|
||||
var row: Dictionary = db.combat.enemy.resolve_rowset(1, depth, kind)[0] # 行集首行(同上,row_kind 随节点类型)
|
||||
var lo := int(row.get("unit_count", 1))
|
||||
var hi := int(row.get("unit_count_max", lo))
|
||||
if n < 1 or n < lo or n > hi:
|
||||
|
||||
@@ -19,7 +19,7 @@ func _initialize() -> void:
|
||||
level = int(OS.get_cmdline_user_args()[0])
|
||||
if OS.get_cmdline_user_args().size() > 1:
|
||||
rng.set_master_seed(int(OS.get_cmdline_user_args()[1]))
|
||||
settlement.begin_battle({"level_index": level, "node_depth": 0, "hero_hp": db.hero.max_hp})
|
||||
settlement.begin_battle({"level_index": level, "node_depth": 0, "hero_hp": db.combat.hero.max_hp})
|
||||
print("=== 棋盘摘要 L%d ===" % level)
|
||||
var bd = settlement.minesweeper.current_board()
|
||||
print("尺寸 %dx%d · 雷 %d · 安全格 %d · R₀ %d" % [bd.width, bd.height, bd.mines.size(), bd.safe_cells().size(), bd.metrics.r0_count])
|
||||
|
||||
@@ -56,13 +56,17 @@ func _params_from(row: Dictionary) -> Dictionary:
|
||||
}
|
||||
|
||||
class FakeDB:
|
||||
var row: Dictionary
|
||||
func resolve_board_gen(_L: int, _D: int) -> Dictionary:
|
||||
return row
|
||||
class _FakeBoardGen:
|
||||
var row: Dictionary
|
||||
func resolve_row(_L: int, _D: int) -> Dictionary:
|
||||
return row
|
||||
class _FakeCombat:
|
||||
var board_gen := _FakeBoardGen.new()
|
||||
var combat := _FakeCombat.new()
|
||||
func status_effect_row(_id: String) -> Dictionary:
|
||||
return {}
|
||||
|
||||
func _db(_real, row: Dictionary):
|
||||
var f := FakeDB.new()
|
||||
f.row = row
|
||||
f.combat.board_gen.row = row
|
||||
return f
|
||||
@@ -39,7 +39,7 @@ static func run_level(db, seed_val: int, level_index: int, atk_pct: int, hp_pct:
|
||||
if own_session:
|
||||
logger = load("res://presentation/playtest_logger.gd").new()
|
||||
logger.start_session(seed_val)
|
||||
logger.ap_per_round = int(db.action_economy.ap_per_round)
|
||||
logger.ap_per_round = int(db.run.action_economy.ap_per_round)
|
||||
logger.log_run({"seed": seed_val, "level_index": level_index, "atk_pct": atk_pct, "hp_pct": hp_pct, "policy": policy, "route": route})
|
||||
var rng = load("res://core/rng_service.gd").new()
|
||||
rng.set_master_seed(seed_val)
|
||||
@@ -117,7 +117,7 @@ static func run_adventure(db, seed_val: int, atk_pct: int, hp_pct: int, collect_
|
||||
if own_session:
|
||||
logger = load("res://presentation/playtest_logger.gd").new()
|
||||
logger.start_session(seed_val)
|
||||
logger.ap_per_round = int(db.action_economy.ap_per_round)
|
||||
logger.ap_per_round = int(db.run.action_economy.ap_per_round)
|
||||
logger.log_run({"seed": seed_val, "adventure": true, "atk_pct": atk_pct, "hp_pct": hp_pct,
|
||||
"policy": policy, "route": route, "hero_override": hero_override})
|
||||
var rng = load("res://core/rng_service.gd").new()
|
||||
|
||||
@@ -44,7 +44,7 @@ func _run_encounter(db, L: int) -> void:
|
||||
print("=== L%d · %d 种子(败 %d / 超时 %d)===" % [L, N, losses, timeouts])
|
||||
print(" 胜利轮数 均值 %.2f 中位 %d p10 %.1f p90 %.1f 分布 %s" % [_mean(rounds_v), _median(rounds_v), _pct_f(rounds_v, 0.1), _pct_f(rounds_v, 0.9), _hist(rounds_v)])
|
||||
print(" 每轮承伤 均值 %.2f 中位 %.1f p90 %.1f" % [_mean(dmg_per_round), _median_f(dmg_per_round), _pct_f(dmg_per_round, 0.9)])
|
||||
print(" 整场承伤 均值 %.1f 中位 %.0f(主角 max_hp=%d)" % [_mean(dmg_total), _median_f(dmg_total), int(db.hero.max_hp)])
|
||||
print(" 整场承伤 均值 %.1f 中位 %.0f(主角 max_hp=%d)" % [_mean(dmg_total), _median_f(dmg_total), int(db.combat.hero.max_hp)])
|
||||
print(" 开安全格 均值 %.1f 中位 %d" % [_mean(safe_opened), _median(safe_opened)])
|
||||
print(" 清图(换板) 均值 %.2f 中位 %d" % [_mean(clears), _median(clears)])
|
||||
print(" 攻防buff授予 均值 %.2f" % [_mean(buff_grants)])
|
||||
@@ -65,7 +65,7 @@ func _run_battle(db, seed_val: int, L: int) -> Dictionary:
|
||||
settlement.minesweeper.cell_revealed.connect(func(evt):
|
||||
if evt.kind == "safe" and evt.channel != "initial":
|
||||
out.safe_opened += 1)
|
||||
if not settlement.begin_battle({"level_index": L, "node_depth": 0, "hero_hp": int(db.hero.max_hp)}):
|
||||
if not settlement.begin_battle({"level_index": L, "node_depth": 0, "hero_hp": int(db.combat.hero.max_hp)}):
|
||||
return out
|
||||
var guard_rounds := 40
|
||||
while out.result == "" and guard_rounds > 0:
|
||||
|
||||
@@ -19,7 +19,7 @@ func _initialize() -> void:
|
||||
quit(1)
|
||||
return
|
||||
var logger = load("res://presentation/playtest_logger.gd").new()
|
||||
logger.ap_per_round = int(db.action_economy.ap_per_round)
|
||||
logger.ap_per_round = int(db.run.action_economy.ap_per_round)
|
||||
var log_path: String = logger.start_session(seed0)
|
||||
print("节奏批日志: %s" % log_path)
|
||||
var autoplay = load("res://sim/level_autoplay.gd")
|
||||
|
||||
@@ -14,7 +14,7 @@ func _initialize() -> void:
|
||||
db.load_tables("res://data/tables", false)
|
||||
|
||||
# ① 表侧(行集数组取首行,战斗节点查 battle 键域)
|
||||
var row = db.resolve_enemy_rowset(1, 1, "battle")[0]
|
||||
var row = db.combat.enemy.resolve_rowset(1, 1, "battle")[0]
|
||||
print("[1] row(1,1) display=", row.get("display_name"), " unit_count=", row.get("unit_count"),
|
||||
" pool=", row.get("pool"))
|
||||
|
||||
@@ -50,7 +50,7 @@ func _run_battle(db, seed_val: int, L: int, depth: int, casts: Dictionary) -> Di
|
||||
if row.type == "damage" and row.source_class == "enemy_action":
|
||||
var sid: String = str(row.get("skill_id", "?"))
|
||||
casts[sid] = casts.get(sid, 0) + 1)
|
||||
if not settlement.begin_battle({"level_index": L, "node_depth": depth, "hero_hp": int(db.hero.max_hp)}):
|
||||
if not settlement.begin_battle({"level_index": L, "node_depth": depth, "hero_hp": int(db.combat.hero.max_hp)}):
|
||||
return out
|
||||
var guard_rounds := 40
|
||||
while out.result == "" and guard_rounds > 0:
|
||||
|
||||
@@ -39,7 +39,7 @@ func _initialize() -> void:
|
||||
quit(1)
|
||||
return
|
||||
var logger = load("res://presentation/playtest_logger.gd").new()
|
||||
logger.ap_per_round = int(db.action_economy.ap_per_round)
|
||||
logger.ap_per_round = int(db.run.action_economy.ap_per_round)
|
||||
var log_path: String = logger.start_session(opt.seed0)
|
||||
print("模拟日志: %s" % log_path)
|
||||
var autoplay = load("res://sim/level_autoplay.gd")
|
||||
@@ -154,7 +154,7 @@ func _seg_campaign(db, autoplay, logger, opt: Dictionary, counts: Dictionary) ->
|
||||
for r in range(int(opt.k)):
|
||||
var seed_val := _next_seed(int(opt.seed0))
|
||||
var ov: Dictionary = meta.hero_override()
|
||||
var keystone_before: int = meta.keystone_balance()
|
||||
var keystone_before: int = meta.meta_state.keystone_balance()
|
||||
var st: Dictionary = autoplay.run_adventure(db, seed_val, 0, 0, false, logger, policy, "min", ov)
|
||||
var row := _adventure_row(st, policy, seed_val)
|
||||
row["seg"] = "campaign"
|
||||
@@ -171,15 +171,15 @@ func _seg_campaign(db, autoplay, logger, opt: Dictionary, counts: Dictionary) ->
|
||||
var settle: Dictionary = meta.settle_adventure(st.summary)
|
||||
row["keystone_income"] = int(settle.total)
|
||||
row["purchases"] = _spend(meta, String(opt.spend))
|
||||
row["keystone_after"] = meta.keystone_balance()
|
||||
row["tree"] = {"sp_hp": meta.save.tree_level("sp_hp"), "sp_hit": meta.save.tree_level("sp_hit")}
|
||||
row["keystone_after"] = meta.meta_state.keystone_balance()
|
||||
row["tree"] = {"sp_hp": meta.save.meta_state.tree_state().nodes.get("sp_hp", 0), "sp_hit": meta.save.meta_state.tree_state().nodes.get("sp_hit", 0)}
|
||||
row["new_unlocks"] = settle.new_unlocks
|
||||
_emit(row)
|
||||
counts.campaign += 1
|
||||
if (r + 1) % 10 == 0:
|
||||
print("进度 campaign %s %d/%d(树 血%d/命中%d · 钥石 %d)" % [policy, r + 1, int(opt.k),
|
||||
meta.save.tree_level("sp_hp"), meta.save.tree_level("sp_hit"),
|
||||
meta.keystone_balance()])
|
||||
meta.save.meta_state.tree_state().nodes.get("sp_hp", 0), meta.save.meta_state.tree_state().nodes.get("sp_hit", 0),
|
||||
meta.meta_state.keystone_balance()])
|
||||
_remove_save(save_path) # 段尾清理(与 smoke 存档纪律同源——不遗留隔离档)
|
||||
|
||||
|
||||
@@ -193,9 +193,9 @@ func _spend(meta, policy_name: String) -> Array:
|
||||
guard -= 1
|
||||
var bought := false
|
||||
for nid in order:
|
||||
var price: int = int(meta.db.tree_node(nid).get("price", 0))
|
||||
if price > 0 and meta.keystone_balance() >= price and meta.tree_buy(nid):
|
||||
out.append({"node_id": nid, "level": meta.save.tree_level(nid), "price": price})
|
||||
var price: int = int(meta.db.crossrun.meta_tree.row_by_id(nid).get("price", 0))
|
||||
if price > 0 and meta.meta_state.keystone_balance() >= price and meta.tree_buy(nid):
|
||||
out.append({"node_id": nid, "level": meta.save.meta_state.tree_state().nodes.get(nid, 0), "price": price})
|
||||
bought = true
|
||||
break
|
||||
if not bought:
|
||||
|
||||
Reference in new issue
Block a user