Contested regions / frontlines: a region can be disputed by two factions (sieges, advancing fronts) — relaxes the sole-ownership invariant #66

Closed
opened 2026-10-15 17:05:42 +02:00 by kamilkrzywda · 0 comments
Owner

What

Allow a region to be contested by two factions at once — a frontline: sieges, advancing/retreating front lines, disputed sub-regions. This relaxes the load-bearing sole-ownership invariant (a region key having at most one owner) in a controlled, reversible way.

Mechanism

  • Introduce a disputed state on a region key: GameState.disputed: { regionKey: string; claimants: [string,string] }[] — a region can have one controlling owner + one contesting claimant (or be genuinely contested with no clear owner). The map renders a contested region distinctly (e.g. a hatched/mixed fill).
  • TurnResult gains fronts: { contest: { regionKey: string; by: string /* the new claimant */ }[]; resolve: { regionKey: string; to: string }[] } — required, default []s. A contest adds a claimant (region is now disputed); a resolve collapses it back to sole ownership by to.
  • Hard rule preserved: a region can never have more than 2 owners, and it always has at least a resolving owner; the existing "never orphaned / never duplicated" guarantee is generalized to "at most one controller + one claimant, always resolvable." The canConquer gate still applies to resolving a contest by force.
  • This is the riskiest change (touches the core invariant) — do it last, carefully, with exhaustive tests.
  • Builds on: sole-ownership (generalized), territory, the canConquer gate, the choropleth.

Acceptance

  • e2e safety (critical): stub returns fronts: { contest: [], resolve: [] }, deterministic; additive (legacy games load with disputed: []); must not break the UK-in-wwii-1939 e2e (empty frontier, military 80→85, 7-item legend) or any existing sole-ownership unit/e2e assertion (update them deliberately, not by accident).
  • Exhaustive unit tests in server/engine.test.ts + server/map.test.ts (contest, resolve, ≤2 owners, no orphan, no duplicate, conquest-resolution gate, contested map fill).
  • Version bump at PR time. Do last (most invasive).
## What Allow a **region to be contested** by two factions at once — a **frontline**: sieges, advancing/retreating front lines, disputed sub-regions. This **relaxes the load-bearing sole-ownership invariant** (a region key having at most one owner) in a controlled, reversible way. ## Mechanism - Introduce a `disputed` state on a region key: `GameState.disputed: { regionKey: string; claimants: [string,string] }[]` — a region can have **one controlling owner + one contesting claimant** (or be genuinely contested with no clear owner). The map renders a contested region distinctly (e.g. a hatched/mixed fill). - `TurnResult` gains `fronts: { contest: { regionKey: string; by: string /* the new claimant */ }[]; resolve: { regionKey: string; to: string }[] }` — required, default `[]`s. A `contest` adds a claimant (region is now disputed); a `resolve` collapses it back to sole ownership by `to`. - **Hard rule preserved:** a region can never have **more than 2** owners, and it always has at least a resolving owner; the existing "never orphaned / never duplicated" guarantee is generalized to "at most one controller + one claimant, always resolvable." The `canConquer` gate still applies to resolving a contest by force. - This is the **riskiest** change (touches the core invariant) — do it last, carefully, with exhaustive tests. - Builds on: sole-ownership (generalized), territory, the `canConquer` gate, the choropleth. ## Acceptance - **e2e safety (critical):** stub returns `fronts: { contest: [], resolve: [] }`, deterministic; additive (legacy games load with `disputed: []`); must **not** break the UK-in-`wwii-1939` e2e (empty frontier, military 80→85, 7-item legend) or any existing sole-ownership unit/e2e assertion (update them deliberately, not by accident). - **Exhaustive** unit tests in `server/engine.test.ts` + `server/map.test.ts` (contest, resolve, ≤2 owners, no orphan, no duplicate, conquest-resolution gate, contested map fill). - Version bump at PR time. Do **last** (most invasive).
Sign in to join this conversation.
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: OpenCode/History#66