Add three-member short alliance prototype
Verify and publish checkpoint container / container (push) Canceled after 0s
Verify and publish checkpoint container / container (push) Canceled after 0s
This commit is contained in:
1 parent
cdc47ed2e1
commit
ef92ddf2b9
12 files changed
+343
-12
No files matched your search
@@ -0,0 +1,99 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
import { createElement } from 'react';
|
||||
import { renderToStaticMarkup } from 'react-dom/server';
|
||||
import { CoalitionNexus, coalitionNexusOptions } from '../components/coalition-nexus';
|
||||
import { botActions } from '../game/bots';
|
||||
import { DIFFICULTIES } from '../game/bot-profiles';
|
||||
import { coalitionOf, emptyCoalitionState } from '../game/coalition-alliance';
|
||||
import { applyAction, joinGame, newPlayer, viewGame, type Game } from '../game/engine';
|
||||
import { quoteVictory } from '../game/victory-quote';
|
||||
import { advanceShortAlliance, shortAllianceCollection, shortAllianceLobby, type ShortAllianceStep } from './fixture-short-alliance-victory';
|
||||
|
||||
/** Real four-seat Basic deal, first turn, normal worm and Nexus; the empty
|
||||
* preview ledger and unopened worm order are the only staged inputs here. */
|
||||
function nexus(): Game {
|
||||
let game = shortAllianceLobby();
|
||||
joinGame(game, newPlayer('ha', 'Harkonnen', 'harkonnen'));
|
||||
for (const player of game.players) game = applyAction(game, player.id, {type:'ready'});
|
||||
game = applyAction(game, 'at', {type:'start'});
|
||||
const actions: ShortAllianceStep[] = [];
|
||||
game = advanceShortAlliance(game, state => state.status === 'playing' && state.turn === 2 && state.phase === 0, actions);
|
||||
const index = game.spiceDeck.findIndex(card => 'worm' in card && !card.greatMaker && !card.suppressed && !card.thumper);
|
||||
assert.ok(index >= 0);
|
||||
game.spiceDeck.unshift(game.spiceDeck.splice(index, 1)[0]);
|
||||
game = advanceShortAlliance(game, state => state.nexus && !state.spiceWindow && !state.spiceResolution &&
|
||||
!state.response && !state.decision && !state.truthtrance && !state.phaseOpening, actions);
|
||||
assert.ok(game.players.every(player => !player.ally));
|
||||
game.coalitionState = emptyCoalitionState('three-short');
|
||||
game = applyAction(game, 'at', {type:'alliance', target:'em'});
|
||||
return applyAction(game, 'em', {type:'alliance', target:'at'});
|
||||
}
|
||||
|
||||
function accept(game: Game, actor: string): Game {
|
||||
const offer = game.coalitionState!.proposal; assert.ok(offer);
|
||||
return applyAction(game, actor, {type:'coalitionAnswer', event:offer.event, nexus:offer.nexus,
|
||||
turn:offer.turn, accept:true, expectedRevision:game.coalitionState!.revision});
|
||||
}
|
||||
|
||||
void test('three-short pair invites a third with unanimous native consent and each bot can propose', () => {
|
||||
const game = nexus();
|
||||
const pair = coalitionOf(game.players, game.coalitionState, 'at'); assert.ok(pair);
|
||||
assert.deepEqual(new Set(pair.members), new Set(['at','em']));
|
||||
const controls = viewGame(game, 'gu').coalitionNexus; assert.ok(controls);
|
||||
assert.equal(controls.variant, 'three-short');
|
||||
assert.deepEqual(coalitionNexusOptions(controls).invitations.map(row => row.entrant), ['gu']);
|
||||
const markup = renderToStaticMarkup(createElement(CoalitionNexus, {view:controls, onIntent(){}}));
|
||||
assert.match(markup, /up to three members and need three strongholds/);
|
||||
assert.match(markup, /Ask to join Atreides, Emperor/);
|
||||
for (const difficulty of DIFFICULTIES) {
|
||||
const view = viewGame(game, 'gu'); view.players.find(player => player.id === 'gu')!.bot = difficulty;
|
||||
const action = botActions(view).find(row => row.type === 'coalitionPropose');
|
||||
assert.ok(action, difficulty);
|
||||
let pending = applyAction(game, 'gu', action);
|
||||
assert.deepEqual(pending.coalitionState!.proposal!.approvals, ['gu']);
|
||||
assert.equal(viewGame(pending, 'em').coalitionNexus!.proposal!.entrant, 'gu');
|
||||
pending = accept(pending, 'at');
|
||||
assert.deepEqual(new Set(coalitionOf(pending.players, pending.coalitionState, 'at')!.members), new Set(['at','em']));
|
||||
pending = accept(pending, 'em');
|
||||
assert.deepEqual(new Set(coalitionOf(pending.players, pending.coalitionState, 'at')!.members), new Set(['at','em','gu']));
|
||||
const full = viewGame(pending, 'ha').coalitionNexus; assert.ok(full);
|
||||
assert.deepEqual(coalitionNexusOptions(full).invitations, []);
|
||||
const before = structuredClone(pending);
|
||||
assert.throws(() => applyAction(pending, 'ha', {type:'coalitionPropose', groupId:pair.id,
|
||||
entrant:'ha', expectedRevision:pending.coalitionState!.revision}));
|
||||
assert.deepEqual(pending, before);
|
||||
}
|
||||
});
|
||||
|
||||
void test('three-short pair and triple share the three-stronghold target and native win', () => {
|
||||
const pair = nexus();
|
||||
const group = coalitionOf(pair.players, pair.coalitionState, 'at'); assert.ok(group);
|
||||
let triple = applyAction(pair, 'at', {type:'coalitionPropose', groupId:group.id, entrant:'gu',
|
||||
expectedRevision:pair.coalitionState!.revision});
|
||||
triple = accept(triple, 'em');
|
||||
triple = accept(triple, 'gu');
|
||||
for (const [source, members] of [[pair, ['at','em']], [triple, ['at','em','gu']]] as const) {
|
||||
let closed = source;
|
||||
for (const player of source.players) {
|
||||
if (closed.nexus && !closed.ready.includes(player.id))
|
||||
closed = applyAction(closed, player.id, {type:'ready'});
|
||||
}
|
||||
closed = advanceShortAlliance(closed, state => !state.nexus && state.phase >= 2, []);
|
||||
// Preserve the actual alliance; only the Collection boundary and original
|
||||
// physical forces are staged by the common stronghold fixture.
|
||||
const game = shortAllianceCollection(closed);
|
||||
const row = viewGame(game, 'at').victoryProgress.find(progress => progress.player === 'at'); assert.ok(row);
|
||||
assert.equal(row.target, 3);
|
||||
assert.equal(row.qualifies, true);
|
||||
assert.equal(row.strongholds.length, 3);
|
||||
assert.deepEqual(new Set(quoteVictory(game).winner), new Set(members));
|
||||
let current = game;
|
||||
for (const player of game.players) {
|
||||
if (current.status === 'finished') break;
|
||||
current = applyAction(current, player.id, {type:'ready'});
|
||||
}
|
||||
assert.equal(current.status, 'finished');
|
||||
assert.deepEqual(new Set(current.winner), new Set(members));
|
||||
}
|
||||
});
|
||||
Reference in new issue
Block a user