85 lines
4.8 KiB
TypeScript
85 lines
4.8 KiB
TypeScript
import assert from 'node:assert/strict';
|
|
import test from 'node:test';
|
|
import { applyAction, viewGame, type Game } from '../game/engine';
|
|
import {
|
|
allowE3Responses, chooseBureaucratE3Payment, createBureaucratE3AuctionFixture,
|
|
payBureaucratE3Auction,
|
|
} from './fixture-bureaucrat-e3-funding';
|
|
import { createSpiceBankerIncomeFixture } from './fixture-spice-banker-income';
|
|
import { freshBiddingGame, biddingPlayer } from './fixture-homeworld-occupied-bidding';
|
|
import { qualifyDefensePosition } from './fixture-homeworld-occupied-defenses';
|
|
import { advanceOriginal } from './fixture-homeworld-occupied-producers';
|
|
|
|
const seat = (game: Game, id: string) => {
|
|
const player = game.players.find(candidate => candidate.id === id);
|
|
assert.ok(player);
|
|
return player;
|
|
};
|
|
|
|
void test('Advanced Emperor ally bank share does not inflate a Bureaucrat diversion from the buyer-funded income', () => {
|
|
// Real E3 setup, original Bureaucrat assignment and turn-two alliance;
|
|
// the fixture uses the original wallets and physical card.
|
|
const f = createBureaucratE3AuctionFixture({ advanced: true, donor: 'emperor', payerShare: 5, donorShare: 3 });
|
|
const paid = payBureaucratE3Auction(f);
|
|
assert.deepEqual(paid.currentAuctionSale?.paymentRouting, [
|
|
{ payer: f.payer, amount: 5, recipient: 'emperor' },
|
|
{ payer: f.donor, amount: 3, recipient: 'bank' },
|
|
]);
|
|
const pending = allowE3Responses(paid);
|
|
assert.equal(pending.decision?.kind, 'bureaucratPayment');
|
|
assert.equal(pending.decision?.player, f.trainer);
|
|
const done = allowE3Responses(chooseBureaucratE3Payment(pending, true));
|
|
assert.equal(seat(done, f.payer).spice, seat(f.beforePledge, f.payer).spice - 5);
|
|
assert.equal(seat(done, f.donor).spice, seat(f.beforePledge, f.donor).spice - 3 + 3);
|
|
assert.equal(done.bureaucratPayments?.used.length, 1);
|
|
assert.ok(seat(done, f.payer).hand.some(card => card.id === f.card));
|
|
});
|
|
|
|
void test('Advanced Emperor ally bank share creates a Spice Banker grant from the actual bank leg', () => {
|
|
// Real Skills setup and native bidding; the existing fixture stages the
|
|
// reciprocal alliance and its original aid before the physical purchase.
|
|
const f = createSpiceBankerIncomeFixture({ advanced: true, kind: 'auction',
|
|
emperor: true, amount: 6, allySplit: 4 });
|
|
const emperor = f.game.players.find(player => player.faction === 'emperor')!;
|
|
let game = applyAction(f.beforePayment, f.actor, f.paymentAction);
|
|
game = allowE3Responses(game);
|
|
const sources = game.spiceBankerIncome!.sources.slice(f.beforePayment.spiceBankerIncome!.sources.length);
|
|
assert.equal(sources.length, 1);
|
|
assert.deepEqual(sources[0].source.bankLegs, [{ payer: emperor.id, amount: 4 }]);
|
|
assert.equal(sources[0].grant?.owner, f.owner);
|
|
assert.equal(seat(game, emperor.id).spice, seat(f.beforePayment, emperor.id).spice + 2);
|
|
assert.equal(seat(game, f.payer).spice, seat(f.beforePayment, f.payer).spice - 2);
|
|
});
|
|
|
|
void test('occupied Kaitain allocates only the buyer-funded Emperor income from an Advanced split bid', () => {
|
|
let game = freshBiddingGame('emperor', 'harkonnen');
|
|
// Existing physical occupation fixture stages Kaitain's qualifying position;
|
|
// this test also stages the pair and its wallets, then uses native actions.
|
|
qualifyDefensePosition(game, 'homeworld:emperor');
|
|
biddingPlayer(game, 'ally').ally = 'native';
|
|
biddingPlayer(game, 'native').ally = 'ally';
|
|
game = advanceOriginal(game, state => state.phase === 3 && !!state.auction && !state.response && !state.decision);
|
|
for (let n = 0; game.auction?.active !== 'ally' && n < game.players.length; n++)
|
|
game = applyAction(game, game.auction!.active, { type: 'passBid' });
|
|
assert.equal(game.auction?.active, 'ally');
|
|
biddingPlayer(game, 'ally').spice = 4;
|
|
biddingPlayer(game, 'native').spice = 10;
|
|
const occupierBefore = biddingPlayer(game, 'occupier').spice;
|
|
game = applyAction(game, 'native', { type: 'pledgeAid', amount: 2 });
|
|
game = applyAction(game, 'ally', { type: 'bid', amount: 6, allyPayment: 2 });
|
|
for (let n = 0; !game.currentAuctionSale && n < game.players.length + 2; n++) {
|
|
if (game.decision?.kind === 'auctionPayment')
|
|
game = applyAction(game, 'ally', { type: 'decision', karama: false });
|
|
else game = applyAction(game, game.auction!.active, { type: 'passBid' });
|
|
}
|
|
game = advanceOriginal(game, state => state.decision?.kind === 'homeworldOccupiedPercentage');
|
|
const offer = viewGame(game, 'occupier').homeworldOccupiedPercentage!;
|
|
assert.equal(offer.amount, 2);
|
|
const receipt = game.homeworldOccupiedPercentageLedger!.receipts.at(-1)!;
|
|
assert.equal(receipt.source.amount, 4);
|
|
game = applyAction(game, 'occupier', { type: 'decision', event: offer.event, ownAmount: 2 });
|
|
assert.equal(seat(game, 'native').spice, 10);
|
|
assert.equal(seat(game, 'ally').spice, 0);
|
|
assert.equal(seat(game, 'occupier').spice, occupierBefore + 2);
|
|
});
|