66 lines
3.8 KiB
TypeScript
66 lines
3.8 KiB
TypeScript
import assert from 'node:assert/strict';
|
|
import test from 'node:test';
|
|
import { leaders, treacheryDeck } from '../game/cards';
|
|
import { ecazTreacheryCards } from '../game/ecaz-cards';
|
|
import { richeseCards } from '../game/richese-cards';
|
|
import { NEXUS_FACTIONS } from '../game/nexus-cards';
|
|
import { createTraitorDeclaration } from '../game/traitor-declarations';
|
|
import { CHEAP_HERO_TRAITOR, traitorDeck } from '../game/traitors';
|
|
import {
|
|
closeHarkonnenBetrayalSource, createHarkonnenBetrayalReplacement,
|
|
createHarkonnenBetrayalSource, drawHarkonnenBetrayalReplacement,
|
|
initialHarkonnenBetrayalCursor, validateHarkonnenBetrayalFrame,
|
|
validateHarkonnenBetrayalHistory, type HarkonnenBetrayalContext,
|
|
} from '../game/nexus-harkonnen-betrayal';
|
|
|
|
function expanded(hero: boolean): HarkonnenBetrayalContext {
|
|
const players: HarkonnenBetrayalContext['players'] = [
|
|
{id:'h', faction:'harkonnen', ally:null}, {id:'i', faction:'ixians', ally:null},
|
|
{id:'r', faction:'richese', ally:null}, {id:'a', faction:'atreides', ally:null},
|
|
];
|
|
const expansions = ['ix', 'choam'];
|
|
const physicalCards = [...treacheryDeck(expansions), ...ecazTreacheryCards(), ...richeseCards()];
|
|
const heroLeaderIds = physicalCards.filter(card => card.kind === 'hero').map(card => card.id);
|
|
const identity = hero ? CHEAP_HERO_TRAITOR : leaders('ixians')[0].id;
|
|
const universe = traitorDeck(players.map(player => ({leaders:leaders(player.faction)})), true);
|
|
return {
|
|
status:'playing', phase:6, turn:2, advanced:false, sequence:0,
|
|
expansions, ecazTreachery:true, players, physicalCards, universe,
|
|
cards:{version:1, deck:NEXUS_FACTIONS.filter(faction => faction !== 'harkonnen'),
|
|
discard:[], hands:{h:null, i:null, r:null, a:'harkonnen'}},
|
|
traitors:{reserve:universe.filter(card => card !== identity), players:players.map(player => ({
|
|
id:player.id, faction:player.faction, traitors:player.id === 'h' ? [identity] : [],
|
|
}))},
|
|
battle:{event:'expanded-battle', attacker:'h', defender:'i', heroLeaderIds,
|
|
plans:{h:{leader:leaders('harkonnen')[0].id}, i:{leader:hero ? heroLeaderIds[0] : identity}}},
|
|
};
|
|
}
|
|
|
|
for (const hero of [false, true]) void test(`Basic selected Ix and CHOAM census binds a ${hero ? 'Cheap Hero' : 'native Ixian'} traitor and continues`, () => {
|
|
const ctx = expanded(hero);
|
|
const declaration = createTraitorDeclaration({...ctx.battle, players:ctx.players.map(player => ({...player,
|
|
traitors:ctx.traitors.players.find(seat => seat.id === player.id)!.traitors}))}, 'h');
|
|
const authority = {provider:'h', declaration, nativeWindow:'direct' as const,
|
|
nativeContext:'original-direct-call', nativeRequired:[], nativePassed:[]};
|
|
const source = createHarkonnenBetrayalSource(ctx, authority, 'original-battle');
|
|
assert.equal(source.declaration.identity, hero ? CHEAP_HERO_TRAITOR : leaders('ixians')[0].id);
|
|
validateHarkonnenBetrayalFrame(ctx, source, authority, 'original-battle', ['a'], []);
|
|
for (const outcome of ['pass', 'use'] as const) {
|
|
const closed = closeHarkonnenBetrayalSource(source, outcome, outcome === 'use' ? 'a' : null,
|
|
initialHarkonnenBetrayalCursor());
|
|
validateHarkonnenBetrayalHistory([closed.receipt], closed.cursor, ctx.universe);
|
|
if (outcome === 'use') {
|
|
const due = createHarkonnenBetrayalReplacement(closed.receipt);
|
|
const done = drawHarkonnenBetrayalReplacement(due, [closed.receipt], closed.cursor, ctx.traitors.reserve[0], ctx.universe);
|
|
assert.equal(done.status, 'drawn');
|
|
}
|
|
}
|
|
for (const bad of [
|
|
{...ctx, expansions:['choam']},
|
|
{...ctx, physicalCards:ctx.physicalCards.slice(1)},
|
|
{...ctx, universe:ctx.universe.filter(id => id !== CHEAP_HERO_TRAITOR)},
|
|
{...ctx, battle:{...ctx.battle, heroLeaderIds:[]}},
|
|
{...ctx, advanced:true},
|
|
]) assert.throws(() => createHarkonnenBetrayalSource(bad, authority, 'original-battle'));
|
|
});
|