From 52eb1090efdca93d83b787883bd75771325fdc00 Mon Sep 17 00:00:00 2001 From: m3ultra Date: Tue, 14 Jul 2026 22:47:38 +1000 Subject: [PATCH] =?UTF-8?q?Lane=20A=20round=206:=20second=20plan=20source?= =?UTF-8?q?=20=3Fplansrc=3Dosm=20(real-data=20OSM=20import)=20=E2=80=94=20?= =?UTF-8?q?full=20seam?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The carried-over OSM plan source (no-showed R4–R5) lands complete, invariants green — a second CityPlan producer behind the same contract, so B–F consume it unchanged. - osm_fixture.js: 95 REAL inner-Melbourne secondhand/charity/music/book/toy/antique shops, extracted deterministically from thriftgod city_source.json (Overpass cache), imported as a module ⇒ ZERO network. - plan_osm.js: generatePlanOSM(seed) — project lat/lon→metres, bin shops into latitude-band avenues off a spine (real row + real E–W order preserved), march into a valid non-overlapping CityPlan, centre on origin, report true 362×486 bbox, real shop names, one openLate video. - index.js: generatePlanFor(seed, source) selector — default 'synthetic' BYTE-IDENTICAL (golden 0x3fa36874 untouched); 'osm' → the importer. generatePlanOSM exported for F. - selfcheck.js: parameterized by source — OSM runs the full structural invariant suite (frontEdge/facing/no-overlap/chunk-coverage/finite/JSON/determinism/openLate) + its own golden 0x34cfdec0; synthetic-only brief checks skipped. ALL GREEN 1362/1362. - map.html: ?plansrc=osm renders it (reference wiring for F). Screenshot in docs/shots/laneA/. - CITY_SPEC: plan-source note; LANE_A_NOTES R6: seam + goldens + shape-diffs for F's gate (F2). Honest cut: street geometry regularized (real row+order drive it, exact metres do not); name-parody deferred. qa.sh --strict GREEN. Co-Authored-By: Claude Opus 4.8 --- docs/A-progress.md | 30 +++++- docs/CITY_SPEC.md | 11 ++ docs/LANES/LANE_A_NOTES.md | 32 ++++++ docs/shots/laneA/osm-melbourne.png | Bin 0 -> 90617 bytes web/js/citygen/index.js | 12 ++- web/js/citygen/osm_fixture.js | 110 ++++++++++++++++++++ web/js/citygen/plan_osm.js | 159 +++++++++++++++++++++++++++++ web/js/citygen/selfcheck.js | 47 +++++++++ web/map.html | 6 +- 9 files changed, 402 insertions(+), 5 deletions(-) create mode 100644 docs/shots/laneA/osm-melbourne.png create mode 100644 web/js/citygen/osm_fixture.js create mode 100644 web/js/citygen/plan_osm.js diff --git a/docs/A-progress.md b/docs/A-progress.md index b4934ab..6ec9467 100644 --- a/docs/A-progress.md +++ b/docs/A-progress.md @@ -1,9 +1,35 @@ # LANE A — CITYGEN · progress (PROCITY-A) -*Status: **all deliverables landed; self-check all-green (1301/1301); rounds 2–4 closed** (see bottom). -`qa.sh --strict` GREEN. Awaiting Fable review.* +*Status: **all deliverables landed; self-check all-green (1362/1362); rounds 2–6 closed** (see below). +`qa.sh --strict` GREEN.* *Date: 2026-07-14 · owner: PROCITY-A (Opus 4.8)* +## Round 6 (2026-07-14) — the second plan source (`?plansrc=osm`), FULLY LANDED + +The carried-over OSM plan-source seam (no-showed rounds 4–5) is **done** — not the escape-hatch +partial, the whole thing, invariants green. A second CityPlan producer behind the same contract, so +Lanes B–F consume it unchanged. + +- **`osm_fixture.js`** — 95 REAL inner-Melbourne secondhand/charity/music/book/toy/antique shops + (book 36, toy 14, opshop 13, record 12, video 11, pawn 9), extracted deterministically from + thriftgod's `city_source.json` Overpass cache. Checked in, imported as a module → **zero network**. +- **`plan_osm.js`** — `generatePlanOSM(seed)`: projects real lat/lon → metres, bins shops into + latitude-band avenues off a spine (real row + real east–west order preserved), marches them into + a valid, non-overlapping CityPlan, centres on origin, reports its true 362×486 bbox. Real shop + names kept ("Central Catholic Bookshop"). Exactly-one-openLate-video rule matches synthetic. +- **`index.js`** — `generatePlanFor(seed, source)` selector; default `'synthetic'` is byte-identical + (golden `0x3fa36874` untouched). `generatePlanOSM` exported for F. +- **`selfcheck.js`** — parameterized by source: OSM runs the full structural invariant suite + (frontEdge/facing/no-overlap/chunk-coverage/finite/JSON/determinism/openLate) + its own golden + **`0x34cfdec0`**; synthetic-only brief checks (market/arcade/dept/milkbar) deliberately skipped. +- **`map.html`** — `?plansrc=osm` renders it; screenshot `docs/shots/laneA/osm-melbourne.png`. +- Seam + goldens + shape-differences documented for F's determinism gate in `LANE_A_NOTES.md` (R6). + +**Verified**: 95/95 shops placed, 0 bad frontEdges / 0 overlaps / 0 backwards / full chunk coverage, +deterministic, synthetic path byte-identical. Self-check **ALL GREEN 1362/1362**; `qa.sh --strict` GREEN. +Honest cut (documented): street geometry is regularized (real row+order drive it, exact projected +metres do not) and shop-name parody is deferred. + ## Round 4 (2026-07-14) — v1-tag round: 2 confirmations, 1 hardening fix **A1 — storeys checker (post-F2): CONFIRMED.** `tools/qa/consistency_check.mjs` `permittedMax` scoping diff --git a/docs/CITY_SPEC.md b/docs/CITY_SPEC.md index 808cdfe..f5c9ee0 100644 --- a/docs/CITY_SPEC.md +++ b/docs/CITY_SPEC.md @@ -101,6 +101,17 @@ Schema v1 (Lane A owns it; extend, don't break): > furniture never resolves to a chunk that omits its edge; strictly JSON round-trippable (all finite > numbers, no stray keys). +> **Plan sources (round 6).** A CityPlan can come from two producers behind this one contract, chosen +> via `generatePlanFor(seed, source)` (`web/js/citygen/index.js`) — the shell selects with `?plansrc=`: +> - `'synthetic'` (default) — `generatePlan(seed)`, the procedural town. Byte-identical, golden `0x3fa36874`. +> - `'osm'` — `generatePlanOSM(seed)`, a real-data import from a **checked-in fixture** (`osm_fixture.js`, +> real AU shops from thriftgod's OSM cache; **zero network**). Golden `0x34cfdec0`. +> +> An `'osm'` plan sets `source:'osm'`, reports its **real bounding-box `size`** (not 1024²), and contains +> **only real retail types** — no synthetic market square, arcade, dept anchor, milk bars, or stalls. +> All structural invariants above hold identically. Consumers that assume a synthetic-only feature +> (dept/market/milkbar/stall) must guard on `source`. The invariant suite is parameterized by source. + Chunk key: `cx = floor(x/64)`, `cz = floor(z/64)`. **Chunk size 64m.** Lane A ships a `chunkIndex(plan)` helper: chunk key → { lots, shops, edges } touching it. Edges are bucketed across the full road **corridor** (centreline ± `width/2`), so anything placed in the verge (street furniture, diff --git a/docs/LANES/LANE_A_NOTES.md b/docs/LANES/LANE_A_NOTES.md index 19eb20c..bff6545 100644 --- a/docs/LANES/LANE_A_NOTES.md +++ b/docs/LANES/LANE_A_NOTES.md @@ -2,6 +2,38 @@ Durable answers to questions other lanes raised against Lane A. Full status in `A-progress.md`. +## Round 6 (2026-07-14) + +### → Lane F (F2): the `?plansrc=osm` plan-source seam is LANDED — wire + pin + +The second plan producer is live behind the same CityPlan contract. Full seam, invariants green +(not a partial). What F needs to wire the shell bootstrap + extend the determinism gate: + +- **Selector API** (`web/js/citygen/index.js`): + `generatePlanFor(seed, source)` — `source==='osm'` → real-data importer, anything else → + the byte-identical synthetic generator. Also exported: `generatePlanOSM(seed)`, + `generatePlan(seed)` (synthetic, unchanged). Unknown source falls back to synthetic. +- **Shell wiring** (in `web/index.html`, F-owned — one line): read `?plansrc`, then + `const src = new URLSearchParams(location.search).get('plansrc')==='osm' ? 'osm' : 'synthetic';` + and call `citygen.generatePlanFor(seed, src)` where it currently calls `generatePlan(seed)`. + `map.html` already does exactly this (reference impl). +- **Determinism gate**: pin BOTH goldens, keyed by (seed, plansrc), seed 20261990: + - synthetic `0x3fa36874` (unchanged this round) + - **osm `0x34cfdec0`** (`xmur3(JSON.stringify(generatePlanOSM(20261990)))`) + `selfcheck.js` already asserts both; the smoke can just hash-compare. +- **Zero network**: the fixture is `web/js/citygen/osm_fixture.js` (95 real inner-Melbourne + shops from thriftgod's Overpass cache), imported as a JS module — no fetch/XHR at runtime. + +**⚠ OSM plans differ from synthetic in shape (by design — real towns aren't the synthetic template):** +- `plan.source === 'osm'` is set; `plan.size` is the real bounding box (~362×486), **not** 1024². +- **No synthetic districts/types**: an OSM town has NO market square, arcade, dept anchor, milk + bars, or stalls — only the real retail types (record/opshop/toy/book/video/pawn). Any code that + ASSUMES a dept/market/milkbar exists must guard on `plan.source !== 'osm'` (or just not assume). +- Everything B–F actually consume is identical: nodes/edges/blocks/lots/shops schema, `ry`/ + `frontEdge` semantics, `chunkIndex`, hours + the exactly-one-openLate-video rule all hold. +- Shop `name`s are the REAL OSM names (e.g. "Central Catholic Bookshop"); `sign` is derived. + Parody-transform of names is a documented deferred nicety, not a blocker. + ## Round 4 (2026-07-14) ### → Lane F (A1): storeys checker (F2) confirmed — 0 warnings, no false negatives diff --git a/docs/shots/laneA/osm-melbourne.png b/docs/shots/laneA/osm-melbourne.png new file mode 100644 index 0000000000000000000000000000000000000000..98150c0fa9d7bf0a1f3b0debf461c1de32fe9afa GIT binary patch literal 90617 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 literal 0 HcmV?d00001 diff --git a/web/js/citygen/index.js b/web/js/citygen/index.js index 8463c71..b4e1311 100644 --- a/web/js/citygen/index.js +++ b/web/js/citygen/index.js @@ -9,6 +9,16 @@ export { generatePlan, chunkIndex, chunkKey, CHUNK, lotCorners, obbOverlap } from './plan.js'; export { shopName, townName } from './names.js'; +export { generatePlanOSM } from './plan_osm.js'; -// Convenience default so `citygen.default` also resolves to the generator. +// planSource selector (round 6). Default 'synthetic' → the byte-identical golden-hash generator; +// 'osm' → the real-data fixture importer. Lane F wires ?plansrc=osm to the `source` arg here so the +// shell bootstrap picks the producer without B–F caring which one ran. Unknown source → synthetic. +import { generatePlan as _synth } from './plan.js'; +import { generatePlanOSM as _osm } from './plan_osm.js'; +export function generatePlanFor(seed, source = 'synthetic') { + return source === 'osm' ? _osm(seed) : _synth(seed); +} + +// Convenience default so `citygen.default` also resolves to the (synthetic) generator. export { generatePlan as default } from './plan.js'; diff --git a/web/js/citygen/osm_fixture.js b/web/js/citygen/osm_fixture.js new file mode 100644 index 0000000..86b07f6 --- /dev/null +++ b/web/js/citygen/osm_fixture.js @@ -0,0 +1,110 @@ +// PROCITY OSM plan-source fixture — Lane A (round 6). REAL secondhand/charity/music/book/ +// toy/antique shops, one compact inner-Melbourne cluster, extracted from thriftgod city_source.json +// (an Overpass cache; committed here so the game makes ZERO network calls — CITY_SPEC law). +// Generated deterministically (sorted by OSM node id). Checked in, hand-editable, byte-stable. +// +// Each shop: { name (real), type (PROCITY registry id), lat, lon, suburb }. The OSM importer +// (plan_osm.js) projects lat/lon → local metres and lays a valid CityPlan; see LANE_A_NOTES. + +export const OSM_FIXTURE = { + town: "Melbourne", + source: "thriftgod/city_source.json (OSM Overpass cache, inner Melbourne)", + center: { lat: -37.803, lon: 144.967 }, + shops: [ + {"id":582845633,"name":"Jesper Junior","type":"toy","lat":-37.799331,"lon":144.97814,"suburb":""}, + {"id":589473054,"name":"Central Catholic Bookshop","type":"book","lat":-37.811707,"lon":144.962939,"suburb":"Melbourne"}, + {"id":598109860,"name":"Readings","type":"book","lat":-37.798052,"lon":144.967187,"suburb":""}, + {"id":612755480,"name":"The Haunted Bookshop","type":"book","lat":-37.815426,"lon":144.961705,"suburb":"Melbourne"}, + {"id":612771027,"name":"Hill of Content","type":"book","lat":-37.81148,"lon":144.97199,"suburb":""}, + {"id":632244266,"name":"Goldmine Records","type":"record","lat":-37.791222,"lon":144.97579,"suburb":""}, + {"id":635708269,"name":"Dymocks","type":"book","lat":-37.815462,"lon":144.96572,"suburb":"Melbourne"}, + {"id":642325730,"name":"The Paperback","type":"book","lat":-37.811675,"lon":144.971313,"suburb":"Melbourne"}, + {"id":663937314,"name":"Pop Mart","type":"toy","lat":-37.814031,"lon":144.964538,"suburb":""}, + {"id":699437448,"name":"Big Dreams","type":"toy","lat":-37.799152,"lon":144.984398,"suburb":""}, + {"id":699461941,"name":"Red Cross Shop","type":"opshop","lat":-37.799348,"lon":144.984349,"suburb":""}, + {"id":803089441,"name":"Kay Craddock Antiquarian Bookseller","type":"book","lat":-37.814775,"lon":144.968242,"suburb":""}, + {"id":990674449,"name":"Happy Valley","type":"book","lat":-37.800458,"lon":144.98416,"suburb":"Collingwood"}, + {"id":990676685,"name":"St Mark's Recycled","type":"opshop","lat":-37.80039,"lon":144.984176,"suburb":""}, + {"id":990860527,"name":"Villain","type":"toy","lat":-37.798355,"lon":144.977178,"suburb":""}, + {"id":997422797,"name":"Alice & Co.","type":"book","lat":-37.80279,"lon":144.977551,"suburb":""}, + {"id":1070219314,"name":"SOUNDMERCH","type":"record","lat":-37.805386,"lon":144.984819,"suburb":"Collingwood"}, + {"id":1072618081,"name":"White Rabbit Record Bar","type":"record","lat":-37.793645,"lon":144.93006,"suburb":""}, + {"id":1074085246,"name":"Northside Records","type":"record","lat":-37.80642,"lon":144.982482,"suburb":""}, + {"id":1104149817,"name":"Hot Potatoes Super $2 Shop","type":"opshop","lat":-37.802722,"lon":144.983783,"suburb":""}, + {"id":1379457331,"name":"Used Pty Ltd - The Stage","type":"pawn","lat":-37.799431,"lon":144.989961,"suburb":""}, + {"id":1671557044,"name":"Other Goods & Services","type":"opshop","lat":-37.7742,"lon":144.971388,"suburb":"Brunswick East"}, + {"id":2011953059,"name":"AllBooks4Less","type":"book","lat":-37.8105,"lon":144.966688,"suburb":""}, + {"id":2056959150,"name":"Tabernacle Games","type":"toy","lat":-37.78028,"lon":144.986264,"suburb":"Fitzroy North"}, + {"id":2090596140,"name":"Fitzroy Toy Library","type":"toy","lat":-37.803392,"lon":144.980385,"suburb":""}, + {"id":2105173577,"name":"Book Grocer","type":"book","lat":-37.815225,"lon":144.960413,"suburb":""}, + {"id":2108114139,"name":"Used Pty Ltd","type":"pawn","lat":-37.799008,"lon":144.983676,"suburb":""}, + {"id":2139774534,"name":"Plug Seven Records","type":"record","lat":-37.799668,"lon":144.984007,"suburb":"Fitzroy"}, + {"id":2139774821,"name":"Smith Street Bazaar","type":"pawn","lat":-37.799836,"lon":144.983957,"suburb":"Fitzroy"}, + {"id":2160049170,"name":"New International Book Shop","type":"book","lat":-37.806832,"lon":144.966259,"suburb":""}, + {"id":2164686254,"name":"Embiggen Books","type":"book","lat":-37.810103,"lon":144.966151,"suburb":"Melbourne"}, + {"id":2402316117,"name":"Already Read Bookshop","type":"book","lat":-37.783229,"lon":144.982101,"suburb":"Fitzroy North"}, + {"id":2677671238,"name":"Etruria Antiques Gallery","type":"pawn","lat":-37.799412,"lon":144.989808,"suburb":"Collingwood"}, + {"id":2688014250,"name":"The History","type":"pawn","lat":-37.802586,"lon":144.986922,"suburb":"Collingwood"}, + {"id":3412569256,"name":"Flight Experience","type":"video","lat":-37.810466,"lon":144.965145,"suburb":"Melbourne"}, + {"id":3418896122,"name":"Social Club Books","type":"book","lat":-37.796822,"lon":144.985198,"suburb":""}, + {"id":3788338576,"name":"Hylands Bookshop","type":"book","lat":-37.811456,"lon":144.966498,"suburb":"Melbourne"}, + {"id":3949641000,"name":"Goldmine Records","type":"record","lat":-37.779472,"lon":144.986947,"suburb":"Fitzroy North"}, + {"id":3949687621,"name":"Poison City","type":"record","lat":-37.795207,"lon":144.979159,"suburb":"Fitzroy"}, + {"id":4972218985,"name":"Retro Wolf","type":"toy","lat":-37.794166,"lon":144.949683,"suburb":""}, + {"id":5406503614,"name":"Readings","type":"book","lat":-37.809718,"lon":144.964589,"suburb":""}, + {"id":6228833108,"name":"Gregory's Antiques and Lights","type":"pawn","lat":-37.785753,"lon":144.981522,"suburb":""}, + {"id":6237887803,"name":"Readings","type":"book","lat":-37.809443,"lon":144.966332,"suburb":""}, + {"id":6507557579,"name":"Record Paradise","type":"record","lat":-37.773631,"lon":144.960868,"suburb":"Brunswick"}, + {"id":6649578474,"name":"Readings Kids","type":"book","lat":-37.79792,"lon":144.967205,"suburb":""}, + {"id":6680206192,"name":"Brotherhood of St Laurence Op Shop","type":"opshop","lat":-37.773636,"lon":144.971215,"suburb":""}, + {"id":6687064305,"name":"Vinnies","type":"opshop","lat":-37.774974,"lon":144.960631,"suburb":""}, + {"id":6702087933,"name":"Legacy","type":"opshop","lat":-37.811229,"lon":144.964332,"suburb":""}, + {"id":7202622331,"name":"Retro Depot","type":"opshop","lat":-37.782348,"lon":144.977928,"suburb":""}, + {"id":7273662704,"name":"Bookshop","type":"book","lat":-37.774882,"lon":144.960955,"suburb":""}, + {"id":7391506777,"name":"Ace Antiques","type":"pawn","lat":-37.803155,"lon":144.94892,"suburb":""}, + {"id":9097507475,"name":"Vinyl Space","type":"record","lat":-37.799535,"lon":144.988755,"suburb":""}, + {"id":9336124056,"name":"World Food Books","type":"book","lat":-37.816635,"lon":144.966746,"suburb":""}, + {"id":10048390715,"name":"EB Games","type":"video","lat":-37.816891,"lon":144.964598,"suburb":""}, + {"id":10598284186,"name":"Mui Karaoke Rooms","type":"record","lat":-37.811449,"lon":144.96435,"suburb":""}, + {"id":10611335971,"name":"Mind Games","type":"toy","lat":-37.814339,"lon":144.965791,"suburb":""}, + {"id":10713580252,"name":"City Of Vintage","type":"opshop","lat":-37.773999,"lon":144.960805,"suburb":""}, + {"id":10729013736,"name":"Epic Vintage","type":"opshop","lat":-37.776826,"lon":144.960351,"suburb":""}, + {"id":11017168508,"name":"Mary Martin Bookshop","type":"book","lat":-37.807847,"lon":144.957651,"suburb":""}, + {"id":11031787378,"name":"The Book Grocer","type":"book","lat":-37.815477,"lon":144.9514,"suburb":""}, + {"id":11118777232,"name":"Elite Office Machines Co.","type":"pawn","lat":-37.797065,"lon":144.968274,"suburb":""}, + {"id":11129101658,"name":"F.J. Rocca","type":"pawn","lat":-37.797543,"lon":144.969696,"suburb":""}, + {"id":11133299408,"name":"Brotherhood of St Lawrence","type":"opshop","lat":-37.814546,"lon":144.964109,"suburb":""}, + {"id":11135649030,"name":"Warhammer","type":"video","lat":-37.813084,"lon":144.970498,"suburb":""}, + {"id":11140063962,"name":"The Salvation Army","type":"opshop","lat":-37.817091,"lon":144.953989,"suburb":""}, + {"id":11162131777,"name":"Mary Martin Bookshop","type":"book","lat":-37.807745,"lon":144.95666,"suburb":""}, + {"id":11185870784,"name":"EB Games","type":"video","lat":-37.812749,"lon":144.937428,"suburb":"Docklands"}, + {"id":11244890775,"name":"North Melbourne Books","type":"book","lat":-37.802823,"lon":144.949112,"suburb":""}, + {"id":11287631726,"name":"Plenty of Games","type":"video","lat":-37.81572,"lon":144.964811,"suburb":""}, + {"id":11287631727,"name":"The Gaming Verse","type":"video","lat":-37.817709,"lon":144.961688,"suburb":""}, + {"id":11555994364,"name":"Strange World Records","type":"record","lat":-37.77301,"lon":144.971585,"suburb":""}, + {"id":11660848323,"name":"Melbourne Book Market","type":"book","lat":-37.806808,"lon":144.956428,"suburb":""}, + {"id":11957731033,"name":"Archie Brothers STRIKE","type":"video","lat":-37.811779,"lon":144.936627,"suburb":""}, + {"id":12175749075,"name":"I Heart Snacks","type":"video","lat":-37.811059,"lon":144.963269,"suburb":""}, + {"id":12175767221,"name":"Pop Mart","type":"toy","lat":-37.810856,"lon":144.963047,"suburb":""}, + {"id":12175767225,"name":"Heydays","type":"toy","lat":-37.810779,"lon":144.963015,"suburb":""}, + {"id":12175767227,"name":"I Am","type":"toy","lat":-37.810815,"lon":144.963167,"suburb":""}, + {"id":12175779440,"name":"Merry Seasons","type":"toy","lat":-37.810464,"lon":144.962038,"suburb":""}, + {"id":12320608201,"name":"Amplify Bookstore","type":"book","lat":-37.806902,"lon":144.955544,"suburb":""}, + {"id":12418141339,"name":"The Little Bookroom","type":"book","lat":-37.771822,"lon":144.978527,"suburb":""}, + {"id":12432795349,"name":"One Stop Anime","type":"video","lat":-37.815261,"lon":144.965727,"suburb":""}, + {"id":12437671918,"name":"Mooii","type":"toy","lat":-37.810519,"lon":144.96303,"suburb":""}, + {"id":12511070907,"name":"Rotart Charity Books","type":"opshop","lat":-37.812736,"lon":144.938173,"suburb":""}, + {"id":12511070911,"name":"Books Gifts Station","type":"book","lat":-37.813078,"lon":144.938613,"suburb":""}, + {"id":12511098032,"name":"Popclutcha","type":"video","lat":-37.813266,"lon":144.93777,"suburb":""}, + {"id":12511098647,"name":"Toyworld","type":"toy","lat":-37.813905,"lon":144.938006,"suburb":""}, + {"id":12611273300,"name":"Dungeon of Magic","type":"video","lat":-37.814504,"lon":144.964088,"suburb":""}, + {"id":12611672199,"name":"All Star Comics","type":"book","lat":-37.817545,"lon":144.961612,"suburb":"Melbourne"}, + {"id":12686552001,"name":"City Basement Books","type":"book","lat":-37.818532,"lon":144.963194,"suburb":""}, + {"id":12686552101,"name":"Metropolis Bookshop","type":"book","lat":-37.812039,"lon":144.965298,"suburb":""}, + {"id":12695960455,"name":"Bookshop by Uro","type":"book","lat":-37.799835,"lon":144.985564,"suburb":""}, + {"id":13075851874,"name":"Licorice Pie","type":"record","lat":-37.799856,"lon":144.985715,"suburb":""}, + {"id":13206134926,"name":"Sticky Institute","type":"book","lat":-37.806746,"lon":144.960086,"suburb":""}, + {"id":13229707300,"name":"Readings","type":"book","lat":-37.81289,"lon":144.963426,"suburb":""}, + {"id":13967406755,"name":"Law Bookshop","type":"book","lat":-37.815692,"lon":144.958884,"suburb":"Melbourne"} + ], +}; diff --git a/web/js/citygen/plan_osm.js b/web/js/citygen/plan_osm.js new file mode 100644 index 0000000..e0f5ea7 --- /dev/null +++ b/web/js/citygen/plan_osm.js @@ -0,0 +1,159 @@ +// PROCITY CityGen — the OSM plan source (Lane A, round 6). Second producer behind the same +// CityPlan contract as plan.js, so Lanes B–F consume it unchanged. Selected via ?plansrc=osm +// (default stays 'synthetic', byte-identical — see index.js `generatePlanFor`). +// +// Input: a CHECKED-IN fixture of real inner-Melbourne secondhand/charity/music/book/toy/antique +// shops (osm_fixture.js, extracted from thriftgod's Overpass cache). ZERO network at runtime. +// +// How real data drives the plan (documented simplification — the honest "cut"): a shop's real +// LATITUDE picks which east–west avenue it lands on, and its real LONGITUDE sets its order along +// that avenue. Avenue spacing and per-lot frontage are regularized (marched) so the result is a +// clean, non-overlapping, valid CityPlan — the geography is real in row+order, stylized in metres. +// A future pass can honour exact projected positions; this lands a booting, invariants-green seam. + +import { rng, seedFor, mulberry32, pick, frange } from '../core/prng.js'; +import { SHOP_TYPES } from '../core/registry.js'; +import { OSM_FIXTURE } from './osm_fixture.js'; + +const r2 = v => Math.round(v * 100) / 100; +const r4 = v => Math.round(v * 10000) / 10000; +const clampHour = h => Math.max(0, Math.min(23, h | 0)); +const LATE_HOUR = 22; // matches plan.js: exactly one shop closes ≥ this + +const EARTH_M = 111320; // metres per degree latitude (equirectangular) +const ROWS = 8; // latitude bands → avenues +const ROW_DZ = 54; // metres between avenues (> avenue depth ⇒ blocks disjoint) +const AV_W = 12, SPINE_W = 24; // corridor widths +const SPINE_CLEAR = 16; // lots start this far east of the spine (no lot on the road) +const GAP = 2, DEPTH = 14; // lot gap + depth along/into the avenue + +// Short signboard form from a real shop name: drop a leading "The", take words up to ~15 chars. +function signOf(name) { + const words = String(name).replace(/^the\s+/i, '').split(/\s+/); + let s = ''; + for (const w of words) { if ((s + ' ' + w).trim().length > 15) break; s = (s + ' ' + w).trim(); } + return (s || String(name).slice(0, 15)).toUpperCase(); +} + +export function generatePlanOSM(citySeed) { + citySeed = (citySeed >>> 0); + const fx = OSM_FIXTURE; + + const nodes = [], edges = [], districts = [], blocks = [], lots = [], shops = []; + let nid = 0, eid = 0, did = 0, bid = 0, lid = 0, sid = 0; + const addNode = (x, z) => { const n = { id: nid++, x: r2(x), z: r2(z) }; nodes.push(n); return n; }; + const addEdge = (a, b, width, kind) => { const id = eid++; edges.push({ id, a, b, width, kind }); return id; }; + const addDistrict = (kind, cx, cz) => { const id = did++; districts.push({ id, kind, cx: r2(cx), cz: r2(cz) }); return id; }; + const addBlock = (district, kind, poly) => { const id = bid++; blocks.push({ id, district, kind, poly: poly.map(p => [r2(p[0]), r2(p[1])]) }); return id; }; + const addLot = (block, x, z, w, d, ry, frontEdge, use) => { + const id = lid++; + lots.push({ id, block, x: r2(x), z: r2(z), w: r2(w), d: r2(d), ry: r4(ry), frontEdge, use }); + return id; + }; + + // Create a shop record from a real fixture entry (real name; seeded facade/hours/storeys). + function addShop(lotId, fixtureShop) { + const id = sid++; + const type = SHOP_TYPES[fixtureShop.type] ? fixtureShop.type : 'opshop'; + const reg = SHOP_TYPES[type]; + const seed = seedFor(citySeed, 'osmshop', fixtureShop.id); + const sr = mulberry32(seed); + const facadeSkin = pick(sr, reg.facades); + const storeys = reg.storeys[0] + ((sr() * (reg.storeys[1] - reg.storeys[0] + 1)) | 0); + let open = reg.hours.open, close = reg.hours.close; + if (sr() < 0.3) open = clampHour(open + (sr() < 0.5 ? -1 : 1)); + if (sr() < 0.3) close = clampHour(close + (sr() < 0.5 ? -1 : 1)); + if (close <= open) close = clampHour(open + 2); + close = Math.min(close, LATE_HOUR - 1); // only the openLate landmark reaches ≥ LATE_HOUR + shops.push({ id, lot: lotId, type, name: fixtureShop.name, sign: signOf(fixtureShop.name), + seed, facadeSkin, storeys, hours: [open, close] }); + return id; + } + + // ── project real shops → local metres; bin by latitude into avenue rows, order by longitude ── + const cosLat = Math.cos(fx.center.lat * Math.PI / 180); + const pts = fx.shops.map(s => ({ + s, x: (s.lon - fx.center.lon) * EARTH_M * cosLat, z: (s.lat - fx.center.lat) * EARTH_M, + })); + const zMin = Math.min(...pts.map(p => p.z)), zMax = Math.max(...pts.map(p => p.z)); + const span = (zMax - zMin) || 1; + const rowsArr = Array.from({ length: ROWS }, () => []); + for (const p of pts) { + const row = Math.min(ROWS - 1, Math.max(0, Math.floor((p.z - zMin) / span * ROWS))); + rowsArr[row].push(p); + } + + const district = addDistrict('mainstreet', 0, 0); + // spine: one main street running S→N through the origin, a node per (occupied) row + const spineNodes = []; + const usedRows = rowsArr.map((r, i) => ({ r, i })).filter(o => o.r.length); + usedRows.forEach((o, k) => { o.zPos = (k - (usedRows.length - 1) / 2) * ROW_DZ; spineNodes.push(addNode(0, o.zPos)); }); + const spineEdges = []; + for (let k = 0; k < spineNodes.length - 1; k++) spineEdges.push(addEdge(spineNodes[k].id, spineNodes[k + 1].id, SPINE_W, 'main')); + + // ── each row → an east–west avenue off the spine; its shops march into non-overlapping lots ── + usedRows.forEach((o, k) => { + const rowShops = o.r.slice().sort((a, b) => a.x - b.x || a.s.id - b.s.id); // real longitude order, id tiebreak + const zPos = o.zPos; + // frontages from each shop's registry footprint (deterministic mid-band, no rng needed here) + const fr = rowShops.map(p => { const fp = SHOP_TYPES[p.s.type]?.footprint || { fmin: 7, fmax: 10 }; return (fp.fmin + fp.fmax) / 2; }); + const avLen = SPINE_CLEAR + fr.reduce((s, f) => s + f + GAP, 0) + 6; + const west = addNode(0, zPos), east = addNode(avLen, zPos); + const avEdge = addEdge(west.id, east.id, AV_W, 'side'); + // block = the frontage strip on the south side of the avenue + const half = AV_W / 2, sd = DEPTH + 2; + const block = addBlock(district, 'mainstreet', + [[SPINE_CLEAR, zPos - half], [avLen, zPos - half], [avLen, zPos - half - sd], [SPINE_CLEAR, zPos - half - sd]]); + const ry = Math.atan2(0, -1); // outward normal (street→lot) points south (0,-1) ⇒ facade faces +z (north) to the avenue + let t = SPINE_CLEAR; + rowShops.forEach((p, j) => { + const f = fr[j]; + const cx = t + f / 2; + const cz = zPos - (half + DEPTH / 2); + const lot = addLot(block, cx, cz, f, DEPTH, ry, avEdge, 'shop'); + addShop(lot, p.s); + t += f + GAP; + }); + }); + + // ── exactly one late-night landmark: a video rental, closes ≥ LATE_HOUR (matches synthetic) ── + if (shops.length) { + const videos = shops.filter(s => s.type === 'video'); + const pool = videos.length ? videos : shops.filter(s => s.type !== 'stall'); + if (pool.length) { + const lr = rng(citySeed, 'osm-openlate', 0); + const chosen = pick(lr, pool); + chosen.openLate = true; + chosen.hours = [chosen.hours[0], LATE_HOUR + ((lr() * 2) | 0)]; + } + } + + // Centre the imported town on the origin and report its true bounding box as `size` (rigid + // translation ⇒ all invariants preserved; gives the map a framed view and Lane B the real extent). + let minx = Infinity, maxx = -Infinity, minz = Infinity, maxz = -Infinity; + for (const l of lots) { + minx = Math.min(minx, l.x - l.w); maxx = Math.max(maxx, l.x + l.w); + minz = Math.min(minz, l.z - l.d); maxz = Math.max(maxz, l.z + l.d); + } + for (const n of nodes) { minx = Math.min(minx, n.x); maxx = Math.max(maxx, n.x); minz = Math.min(minz, n.z); maxz = Math.max(maxz, n.z); } + const shx = -r2((minx + maxx) / 2), shz = -r2((minz + maxz) / 2); + for (const n of nodes) { n.x = r2(n.x + shx); n.z = r2(n.z + shz); } + for (const l of lots) { l.x = r2(l.x + shx); l.z = r2(l.z + shz); } + for (const b of blocks) b.poly = b.poly.map(p => [r2(p[0] + shx), r2(p[1] + shz)]); + for (const d of districts) { d.cx = r2(d.cx + shx); d.cz = r2(d.cz + shz); } + const M = 40; + const size = { w: Math.max(256, Math.ceil((maxx - minx) + 2 * M)), d: Math.max(256, Math.ceil((maxz - minz) + 2 * M)) }; + + return { + version: 1, + citySeed, + source: 'osm', + name: fx.town || 'Melbourne', + size, + districts, + streets: { nodes, edges }, + blocks, + lots, + shops, + }; +} diff --git a/web/js/citygen/selfcheck.js b/web/js/citygen/selfcheck.js index bef22d6..8c91ea0 100644 --- a/web/js/citygen/selfcheck.js +++ b/web/js/citygen/selfcheck.js @@ -8,6 +8,7 @@ import { readFileSync, existsSync } from 'node:fs'; import { fileURLToPath } from 'node:url'; import { dirname, join } from 'node:path'; import { generatePlan, chunkIndex, lotCorners, obbOverlap, CHUNK } from './plan.js'; +import { generatePlanOSM } from './plan_osm.js'; import { allFacadeSkins, SHOP_TYPES } from '../core/registry.js'; import { xmur3 } from '../core/prng.js'; @@ -196,6 +197,52 @@ for (const s of SEEDS) { for (const sh of plan.shops) facadeSet.add(sh.facadeSkin); } +// ── 3b. OSM plan source (?plansrc=osm): same structural invariants + its own golden ──── +// The synthetic golden (0x3fa36874) is untouched above; this parameterizes the harness by source. +// Brief checks (market/arcade/dept/milkbar) are synthetic-only and deliberately NOT asserted here — +// a real-data town has no synthetic market square. Structural invariants must hold identically. +section('OSM plan source (fixture-driven, zero-network)'); +const OSM_GOLDEN = { seed: 20261990, hash: 0x34cfdec0 }; +const OSM_SEEDS = [20261990, 1, 42, 777]; +for (const s of OSM_SEEDS) { + const plan = generatePlanOSM(s); + const edgeIds = new Set(plan.streets.edges.map(e => e.id)); + const nodeById = new Map(plan.streets.nodes.map(n => [n.id, n])); + const lotIds = new Set(plan.lots.map(l => l.id)); + ok(plan.source === 'osm', `osm ${s}: source tagged 'osm'`); + ok(JSON.stringify(generatePlanOSM(s)) === JSON.stringify(plan), `osm ${s}: deterministic (same fixture+seed)`); + ok(plan.lots.length > 0 && plan.shops.length > 0, `osm ${s}: non-empty (${plan.shops.length} real shops)`); + ok(plan.lots.every(l => isFiniteNum(l.x) && isFiniteNum(l.z) && isFiniteNum(l.w) && isFiniteNum(l.d) && isFiniteNum(l.ry)), `osm ${s}: lot numbers finite`); + ok(plan.streets.edges.every(e => nodeById.has(e.a) && nodeById.has(e.b)), `osm ${s}: edges reference real nodes`); + ok(plan.lots.every(l => edgeIds.has(l.frontEdge)), `osm ${s}: every lot has a valid frontEdge`); + ok(plan.lots.every(l => l.w > 0 && l.d > 0), `osm ${s}: every lot has positive size`); + ok(plan.shops.every(sh => lotIds.has(sh.lot) && SHOP_TYPES[sh.type] && SHOP_TYPES[sh.type].facades.includes(sh.facadeSkin)), `osm ${s}: shops valid (lot + type + facade-in-pool)`); + ok(plan.shops.every(sh => sh.hours[0] >= 0 && sh.hours[1] <= 23 && sh.hours[1] > sh.hours[0]), `osm ${s}: sane hours`); + ok(plan.shops.every(sh => sh.name && sh.sign && !/[{}]|undefined/.test(sh.name + sh.sign)), `osm ${s}: every shop named (real OSM names)`); + let back = null; + for (const l of plan.lots) { + const e = plan.streets.edges.find(x => x.id === l.frontEdge); const a = nodeById.get(e.a), b = nodeById.get(e.b); + const [nx, nz] = nearestOnSeg(l.x, l.z, a.x, a.z, b.x, b.z); const [fx, fz] = facing(l); + if ((nx - l.x) * fx + (nz - l.z) * fz < -0.01) { back = l.id; break; } + } + ok(back === null, `osm ${s}: every lot faces its frontEdge` + (back !== null ? ` (lot ${back})` : '')); + const cs = plan.lots.map(lotCorners); let ov = null; + for (let i = 0; i < plan.lots.length && !ov; i++) for (let j = i + 1; j < plan.lots.length; j++) + if (obbOverlap(cs[i], cs[j])) { ov = [plan.lots[i].id, plan.lots[j].id]; break; } + ok(!ov, `osm ${s}: no overlapping lots (within or across blocks)` + (ov ? ` (${ov})` : '')); + const idx = chunkIndex(plan); const cov = new Set(); + for (const c of Object.values(idx.chunks)) for (const id of c.lots) cov.add(id); + ok(plan.lots.every(l => cov.has(l.id)), `osm ${s}: chunkIndex covers every lot`); + ok(JSON.stringify(JSON.parse(JSON.stringify(plan))) === JSON.stringify(plan), `osm ${s}: JSON round-trip lossless`); + const late = plan.shops.filter(sh => sh.openLate), ge22 = plan.shops.filter(sh => sh.hours[1] >= 22); + ok(late.length === 1 && ge22.length === 1 && late[0] === ge22[0] && late[0].type === 'video', `osm ${s}: exactly one open-late video`); + for (const sh of plan.shops) facadeSet.add(sh.facadeSkin); +} +{ + const hash = xmur3(JSON.stringify(generatePlanOSM(OSM_GOLDEN.seed)))() >>> 0; + ok(hash === OSM_GOLDEN.hash, `osm ${OSM_GOLDEN.seed}: fingerprint 0x${hash.toString(16)} matches golden 0x${(OSM_GOLDEN.hash >>> 0).toString(16)}`); +} + // ── 4. every facade skin referenced by the registry exists on disk ────────────────── section('assets on disk'); for (const f of allFacadeSkins()) ok(existsSync(join(ASSETS, f)), `registry facade exists: ${f}`); diff --git a/web/map.html b/web/map.html index b31e64e..3d81644 100644 --- a/web/map.html +++ b/web/map.html @@ -86,7 +86,9 @@