69 lines
3.8 KiB
TypeScript
69 lines
3.8 KiB
TypeScript
import assert from 'node:assert/strict';
|
|
import test from 'node:test';
|
|
import { viewGame } from '../game/engine';
|
|
import { act, advance, clean, createKullBribeFixture, inventory, player } from './fixture-kull-bribe';
|
|
|
|
void test('a Basic Kull phase ban cannot fund a later shipment with the blocked auction Karama', () => {
|
|
// The shared fixture selects the original unopened deal and Spice position;
|
|
// it uses public Basic opt-in and real setup. No wallet or force is staged.
|
|
const f = createKullBribeFixture();
|
|
let game = structuredClone(f.charity);
|
|
game = advance(game, g => g.phase === 3 && clean(g) && g.auction?.active === f.actor);
|
|
assert.equal(player(game, f.actor).spice, 10);
|
|
const lot = game.auction!.cards[game.auction!.index].id;
|
|
|
|
// The holder actually attempts an ordinary free purchase. CHOAM interrupts
|
|
// with its original Kull; everyone with a counter opportunity passes.
|
|
game = act(game, f.actor, { type: 'card', mode: 'purchase', card: f.karama });
|
|
const reaction = viewGame(game, f.reactor).kullReaction;
|
|
assert.ok(reaction && reaction.plays.some(play => play.card.id === f.kull && !play.blocked));
|
|
game = act(game, f.reactor, { type: 'kullDecision', event: reaction.event, source: 'printed', card: f.kull });
|
|
game = advance(game, clean);
|
|
assert.equal(game.phase, 3);
|
|
assert.ok(game.kullRestrictions?.some(row => row.player === f.actor && row.turn === game.turn && row.phase === 3));
|
|
assert.ok(player(game, f.actor).hand.some(card => card.id === f.karama));
|
|
assert.ok(viewGame(game, f.actor).karamaBlocked);
|
|
const blockedPurchase = structuredClone(game);
|
|
assert.throws(() => act(game, f.actor, { type: 'card', mode: 'purchase', card: f.karama }), /Kull Wahad/);
|
|
assert.deepEqual(game, blockedPurchase);
|
|
|
|
// Seven of ten spice is an actually funded bid; the real invoice leaves
|
|
// three. Eight to Carthag costs eight normally or four at the Karama rate.
|
|
// The one retained Karama cannot both evade this Bidding invoice (Kull) and
|
|
// legitimately fund that shipment after cash payment.
|
|
assert.equal(game.auction?.active, f.actor);
|
|
game = act(game, f.actor, { type: 'bid', amount: 7, allyPayment: 0 });
|
|
assert.equal(game.auction?.bidder, f.actor);
|
|
assert.equal(game.auction?.bid, 7);
|
|
assert.equal(player(game, f.actor).spice, 10);
|
|
|
|
game = act(game, f.asker, { type: 'card', card: f.truth });
|
|
while (game.truthtrance?.stage === 'priority') {
|
|
const passer = game.players.find(p => !game.truthtrance!.passed.includes(p.id));
|
|
assert.ok(passer);
|
|
game = act(game, passer.id, { type: 'truthPass' });
|
|
}
|
|
assert.equal(game.truthtrance?.stage, 'ask');
|
|
game = act(game, f.asker, { type: 'truthAsk', question: {
|
|
kind: 'shipment', target: f.actor, territory: 'carthag', minimum: 8,
|
|
} });
|
|
const respondent = viewGame(game, f.actor);
|
|
assert.deepEqual(respondent.truthShipmentAnswers, ['no']);
|
|
assert.ok(respondent.shipmentPromisePolicy == null);
|
|
assert.equal(respondent.shipmentPreparation, null);
|
|
const beforeRejected = structuredClone(game);
|
|
assert.throws(() => act(game, f.actor, { type: 'truthAnswer', answer: 'yes' }));
|
|
assert.deepEqual(game, beforeRejected);
|
|
// The other seat's private counter remains absent from an observer's view.
|
|
assert.ok(!JSON.stringify(viewGame(game, f.asker)).includes(f.counter));
|
|
game = act(game, f.actor, { type: 'truthAnswer', answer: 'no' });
|
|
|
|
// Complete the actual auction; the cash invoice settles automatically when
|
|
// the blocked Karama leaves no legal alternative payment choice.
|
|
game = advance(game, g => player(g, f.actor).hand.some(card => card.id === lot));
|
|
assert.equal(player(game, f.actor).spice, 3);
|
|
assert.ok(player(game, f.actor).hand.some(card => card.id === lot));
|
|
assert.ok(player(game, f.actor).hand.some(card => card.id === f.karama));
|
|
assert.deepEqual(inventory(game), f.originalCards);
|
|
});
|