Files
Dune/tests/advisor-worm-ride-flip.test.ts
T
Quintine c608d6fea6
Verify and publish checkpoint container / container (push) Canceled after 0s
Handle advisor conversion after worm rides
2026-10-29 09:10:53 +11:00

120 lines
6.6 KiB
TypeScript

import assert from 'node:assert/strict';
import test from 'node:test';
import { viewGame } from '../game/engine';
import { isAdvisor } from '../game/advisors';
import { botActions } from '../game/bots';
import { DIFFICULTIES } from '../game/bot-profiles';
import { createAdvisorWormRideFixture as fixture, actAdvisorWormRide as act,
advisorWormRideInventory as inventory, advisorWormRidePlayer as player,
passAdvisorWormRide as pass } from './fixture-advisor-worm-ride-flip';
import { sandmasterRide, sandmasterWormGame } from './fixture-sandmaster-worm';
for (const canceled of [false,true]) void test(
`native worm ride remains committed when source advisor conversion is ${canceled ? 'prevented' : 'allowed'}`, () => {
const f = fixture(), original = structuredClone(f.game);
let game = act(f.game,f.fremen,f.action);
assert.deepEqual(f.game,original);
assert.equal(game.response?.kind,'advisorFlip');
assert.equal(game.response?.automaticAdvisorFlip?.producer,'wormRide');
assert.equal(player(game,f.fremen).forces[f.source] ?? 0,0);
assert.equal(player(game,f.fremen).forces[f.destination],2);
assert.equal(player(game,f.fremen).forces['hagga_basin:12'],1);
assert.ok(isAdvisor(player(game,f.bg),f.origin));
assert.ok(viewGame(game,f.holder).responseControls?.cancelCards.includes(f.karama));
const committed = structuredClone(player(game,f.fremen));
game = canceled ? act(game,f.holder,{type:'card',card:f.karama,mode:'cancel'}) : pass(game);
assert.deepEqual(player(game,f.fremen),committed);
assert.equal(isAdvisor(player(game,f.bg),f.origin),canceled);
assert.equal(game.pendingAutomaticAdvisorFlips,undefined);
assert.equal(game.decision?.kind,'wormRide');
if (game.decision?.kind === 'wormRide') assert.equal(game.decision.territory,f.second);
assert.deepEqual(game.wormRides,[]);
assert.equal(game.discard.filter(card => card.id === f.karama).length,Number(canceled));
assert.deepEqual(inventory(game),f.originalCards);
},
);
void test('a partial ride leaves Fremen in the source and produces no alone conversion', () => {
const f=fixture({partial:true}), game=act(f.game,f.fremen,f.action);
assert.equal(player(game,f.fremen).forces[f.source],1);
assert.equal(player(game,f.fremen).forces[f.destination],1);
assert.ok(isAdvisor(player(game,f.bg),f.origin));
assert.equal(game.response?.automaticAdvisorFlip,undefined);
assert.deepEqual(inventory(game),f.originalCards);
});
void test('source conversion precedes the real destination intrusion and resumes it once', () => {
const f=fixture({intrusion:true});
let game=act(f.game,f.fremen,f.action);
assert.equal(game.response?.automaticAdvisorFlip?.producer,'wormRide');
assert.equal(game.decision,null);
game=pass(game);
assert.equal(game.decision?.kind,'intrusion');
if (game.decision?.kind === 'intrusion') assert.equal(game.decision.territory,'imperial_basin');
assert.equal(player(game,f.fremen).forces[f.destination],2);
assert.deepEqual(inventory(game),f.originalCards);
});
void test('Fremen cannot nest a special worm during the committed source conversion', () => {
const f=fixture(), source=structuredClone(f.game);
// Transfer the remaining physical Karama from the original deck to Fremen;
// the separately held Karama still keeps the advisor response open.
const index=source.deck.findIndex(card => card.effect === 'karama'); assert.ok(index >= 0);
const [card]=source.deck.splice(index,1); player(source,f.fremen).hand.push(card);
const cards=inventory(source);
const summon={type:'card',mode:'special',card:card.id,territory:'the_great_flat'};
let game=act(source,f.fremen,f.action);
assert.equal(game.response?.automaticAdvisorFlip?.producer,'wormRide');
const before=structuredClone(game);
assert.throws(() => act(game,f.fremen,summon));
assert.deepEqual(game,before);
assert.ok(player(game,f.fremen).hand.some(candidate => candidate.id === card.id));
assert.equal(player(game,f.fremen).specialKaramaUsed ?? false,false);
game=pass(game);
assert.equal(game.decision?.kind,'wormRide');
assert.equal(player(game,f.fremen).forces[f.destination],2);
game=act(game,f.fremen,summon);
assert.equal(player(game,f.fremen).specialKaramaUsed,true);
assert.equal(game.discard.filter(candidate => candidate.id === card.id).length,1);
assert.deepEqual(inventory(game),cards);
});
void test('all four bot profiles can resolve the original worm-ride conversion legally', () => {
const f=fixture(), pending=act(f.game,f.fremen,f.action);
for (const difficulty of DIFFICULTIES) {
const view=viewGame(pending,f.holder);
view.players.find(seat => seat.id === f.holder)!.bot=difficulty;
const action=botActions(view)[0]; assert.ok(action);
const game=pass(act(structuredClone(pending),f.holder,action));
assert.equal(game.decision?.kind,'wormRide');
if (game.decision?.kind === 'wormRide') assert.equal(game.decision.territory,f.second);
assert.equal(player(game,f.fremen).forces[f.destination],2);
assert.deepEqual(inventory(game),f.originalCards);
}
});
for (const canceled of [false,true]) void test(
`Sandmaster's native worm-ride collection occurs once when origin conversion is ${canceled ? 'prevented' : 'allowed'}`, () => {
// This fixture uses genuine Leader Skills setup and a staged, conserved
// ride. Move the actual BG force from the destination to the ride origin,
// and transfer one physical Karama to Fremen for the response.
const source=sandmasterWormGame(true,true), bg=player(source,'h'), rider=player(source,'p');
bg.forces={'wind_pass:14':1}; bg.reserves=19; bg.advisors={wind_pass:{}};
const index=source.deck.findIndex(card => card.effect === 'karama'); assert.ok(index >= 0);
const [card]=source.deck.splice(index,1); rider.hand.push(card);
const cards=inventory(source), beforeSpice=rider.spice, beforePile=source.spice['red_chasm:7'];
let game=act(source,'p',sandmasterRide());
assert.equal(game.response?.automaticAdvisorFlip?.producer,'wormRide');
assert.equal(player(game,'p').spice,beforeSpice+1);
assert.equal(game.spice['red_chasm:7'],beforePile-1);
assert.equal(game.log.filter(entry => entry.automatic?.name === 'Sandmaster collection').length,1);
game=canceled ? act(game,'p',{type:'card',card:card.id,mode:'cancel'}) : pass(game);
assert.equal(player(game,'p').spice,beforeSpice+1);
assert.equal(game.spice['red_chasm:7'],beforePile-1);
assert.equal(player(game,'p').forces['red_chasm:7'],4);
assert.equal(isAdvisor(player(game,'h'),'wind_pass'),canceled);
assert.equal(game.log.filter(entry => entry.automatic?.name === 'Sandmaster collection').length,1);
assert.deepEqual(inventory(game),cards);
},
);