feat(#62): ideology / government-type change (v0.39.0)
CI / build + unit + e2e (pull_request) Successful in 2m33s
CI / build + unit + e2e (pull_request) Successful in 2m33s
Add a per-nation free-form ideology (form of government) field and a
TurnResult.ideology lever (coups / revolutions / reforms / constitutional
changes) with a deterministic -5 stability dip and political narration.
- shared/types.ts: Nation.ideology?: string (optional/additive)
- shared/protocol.ts: IdeologyChange / IdeologyOps, IDEOLOGY_STABILITY_EFFECT
= -5, TurnResult.ideology (required, between leaders and markers),
nationForPrompt now carries ideology
- server/engine.ts: validateIdeologyOps (exported, never throws),
applyIdeologyChange (resolve -> set field -> -5 dip mirrored to regions +
re-derived -> political event), ideology block in applyTurnResult after
leaders / before markers, isMeaningful + validateTurnResult + system prompt
+ runTurn unclear fallback
- server/stub.ts: StubPayload gains ideology; deterministicTurn returns
ideology: { changes: [] } (never changes one, e2e unaffected)
- Tests: engine.test.ts (validate / isMeaningful / applyTurnResult /
buildUserPrompt), protocol.test.ts (constant + nationForPrompt),
db.test.ts (legacy load + round-trip); every TurnResult fixture updated
- package.json -> 0.39.0; CHANGELOG v0.39.0 entry
This commit is contained in:
@@ -510,6 +510,44 @@ export const LEADER_STABILITY_EFFECT: Record<LeaderCause, number> = {
|
||||
appointed: 0,
|
||||
};
|
||||
|
||||
// --- Ideology / form of government (v0.39, #62) ------------------------------
|
||||
//
|
||||
// Each nation may carry a free-form `ideology` label — its form of
|
||||
// government ("democracy", "fascism", "military junta", …). The LLM changes it
|
||||
// each turn via `TurnResult.ideology`: a change names a target nation (id or
|
||||
// name) and the new government type. The engine sets the nation's `ideology`
|
||||
// and applies a DETERMINISTIC stability dip (see `IDEOLOGY_STABILITY_EFFECT`
|
||||
// below) — a regime shift always unsettles a nation — no hidden randomness.
|
||||
// It is a third, independent axis next to `leader` (who is in charge) and the
|
||||
// v0.32.0 takeover (which renames the nation): the id, name, territory,
|
||||
// relations, and the leader all stay valid, only the form of government and
|
||||
// stability move.
|
||||
|
||||
/** A single ideology change: the target nation and its new form of government. */
|
||||
export interface IdeologyChange {
|
||||
/** The target nation, by id or name (resolved by the shared resolver). */
|
||||
target: string;
|
||||
/** The new form of government (a free-form label, e.g. "democracy"). */
|
||||
to: string;
|
||||
}
|
||||
|
||||
/** The ideology-ops block of a `TurnResult`. Required; default `{ changes: [] }`. */
|
||||
export interface IdeologyOps {
|
||||
changes: IdeologyChange[];
|
||||
}
|
||||
|
||||
/**
|
||||
* The deterministic stability effect of an ideology change (v0.39, #62). A
|
||||
* regime shift (a coup, a revolution, a reform, a constitutional change)
|
||||
* unsettles the nation: a small −5 dip applied to the target's `stability`
|
||||
* (clamped 0–100), mirrored to its `regionStats`, and its derived stats
|
||||
* re-derived afterwards. Applied the same way a leader-cause effect is —
|
||||
* re-setting the SAME ideology still dips (the shift just happened, even if
|
||||
* the label does not change) but is idempotent for the `ideology` field
|
||||
* itself.
|
||||
*/
|
||||
export const IDEOLOGY_STABILITY_EFFECT: number = -5;
|
||||
|
||||
// --- Nuclear / WMD effect math (v0.37, #60) ----------------------------------
|
||||
//
|
||||
// A nuclear strike is a DETERMINISTIC, devastating stat collapse — there is no
|
||||
@@ -694,6 +732,23 @@ export interface TurnResult {
|
||||
* no leader replacement). Required; default `{ changes: [] }`.
|
||||
*/
|
||||
leaders: LeaderOps;
|
||||
/**
|
||||
* (v0.39, #62) Ideology (form of government) changes this turn. Each
|
||||
* change names a target nation (id or name) and its new form of
|
||||
* government — a free-form label such as "democracy", "fascism", or
|
||||
* "military junta". Applied directly by `applyTurnResult` (like leaders /
|
||||
* hybrid / economy): the target's `ideology` is set to the new label, a
|
||||
* small, deterministic stability dip (`IDEOLOGY_STABILITY_EFFECT`, −5) is
|
||||
* applied (clamped, mirrored to the nation's regions, re-derived), and a
|
||||
* `political` narration event is appended. Re-setting the same label is
|
||||
* idempotent for the field (still narrated, still dips). Unknown /
|
||||
* ambiguous targets and empty labels are dropped, never thrown; the
|
||||
* change never moves territory (sole ownership is untouched). Distinct
|
||||
* from a takeover (which renames the nation) and from a leader change
|
||||
* (who is in charge). At most 1–2 per turn, each narrated in `events`.
|
||||
* Required; default `{ changes: [] }`.
|
||||
*/
|
||||
ideology: IdeologyOps;
|
||||
/**
|
||||
* (v0.27, #44) Map-marker ops this turn. `add` places new named markers
|
||||
* (the engine positions each at its region's centroid + a small
|
||||
@@ -1007,6 +1062,7 @@ export function nationForPrompt(n: Nation) {
|
||||
territory: n.territory,
|
||||
relations: n.relations,
|
||||
leader: n.leader,
|
||||
ideology: n.ideology,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -88,6 +88,18 @@ export interface Nation {
|
||||
* each change carries a deterministic stability effect + a narration event.
|
||||
*/
|
||||
leader?: { name: string };
|
||||
/**
|
||||
* The nation's current form of government (v0.39, #62) — a free-form label
|
||||
* such as `"democracy"`, `"fascism"` or `"military junta"`. Optional — a
|
||||
* legacy game (or a nation the LLM has never given an ideology to) has none,
|
||||
* which is the "no recorded government" baseline. The LLM changes it via
|
||||
* `TurnResult.ideology` (coups / revolutions / reforms / constitutional
|
||||
* changes, see `IdeologyChange` in `protocol.ts`); each change carries a
|
||||
* deterministic stability dip + a political narration event. Distinct from
|
||||
* the v0.32.0 takeover (which renames the nation) and from `leader` (who is
|
||||
* in charge): only the form of government and stability move.
|
||||
*/
|
||||
ideology?: string;
|
||||
}
|
||||
|
||||
/** Categories a narrative event can take. */
|
||||
|
||||
Reference in New Issue
Block a user