feat(迭代35卡①): 节点图参数化生成器核心批落地——五旋钮参数档 schema+.tres 三行 provisional+ConfigDB 注册与 level_gen_params_row 单点读;RngService 新增 pure_stream 纯流面(不推计数器不落日志,derive_at 派生面流化);NodeMapGenerator 产图 generate(params,seed)+产出侧自证 validate_output(产出约束五条/结构铁律/摆放约束三条/多终点深度互异/深度=自入口最长路径步数)+参数无解致命中止无兜底图;三套断言宿主 68 条(fv1–fv10)+run_all manifest 三行登记;生长读数 L1/L2/L3 = 11/19/29 节点、层高 6/9/13。卡② 停工呈报随批(§0 硬交付序列——覆盖批未立卡)

This commit is contained in:
DKChan committed 2026-10-24 05:40:04 +08:00
1 parent 2931b1f250
commit 63a6ea8055
15 files changed
+1372

No files matched your search

+80
View File
@@ -0,0 +1,80 @@
## 关卡节点图参数档 schema(节点图生成器核心批 卡① T1):五旋钮+战斗保底+岔口去路数的行式数据表。
## 键 = level_index(关卡行集单点),消费面经 ConfigDB.level_gen_params_row() 读取;生成器只吃参数档、不认关卡号(契约 6)。
## 数值全 provisional(定值批落终值);域校验沿关卡节点图生成.md v1.1 调参表已锁条款——层深 L1≥6 递增增幅变大、
## 配比四类合计 1、出口 L1 恒 1/L2-L3 1-3、岔口去路数恒 2/3、战斗保底≥0。
class_name LevelGenParams
extends Resource
const RATIO_KEYS := ["battle_ratio", "reward_ratio", "rest_ratio", "shop_ratio"]
const ROW_KEYS := ["level_index", "layers", "branch_density", "entry_count", "exit_count",
"battle_floor", "fork_ways_min", "fork_ways_max"] + RATIO_KEYS
@export var rows: Array[Dictionary] = [] # 每行 = ROW_KEYS 键集(level_index 唯一正整数)
func validate() -> Array:
var errs: Array[Dictionary] = []
var seen := {}
var layers_by_level := {}
for row in rows:
var L: int = int(row.get("level_index", 0))
var key := "level %d" % L
if seen.has(L):
errs.append({"category": "dup_key", "message": "LevelGenParams 键重复: %d" % L})
seen[L] = true
if L < 1:
errs.append({"category": "domain", "message": "%s 关卡序号越域(≥1)" % key})
var ks := (row as Dictionary).keys()
ks.sort()
var expect := ROW_KEYS.duplicate()
expect.sort()
if ks != expect:
errs.append({"category": "structural", "message": "%s 字段键集非 ROW_KEYS 闭集" % key})
continue
var layers: int = int(row.get("layers", 0))
if layers < 6:
errs.append({"category": "domain", "message": "%s 层深 %d 低于下限 6(层深 L1≥6 已锁)" % [key, layers]})
layers_by_level[L] = layers
var bd: float = float(row.get("branch_density", -1.0))
if bd < 0.0 or bd > 1.0:
errs.append({"category": "domain", "message": "%s 分支密度 %f 越域 [0,1]" % [key, bd]})
var ratio_sum := 0.0
for rk in RATIO_KEYS:
var v: float = float(row.get(rk, -1.0))
if v < 0.0 or v > 1.0:
errs.append({"category": "domain", "message": "%s %s=%f 越域 [0,1]" % [key, rk, v]})
ratio_sum += v
if not is_equal_approx(ratio_sum, 1.0):
errs.append({"category": "domain", "message": "%s 配比四类合计 %f ≠ 1(已锁)" % [key, ratio_sum]})
var entry_count: int = int(row.get("entry_count", 0))
if entry_count < 1:
errs.append({"category": "domain", "message": "%s 入口个数 %d 越域(≥1)" % [key, entry_count]})
var exit_count: int = int(row.get("exit_count", 0))
if exit_count < 1 or exit_count > 3:
errs.append({"category": "domain", "message": "%s 出口个数 %d 越域 [1,3](已锁范围)" % [key, exit_count]})
if L == 1 and exit_count != 1:
errs.append({"category": "domain", "message": "%s 出口个数 %d ≠ 1(L1 出口恒 1 已锁)" % [key, exit_count]})
var bf: int = int(row.get("battle_floor", -1))
if bf < 0:
errs.append({"category": "domain", "message": "%s 战斗保底 %d 越域(≥0)" % [key, bf]})
var fmin: int = int(row.get("fork_ways_min", 0))
var fmax: int = int(row.get("fork_ways_max", 0))
if fmin != 2 or fmax != 3:
errs.append({"category": "domain", "message": "%s 岔口去路数 (%d,%d) ≠ (2,3)(已锁)" % [key, fmin, fmax]})
if layers_by_level.has(1) and layers_by_level.has(2) and layers_by_level.has(3):
var d12: int = int(layers_by_level[2]) - int(layers_by_level[1])
var d23: int = int(layers_by_level[3]) - int(layers_by_level[2])
if not (int(layers_by_level[1]) < int(layers_by_level[2]) and int(layers_by_level[2]) < int(layers_by_level[3])):
errs.append({"category": "domain", "message": "层深逐关递增不成立(L1/L2/L3 = %d/%d/%d)" % [
layers_by_level[1], layers_by_level[2], layers_by_level[3]]})
elif d23 <= d12:
errs.append({"category": "domain", "message": "层深增幅未变大(Δ(L1→L2)=%d、Δ(L2→L3)=%d)" % [d12, d23]})
return errs
# 参数档单点读取(键 = level_index,缺行返回空字典——沿 level_row() 体例)
func row_by_level_index(level_index: int) -> Dictionary:
for row in rows:
if int(row.get("level_index", 0)) == level_index:
return row
return {}
@@ -0,0 +1 @@
uid://c3yc3mhl1w7w4
+49
View File
@@ -0,0 +1,49 @@
[gd_resource type="Resource" script_class="LevelGenParams" load_steps=2 format=3]
[ext_resource type="Script" path="res://data/schemas/level_gen_params.gd" id="1"]
[resource]
script = ExtResource("1")
rows = [{
"level_index": 1,
"layers": 6,
"branch_density": 0.3,
"battle_ratio": 0.5,
"reward_ratio": 0.2,
"rest_ratio": 0.2,
"shop_ratio": 0.1,
"entry_count": 1,
"exit_count": 1,
"battle_floor": 4,
"fork_ways_min": 2,
"fork_ways_max": 3
}, {
"level_index": 2,
"layers": 9,
"branch_density": 0.5,
"battle_ratio": 0.5,
"reward_ratio": 0.2,
"rest_ratio": 0.15,
"shop_ratio": 0.15,
"entry_count": 2,
"exit_count": 2,
"battle_floor": 6,
"fork_ways_min": 2,
"fork_ways_max": 3
}, {
"level_index": 3,
"layers": 13,
"branch_density": 0.7,
"battle_ratio": 0.55,
"reward_ratio": 0.15,
"rest_ratio": 0.15,
"shop_ratio": 0.15,
"entry_count": 2,
"exit_count": 3,
"battle_floor": 8,
"fork_ways_min": 2,
"fork_ways_max": 3
}]
; 节点图生成器核心批 卡① §8.1 参数档 provisional 取值表逐行填列(定值批落终值,数值全 provisional)。
; 层深/出口/岔口去路数为已锁域;商店实数由摆放约束钉死=出口数(shop_ratio 为名义值,配比填余次序见卡① §8.1 注)。