79 lines
4.5 KiB
TypeScript
79 lines
4.5 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';
|
|
import { publicKullInventory } from './fixture-public-choam-kull';
|
|
|
|
export const act = actKullPurpose;
|
|
export const advance = advanceKullPurpose;
|
|
export const clean = kullPurposeClean;
|
|
export const player = kullPurposePlayer;
|
|
export const inventory = publicKullInventory;
|
|
export type IxNativeRoster = 'ixians' | 'tleilaxu' | 'both';
|
|
|
|
/** Host-created Basic Kull lobby with genuine native Ix setup. Only original
|
|
* unopened Treachery and Spice positions are selected; every later payment,
|
|
* decision, force, Face Dancer and movement transition comes from native play.
|
|
* The scalar shuffle preserves the selected Ixian starting-pool deal order. */
|
|
export function createKullNativeIxFixture(roster: IxNativeRoster) {
|
|
const actor = roster === 'ixians' ? 'ixians' : 'tleilaxu';
|
|
let game = createGame('NATIVEKULL', newPlayer('choam','CHOAM','choam'), false, ['choam','ix']);
|
|
const natives = roster === 'both' ? ['ixians','tleilaxu'] as const : [roster] as const;
|
|
for (const faction of natives) joinGame(game, newPlayer(faction,faction,faction));
|
|
for (const faction of ['emperor','guild'] as const) joinGame(game, newPlayer(faction,faction,faction));
|
|
const actions: KullPurposeStep[] = [];
|
|
const send = (seat: string, action: Action) => { game = act(game,seat,action,actions); };
|
|
const reach = (until: (state: Game) => boolean) => { game = advance(game,until,actions); };
|
|
send('choam',{type:'moduleOption',module:'kullPreview',enabled:true});
|
|
for (const seat of game.players) send(seat.id,{type:'ready'});
|
|
const lobby = structuredClone(game);
|
|
send('choam',{type:'start'});
|
|
const setup = structuredClone(game);
|
|
assert.equal(game.status,'setup');
|
|
assert.equal(game.kullPreview,true);
|
|
assert.ok(game.players.every(seat => seat.hand.length === 0));
|
|
const originalCards = game.deck.map(card=>card.id).sort();
|
|
const selected: Game['deck'] = [];
|
|
// In the Ixian starting pool, the first offered original card is selected,
|
|
// then the remaining pool is dealt in non-Ixian seat order. The 'both'
|
|
// roster therefore gives Ixians a harmless original card before the other
|
|
// native faction receives its own Karama.
|
|
const kinds = roster === 'ixians' ? ['karama','kull','karama','truth'] :
|
|
roster === 'tleilaxu' ? ['kull','karama','karama','truth'] :
|
|
['filler','kull','karama','karama','truth'];
|
|
for (const kind of kinds) {
|
|
const index = game.deck.findIndex(card => kind === 'kull' ? card.id === 'ix-kull-wahad' :
|
|
kind === 'truth' ? card.effect === 'truthtrance' : kind === 'karama' ? card.effect === 'karama' :
|
|
card.kind === 'worthless' && card.id !== 'ix-kull-wahad');
|
|
assert.ok(index >= 0,`Original ${kind} is available before deal`);
|
|
selected.push(game.deck.splice(index,1)[0]);
|
|
}
|
|
game.deck.unshift(...selected);
|
|
const beforeDeal = structuredClone(game);
|
|
reach(state=>state.status === 'playing');
|
|
const afterSetup = structuredClone(game);
|
|
const kull = 'ix-kull-wahad';
|
|
const karama = player(game,actor).hand.find(card=>card.effect === 'karama')?.id;
|
|
const counter = player(game,'emperor').hand.find(card=>card.effect === 'karama')?.id;
|
|
const truth = player(game,'guild').hand.find(card=>card.effect === 'truthtrance')?.id;
|
|
assert.ok(karama && counter && truth);
|
|
assert.notEqual(karama,counter);
|
|
assert.ok(player(game,'choam').hand.some(card=>card.id === kull));
|
|
assert.deepEqual(inventory(game),originalCards);
|
|
if (roster !== 'tleilaxu') assert.ok(game.mobileStronghold);
|
|
if (roster !== 'ixians') assert.equal(player(game,'tleilaxu').faceDancers?.length,3);
|
|
const spice = game.spiceDeck.findIndex(card=>'territory' in card && !card.discovery);
|
|
assert.ok(spice >= 0);
|
|
game.spiceDeck.unshift(game.spiceDeck.splice(spice,1)[0]);
|
|
reach(state=>state.phase <= 1 && clean(state));
|
|
const early = structuredClone(game);
|
|
reach(state=>state.phase === 5 && state.active === actor && clean(state));
|
|
assert.deepEqual(inventory(game),originalCards);
|
|
return { game, early, lobby, setup, beforeDeal, afterSetup, actions, originalCards,
|
|
actor, reactor:'choam', counterOwner:'emperor', asker:'guild', recipient:'emperor',
|
|
kull, karama, counter, truth };
|
|
}
|
|
export type KullNativeIxFixture = ReturnType<typeof createKullNativeIxFixture>;
|
|
export const attempt = (f: KullNativeIxFixture): Action =>
|
|
({type:'card',mode:'shipment',card:f.karama,target:f.actor});
|