66 lines
4.0 KiB
TypeScript
66 lines
4.0 KiB
TypeScript
import assert from 'node:assert/strict';
|
|
import { createGame, joinGame, newPlayer, type Action, type Game } from '../game/engine';
|
|
import { actKullPurpose, advanceKullPurpose, kullPurposeClean, kullPurposePlayer, type KullPurposeStep } from './fixture-kull-purpose';
|
|
|
|
export const act = actKullPurpose;
|
|
export const advance = advanceKullPurpose;
|
|
export const clean = kullPurposeClean;
|
|
export const player = kullPurposePlayer;
|
|
|
|
/** Treachery cards in the physical zones exercised by this native programme. */
|
|
export function defaultKullInventory(g: Game): string[] {
|
|
const cards = [...g.deck, ...g.discard, ...g.players.flatMap(p => p.hand),
|
|
...(g.auction?.cards.slice(g.auction.index) ?? []), ...(g.richeseCache ?? [])].map(card => card.id).sort();
|
|
assert.equal(new Set(cards).size, cards.length, 'Every Treachery card is in one physical zone');
|
|
return cards;
|
|
}
|
|
|
|
/** Normal public Basic CHOAM/Emperor/Guild lobby. The host really starts, then
|
|
* no Kull opt-in is submitted. We select only an unopened original starting deal and Spice prefix.
|
|
* Everything after that uses native setup, auction and movement actions. */
|
|
export function createDefaultChoamKullFixture() {
|
|
let game = createGame('PUBLICKULL', newPlayer('choam', 'CHOAM', 'choam'), false, ['choam', 'ix']);
|
|
for (const faction of ['emperor', 'guild'] as const) joinGame(game, newPlayer(faction, faction, faction));
|
|
const actions: KullPurposeStep[] = [];
|
|
const send = (actor: string, action: Action) => { game = act(game,actor,action,actions); };
|
|
const reach = (predicate: (g: Game) => boolean) => { game = advance(game,predicate,actions); };
|
|
for (const p of game.players) send(p.id,{type:'ready'});
|
|
const lobby = structuredClone(game);
|
|
send(game.host,{type:'start'});
|
|
const setup = structuredClone(game);
|
|
assert.equal(game.status,'setup');
|
|
assert.equal(game.kullPreview,true,'A supported fresh Basic start enables printed Kull without a separate opt-in');
|
|
assert.ok(game.players.every(p => p.hand.length === 0));
|
|
const originalCards = game.deck.map(card => card.id).sort();
|
|
const selected: Game['deck'] = [];
|
|
for (const name of ['kull','karama','karama']) {
|
|
const i = game.deck.findIndex(card => name === 'kull' ? card.id === 'ix-kull-wahad' : card.effect === 'karama');
|
|
assert.ok(i >= 0); selected.push(game.deck.splice(i,1)[0]);
|
|
}
|
|
const unopenedDeal = selected.map((card,index) => ({player:game.players[index].id,card:card.id}));
|
|
game.deck.unshift(...selected);
|
|
const beforeDeal = structuredClone(game);
|
|
reach(g => g.status === 'playing');
|
|
const afterSetup = structuredClone(game);
|
|
const [kull,karama,counter] = selected.map(card => card.id);
|
|
assert.ok(player(game,'choam').hand.some(card => card.id === kull));
|
|
assert.ok(player(game,'emperor').hand.some(card => card.id === karama));
|
|
assert.ok(player(game,'guild').hand.some(card => card.id === counter));
|
|
assert.deepEqual(defaultKullInventory(game),originalCards);
|
|
const i = game.spiceDeck.findIndex(card => 'territory' in card && !card.discovery); assert.ok(i >= 0);
|
|
game.spiceDeck.unshift(game.spiceDeck.splice(i,1)[0]);
|
|
reach(g => g.phase === 3 && !!g.auction && g.auction.active === 'emperor' && clean(g));
|
|
const auction = structuredClone(game);
|
|
assert.deepEqual(defaultKullInventory(auction),originalCards);
|
|
reach(g => g.phase === 5 && g.active === 'choam' && clean(g));
|
|
send('choam',{type:'endMovement'});
|
|
reach(g => g.phase === 5 && g.active === 'emperor' && clean(g));
|
|
assert.deepEqual(defaultKullInventory(game),originalCards);
|
|
return {game,lobby,setup,beforeDeal,afterSetup,auction,actions,originalCards,unopenedDeal,
|
|
kull,karama,counter,actor:'emperor',reactor:'choam',counterOwner:'guild',
|
|
staging:'No Kull opt-in is submitted. Selected only original unopened starting cards and a safe unopened Spice prefix; all setup, auction passes and turn changes use real native actions.'};
|
|
}
|
|
export type DefaultChoamKullFixture = ReturnType<typeof createDefaultChoamKullFixture>;
|
|
export const defaultKullAttempt = (f: DefaultChoamKullFixture): Action =>
|
|
({type:'card',mode:'shipment',card:f.karama,target:f.actor});
|