Files
Dune/tests/kull-whole-original-owner-guard.test.ts
T
Quintine 07f1f1f2ae
Verify and publish checkpoint container / container (push) Successful in 10m21s
Support whole-turn answers with Basic Kull
2026-10-29 01:37:33 +11:00

69 lines
3.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import assert from 'node:assert/strict';
import test from 'node:test';
import { handLimit, viewGame, type Game } from '../game/engine';
import { act, advance, clean, createKullRicheseSemutaFixture, inventory, player } from './fixture-kull-richese-semuta';
/** Real Basic setup, cache purchase and active Emperor shipment window.
* Only an existing physical Truthtrance is reassigned to Richese's hand. */
function source() {
const f=createKullRicheseSemutaFixture(), game=structuredClone(f.game);
const zones=[game.deck,game.discard,...(game.richeseCache?[game.richeseCache]:[]),
...(game.richeseRemoved?[game.richeseRemoved]:[]),...game.players.map(seat=>seat.hand)];
const zone=zones.find(cards=>cards.some(card=>card.effect==='truthtrance')); assert.ok(zone);
const [truth]=zone.splice(zone.findIndex(card=>card.effect==='truthtrance'),1);
assert.ok(player(game,'richese').hand.length<handLimit(player(game,'richese')));
player(game,'richese').hand.push(truth);
assert.deepEqual(inventory(game),f.originalCards);
return {...f,game,truth:truth.id};
}
function passDiscard(game:Game):Game {
const pending=game.pendingTreacheryDiscard; assert.ok(pending?.reaction);
const event=pending.batch.event;
for (let count=0;game.pendingTreacheryDiscard?.batch.event===event && count<game.players.length;count++) {
const reaction=game.pendingTreacheryDiscard!.reaction; assert.ok(reaction?.stage==='offer');
const seat=game.players.find(row=>!reaction.passed.includes(row.id)); assert.ok(seat);
game=act(game,seat.id,{type:'semutaPass',event});
}
assert.notEqual(game.pendingTreacheryDiscard?.batch.event,event);
return game;
}
function answer(f:ReturnType<typeof source>, value:'yes'|'no') {
let game=act(f.game,'richese',{type:'card',card:f.truth});
while (game.truthtrance?.stage==='priority') {
const seat=game.players.find(row=>!game.truthtrance!.passed.includes(row.id)); assert.ok(seat);
game=act(game,seat.id,{type:'truthPass'});
}
const source=JSON.stringify(['karamaShipment',game.turn,'emperor','original']);
game=act(game,'richese',{type:'truthAsk',question:{kind:'turnActions',target:'emperor',turn:game.turn,
mixed:{kind:'action',action:{kind:'windowCardPlay',effect:'karamaShipment',source}}}});
assert.ok(viewGame(game,'emperor').truthTurnActionAnswers?.includes(value));
game=act(game,'emperor',{type:'truthAnswer',answer:value});
const promise=game.turnActionPromises!.at(-1); assert.ok(promise);
if (game.pendingTreacheryDiscard) game=passDiscard(game);
return {game,event:promise.event};
}
void test('an original Karama forbidden by the actor’s whole No cannot open Kull',()=>{
const f=source(); const {game:initialGame,event}=answer(f,'no'); let game=initialGame;
const before=structuredClone(game);
assert.throws(()=>act(game,'emperor',{type:'card',mode:'shipment',card:f.karama,target:'emperor'}));
assert.deepEqual(game,before);
assert.equal(game.pendingKull ?? null,null);
game=act(game,'emperor',{type:'ship',territory:'polar_sink',sector:0,amount:1,elite:0,allyPayment:0});
game=advance(game,clean);
assert.ok(!game.turnActionPromises!.find(row=>row.event===event)?.released);
assert.deepEqual(inventory(game),f.originalCards);
});
void test('an original printed Karama required by the actor’s whole Yes stays legal through Kull decline',()=>{
const f=source(); const {game:initialGame,event}=answer(f,'yes'); let game=initialGame;
game=act(game,'emperor',{type:'card',mode:'shipment',card:f.karama,target:'emperor'});
const opportunity=viewGame(game,'choam').kullReaction; assert.ok(opportunity);
game=act(game,'choam',{type:'kullDecision',event:opportunity.event,decline:true});
if (game.pendingTreacheryDiscard) game=passDiscard(game);
game=advance(game,clean);
assert.equal(game.turnActionPromises!.find(row=>row.event===event)?.fulfilled,true);
assert.equal(game.karamaShipping?.card,f.karama);
assert.deepEqual(inventory(game),f.originalCards);
});