87 lines
4.4 KiB
TypeScript
87 lines
4.4 KiB
TypeScript
import assert from 'node:assert/strict';
|
|
import test from 'node:test';
|
|
import { baseDeck } from '../game/cards';
|
|
import { coalitionOf, emptyCoalitionState } from '../game/coalition-alliance';
|
|
import { applyAction, createGame, newPlayer, type Game } from '../game/engine';
|
|
import { strongholdProgress } from '../game/victory-progress';
|
|
import { quoteVictory } from '../game/victory-quote';
|
|
|
|
/** The ledger and Nexus are staged because the larger-alliance mode is not
|
|
* selectable. Pair formation and, when requested, unanimous group admission
|
|
* use real engine actions; the later public stronghold position is staged. */
|
|
function board(variant: 'three-short' | 'unlimited' | undefined, group = true): Game {
|
|
let game = createGame('THREESHORT', newPlayer('a', 'Atreides', 'atreides'), false);
|
|
game.players.push(newPlayer('b', 'Emperor', 'emperor'), newPlayer('c', 'Fremen', 'fremen'),
|
|
newPlayer('d', 'Harkonnen', 'harkonnen'));
|
|
Object.assign(game, {status:'playing', phase:1, turn:2, nexus:true, storm:18,
|
|
order:['a','b','c','d'], ready:[], deck:baseDeck()});
|
|
game.coalitionState = emptyCoalitionState(variant);
|
|
game = applyAction(game, 'a', {type:'alliance', target:'b'});
|
|
game = applyAction(game, 'b', {type:'alliance', target:'a'});
|
|
if (group) {
|
|
const pair = coalitionOf(game.players, game.coalitionState, 'a'); assert.ok(pair);
|
|
game = applyAction(game, 'a', {type:'coalitionPropose', groupId:pair.id, entrant:'c',
|
|
expectedRevision:game.coalitionState!.revision});
|
|
for (const actor of ['b', 'c']) {
|
|
const offer = game.coalitionState!.proposal!;
|
|
game = applyAction(game, actor, {type:'coalitionAnswer', event:offer.event, nexus:offer.nexus,
|
|
turn:offer.turn, accept:true, expectedRevision:game.coalitionState!.revision});
|
|
}
|
|
}
|
|
game.phase = 8;
|
|
game.nexus = false;
|
|
for (const player of game.players) { player.forces = {}; player.reserves = 20; player.tanks = 0; }
|
|
const placements: [string, string][] = group
|
|
? [['a','arrakeen:10'], ['b','carthag:11'], ['c','sietch_tabr:14']]
|
|
: [['a','arrakeen:10'], ['b','carthag:11'], ['b','sietch_tabr:14']];
|
|
for (const [id, location] of placements) {
|
|
const player = game.players.find(p => p.id === id)!;
|
|
player.reserves--;
|
|
player.forces[location] = 1;
|
|
}
|
|
return game;
|
|
}
|
|
const progress = (game: Game, actor = 'a') => strongholdProgress(game).progress.find(row => row.player === actor)!;
|
|
|
|
for (const group of [false, true])
|
|
void test(`three-short gives ${group ? 'three allied members' : 'an allied pair'} the official three-stronghold target`, () => {
|
|
const game = board('three-short', group);
|
|
const row = progress(game);
|
|
assert.equal(row.target, 3);
|
|
assert.equal(row.qualifies, true);
|
|
assert.deepEqual(row.members, group ? ['a','b','c'] : ['a','b']);
|
|
assert.deepEqual(quoteVictory(game).winner, row.members);
|
|
assert.equal(progress(game, 'd').target, 3, 'Solo victory retains its own target.');
|
|
const before = structuredClone(game);
|
|
const enemy = game.players.find(p => p.id === 'd')!;
|
|
enemy.reserves--;
|
|
enemy.forces['arrakeen:10'] = 1;
|
|
assert.equal(progress(game).qualifies, false, 'A contested stronghold is not controlled by the alliance.');
|
|
assert.deepEqual(quoteVictory(game).winner, []);
|
|
assert.deepEqual(before.players.find(p => p.id === 'a')!.forces, {'arrakeen:10':1});
|
|
});
|
|
|
|
void test('three-short remains distinct from the prior longer three-member and unlimited variants', () => {
|
|
const legacy = board(undefined), longer = progress(legacy);
|
|
assert.equal(longer.target, 4);
|
|
assert.equal(longer.qualifies, false);
|
|
assert.deepEqual(quoteVictory(legacy).winner, []);
|
|
const unlimited = board('unlimited');
|
|
assert.equal(progress(unlimited).target, 3);
|
|
assert.deepEqual(quoteVictory(unlimited).winner, ['a','b','c']);
|
|
const short = board('three-short');
|
|
short.victoryExtraStrongholds = 1;
|
|
assert.equal(progress(short).target, 4, 'The separately agreed increment remains additive.');
|
|
assert.equal(progress(short).qualifies, false);
|
|
});
|
|
|
|
void test('two physical controllers retain the existing four-stronghold precedence', () => {
|
|
const game = board('three-short', false);
|
|
game.twoPlayerControllers = {version:1, controllers:[
|
|
{id:'a', seats:['a','c']}, {id:'b', seats:['b','d']},
|
|
]};
|
|
assert.equal(progress(game).target, 4);
|
|
assert.equal(progress(game).qualifies, false);
|
|
assert.deepEqual(quoteVictory(game).winner, []);
|
|
});
|