"""図面のある大型店(⑧⑨)の試算の結果と平面図を、公開中の試算のページに足す(2026-11-02) 使い方: python3 build_page_large.py <公開中のページ> <大型店の results.json> <出力するページ> [--replace large_p7 ...] --replace:もう入っている店を入れ替える(その店の行・店・平面図を消してから足し直す。2026-11-02 に⑨の店の形を直したとき) - 前からある条件の行・店・平面図は変えない。大型店の図面のある店(large_p6・large_p7)の店・平面図・行を後ろに足す - 画面:店の形を選ぶ欄を、標準ファミレスに加えて大型店でも出す(もとにする店ごとに、その店の図面だけを並べる) - 結果の前提(common・回数)が今の haizen_sim.py と同じことを確かめる""" import json import os import sys import haizen_sim as h live, res, out = sys.argv[1:4] REPLACE = sys.argv[sys.argv.index("--replace") + 1:] if "--replace" in sys.argv else [] KEYS = REPLACE or ["large_p6", "large_p7"] SHORT = {"large_p6": "⑧ 大型・受け渡し口が奥の真ん中", "large_p7": "⑨ 大型・厨房が右・入口が角"} s = open(live, encoding="utf-8").read() N = json.load(open(res, encoding="utf-8")) assert N["common"] == json.loads(json.dumps(h.COMMON)), "結果が今の haizen_sim.py の前提と違う" assert N["reps"] == h.REPS lines = s.split("\n") idx = [i for i, l in enumerate(lines) if l.startswith("const D = ")] assert len(idx) == 1 D = json.loads(lines[idx[0]][len("const D = "):].rstrip(";")) K = D["k"] cond, mets = K[:8], K[8:-1] assert cond == ["store", "staff", "robots", "arrivals_per_h", "mode", "trays", "fragile_by", "registers"] and K[-1] == "ng" if REPLACE: assert all(k in D["stores"] for k in KEYS), "入れ替える店がまだ入っていない" D["r"] = [r for r in D["r"] if r[0] not in KEYS] for k in KEYS: D["stores"].pop(k) D["plans"].pop(k) else: assert not any(k in D["stores"] for k in KEYS), "もう入っている" assert {k: v for k, v in D["common"].items()} == json.loads(json.dumps(h.COMMON)), "ページの前提と今の試算の前提が違う" # 行:条件の並び(haizen_sim.conditions())のうち大型店の図面のある店の分を、全部そろっているか確かめて足す rows = {h.key(r): r for r in N["rows"]} conds = [c for c in h.conditions() if c["store"] in KEYS] missing = [c for c in conds if h.key(c) not in rows] assert not missing, f"結果が足りない:{len(missing)} 条件" for c in conds: r = rows[h.key(c)] D["r"].append([c[k] for k in cond] + [round(r[k], 3) for k in mets] + [1 if r["staff_wait_mean"] >= h.NG_MIN else 0]) # 店と平面図:public/sim/layouts/large/ の店の形から gens = set() for k in KEYS: st = {a: b for a, b in h.STORES[k].items() if a != "dist_list"} nm = st["layout"] f = [x for x in os.listdir(h.LAYOUT_DIR_LARGE) if x.startswith(nm + "_") and x.endswith("_analysis.json")][0] A = json.load(open(os.path.join(h.LAYOUT_DIR_LARGE, f), encoding="utf-8")) Lf = json.load(open(os.path.join(h.LAYOUT_DIR_LARGE, f.replace("_analysis.json", ".json")), encoding="utf-8")) gens |= {A["meta"]["generation"], Lf["meta"]["generation"]} L = Lf["layout"] st.update(label=f"大型店({SHORT[k]})", short=SHORT[k], dist_m=list(st["dist_m"]), dist_mean=A["summary"]["robot_dist_mean"]) D["stores"][k] = st W, Dp = L["outer"]["w"], L["outer"]["d"] D["plans"][k] = dict( w=W, d=Dp, outline=L.get("outline") or [[0, 0], [W, 0], [W, Dp], [0, Dp]], rooms=[[*r["rect"], r.get("kind", "back"), r["label"]] for r in L["rooms"]], walls=[w["rect"] for w in L["walls"]], tables=[[t["x"], t["y"], round(t["x"] + t["w"], 3), round(t["y"] + t["h"], 3), t["seats"]] for t in L["tables"]], dish=[d["rect"] for d in L["dish_up"]], standby=L["robot_standby"]["rect"], ent=[L["entrance"]["side"], L["entrance"]["a0"], L["entrance"]["a1"]]) assert len(gens) == 1, gens D["layout_generation_large"] = gens.pop() lines[idx[0]] = "const D = " + json.dumps(D, ensure_ascii=False, separators=(",", ":")) + ";" s = "\n".join(lines) def rep(a, b): global s assert s.count(a) == 1, (a, s.count(a)) s = s.replace(a, b) if REPLACE: # 画面の書き替えは最初に足したときに済んでいる open(out, "w", encoding="utf-8").write(s) print(out, len(D["r"]), "rows", "large gen", D["layout_generation_large"], "replaced", KEYS) sys.exit(0) # もとにする店(図面のある店なら base、そうでなければ自分) rep("const isLayout = k => !!(STORES[k] && STORES[k].layout);", "const isLayout = k => !!(STORES[k] && STORES[k].layout);\n" "// 図面のある店のもとにする店(標準ファミレス・大型店。2026-11-02 に大型店を足した)\n" "const baseOf = k => (STORES[k] && STORES[k].base) || k;\n" "const LAYOUT_BASES = [...new Set(LAYOUT_KEYS.map(baseOf))];") rep(''' const on = state.store === "family" || isLayout(state.store); el.hidden = !on; if (!on) return; const fam = STORES.family; const items = [["family", "① 図面なし", `${fam.tables}卓・${fam.dist_m[0]}〜${fam.dist_m[1]}m`, `
店の形を選ぶ(標準ファミレス)。平面図から、卓の数と厨房から卓までの距離を取ります
`''', ''' const base = baseOf(state.store); const on = LAYOUT_BASES.includes(base); el.hidden = !on; if (!on) return; const fam = STORES[base]; const items = [[base, base === "family" ? "① 図面なし" : "図面なし", `${fam.tables}卓・${fam.dist_m[0]}〜${fam.dist_m[1]}m`, `店の形を選ぶ(${fam.label})。平面図から、卓の数と厨房から卓までの距離を取ります
`''') rep('const on = state.store === k || (k === "family" && isLayout(state.store));', 'const on = state.store === k || (isLayout(state.store) && k === baseOf(state.store));') rep('`標準ファミレス(${STORES.family.tables}卓)を、卓の数(${s.tables}卓)に合わせて増減。ほかの前提は標準ファミレスと同じ`', '`${STORES[s.base].label}(${STORES[s.base].tables}卓)を、卓の数(${s.tables}卓)に合わせて増減。ほかの前提は${STORES[s.base].label}と同じ`') open(out, "w", encoding="utf-8").write(s) print(out, len(D["r"]), "rows", "large gen", D["layout_generation_large"])