102 lines
3.5 KiB
GDScript
102 lines
3.5 KiB
GDScript
# VERTICAL SLICE - NOT FOR PRODUCTION
|
||
# Date: 2026-09-22
|
||
# 棋盘口径校准(分析会话取数工具——冒险零加成批完成度分母实证):
|
||
# 逐 board_gen 行真实生成 N 板,统计 实际雷数/安全格数/R₀ 初始展开占比——
|
||
# 生成期「先布后删+可推理率重试」使实际雷数远低于表口径 round(d×(W×H−9)),
|
||
# 日志侧只有清图板可反推 safe_total(未清图深度无样本),本工具给全网格精确分母。
|
||
# 运行:Godot_console.exe --headless --path . --script res://smoke/calibrate_boards.gd -- [N]
|
||
# 输出:stdout CSV 行 level,depth,w,h,n,mines_mean,mines_med,safe_mean,safe_med,r0_mean,inf_mean
|
||
extends SceneTree
|
||
|
||
|
||
class FakeDB:
|
||
var row: Dictionary
|
||
func resolve_board_gen(_L: int, _D: int) -> Dictionary:
|
||
return row
|
||
func status_effect_row(_id: String) -> Dictionary:
|
||
return {}
|
||
|
||
|
||
func _initialize() -> void:
|
||
var args := OS.get_cmdline_user_args()
|
||
var n := 200
|
||
if args.size() >= 1 and args[0].is_valid_int():
|
||
n = maxi(10, int(args[0]))
|
||
var db = load("res://core/config_db.gd").new()
|
||
if not db.load_tables("res://data/tables", false):
|
||
print("表加载失败")
|
||
quit(1)
|
||
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:
|
||
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),
|
||
"mine_density": float(row.mine_density), "inferability_target": float(row.inferability_target),
|
||
"solver_rules": String(row.solver_rules), "min_safe_count": int(row.min_safe_count),
|
||
"max_gen_attempts": int(row.max_gen_attempts), "board_type": String(row.board_type),
|
||
# 初始展开收窄批(迭代14①)两新参数透传(.get 缺省与生成器防御口径一致)
|
||
"shave_ring_radius": int(row.get("shave_ring_radius", 1)),
|
||
"r0_max_cap": int(row.get("r0_max_cap", 0)),
|
||
}
|
||
var fdb := FakeDB.new()
|
||
fdb.row = rd
|
||
var mines: Array = []
|
||
var safes: Array = []
|
||
var r0s: Array = []
|
||
var infs: Array = []
|
||
var cap_rejs: Array = []
|
||
for i in range(n):
|
||
var rg := RandomNumberGenerator.new()
|
||
rg.seed = 900000 + i
|
||
var b = gen.generate(int(row.level_index), int(row.node_depth), rg, fdb)
|
||
if b == null:
|
||
continue
|
||
mines.append(b.mines.size())
|
||
safes.append(b.safe_cells().size())
|
||
r0s.append(int(b.metrics.r0_count))
|
||
infs.append(float(b.metrics.inferability))
|
||
cap_rejs.append(int(b.metrics.get("r0_cap_rejected", 0)))
|
||
if mines.is_empty():
|
||
printerr("L%d d%d 生成全部失败" % [int(row.level_index), int(row.node_depth)])
|
||
continue
|
||
print("%d,%d,%d,%d,%d,%.1f,%d,%.1f,%d,%.1f,%.2f,%d,%d,%.3f,%.2f" % [
|
||
int(row.level_index), int(row.node_depth), int(row.board_width), int(row.board_height),
|
||
mines.size(), _mean(mines), _med(mines), _mean(safes), _med(safes), _mean(r0s),
|
||
_sd(r0s), _p90(r0s), _max(r0s), _mean(infs), _mean(cap_rejs)])
|
||
quit(0)
|
||
|
||
|
||
func _mean(a: Array) -> float:
|
||
var t := 0.0
|
||
for v in a:
|
||
t += float(v)
|
||
return t / float(a.size())
|
||
|
||
|
||
func _sd(a: Array) -> float:
|
||
var m := _mean(a)
|
||
var t := 0.0
|
||
for v in a:
|
||
t += (float(v) - m) * (float(v) - m)
|
||
return sqrt(t / float(a.size()))
|
||
|
||
|
||
func _p90(a: Array) -> int:
|
||
var b := a.duplicate()
|
||
b.sort()
|
||
return int(b[mini(a.size() - 1, int(ceil(a.size() * 0.9)) - 1)])
|
||
|
||
|
||
func _max(a: Array) -> int:
|
||
var b := a.duplicate()
|
||
b.sort()
|
||
return int(b[b.size() - 1])
|
||
|
||
|
||
func _med(a: Array) -> int:
|
||
var b := a.duplicate()
|
||
b.sort()
|
||
return int(b[b.size() / 2])
|