feat: #66 contested regions / frontlines — 12th ability (v0.44.0)
CI / build + unit + e2e (pull_request) Successful in 2m10s
CI / build + unit + e2e (pull_request) Successful in 2m10s
A third region state (contested frontline): a region in no nation's territory and not in unowned, opened/resolved by the LLM, tied to an active war. Sole-ownership invariant preserved. Durable GameState.contested; contested-inert stub; map legend stays at 7.
This commit is contained in:
+15
-2
@@ -108,6 +108,13 @@ export interface StubPayload {
|
||||
* deterministicTurn), so the sea path is covered by unit tests only.
|
||||
*/
|
||||
seaZones?: Array<{ zone: string; by: string }>;
|
||||
/**
|
||||
* (v0.44.0, #66) The currently contested regions (war frontlines,
|
||||
* `{ regionKey, parties }`), emitted by the engine's user prompt. The stub
|
||||
* ignores this field — it never opens or resolves a contest (see
|
||||
* deterministicTurn), so the contested path is covered by unit tests only.
|
||||
*/
|
||||
contested?: Array<{ regionKey: string; parties: string[] }>;
|
||||
}
|
||||
|
||||
function truncate(s: string, n: number): string {
|
||||
@@ -193,6 +200,11 @@ function truncate(s: string, n: number): string {
|
||||
* (v0.43.0, #65) The stub likewise NEVER claims, contests or re-claims a sea
|
||||
* zone (it always returns `sea: { control: [] }`) — the sea-control path is
|
||||
* covered by unit tests only, keeping the e2e assertions untouched.
|
||||
*
|
||||
* (v0.44.0, #66) The stub likewise NEVER opens or resolves a contested region
|
||||
* (a war frontline) — it always returns `contested: { open: [], resolve: [] }`
|
||||
* — the contested path is covered by unit tests only, keeping the e2e
|
||||
* assertions untouched.
|
||||
*/
|
||||
export function deterministicTurn(p: StubPayload): TurnResult {
|
||||
const state = p.state;
|
||||
@@ -272,13 +284,14 @@ export function deterministicTurn(p: StubPayload): TurnResult {
|
||||
nationOps: [], // the stub never proposes nation ops (see doc comment)
|
||||
};
|
||||
|
||||
// The 11 ability fields, filled by the registry in its canonical order. The
|
||||
// The 12 ability fields, filled by the registry in its canonical order. The
|
||||
// stub is ability-inert: every ability contributes its own no-op default
|
||||
// (each ability's `stub()` — see the per-ability doc comments above), so the
|
||||
// e2e assertions are unaffected. The insertion order here (ABILITIES) is the
|
||||
// historical key order pinned by the stub's deterministic output
|
||||
// (takeovers → wars → hybrid → economy → industry → nuclear → leaders →
|
||||
// ideology → disasters → migration → sea), which must stay byte-identical.
|
||||
// ideology → disasters → migration → sea → contested), which must stay
|
||||
// byte-identical.
|
||||
const abilities = {} as Record<AbilityField, unknown>;
|
||||
for (const a of ABILITIES) abilities[a.field] = a.stub();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user