From e3e206d0f5646ecb8ece22ac08018c26e5591e1d Mon Sep 17 00:00:00 2001 From: DKChan <524912269@qq.com> Date: Sun, 1 Nov 2026 02:53:31 +0800 Subject: [PATCH] =?UTF-8?q?=E8=BF=AD=E4=BB=A346=E5=8D=A1C(=E6=A3=8B?= =?UTF-8?q?=E7=9B=98=E6=A0=BC=E8=BE=B9=E9=95=BF=E5=8F=A3=E5=BE=84=E6=94=B6?= =?UTF-8?q?=E5=8F=A3=E6=89=B9v2.0):=20=E6=A0=BC=E8=BE=B9=E9=95=BF=E7=AE=97?= =?UTF-8?q?=E5=BC=8F=E5=8D=95=E7=82=B9=E5=8C=96=E2=80=94=E2=80=94board=5Fv?= =?UTF-8?q?iew=E6=8A=BDstatic=20=5Fcell=5Ffor=E9=80=90=E4=BD=8D=E5=90=8C?= =?UTF-8?q?=E5=BC=8F(SEP/DEPTH=5FPX=E8=AF=BB=E6=9C=AC=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E5=B8=B8=E9=87=8F=C2=B7clampi[26,46]=E7=95=99=E5=87=BD?= =?UTF-8?q?=E6=95=B0=E5=86=85=C2=B7S13=E9=A1=B6=E6=A0=BC27/31=E6=8C=81?= =?UTF-8?q?=E5=B9=B3=E9=9B=B6=E6=96=B0=E5=A2=9E=E6=88=90=E5=91=98)+set=5Fc?= =?UTF-8?q?ell=5Farea=E6=94=B9=E8=B0=83=C2=B7=E5=89=AF=E4=BD=9C=E7=94=A8?= =?UTF-8?q?=E5=B0=BE=E9=93=BE=E9=9B=B6=E5=8A=A8(=E8=A1=8C=E4=B8=BA?= =?UTF-8?q?=E9=9B=B6=E5=8F=98=E5=8C=96)=C2=B7=E6=8E=A2=E9=92=88=E9=98=88?= =?UTF-8?q?=E5=80=BC=E5=88=A4=E6=8D=AE=E9=80=80=E5=BD=B9(MIN=5FCELL?= =?UTF-8?q?=E5=B8=B8=E9=87=8F=E6=95=B4=E8=A1=8C=E5=88=A0=E9=99=A4=C2=B7?= =?UTF-8?q?=E9=98=88=E5=80=BC=E6=96=AD=E8=A8=80=E6=8D=A2=E7=AD=89=E4=BB=B7?= =?UTF-8?q?=E6=96=AD=E8=A8=80cell=3D=3D=5Fcell=5Ffor(n,bv.size)=C2=B7=5Fbo?= =?UTF-8?q?ard=E5=86=85=E9=83=A8=E5=AD=97=E6=AE=B5=E7=9B=B4=E6=9F=A5?= =?UTF-8?q?=E5=8F=97=E6=8E=A7=E4=BE=8B=E5=A4=96=3DN=E6=97=A0=E5=85=AC?= =?UTF-8?q?=E5=BC=80=E5=87=BA=E5=8F=A3=C2=B7PASS=E8=A1=8C=E5=90=AB?= =?UTF-8?q?=E5=9F=BA=E5=BA=A7=E5=AE=9E=E8=AF=BB+13/14=E6=8E=A8=E5=AE=9A?= =?UTF-8?q?=E7=95=99=E7=97=95=E6=89=93=E5=8D=B0=E4=B8=8D=E4=BD=9C=E5=88=A4?= =?UTF-8?q?=E7=BA=A2=C2=B7=E9=9B=B6=E5=B0=BA=E5=AF=B8=E5=89=8D=E7=BD=AE?= =?UTF-8?q?=E8=AE=B0=E7=BA=A2)=C2=B7=E5=8D=95=E5=85=83=E5=86=92=E7=83=9F?= =?UTF-8?q?=E6=89=A9=E5=9B=9B=E6=A1=A3=E6=9D=BF=E5=9E=8B((L,D)=E5=8F=96?= =?UTF-8?q?=E6=A0=B710/12/13/14=C2=B7=E6=9D=BF=E5=9E=8B=E4=B8=80=E8=87=B4+?= =?UTF-8?q?=E7=AE=97=E5=BC=8F=E5=8D=95=E7=82=B9=E7=AD=89=E4=BB=B7=E4=B8=A4?= =?UTF-8?q?=E6=96=AD=E8=A8=80=C2=B7=E6=97=A2=E6=9C=89=E7=BD=91=E6=A0=BC?= =?UTF-8?q?=E9=AB=98=E4=B8=8E26-46=E5=8C=BA=E9=97=B4=E6=96=AD=E8=A8=80?= =?UTF-8?q?=E9=80=90=E5=AD=97=E4=BF=9D=E7=95=99=C2=B762=E2=86=9274checks?= =?UTF-8?q?=E5=85=A8=E7=BB=BF)=C2=B7=E6=8E=A2=E9=92=88=E4=B8=A4=E6=A1=A3?= =?UTF-8?q?=E7=BB=BF(=E5=9F=BA=E5=BA=A7=E5=AE=9E=E8=AF=BB40px=C2=B713/14?= =?UTF-8?q?=E6=8E=A8=E5=AE=9A30/28=E4=B8=8E=E5=8D=A1=C2=A72.7=E9=94=9A?= =?UTF-8?q?=E7=82=B9=E9=80=90=E5=80=BC=E4=B8=80=E8=87=B4=C2=B7=E5=85=A8?= =?UTF-8?q?=E5=B1=8F46/46=C2=B7=E5=8F=AF=E7=94=A8=E9=9D=A2=E7=A7=AF1022x45?= =?UTF-8?q?4/1662x734)=C2=B7grep=20MIN=5FCELL/=E5=8F=AF=E8=AF=BB=E9=97=A8?= =?UTF-8?q?=E6=A7=9B/=E5=8F=AF=E8=AF=BB=E4=B8=8B=E9=99=90=E5=85=A8?= =?UTF-8?q?=E4=BB=93=E9=9B=B6=E5=91=BD=E4=B8=AD=C2=B7probe=5Fgate=E9=BB=98?= =?UTF-8?q?=E8=AE=A4=E6=AE=B5=E7=BB=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- game/presentation/views/board_view.gd | 19 +++++++--- game/probe/layout_probe.gd | 37 ++++++++++++++++--- game/tests/unit/core/board_gen_reward_test.gd | 18 ++++++--- 3 files changed, 57 insertions(+), 17 deletions(-) diff --git a/game/presentation/views/board_view.gd b/game/presentation/views/board_view.gd index a271ca4b..8af30583 100644 --- a/game/presentation/views/board_view.gd +++ b/game/presentation/views/board_view.gd @@ -203,15 +203,11 @@ func set_world_font(f: Font) -> void: # D 区可用面积(AP 条之外)→ 格边长 = floor((min(宽, 高) − 预留 − (N−1)×格间距) / N)(battle.md 格尺寸公式) # 公式须补算 (N−1)×2px 网格间距与垂直预留 24px,否则 12×12 板实高溢出可用区 ~6px,下缘被 clip_contents 裁掉。 +# 算式唯一落点 = _cell_for(迭代 46 卡C——探针与单元测试共用该静态函数求值,禁复写算式造第二口径)。 func set_cell_area(avail: Vector2) -> void: if _board == null or _board.width == 0: return - var n: int = maxi(_board.width, _board.height) - var sep := SEP # 格间距(_init 同值) - var d := DEPTH_PX - var reserve := 24.0 + d # 垂直总预留(上 12 + 下 12 + 悬崖外延——悬崖占预算防溢出) - var cell := int(floor((minf(avail.x - 12.0 - d, avail.y - reserve) - float((n - 1) * sep)) / float(n))) - _cell = float(clampi(cell, 26, 46)) + _cell = float(_cell_for(maxi(_board.width, _board.height), avail)) _hero_sprite.set_cell_size(_cell, float(SEP)) var stride := _cell + float(SEP) for c in _cells: @@ -224,6 +220,17 @@ func set_cell_area(avail: Vector2) -> void: _update_world_size() +# 格边长算式唯一落点(迭代 46 卡C T1 自 set_cell_area 抽出——逐位同式:SEP/DEPTH_PX 读本文件 +# 既有常量、clampi [26,46] 留本函数内;名字带下划线是 S13 max-public-methods 27 顶格的约束,禁改公开名。 +# 探针(非当前板型 13/14×实板可用面积)与单元测试(四档板型)经本函数求值,复写算式即造第二口径) +static func _cell_for(n: int, avail: Vector2) -> int: + var sep := SEP # 格间距(_init 同值) + var d := DEPTH_PX + var reserve := 24.0 + d # 垂直总预留(上 12 + 下 12 + 悬崖外延——悬崖占预算防溢出) + var cell := int(floor((minf(avail.x - 12.0 - d, avail.y - reserve) - float((n - 1) * sep)) / float(n))) + return clampi(cell, 26, 46) + + # 悬崖参数下发(网格像素尺寸/步距/列数/材质锚色——BoardWorld._draw 消费) func _refresh_slab() -> void: if _board == null or _board.width == 0: diff --git a/game/probe/layout_probe.gd b/game/probe/layout_probe.gd index ea8e366c..965732fd 100644 --- a/game/probe/layout_probe.gd +++ b/game/probe/layout_probe.gd @@ -6,15 +6,16 @@ extends Node const ProbeUiUtil := preload("res://probe/probe_ui_util.gd") # 隔离样板单点 +const BoardViewScript := preload("res://presentation/views/board_view.gd") # 算式单点静态调用载体(迭代 46 卡C) # 分辨率档位表(逻辑视口尺寸;数据侧——增删档位只改这里,断言代码零改动) const VIEWPORTS := [ {"label": "基座 1280x800", "size": Vector2i(1280, 800)}, {"label": "16:9 全屏 1920x1080", "size": Vector2i(1920, 1080)}, ] -const MIN_CELL := 36.0 # 格子可读门槛(battle.md §棋盘格尺寸公式 1280 档 12×12 → 36px) - var _problems: PackedStringArray = [] +var _cell_reads: Dictionary = {} # 逐档格边长实读留痕(label→cell_size;PASS 行引基座值) +var _est_reads: Dictionary = {} # 逐档 13/14 板推定读数留痕(label→{13,14,avail};PASS 后打印对拍) func _ready() -> void: @@ -41,8 +42,12 @@ func _ready() -> void: # 复原本项目基座(探针不该把进程留在末档尺寸) get_tree().root.content_scale_size = Vector2i(1280, 800) if _problems.is_empty(): - print("[layout_probe] PASS — %d 档几何不变量全过(三区非零/不越视口/互不重叠/view 含于 D 区/格子 ≥%.0fpx)" - % [VIEWPORTS.size(), MIN_CELL]) + print("[layout_probe] PASS — %d 档几何不变量全过+格边长等价成立(基座实读 %.0fpx)〔格边长 V1〕" + % [VIEWPORTS.size(), float(_cell_reads.get("基座 1280x800", -1.0))]) + for label in _est_reads: + var e: Dictionary = _est_reads[label] + print("[layout_probe] [%s] 13 板推定 %dpx/14 板推定 %dpx(实板可用面积 %s)〔格边长 V4〕" + % [label, int(e["13"]), int(e["14"]), str(e["avail"])]) get_tree().quit(0) else: for p in _problems: @@ -91,5 +96,25 @@ func _check_one(label: String, main: Node) -> void: _problems.append("[%s] 顶层区重叠 %s×%s(%s ∩ %s)" % [label, keys[i], keys[j], rects[keys[i]], rects[keys[j]]]) var cell: float = main.ui.battle.board_zone.board_view.get("cell_size") - if cell < MIN_CELL: - _problems.append("[%s] 格子边长 %.1fpx < 可读门槛 %.0fpx" % [label, cell, MIN_CELL]) \ No newline at end of file + _cell_reads[label] = cell + # 格边长接线守卫——判据=等价(生产写入值与单点算式严格相等,生产接线断开即红; + # 全仓禁任何人造格边长可读阈值)。 + # 板型 N 无公开出口→`bv.get("_board")` 内部字段直查=受控例外(改动—验收对照表行 1 注明理由)。 + var b = bv.get("_board") + if b == null: + _problems.append("[%s] _board 为 null(板型 N 无从取值,等价断言无依据)〔格边长 V1〕" % label) + elif cell != float(BoardViewScript._cell_for(maxi(int(b.width), int(b.height)), bv.size)): + _problems.append("[%s] 格边长等价破——生产写入 %.1fpx ≠ _cell_for(n=%d, avail=%s) 返回 %.1fpx(实板可用面积 %s)〔格边长 V1〕" + % [label, cell, maxi(int(b.width), int(b.height)), str(bv.size), + float(BoardViewScript._cell_for(maxi(int(b.width), int(b.height)), bv.size)), str(bv.size)]) + # 最大板推定留痕(迭代 46 卡C T2——实板可用面积代入 13/14 板型求算式单点,读数打印不作判红: + # 锚点=用户验收的现行渲染值;对拍归实现报告,对拍不一致=卡 §9 L3 #2 呈报,禁实现侧改锚; + # 零尺寸前置=可用面积不可信时推定无依据) + if bv.size.x <= 0.0 or bv.size.y <= 0.0: + _problems.append("[%s] board_view 零尺寸(可用面积不可信,格边长推定无依据)〔格边长 V4〕" % label) + else: + _est_reads[label] = { + "13": BoardViewScript._cell_for(13, bv.size), + "14": BoardViewScript._cell_for(14, bv.size), + "avail": str(bv.size), + } \ No newline at end of file diff --git a/game/tests/unit/core/board_gen_reward_test.gd b/game/tests/unit/core/board_gen_reward_test.gd index 347d2b42..441314fc 100644 --- a/game/tests/unit/core/board_gen_reward_test.gd +++ b/game/tests/unit/core/board_gen_reward_test.gd @@ -51,21 +51,29 @@ func _mk_stream(stream_name: String, scope: int) -> RandomNumberGenerator: # 公式 N×cell ≤ 可用高−12 必须计入 (N−1)×2px 网格间距,否则网格实高溢出被 clip_contents 裁掉; # 现公式 = 计入间距 + 垂直预留 24px〔上 12/下 12——底部不贴 D 区边框〕)---- func _smoke_layout_cell_formula() -> void: - print("--- 棋盘布局格边长公式(底部预留回归)---") + print("--- 棋盘布局格边长公式(底部预留回归·四档板型)---") var board_generator = load("res://core/board_generator.gd").new() var board_view = load("res://presentation/views/board_view.gd").new() - for L in [1, 2]: - var b = board_generator.generate(L, 0, _mk_stream("board_gen", 555), _db) + var board_view_script := load("res://presentation/views/board_view.gd") + # 四档取样表(board_gen.tres 实表取档——(L,D)→尺寸:10/12/13/14;13×13 与 14×14 首次入断言) + var tiers := [{"L": 1, "D": 0, "n": 10}, {"L": 2, "D": 0, "n": 12}, {"L": 3, "D": 0, "n": 13}, {"L": 3, "D": 9, "n": 14}] + for t_v in tiers: + var t: Dictionary = t_v + var b = board_generator.generate(int(t["L"]), int(t["D"]), _mk_stream("board_gen", 555), _db) board_view.rebuild(b, null) var avail := Vector2(1036, 460) # 受约束 D 区可用面积样本(1280×800 档量级——用户实测裁切场景) board_view.set_cell_area(avail) var n: int = maxi(b.width, b.height) var cell: float = board_view.get("_cell") var grid_h := n * int(cell) + (n - 1) * 2 - Kit.check("L%d %dx%d 板网格高 %d ≤ 可用高 %d − 预留 24(格边 %dpx——下缘不贴边不裁切)" % [L, b.width, b.height, grid_h, int(avail.y), int(cell)], + Kit.check("L%d D%d 板型一致(%dx%d = 取样 %d——表改动致取样落错档即红)〔格边长 V3〕" % [int(t["L"]), int(t["D"]), b.width, b.height, int(t["n"])], + n == int(t["n"])) + Kit.check("L%d D%d %dx%d 板网格高 %d ≤ 可用高 %d − 预留 24(格边 %dpx——下缘不贴边不裁切)〔格边长 V3〕" % [int(t["L"]), int(t["D"]), b.width, b.height, grid_h, int(avail.y), int(cell)], grid_h <= int(avail.y) - 24) - Kit.check("L%d 格边在 26–46 档内(%dpx)" % [L, int(cell)], + Kit.check("L%d D%d 格边在 26–46 档内(%dpx)〔格边长 V3〕" % [int(t["L"]), int(t["D"]), int(cell)], cell >= 26.0 and cell <= 46.0) + Kit.check("L%d D%d 算式单点等价(set_cell_area 写入 %d == _cell_for 返回 %d——同一事实两路读取)〔格边长 V2〕" % [int(t["L"]), int(t["D"]), int(cell), board_view_script._cell_for(n, avail)], + int(cell) == board_view_script._cell_for(n, avail)) board_view.free()