From 39cdc4f6713ea44968c825e2803055ad5fe1af74 Mon Sep 17 00:00:00 2001 From: DKChan <524912269@qq.com> Date: Sun, 25 Oct 2026 00:22:55 +0800 Subject: [PATCH] =?UTF-8?q?style(=E8=BF=AD=E4=BB=A335=E5=8D=A1=E2=91=A1?= =?UTF-8?q?=E8=87=AA=E6=9F=A5):=20=E6=9C=AC=E6=89=B9=E5=94=AF=E4=B8=80?= =?UTF-8?q?=E7=9C=9F=E6=96=B0=E5=A2=9E=E7=9A=84=20S13=20=E8=B4=A8=E9=87=8F?= =?UTF-8?q?=E8=BF=9D=E8=A7=84=E8=87=AA=E4=BF=AE=E2=80=94=E2=80=94=E5=9B=BE?= =?UTF-8?q?=E6=A0=A1=E9=AA=8C=E5=8D=95=E7=82=B9=20validate=5Fgraph?= =?UTF-8?q?=EF=BC=88=E6=96=B0=E5=86=99=E5=87=BD=E6=95=B0=2072=20=E8=A1=8C?= =?UTF-8?q?=EF=BC=8F20=20=E5=88=86=E6=94=AF=EF=BC=8F12=20=E5=B1=80?= =?UTF-8?q?=E9=83=A8=EF=BC=8C=E8=B6=85=E9=98=88=E5=80=BC=E4=B8=94=E6=97=A0?= =?UTF-8?q?=E5=9F=BA=E7=BA=BF=E5=8F=AF=E8=B5=96=EF=BC=89=E6=8C=89=E5=88=A4?= =?UTF-8?q?=E6=8D=AE=E5=88=86=E6=AE=B5=E6=8B=86=E6=88=90=20=5Fcheck=5Fnode?= =?UTF-8?q?s=5Fwellformed/=5Fcheck=5Fedges/=5Fcheck=5Fterminals/=5Fcheck?= =?UTF-8?q?=5Freachabilities=20=E5=9B=9B=E6=AE=B5=E9=9D=99=E6=80=81?= =?UTF-8?q?=E4=BB=B6=EF=BC=8C=E7=9F=AD=E8=B7=AF=E6=AC=A1=E5=BA=8F=E4=B8=8E?= =?UTF-8?q?=E9=94=99=E8=AF=AF=E7=B1=BB=E5=88=AB=E8=AF=8D=E8=A1=A8=E9=80=90?= =?UTF-8?q?=E5=AD=97=E4=B8=8D=E5=8F=98=EF=BC=88=E8=89=AF=E6=9E=84=E9=94=99?= =?UTF-8?q?=E5=85=88=E4=BA=8E=E5=87=BA=E8=BE=B9=E9=94=99=E3=80=81=E5=87=BA?= =?UTF-8?q?=E8=BE=B9=E9=94=99=E5=85=88=E4=BA=8E=E5=85=A5=E5=8F=A3=E7=BB=88?= =?UTF-8?q?=E7=82=B9=E6=A3=80=E3=80=81=E5=86=8D=E5=8F=AF=E8=BE=BE=E6=80=A7?= =?UTF-8?q?=E6=A3=80=EF=BC=89=EF=BC=8C=E3=80=94v1=E3=80=9517=20=E6=9D=A1?= =?UTF-8?q?=E5=A4=B9=E5=85=B7=E6=96=AD=E8=A8=80=E5=A4=8D=E8=B7=91=E5=85=A8?= =?UTF-8?q?=E7=BB=BF=E9=9B=B6=E6=94=B9=E5=88=A4=E6=8D=AE=EF=BC=9B=E9=97=B8?= =?UTF-8?q?=E4=BD=99=2012=20=E9=A1=B9=E7=BB=8F=E9=80=90=E6=9D=A1=E5=AF=B9?= =?UTF-8?q?=E5=9F=BA=E7=BA=BF=E6=97=A7=E5=80=BC=E6=A0=B8=E5=AE=9E=E4=B8=BA?= =?UTF-8?q?=E5=AD=98=E9=87=8F=E5=80=BA=E8=AE=A1=E6=95=B0=E6=BC=82=E7=A7=BB?= =?UTF-8?q?=EF=BC=88cross=5Fvalidate=20159=E2=86=92107=20=E8=A1=8C?= =?UTF-8?q?=EF=BC=8F31=E2=86=9221=20=E5=88=86=E6=94=AF=E7=B3=BB=E6=9C=AC?= =?UTF-8?q?=E6=89=B9=E5=88=A0=E8=B7=A8=E8=A1=A8=E2=91=A3=E6=89=80=E8=87=B4?= =?UTF-8?q?=E7=9A=84=E6=94=B9=E5=B0=8F=EF=BC=8Clevel=5Frunner=20=E7=B1=BB?= =?UTF-8?q?=E5=8F=98=E9=87=8F=2040=E2=86=9238=E3=80=81start=5Flevel=5Fexte?= =?UTF-8?q?rnal=20=E5=8F=82=E6=95=B0=208=E2=86=927=E3=80=81=E5=85=B6?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E9=95=BF=E5=BA=A6=E8=BF=9D=E8=A7=84=E5=B7=B2?= =?UTF-8?q?=E6=B6=88=E5=A4=B1=EF=BC=8Cmain.gd=201509=E2=86=921507=EF=BC=8C?= =?UTF-8?q?resume=5Fadventure=20104=E2=86=92108=20=E8=A1=8C=E4=B8=BA?= =?UTF-8?q?=E6=96=AD=E7=82=B9=E4=BB=A3=E5=B7=AE=E6=A0=87=E8=AE=B0=E6=A3=80?= =?UTF-8?q?=E6=9F=A5=EF=BC=89=E2=80=94=E2=80=94=E5=A4=84=E7=BD=AE=E5=8F=A3?= =?UTF-8?q?=E5=BE=84=E5=BE=85=E7=94=A8=E6=88=B7=E8=A3=81=EF=BC=88=E9=9A=8F?= =?UTF-8?q?=E6=89=B9=E9=87=8D=E7=94=9F=E6=88=90=E5=9F=BA=E7=BA=BF=EF=BC=8F?= =?UTF-8?q?=E9=80=80=20S13=20=E6=89=B9=E8=87=AA=E4=BF=AE=EF=BC=8F=E6=9C=AC?= =?UTF-8?q?=E6=89=B9=E9=87=8D=E6=9E=84=EF=BC=89=EF=BC=8C=E6=9C=AC=E7=AC=94?= =?UTF-8?q?=E4=B8=8D=E5=8A=A8=20tools/gdstyle=5Fbaseline.txt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- game/data/schemas/level_table.gd | 57 +++++++++++++++++++++++--------- 1 file changed, 42 insertions(+), 15 deletions(-) diff --git a/game/data/schemas/level_table.gd b/game/data/schemas/level_table.gd index d9aea353..4d66fbc1 100644 --- a/game/data/schemas/level_table.gd +++ b/game/data/schemas/level_table.gd @@ -48,10 +48,24 @@ func validate() -> Array: # ④ 无环(出边目标深度严格大于源)。良构=句柄唯一·出边全命中·type 在五类枚举·battle/boss 带深度。 # 入口/终点个数与参数档的一致性归产出侧(关卡节点图生成 v1.1),本处不核个数;商店位归冒烟断言把关,本处禁检。 static func validate_graph(nodes: Array) -> Array: - var errs: Array[Dictionary] = [] if nodes.is_empty(): - errs.append({"category": "structural", "message": "图节点集为空"}) - return errs + return [{"category": "structural", "message": "图节点集为空"}] + var well := _check_nodes_wellformed(nodes) + if not (well["errs"] as Array).is_empty(): + return well["errs"] + var by_id: Dictionary = well["by_id"] + var edges := _check_edges(by_id, well["indeg"]) + if not (edges["errs"] as Array).is_empty(): + return edges["errs"] + var stops := _check_terminals(by_id, edges["outs"], edges["indeg"]) + if not (stops["errs"] as Array).is_empty(): + return stops["errs"] + return _check_reachability(by_id, edges["outs"], stops["entries"], stops["terminals"]) + + +# 良构基底:字段齐·type 在枚举·深度非负·句柄唯一(返回 by_id 与入度骨架供后续检复用) +static func _check_nodes_wellformed(nodes: Array) -> Dictionary: + var errs: Array[Dictionary] = [] var by_id := {} var indeg := {} for i in nodes.size(): @@ -73,8 +87,12 @@ static func validate_graph(nodes: Array) -> Array: by_id[nid] = node if not indeg.has(nid): indeg[nid] = 0 - if not errs.is_empty(): - return errs + return {"errs": errs, "by_id": by_id, "indeg": indeg} + + +# 出边良构+无环判据(④):目标句柄存在、目标深度严格大于源;同时产出去边表与入度计数 +static func _check_edges(by_id: Dictionary, indeg: Dictionary) -> Dictionary: + var errs: Array[Dictionary] = [] var outs := {} for nid in by_id.keys(): outs[nid] = [] @@ -87,25 +105,34 @@ static func validate_graph(nodes: Array) -> Array: errs.append({"category": "structural", "message": "连线 %d→%d 违反前进方向(目标深度须严格大于源)" % [nid, int(tid)]}) outs[nid].append(int(tid)) indeg[int(tid)] = int(indeg[int(tid)]) + 1 - if not errs.is_empty(): - return errs + return {"errs": errs, "outs": outs, "indeg": indeg} + + +# 检①+检③:入口(无入边)≥1、终点(无出边)≥1、终点恒为 Boss 且 Boss 无出边 +static func _check_terminals(by_id: Dictionary, outs: Dictionary, indeg: Dictionary) -> Dictionary: + var errs: Array[Dictionary] = [] var entries := [] var terminals := [] for nid in by_id.keys(): if int(indeg[nid]) == 0: entries.append(nid) - if (outs[nid] as Array).is_empty(): - terminals.append(nid) - if String(by_id[nid]["type"]) != "boss": - errs.append({"category": "structural", "message": "终点 %d 非 Boss(终点恒为 Boss)" % nid}) - elif String(by_id[nid]["type"]) == "boss": - errs.append({"category": "structural", "message": "Boss 节点 %d 带出边(终点不可外出)" % nid}) + if not (outs[nid] as Array).is_empty(): + if String(by_id[nid]["type"]) == "boss": + errs.append({"category": "structural", "message": "Boss 节点 %d 带出边(终点不可外出)" % nid}) + continue + terminals.append(nid) + if String(by_id[nid]["type"]) != "boss": + errs.append({"category": "structural", "message": "终点 %d 非 Boss(终点恒为 Boss)" % nid}) if entries.is_empty(): errs.append({"category": "structural", "message": "入口节点数 = 0(须 ≥1)"}) if terminals.is_empty(): errs.append({"category": "structural", "message": "终点节点数 = 0(须 ≥1)"}) - if not errs.is_empty(): - return errs + return {"errs": errs, "entries": entries, "terminals": terminals} + + +# 检②:全部节点从某入口可达且可达某终点(无孤儿、无断头) +static func _check_reachability(by_id: Dictionary, outs: Dictionary, entries: Array, terminals: Array) -> Array: + var errs: Array[Dictionary] = [] var from_entry := {} for eid in entries: _mark_reachable(int(eid), outs, from_entry)