v0.27.0 (#44): LLM-placed named map point markers #45

Merged
kamilkrzywda merged 1 commits from feat/44-llm-map-pointers into master 2026-10-15 00:40:52 +02:00
Owner

What

The LLM can now place named point markers on the map each turn (issue #44). Eight marker types (city, factory, army, port, capital, fleet, rocket, gathering) are anchored to a region key, persisted per game, and rendered on the choropleth as a MapLibre symbol layer.

Contract

  • TurnResult.markers = { add: MarkerSpec[]; remove: string[] } (both required, default []); MarkerSpec = { type, name, regionKey }.
  • Marker id = FNV-1a 32-bit of type + NUL + name + NUL + regionKey -> 4 lowercase base-26 chars -> idempotent re-add, unambiguous remove.
  • GameState.markers: MapMarker[] required ([] at init; legacy SQLite rows load as [] - additive, never wipes).
  • applyTurnResult processes markers after territory claims: remove -> owner recompute (a marker always follows its region; released -> null) -> add (centroid + deterministic jitter, cap MAX_MARKERS=400, never throws).

New data

public/region-centroids.json (1,800 anchors = largest-part centroid + spread ~ 0.1x min-bbox), built by the new idempotent scripts/build-region-centroids.mjs (npm run build:region-centroids).

Rendering (deliberate deviation)

Markers render as canvas-generated per-type ICON images, not text labels: every external glyph endpoint is a dead 404 (OpenFreeMap fonts/...pbf, demotiles.maplibre.org/font/...) and the OFM dark basemap only works because it's raster (labels baked into tiles) - there is no usable vector font. Names show in the hover tooltip; clicking a marker focuses its owning nation's dossier. No separate marker event handlers (MapLibre layer events don't stopPropagation) - the region handlers queryRenderedFeatures the marker layer under the cursor.

Tests

168 -> 189. New scripts/region-centroids.test.ts; server/regions.test.ts (centroids in dev + prod dist/), server/db.test.ts (legacy -> []), engine.test.ts marker suite. Stub stays marker-free -> e2e (CI, LLM_STUB=1) unaffected.

Verification

Typecheck, lint, 189 unit tests, and the production build all pass locally (the build copies region-centroids.json into dist/). Deploy is a manual docker compose up -d --build (no down -v).

Fixes #44

## What The LLM can now place **named point markers** on the map each turn (issue #44). Eight marker types (`city, factory, army, port, capital, fleet, rocket, gathering`) are anchored to a region key, persisted per game, and rendered on the choropleth as a MapLibre symbol layer. ## Contract - `TurnResult.markers = { add: MarkerSpec[]; remove: string[] }` (both required, default `[]`); `MarkerSpec = { type, name, regionKey }`. - Marker `id` = FNV-1a 32-bit of `type + NUL + name + NUL + regionKey` -> 4 lowercase base-26 chars -> **idempotent re-add**, **unambiguous remove**. - `GameState.markers: MapMarker[]` required (`[]` at init; legacy SQLite rows load as `[]` - additive, never wipes). - `applyTurnResult` processes markers **after** territory claims: remove -> **owner recompute** (a marker always follows its region; released -> `null`) -> add (centroid + deterministic jitter, cap `MAX_MARKERS=400`, never throws). ## New data `public/region-centroids.json` (1,800 anchors = **largest-part centroid** + `spread ~ 0.1x min-bbox`), built by the new idempotent `scripts/build-region-centroids.mjs` (`npm run build:region-centroids`). ## Rendering (deliberate deviation) Markers render as **canvas-generated per-type ICON images, not text labels**: every external glyph endpoint is a dead 404 (OpenFreeMap `fonts/...pbf`, `demotiles.maplibre.org/font/...`) and the OFM dark basemap only works because it's raster (labels baked into tiles) - there is no usable vector font. Names show in the **hover tooltip**; clicking a marker focuses its owning nation's dossier. No separate marker event handlers (MapLibre layer events don't stopPropagation) - the region handlers `queryRenderedFeatures` the marker layer under the cursor. ## Tests **168 -> 189.** New `scripts/region-centroids.test.ts`; `server/regions.test.ts` (centroids in dev + prod `dist/`), `server/db.test.ts` (legacy -> `[]`), `engine.test.ts` marker suite. **Stub stays marker-free** -> e2e (CI, `LLM_STUB=1`) unaffected. ## Verification Typecheck, lint, **189 unit tests**, and the production build all pass locally (the build copies `region-centroids.json` into `dist/`). Deploy is a manual `docker compose up -d --build` (no `down -v`). Fixes #44
kamilkrzywda added 1 commit 2026-10-15 00:38:19 +02:00
v0.27.0 (#44): LLM-placed named map point markers
CI / build + unit + e2e (pull_request) Successful in 2m36s
a129c2ab81
- shared: 8 MarkerTypes + MARKER_TYPE_INFO (label/color/glyph); MapMarker;
  GameState.markers (required, [] at init); TurnResult.markers {add,remove};
  MarkerSpec/MarkerInfo; FNV-1a 4-char markerId; MAX_MARKERS=400.
- data: new public/region-centroids.json (1,800 anchors = largest-part centroid
  + spread ~0.1x min-bbox) built by new idempotent scripts/build-region-centroids.mjs.
- server: regions.ts centroid loader (public/ then dist/); engine applyTurnResult
  marker ops (remove -> owner-recompute -> add, jitter, cap, never throws);
  prompt documents the contract; buildUserPrompt carries markers; stub marker-free.
- client: WorldMap symbol layer rendered as canvas per-type icons (glyph CDN dead);
  hover tooltip shows name; click focuses owning nation.
- tests: 168 -> 189 (centroids script, regions centroids, db legacy, engine markers).
kamilkrzywda scheduled this pull request to auto merge when all checks succeed 2026-10-15 00:38:26 +02:00
kamilkrzywda merged commit 327fffb789 into master 2026-10-15 00:40:52 +02:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: OpenCode/History#45