nemocity / tools /gen_web.py
AndresCarreon's picture
NEMOCITY v0 — mock backend, gradio 6.16.0 (pre-SSR)
d72231c verified
Raw
History Blame Contribute Delete
4.12 kB
"""Generate web/city/constants.js + web/mock/world.json from engine constants
and genesis — the ONE source of truth crossing the Python/JS boundary.
Run from the repo root (the integrator runs this; output is deterministic, no
timestamps):
python tools/gen_web.py
"""
from __future__ import annotations
import json
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT))
from engine import constants as C # noqa: E402
from engine.genesis import genesis_features # noqa: E402
def _js(value) -> str:
return json.dumps(value, indent=2, sort_keys=False)
def constants_js() -> str:
buildings = {
kind: {
"w": spec["w"], "d": spec["d"],
"floors": list(spec["floors"]),
"residents": spec["residents"], "jobs": spec["jobs"],
"attract": spec["attract"],
# both spellings: engine uses duration_s, the JS kit reads duration
"duration": spec["duration_s"], "duration_s": spec["duration_s"],
}
for kind, spec in C.BUILDINGS.items()
}
durations = {kind: spec["duration_s"] for kind, spec in C.BUILDINGS.items()}
parts = [
"// GENERATED by tools/gen_web.py from engine/constants.py — do not edit.",
"// Values are the ARCHITECTURE.md contract; regenerate, never hand-tune.",
"",
f"export const GRID = {C.GRID};",
f"export const CELL = {C.CELL};",
f"export const COORD_MIN = {C.COORD_MIN};",
f"export const COORD_MAX = {C.COORD_MAX};",
f"export const CITY_EPOCH_S = {C.CITY_EPOCH_S};",
f"export const DAY_S = {C.DAY_S};",
"",
"export function riverCols(cz) {",
" return cz <= -5 ? [7, 8] : cz <= 3 ? [8, 9] : [9, 10];",
"}",
"",
f"export const BUILDINGS = {_js(buildings)};",
"",
f"export const ROAD_CLASSES = {_js(C.ROAD_CLASSES)};",
f"export const ROAD_DURATION_S = {C.ROAD_DURATION_S};",
f"export const DURATIONS = {_js(durations)};",
f"export const SYNONYMS = {_js(C.SYNONYMS)};",
f"export const STREET_NAMES = {_js(list(C.STREET_NAMES))};",
"",
"// traffic (mirrors engine/traffic.py — the Python side owns the facts)",
f"export const RUSH_HOURS = {_js(list(C.RUSH_HOURS))};",
f"export const RUSH_SIGMA = {C.RUSH_SIGMA};",
f"export const RUSH_BASE = {C.RUSH_BASE};",
f"export const RUSH_AMP = {C.RUSH_AMP};",
f"export const CAR_RATE = {C.CAR_RATE};",
f"export const CAR_MIN = {C.CAR_MIN};",
f"export const CAR_MAX = {C.CAR_MAX};",
f"export const CITIZENS_PER_POP = {C.CITIZENS_PER_POP};",
f"export const CITIZENS_MIN = {C.CITIZENS_MIN};",
f"export const CITIZENS_MAX = {C.CITIZENS_MAX};",
f"export const EMA_TAU_S = {C.EMA_TAU_S};",
f"export const TRIP_ATTRACT_FACTOR = {C.TRIP_ATTRACT_FACTOR};",
f"export const TRIP_DIST_EXP = {C.TRIP_DIST_EXP};",
f"export const DEMAND_PER_COMMUTER = {C.DEMAND_PER_COMMUTER};",
f"export const CONGESTION_COST_FACTOR = {C.CONGESTION_COST_FACTOR};",
f"export const TRAFFIC_TOP_CELLS = {C.TRAFFIC_TOP_CELLS};",
f"export const JAM_RATIO = {C.JAM_RATIO};",
f"export const GROWTH_RADIUS_MIN = {C.GROWTH_RADIUS_MIN};",
"",
]
return "\n".join(parts)
def world_json() -> str:
features = genesis_features()
payload = {
"world": {
"version": len(features),
"epoch": 0,
"features": features,
}
}
return json.dumps(payload, indent=2) + "\n"
def main() -> None:
constants_path = ROOT / "web" / "city" / "constants.js"
world_path = ROOT / "web" / "mock" / "world.json"
world_path.parent.mkdir(parents=True, exist_ok=True)
constants_path.write_text(constants_js(), encoding="utf-8")
world_path.write_text(world_json(), encoding="utf-8")
print(f"wrote {constants_path.relative_to(ROOT)}")
print(f"wrote {world_path.relative_to(ROOT)}")
if __name__ == "__main__":
main()