feat(迭代35覆盖批·同批落): 生成图深度扩面的敌/板表覆盖落地+S5/V11 覆盖断言——用户授权本批随卡② 同批落(卡② §0 原文允许「须先落或与本卡同批落」),数值按现有曲线插值全 provisional

- enemy.tres:战斗带顶扩到各关最大生成深度(L1 3→6、L2 7→9、L3 7→13,共 3 行);Boss 行按终点深度分带使多终点解析带键互异(L1 加 6、L2 加 8、L3 加 11/12/13,共 5 行);敌种一律复用在册 id(零新内容),AC 全部留在已裁带宽(杂鱼 0~5/Boss 8~12),模板时代带键 0-3/4/9 与 hobgoblin 留任位不动
- board_gen.tres:node_depth 键域补齐 0–6/0–9/0–13(新增 12 行,其余字段按该关同档行复制)
- 断言面:level_shop_economy_test 新增 _smoke_generated_coverage 三条〔v11〕(生成图每 battle/boss 深度经 resolve_enemy_rowset 解析非空=顶 clamp 消解禁/每深度 board_gen 精确命中且 node_depth 等值/多终点 Boss 解析带键两两互异),扫描面=三关参数档×种子 42–61,量程读数 L1 0–6、L2 0–9、L3 0–13
- 旧断言随动三条(声明于此,非静默改判):table_structure_guard 〔fv11〕行数与键序 20→28 换代为覆盖表序、〔fv13〕「三关 Boss 各恰一行=图 Boss 深度」换为 Boss 带键覆盖表清单(多终点必然多行,互异判据移交〔v11〕);level_shop_economy 混编段基线行数 20→28、深度 8 的板侧「无行残留」改判「覆盖行在册」(战斗侧仍禁 (L,battle,8) 精确键)
This commit is contained in:
DKChan committed 2026-10-24 09:23:55 +08:00
1 parent 99e1b20ba6
commit 9214ef8e02
4 files changed
+467 -23

No files matched your search

+156
View File
@@ -70,6 +70,32 @@ rows = [{
"shave_ring_radius": 1,
"r0_max_cap": 43
}, {
"level_index": 1,
"node_depth": 5,
"board_width": 12,
"board_height": 12,
"mine_density": 0.25,
"inferability_target": 0.88,
"solver_rules": "single_subset",
"min_safe_count": 60,
"max_gen_attempts": 24,
"board_type": "dungeon",
"shave_ring_radius": 1,
"r0_max_cap": 43
}, {
"level_index": 1,
"node_depth": 6,
"board_width": 12,
"board_height": 12,
"mine_density": 0.25,
"inferability_target": 0.88,
"solver_rules": "single_subset",
"min_safe_count": 60,
"max_gen_attempts": 24,
"board_type": "dungeon",
"shave_ring_radius": 1,
"r0_max_cap": 43
}, {
"level_index": 2,
"node_depth": 0,
"board_width": 12,
@@ -97,6 +123,32 @@ rows = [{
"r0_max_cap": 43
}, {
"level_index": 2,
"node_depth": 2,
"board_width": 12,
"board_height": 12,
"mine_density": 0.25,
"inferability_target": 0.88,
"solver_rules": "single_subset",
"min_safe_count": 60,
"max_gen_attempts": 24,
"board_type": "underdark",
"shave_ring_radius": 1,
"r0_max_cap": 43
}, {
"level_index": 2,
"node_depth": 3,
"board_width": 12,
"board_height": 12,
"mine_density": 0.25,
"inferability_target": 0.88,
"solver_rules": "single_subset",
"min_safe_count": 60,
"max_gen_attempts": 24,
"board_type": "underdark",
"shave_ring_radius": 1,
"r0_max_cap": 43
}, {
"level_index": 2,
"node_depth": 4,
"board_width": 12,
"board_height": 12,
@@ -149,6 +201,19 @@ rows = [{
"r0_max_cap": 43
}, {
"level_index": 2,
"node_depth": 8,
"board_width": 12,
"board_height": 12,
"mine_density": 0.25,
"inferability_target": 0.88,
"solver_rules": "single_subset",
"min_safe_count": 60,
"max_gen_attempts": 24,
"board_type": "underdark",
"shave_ring_radius": 1,
"r0_max_cap": 43
}, {
"level_index": 2,
"node_depth": 9,
"board_width": 12,
"board_height": 12,
@@ -188,6 +253,32 @@ rows = [{
"r0_max_cap": 51
}, {
"level_index": 3,
"node_depth": 2,
"board_width": 13,
"board_height": 13,
"mine_density": 0.25,
"inferability_target": 0.88,
"solver_rules": "single_subset",
"min_safe_count": 70,
"max_gen_attempts": 32,
"board_type": "deep_underdark",
"shave_ring_radius": 1,
"r0_max_cap": 51
}, {
"level_index": 3,
"node_depth": 3,
"board_width": 13,
"board_height": 13,
"mine_density": 0.25,
"inferability_target": 0.88,
"solver_rules": "single_subset",
"min_safe_count": 70,
"max_gen_attempts": 32,
"board_type": "deep_underdark",
"shave_ring_radius": 1,
"r0_max_cap": 51
}, {
"level_index": 3,
"node_depth": 4,
"board_width": 13,
"board_height": 13,
@@ -240,6 +331,19 @@ rows = [{
"r0_max_cap": 51
}, {
"level_index": 3,
"node_depth": 8,
"board_width": 13,
"board_height": 13,
"mine_density": 0.25,
"inferability_target": 0.88,
"solver_rules": "single_subset",
"min_safe_count": 70,
"max_gen_attempts": 32,
"board_type": "deep_underdark",
"shave_ring_radius": 1,
"r0_max_cap": 51
}, {
"level_index": 3,
"node_depth": 9,
"board_width": 14,
"board_height": 14,
@@ -251,6 +355,58 @@ rows = [{
"board_type": "deep_underdark",
"shave_ring_radius": 1,
"r0_max_cap": 59
}, {
"level_index": 3,
"node_depth": 10,
"board_width": 14,
"board_height": 14,
"mine_density": 0.25,
"inferability_target": 0.88,
"solver_rules": "single_subset",
"min_safe_count": 82,
"max_gen_attempts": 32,
"board_type": "deep_underdark",
"shave_ring_radius": 1,
"r0_max_cap": 59
}, {
"level_index": 3,
"node_depth": 11,
"board_width": 14,
"board_height": 14,
"mine_density": 0.25,
"inferability_target": 0.88,
"solver_rules": "single_subset",
"min_safe_count": 82,
"max_gen_attempts": 32,
"board_type": "deep_underdark",
"shave_ring_radius": 1,
"r0_max_cap": 59
}, {
"level_index": 3,
"node_depth": 12,
"board_width": 14,
"board_height": 14,
"mine_density": 0.25,
"inferability_target": 0.88,
"solver_rules": "single_subset",
"min_safe_count": 82,
"max_gen_attempts": 32,
"board_type": "deep_underdark",
"shave_ring_radius": 1,
"r0_max_cap": 59
}, {
"level_index": 3,
"node_depth": 13,
"board_width": 14,
"board_height": 14,
"mine_density": 0.25,
"inferability_target": 0.88,
"solver_rules": "single_subset",
"min_safe_count": 82,
"max_gen_attempts": 32,
"board_type": "deep_underdark",
"shave_ring_radius": 1,
"r0_max_cap": 59
}]
; 切片调参登记(SLICE-TUNED provisional,调参所有权仍归 C3):
+224
View File
@@ -99,6 +99,31 @@ rows = [{
"armor_class": 2
}, {
"level_index": 1,
"row_kind": "battle",
"depth_band": 6,
"enemy_id": "bugbear",
"display_name": "熊地精",
"unit_count": 1,
"unit_count_max": 2,
"hp": 30,
"atk": 1,
"atk_min": 0,
"atk_max": 2,
"pool": [{
"skill_id": "attack",
"weight": 6
}, {
"skill_id": "charge",
"weight": 2
}, {
"skill_id": "heavy",
"weight": 2
}],
"attack_type": "normal",
"armor_type": "light",
"armor_class": 4
}, {
"level_index": 1,
"row_kind": "boss",
"depth_band": 4,
"enemy_id": "ogre_chief",
@@ -126,6 +151,34 @@ rows = [{
"armor_type": "light",
"armor_class": 8
}, {
"level_index": 1,
"row_kind": "boss",
"depth_band": 6,
"enemy_id": "ogre_chief",
"display_name": "食人魔酋长",
"unit_count": 1,
"hp": 88,
"atk": 1,
"atk_min": 0,
"atk_max": 2,
"pool": [{
"skill_id": "attack",
"weight": 6
}, {
"skill_id": "heavy",
"weight": 2
}, {
"skill_id": "harden",
"weight": 2
}, {
"skill_id": "quake_smash",
"weight": 2,
"unlock_hp_pct": 50
}],
"attack_type": "normal",
"armor_type": "light",
"armor_class": 8
}, {
"level_index": 2,
"row_kind": "battle",
"depth_band": 0,
@@ -308,6 +361,65 @@ rows = [{
"armor_class": 3
}, {
"level_index": 2,
"row_kind": "battle",
"depth_band": 9,
"enemy_id": "giant_spider",
"display_name": "巨蜘蛛巢群",
"unit_count": 2,
"unit_count_max": 3,
"hp": 40,
"atk": 2,
"atk_min": 1,
"atk_max": 3,
"pool": [{
"skill_id": "attack",
"weight": 6
}, {
"skill_id": "bite",
"weight": 2
}, {
"skill_id": "charge",
"weight": 1
}, {
"skill_id": "venom_spit",
"weight": 2
}],
"attack_type": "normal",
"armor_type": "light",
"armor_class": 3
}, {
"level_index": 2,
"row_kind": "boss",
"depth_band": 8,
"enemy_id": "drider",
"display_name": "蛛化卓尔精灵",
"unit_count": 1,
"hp": 112,
"atk": 3,
"atk_min": 2,
"atk_max": 4,
"pool": [{
"skill_id": "attack",
"weight": 6
}, {
"skill_id": "heavy",
"weight": 2
}, {
"skill_id": "venom_burst",
"weight": 2
}, {
"skill_id": "web_snare",
"weight": 2
}, {
"skill_id": "venom_curb",
"weight": 2,
"unlock_hp_pct": 50
}],
"attack_type": "normal",
"armor_type": "ward",
"armor_class": 10
}, {
"level_index": 2,
"row_kind": "boss",
"depth_band": 9,
"enemy_id": "drider",
@@ -495,6 +607,34 @@ rows = [{
"armor_class": 4
}, {
"level_index": 3,
"row_kind": "battle",
"depth_band": 13,
"enemy_id": "duergar",
"display_name": "灰矮人小队",
"unit_count": 2,
"unit_count_max": 3,
"hp": 42,
"atk": 3,
"atk_min": 2,
"atk_max": 4,
"pool": [{
"skill_id": "attack",
"weight": 6
}, {
"skill_id": "charge",
"weight": 2
}, {
"skill_id": "heavy",
"weight": 2
}, {
"skill_id": "shield_bash",
"weight": 2
}],
"attack_type": "normal",
"armor_type": "heavy",
"armor_class": 4
}, {
"level_index": 3,
"row_kind": "boss",
"depth_band": 9,
"enemy_id": "duergar_king",
@@ -521,6 +661,90 @@ rows = [{
"attack_type": "normal",
"armor_type": "heavy",
"armor_class": 12
}, {
"level_index": 3,
"row_kind": "boss",
"depth_band": 11,
"enemy_id": "duergar_king",
"display_name": "灰矮人石君王",
"unit_count": 1,
"hp": 150,
"atk": 3,
"atk_min": 2,
"atk_max": 4,
"pool": [{
"skill_id": "attack",
"weight": 6
}, {
"skill_id": "heavy",
"weight": 2
}, {
"skill_id": "earth_pulse",
"weight": 2
}, {
"skill_id": "rockslide",
"weight": 2,
"unlock_hp_pct": 50
}],
"attack_type": "normal",
"armor_type": "heavy",
"armor_class": 12
}, {
"level_index": 3,
"row_kind": "boss",
"depth_band": 12,
"enemy_id": "duergar_king",
"display_name": "灰矮人石君王",
"unit_count": 1,
"hp": 162,
"atk": 3,
"atk_min": 2,
"atk_max": 4,
"pool": [{
"skill_id": "attack",
"weight": 6
}, {
"skill_id": "heavy",
"weight": 2
}, {
"skill_id": "earth_pulse",
"weight": 2
}, {
"skill_id": "rockslide",
"weight": 2,
"unlock_hp_pct": 50
}],
"attack_type": "normal",
"armor_type": "heavy",
"armor_class": 12
}, {
"level_index": 3,
"row_kind": "boss",
"depth_band": 13,
"enemy_id": "duergar_king",
"display_name": "灰矮人石君王",
"unit_count": 1,
"hp": 176,
"atk": 3,
"atk_min": 2,
"atk_max": 4,
"pool": [{
"skill_id": "attack",
"weight": 6
}, {
"skill_id": "heavy",
"weight": 2
}, {
"skill_id": "earth_pulse",
"weight": 2
}, {
"skill_id": "rockslide",
"weight": 2,
"unlock_hp_pct": 50
}],
"attack_type": "normal",
"armor_type": "heavy",
"armor_class": 12
}]
; 切片调参登记 v3(2026-09-12 试玩批次 1 触发——F7 流程;C3 GDD F5 对照):
@@ -37,9 +37,60 @@ func _init() -> void:
_smoke_shop_room()
_smoke_shop_graph()
_smoke_shop_pool()
_smoke_generated_coverage()
quit(Kit.finish("level_shop_economy_test"))
# ---- 生成图深度扩面覆盖断言(迭代 35 卡② S5/V11——节点源=卡① 生成器产图,
# 敌侧/板侧解析一律走 ConfigDB 真实消费链,禁直读行集自算)----
func _smoke_generated_coverage() -> void:
var RngScript := preload("res://core/rng_service.gd")
var GenScript := preload("res://core/node_map_generator.gd")
var gen = GenScript.new(RngScript.new())
var enemy_ok := true
var board_ok := true
var boss_band_ok := true
var readings := []
for L in [1, 2, 3]:
var params: Dictionary = _db.level_gen_params_row(L)
if params.is_empty():
Kit.check("L%d 参数档缺行(覆盖断言无源可扫)〔v11〕" % L, false)
continue
var dmin := 999
var dmax := 0
for seed_val in range(42, 62):
var g: Dictionary = gen.generate(params, seed_val)
var by_id := {}
for n in g["nodes"]:
by_id[int(n["id"])] = n
for n in g["nodes"]:
var d: int = int(n["depth"])
dmin = mini(dmin, d)
dmax = maxi(dmax, d)
var kind := String(n["type"])
if kind == "battle" or kind == "boss":
if _db.resolve_enemy_rowset(L, d, "boss" if kind == "boss" else "battle").is_empty():
enemy_ok = false
var board: Dictionary = _db.resolve_board_gen(L, d)
if board.is_empty() or int(board.get("node_depth", -1)) != d:
board_ok = false
var bands := []
for tid in g["terminals"]:
var bd: int = int(by_id[int(tid)]["depth"])
var rs: Array[Dictionary] = _db.resolve_enemy_rowset(L, bd, "boss")
bands.append(int(rs[0].get("depth_band", -1)) if not rs.is_empty() else -1)
for i in range(bands.size()):
for j in range(i + 1, bands.size()):
if bands[i] == bands[j]:
boss_band_ok = false
readings.append("L%d 深度域 %d–%d" % [L, dmin, dmax])
print("[v11] 生成图覆盖量程读数:" + "; ".join(readings))
Kit.check("生成图每个 battle/boss 节点深度在敌表解析到非空行集(顶 clamp 消解禁)〔v11〕", enemy_ok)
Kit.check("生成图每个节点深度在 board_gen 精确命中(非空且 node_depth 等值)〔v11〕", board_ok)
Kit.check("多终点 Boss 各终点解析带键两两互异(L2/L3×20 种子)〔v11〕", boss_band_ok)
# ---- 关卡节点地图·线性推进冒烟----
func _smoke_level() -> void:
@@ -335,7 +386,7 @@ func _smoke_level_graph() -> void:
_db.enemy.rows.remove_at(_db.enemy.rows.size() - 1)
_db.enemy.rows.remove_at(_db.enemy.rows.size() - 1)
_db.enemy.rows.remove_at(_db.enemy.rows.size() - 1)
Kit.check("混编断言段注入行全量还原(表回 20 行——首批混编投放后基线行数,迭代 18 卡⑤;后续断言面零污染)", int(_db.enemy.rows.size()) == 20)
Kit.check("混编断言段注入行全量还原(表回 28 行——迭代 35 生成图深度扩面覆盖批后的基线行数;首批混编投放基线 20 行+覆盖 8 行)", int(_db.enemy.rows.size()) == 28)
# ⑬ 真图类型可见性(信息迷雾 v2.1):L2 入口停点纯核心面读数——候选 ⊆ 可见集 /
# Boss 恒可见 / 层距 > N 的非豁免节点入雾。N 取数据表(代码零硬编码)。
@@ -710,7 +761,11 @@ func _smoke_shop_graph() -> void:
en_keys["%d_%s_%d" % [int(row.level_index), String(row.get("row_kind", "")), int(row.get("depth_band", -1))]] = true
for L in [2, 3]:
Kit.check("(%d,boss,9) Boss 行在 board_gen + enemy 精确存在" % L, bg_keys.has("%d_9" % L) and en_keys.has("%d_boss_9" % L))
Kit.check("(%d,battle,8) 商店房无板/战斗行集残留(陈旧行已清理)" % L, not bg_keys.has("%d_8" % L) and not en_keys.has("%d_battle_8" % L))
# 迭代 35 生成图深度扩面覆盖批随动(旧判据属模板时代:深度 8 = 商店房专属位,故禁板行与战斗行):
# 生成图下深度 8 是正常可行深度 ⇒ 板侧改判「覆盖行在册」;战斗侧仍禁 (L,battle,8) 精确键
#(战斗落带经顶带 9/13 解析,不落 8 号精确键——两侧判据都保留,只按现行设计换正负号)。
Kit.check("(%d,8) 板侧生成图覆盖行在册·(%d,battle,8) 战斗精确键仍不存在" % [L, L],
bg_keys.has("%d_8" % L) and not en_keys.has("%d_battle_8" % L))
Kit.check("L1 行集零改动(单链 Boss (1,4)——教学期最小修订面)", bg_keys.has("1_4") and en_keys.has("1_boss_4") and not bg_keys.has("1_8") and not en_keys.has("1_battle_8"))
@@ -222,26 +222,31 @@ func _smoke_adventure_table() -> void:
v18_ok and locked_n == 12)
# ---- 敌人表深度带键结构(迭代 34:20 行键迁移 · 键序=迁移表序 · hobgoblin 留任 · 三 Boss 行 kind/band)----
# ---- 敌人表深度带键结构(迭代 34 建·迭代 35 覆盖批换代:28 行键序=覆盖表序·hobgoblin 留任·Boss 行集带键清单)----
func _smoke_enemy_bands() -> void:
print("--- 敌人表深度带键结构(20 行键序 · hobgoblin 留任 · 三 Boss 行)---")
# ① 20 行计数 + 键序 = 迁移表序(文件行序不变——行序即契约)
print("--- 敌人表深度带键结构(28 行键序 · hobgoblin 留任 · Boss 带键覆盖表)---")
# ① 28 行计数 + 键序 = 覆盖表序(文件行序不变——行序即契约)
# 迭代 35 生成图深度扩面覆盖批随动:战斗带顶扩到各关最大生成深度(L1→6/L2→9/L3→13)、
# Boss 行按终点深度分带(L1 6/L2 8·9/L3 11·12·13),模板时代带键 4/9/9 全部在册不动
var expect_keys: Array[String] = [
"1|battle|0|goblin", "1|battle|1|goblin", "1|battle|2|bugbear", "1|battle|3|hobgoblin",
"1|boss|4|ogre_chief",
"1|battle|6|bugbear",
"1|boss|4|ogre_chief", "1|boss|6|ogre_chief",
"2|battle|0|bugbear", "2|battle|1|bugbear", "2|battle|4|giant_spider",
"2|battle|5|bugbear", "2|battle|5|giant_spider", "2|battle|6|giant_spider", "2|battle|7|giant_spider",
"2|boss|9|drider",
"2|battle|9|giant_spider",
"2|boss|8|drider", "2|boss|9|drider",
"3|battle|0|duergar", "3|battle|1|hook_horror", "3|battle|4|hook_horror", "3|battle|5|duergar",
"3|battle|6|hook_horror", "3|battle|7|duergar", "3|boss|9|duergar_king",
"3|battle|6|hook_horror", "3|battle|7|duergar", "3|battle|13|duergar",
"3|boss|9|duergar_king", "3|boss|11|duergar_king", "3|boss|12|duergar_king", "3|boss|13|duergar_king",
]
var got_keys: Array[String] = []
for row in _db.enemy.rows:
got_keys.append("%d|%s|%d|%s" % [int(row.get("level_index", -1)), String(row.get("row_kind", "")),
int(row.get("depth_band", -1)), String(row.get("enemy_id", ""))])
Kit.check("敌人表 20 行且键序 = 迁移表序((L,row_kind,depth_band,enemy_id) 逐行比对——文件行序不变)〔fv11〕",
_db.enemy.rows.size() == 20 and got_keys == expect_keys)
Kit.check("敌人表 28 行且键序 = 覆盖表序((L,row_kind,depth_band,enemy_id) 逐行比对——文件行序不变)〔fv11〕",
_db.enemy.rows.size() == 28 and got_keys == expect_keys)
# ② hobgoblin 缺口行留任随迁(契约 9:不删行不补立绘——缺口登记随行)
var hob := {}
for row in _db.enemy.rows:
@@ -252,23 +257,27 @@ func _smoke_enemy_bands() -> void:
and int(hob.get("depth_band", -1)) == 3)
Kit.check("hobgoblin 缺口登记随行(表文件注释在位——缺口不静默)〔fv25〕",
FileAccess.get_file_as_string("res://data/tables/enemy.tres").contains("hobgoblin 行为缺口行"))
# ③ 三 Boss 行 kind/band 就位(band = 该关图 Boss 节点深度——单终点图数据驱动)
# ③ Boss 行集带键 = 覆盖表(旧判据「各恰一行=图 Boss 节点深度」属单终点模板时代;
# 生成图多终点要求每终点深度解析到互异带键 ⇒ 每关 Boss 行必然多于一行——
# 迭代 35 覆盖批随动改写为带键清单,「带键互异」的运行时把关在 level_shop_economy_test〔v11〕)
var boss_bands := {}
for row in _db.enemy.rows:
if String(row.get("row_kind", "")) == "boss":
var lk: int = int(row.get("level_index", -1))
if not boss_bands.has(lk):
boss_bands[lk] = []
boss_bands[lk].append(int(row.get("depth_band", -1)))
var expect_bands := {1: [4, 6], 2: [8, 9], 3: [9, 11, 12, 13]}
var boss_ok := true
var boss_detail := ""
for L in [1, 2, 3]:
var bdepth := _boss_depth(L)
var hits := 0
for row in _db.enemy.rows:
if int(row.get("level_index", -1)) == L and String(row.get("row_kind", "")) == "boss":
hits += 1
if int(row.get("depth_band", -1)) != bdepth:
boss_ok = false
boss_detail += " L%d:%d≠%d" % [L, int(row.get("depth_band", -1)), bdepth]
if hits != 1:
var got: Array = boss_bands.get(L, [])
got.sort()
if got != expect_bands[L]:
boss_ok = false
boss_detail += " L%d:boss 行×%d" % [L, hits]
Kit.check("三关 Boss 行各恰一行且 row_kind=boss、band=图 Boss 节点深度(%d/%d/%d%s)〔fv13〕" % [
_boss_depth(1), _boss_depth(2), _boss_depth(3), boss_detail], boss_ok)
boss_detail += " L%d:%s≠%s" % [L, str(got), str(expect_bands[L])]
Kit.check("三关 Boss 行集带键 = 覆盖表(模板终点 4/9/9 在册+生成图终点 6/8/9/11/12/13 扩带)%s〔fv13〕" % boss_detail,
boss_ok)
# ② 免伤墙归零护栏泛化保留(BUG-0003 事故类永久免疫):hero_defense 池任何 percent 行