From e067a2243929c19c2ca29a303cdaada96c40d610 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Wed, 14 Oct 2026 21:09:49 +0900 Subject: [PATCH] =?UTF-8?q?fix(=EA=B5=AC=EC=A1=B0=EB=AC=BC=ED=91=9C):=2032?= =?UTF-8?q?3-9=20=E2=91=A2=20M02=20=EB=B6=80=ED=92=88=20=EB=81=94=20?= =?UTF-8?q?=EB=A7=89=ED=9E=98=20=EC=95=88=EB=82=B4=20=E2=80=94=20=ED=98=95?= =?UTF-8?q?=EC=83=81=20=EB=B6=80=ED=92=88=EC=9D=84=20=EA=BA=BC=EC=84=9C=20?= =?UTF-8?q?=ED=98=95=EC=83=81=EC=9D=B4=20=E3=80=8C=EC=97=86=EC=9D=8C?= =?UTF-8?q?=E3=80=8D=EC=9D=B4=20=EB=90=9C=20=EA=B2=83=EC=9D=B4=EB=A9=B4=20?= =?UTF-8?q?=E3=80=8C=EB=B6=80=ED=92=88=20=E3=80=8C=EB=A7=B9=EC=95=94?= =?UTF-8?q?=EA=B1=B0=E3=80=8D=20=EB=81=94=20=E2=80=94=20=EC=9D=B4=20?= =?UTF-8?q?=EB=B6=80=ED=92=88=EC=9D=84=20=EC=93=B0=EB=8A=94=20=EC=A2=85?= =?UTF-8?q?=EB=A5=98(A=20=ED=9A=A1=EB=8B=A8=EB=B0=B0=EC=88=98=20=C2=B7=20?= =?UTF-8?q?=EB=A7=B9=EC=95=94=EA=B1=B0)=EC=9D=98=20=ED=9A=A1=EB=8B=A8?= =?UTF-8?q?=EB=8F=84=EB=A5=BC=20=E3=80=8C=EC=95=88=20=EB=A7=8C=EB=93=A6?= =?UTF-8?q?=E3=80=8D=EC=9C=BC=EB=A1=9C=20=EB=B0=94=EA=BF=94=EC=95=BC=20?= =?UTF-8?q?=EC=A0=80=EC=9E=A5=E3=80=8D=20=ED=95=9C=20=EC=A4=84(=EB=B6=80?= =?UTF-8?q?=ED=92=88=20=EC=9D=B4=EB=A6=84=20+=20=EC=93=B0=EB=8A=94=20?= =?UTF-8?q?=EC=A2=85=EB=A5=98=20=EC=A0=84=EB=B6=80)=20=C2=B7=20=EB=A7=89?= =?UTF-8?q?=EC=9D=8C=EC=9D=80=20=EA=B7=B8=EB=8C=80=EB=A1=9C=20=C2=B7=20?= =?UTF-8?q?=ED=99=94=EB=A9=B4=20=C2=B7=20=EC=84=9C=EB=B2=84=20=EC=A7=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - M02_SO_Def_Check.ts ↔ M02_SO_Check.py: 부품 끄기 전 표로 끈 탓 가름 · 부품마다 한 줄 - 시험: test_322_2_check_mirror(벽 끔 = 다섯 종류 한 줄 · 맹암거 끔 = A 횡단배수 · 맹암거 · 짝 같음) Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Ji4nuyLVHcxrXWHTdb5nRD --- M02_MasterTemplete/StructOptions/M02_SO_Check.py | 15 ++++++++++++++- .../StructOptions/M02_SO_Def_Check.ts | 16 ++++++++++++++-- resources/tester/test_322_2_check_mirror.py | 14 ++++++++++++-- 3 files changed, 40 insertions(+), 5 deletions(-) diff --git a/M02_MasterTemplete/StructOptions/M02_SO_Check.py b/M02_MasterTemplete/StructOptions/M02_SO_Check.py index fd56bf337..04c8e392e 100644 --- a/M02_MasterTemplete/StructOptions/M02_SO_Check.py +++ b/M02_MasterTemplete/StructOptions/M02_SO_Check.py @@ -104,13 +104,20 @@ def _row_errors(doc: dict[str, Any], before: dict[str, Any] | None) -> list[str] """표 줄 — 218 규칙 · 부품 자리꼴 · 정본 · 읽는 칸 보임(새로 숨김만).""" parts = load_parts()["parts"] rows = resolve_catalog(doc) + raw = resolve_catalog({**doc, "부품": {}}) # 부품 끄기 전 — 끈 탓 가름(323-9) + off_users: dict[str, list[str]] = {} # 끈 형상 부품 → 횡단도를 바꿔야 하는 종류 out: list[str] = [] for t in doc.get("types") or []: row = rows.get(t["type_id"]) if row is None: continue if row["shape"] == "none" and row["station"] != "none": - out.append(f"「{row['name']}」 형상이 「없음」이면 횡단도도 「안 만듦」") + was = raw[t["type_id"]]["shape"] + if was != "none" and (doc.get("부품") or {}).get(f"형상.{was}") is False: + label = f"{row['groupName']} · {row['name']}" + off_users.setdefault(was, []).append(label) + else: + out.append(f"「{row['name']}」 형상이 「없음」이면 횡단도도 「안 만듦」") # 앞 판 없음 = 새로 숨긴 것 없음 · 복사한 종류 = 본보기가 앞 판에서 숨긴 칸도 그대로(323-7) kept = [k for tid in (t["type_id"], row["base"]) for k in _hidden_in(before or doc, tid)] hidden = [key for key in _hidden_in(doc, t["type_id"]) if key not in kept] @@ -142,6 +149,12 @@ def _row_errors(doc: dict[str, Any], before: dict[str, Any] | None) -> list[str] f"「{row['name']}」 {branch} 「{name}」 이 쓰는 칸 「{labels[key]}」 — " "숨길 수 없음" ) + for part_id, names in off_users.items(): + name = short_name((parts["형상"].get(part_id) or {}).get("이름", part_id)) + out.append( + f"부품 「{name}」 끔 — 이 부품을 쓰는 종류({', '.join(names)})의 " + "횡단도를 「안 만듦」으로 바꿔야 저장" + ) return out diff --git a/M02_MasterTemplete/StructOptions/M02_SO_Def_Check.ts b/M02_MasterTemplete/StructOptions/M02_SO_Def_Check.ts index fa88aae0d..2433880f6 100644 --- a/M02_MasterTemplete/StructOptions/M02_SO_Def_Check.ts +++ b/M02_MasterTemplete/StructOptions/M02_SO_Def_Check.ts @@ -121,11 +121,17 @@ const hiddenIn = (doc: CheckDef | null, tid: string): string[] => function rowErrors(doc: CheckDef, before: CheckDef | null): string[] { const out: string[] = []; const rows = resolveCatalog(doc); + const raw = resolveCatalog({ ...doc, 부품: {} }); // 부품 끄기 전 — 끈 탓 가름(323-9) + const offUsers = new Map(); // 끈 형상 부품 → 횡단도를 바꿔야 하는 종류 for (const t of doc.types ?? []) { const row = rows.get(t.type_id); if (!row) continue; - if (row.shape === "none" && row.station !== "none") - out.push(`「${row.name}」 형상이 「없음」이면 횡단도도 「안 만듦」`); + if (row.shape === "none" && row.station !== "none") { + const was = raw.get(t.type_id)?.shape ?? "none"; + if (was !== "none" && doc.부품?.[`형상.${was}`] === false) + offUsers.set(was, [...(offUsers.get(was) ?? []), `${row.groupName} · ${row.name}`]); + else out.push(`「${row.name}」 형상이 「없음」이면 횡단도도 「안 만듦」`); + } // 앞 판 없음 = 새로 숨긴 것 없음 · 복사한 종류 = 본보기가 앞 판에서 숨긴 칸도 그대로(323-7) const kept = [t.type_id, row.base].flatMap((id) => hiddenIn(before ?? doc, id)); const hidden = hiddenIn(doc, t.type_id).filter((key) => !kept.includes(key)); @@ -158,6 +164,12 @@ function rowErrors(doc: CheckDef, before: CheckDef | null): string[] { ); } } + offUsers.forEach((names, id) => + out.push( + `부품 「${shortName(partOf("형상", id)?.이름 ?? id)}」 끔 — 이 부품을 쓰는 종류(${names.join(", ")})의 ` + + "횡단도를 「안 만듦」으로 바꿔야 저장", + ), + ); return out; } diff --git a/resources/tester/test_322_2_check_mirror.py b/resources/tester/test_322_2_check_mirror.py index e4380b2dc..4cc5e3673 100644 --- a/resources/tester/test_322_2_check_mirror.py +++ b/resources/tester/test_322_2_check_mirror.py @@ -84,6 +84,9 @@ def _cases() -> dict[str, tuple[dict, dict | None]]: off = copy.deepcopy(real) off["부품"] = {"형상.wall": False} cases["parts_off"] = (off, real) + drain = copy.deepcopy(real) + drain["부품"] = {"형상.underdrain": False} + cases["underdrain_off"] = (drain, real) return cases @@ -140,8 +143,15 @@ def test_check_rules(ts: dict) -> None: "「반사경」 군 글자 — 만든 뒤 고정", "종류 「기타」 — 지울 수 없음", ] - # 형상 부품을 끄면 벽 다섯은 형상 none — 횡단도도 none 으로 바꿔야 저장됨 - assert len(got["parts_off"]) == 5 and all("횡단도도 「안 만듦」" in t for t in got["parts_off"]) + # 형상 부품을 끄면 쓰는 종류 형상 none — 부품 이름 + 쓰는 종류 전부 한 줄(323-9) + walls = "옹벽, 돌쌓기(찰), 돌쌓기(메), 흙막이, 큰돌쌓기".split(", ") + assert got["parts_off"] == [ + f"부품 「벽」 끔 — 이 부품을 쓰는 종류({', '.join(f'C 사면안정 · {w}' for w in walls)})의 " + "횡단도를 「안 만듦」으로 바꿔야 저장" + ] + assert got["underdrain_off"] == [ + "부품 「맹암거」 끔 — 이 부품을 쓰는 종류(A 횡단배수 · 맹암거)의 횡단도를 「안 만듦」으로 바꿔야 저장" + ] def test_reflect_edits(ts: dict) -> None: