feat(#50): make the four 0-100 stats consequential
CI / build + unit + e2e (pull_request) Successful in 2m8s
CI / build + unit + e2e (pull_request) Successful in 2m8s
- Add a hard military conquest gate (canConquer, CONQUEST_MILITARY_MARGIN=15) enforced in applyTurnResult: a contested invasion is repelled when the attacker's military is more than 15 below the defender's; re-claims of unowned regions are never gated. Emits a new 'repelled' event. - Document the soft levers in the system prompt: stability<30 + gathering marker -> non-military takeover; economy growth/decay drives other stats. - Emit bounded frontierMilitary (per-neighbour balance) and takeoverRisks hints in the user payload (only when present). - repelled outcome color (#9f1239); full unit test coverage; stub untouched. - Bump to v0.30.0. Fixes #50
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import {
|
||||
canConquer,
|
||||
CONQUEST_MILITARY_MARGIN,
|
||||
deriveNationStats,
|
||||
migrateRegionStats,
|
||||
seedRegionStats,
|
||||
@@ -218,3 +220,40 @@ describe('migrateRegionStats', () => {
|
||||
expect('uk' in (out.regionStats ?? {})).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
// --- canConquer (v0.30, #50: the military hard gate) -------------------------
|
||||
|
||||
describe('canConquer', () => {
|
||||
it('the default margin constant is 15', () => {
|
||||
expect(CONQUEST_MILITARY_MARGIN).toBe(15);
|
||||
});
|
||||
|
||||
it('an attacker within the margin of the defender proceeds (gap == margin)', () => {
|
||||
// attacker 10, defender 25 → gap of 15 == margin → win (not more than 15 below)
|
||||
expect(canConquer(10, 25)).toBe(true);
|
||||
});
|
||||
|
||||
it('an attacker more than the margin below the defender is repelled (gap == margin+1)', () => {
|
||||
// attacker 9, defender 25 → gap of 16 > margin → repel
|
||||
expect(canConquer(9, 25)).toBe(false);
|
||||
});
|
||||
|
||||
it('an equal attacker proceeds', () => {
|
||||
expect(canConquer(50, 50)).toBe(true);
|
||||
});
|
||||
|
||||
it('an attacker within the margin (gap < margin) proceeds', () => {
|
||||
expect(canConquer(30, 40)).toBe(true); // gap 10
|
||||
expect(canConquer(14, 25)).toBe(true); // gap 11
|
||||
});
|
||||
|
||||
it('an attacker ahead of the defender always proceeds', () => {
|
||||
expect(canConquer(90, 10)).toBe(true);
|
||||
});
|
||||
|
||||
it('a margin of 0 means only an equal-or-ahead attacker proceeds', () => {
|
||||
expect(canConquer(50, 50, 0)).toBe(true); // equal
|
||||
expect(canConquer(51, 50, 0)).toBe(true); // ahead
|
||||
expect(canConquer(49, 50, 0)).toBe(false); // one below → repel
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user