No man's land: region release + re-claim + distinct grey map rendering (v0.22.0) — part 3 of #16 #37

Merged
kamilkrzywda merged 2 commits from feat/16-p3-no-mans-land into master 2026-10-14 14:09:22 +02:00
Owner

Part 3 of #16 — No man's land.

A sub-region can now end a turn unowned (released by a nation) and be re-claimed on a later turn.

What changed

  • TurnResult.unowned: string[] — region keys released to no man's land this turn. A re-claim is an ordinary gain on a currently-unowned key; a release and a claim of the same key in one turn → the claim wins.
  • GameState.unowned: string[] — the cumulative list of mid-game releases; required, [] at game start, and a key leaves the list when it is re-claimed.
  • applyTurnResult applies releases first (sole-owner only; 0-owner / 2+ owner / already-unowned / hallucinated keys are no-ops), then claims against the post-release snapshot — so the sole-ownership and "no key orphaned" invariants hold.
  • Map: a released key renders in a distinct visible grey (#3f3f46 fill / rgba(255,255,255,0.25) stroke) with a "No man's land" tooltip sub-line, distinct from the faint preset-unassigned look ("No owner"). The distinction is decided on the client from GameState.unowned via the shared regionFillStyle / unownedTooltipLabel helpers — never from the RegionMap. An ownerless region click leaves the dossier unchanged.
  • The prompt carries the current unowned list in the user payload and documents the unowned field + its rule in both tool and plain modes; the jump route's region reference includes released keys so the model can name them when re-claiming.
  • No save-format change: unowned defaults to [] on load (legacy SQLite rows), so this is a no-wipe deploy (history-data volume preserved).
  • Tests: unit 105 → 127 (new no-man's-land suite in engine.test.ts + map presentation-contract + db legacy-row tests). The deterministic stub never releases, so the stub-backed e2e suite is unchanged and stays green — no-man's-land is covered by unit tests only.

Gates (local): typecheck ✓ · lint ✓ · 127/127 unit tests ✓ · build ✓ (Playwright e2e runs in CI).

v0.22.0

Part 3 of #16 — **No man's land**. A sub-region can now end a turn **unowned** (released by a nation) and be re-claimed on a later turn. **What changed** - `TurnResult.unowned: string[]` — region keys released to no man's land *this* turn. A **re-claim** is an ordinary `gain` on a currently-unowned key; a release and a claim of the *same* key in one turn → the claim wins. - `GameState.unowned: string[]` — the cumulative list of mid-game releases; required, `[]` at game start, and a key leaves the list when it is re-claimed. - `applyTurnResult` applies **releases first** (sole-owner only; 0-owner / 2+ owner / already-unowned / hallucinated keys are no-ops), **then claims** against the post-release snapshot — so the sole-ownership and "no key orphaned" invariants hold. - **Map:** a released key renders in a distinct visible grey (`#3f3f46` fill / `rgba(255,255,255,0.25)` stroke) with a "No man's land" tooltip sub-line, distinct from the faint preset-unassigned look ("No owner"). The distinction is decided on the client from `GameState.unowned` via the shared `regionFillStyle` / `unownedTooltipLabel` helpers — never from the `RegionMap`. An ownerless region click leaves the dossier unchanged. - The prompt carries the current `unowned` list in the user payload and documents the `unowned` field + its rule in both tool and plain modes; the jump route's region reference includes released keys so the model can name them when re-claiming. - **No save-format change:** `unowned` defaults to `[]` on load (legacy SQLite rows), so this is a **no-wipe deploy** (`history-data` volume preserved). - **Tests:** unit 105 → 127 (new no-man's-land suite in `engine.test.ts` + map presentation-contract + db legacy-row tests). The deterministic stub never releases, so the stub-backed e2e suite is unchanged and stays green — no-man's-land is covered by unit tests only. **Gates (local):** typecheck ✓ · lint ✓ · 127/127 unit tests ✓ · build ✓ (Playwright e2e runs in CI). v0.22.0
kamilkrzywda added 2 commits 2026-10-14 14:07:45 +02:00
- shared/protocol.ts: TurnResult.unowned (keys released this turn) + the map
  presentation contract (regionFillStyle / unownedTooltipLabel, hue-less grey)
- shared/types.ts: required GameState.unowned (cumulative mid-game releases;
  [] at init, distinct from preset-unassigned regions)
- server/engine.ts: applyTurnResult applies releases first (sole-owner only,
  never fabricating no man's land from unassigned keys), then claims against
  the post-release snapshot (gain on an ownerless key = re-claim); lone
  release is isMeaningful; prompts + user payload carry the unowned field
- server/stub.ts: unchanged behavior (never releases; e2e stays stable)
- server/db.ts: legacy rows load with unowned defaulted to []
- server/index.ts: create-game sets []; jump reference table includes
  released keys so the model can name them for re-claim
- WorldMap/App: unowned prop -> visible grey + 'No man's land' tooltip
- unit tests: 105 -> 127 (release/re-claim suites, presentation contract,
  legacy-row normalization)
Bump to v0.22.0 (no man's land, #16 P3) + AGENTS.md/CHANGELOG
CI / build + unit + e2e (pull_request) Successful in 1m33s
6c52e10898
kamilkrzywda scheduled this pull request to auto merge when all checks succeed 2026-10-14 14:07:54 +02:00
kamilkrzywda merged commit edaa8f55d1 into master 2026-10-14 14:09:22 +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#37