Files
Dune/tests/public-deals-cross-turn-bot.test.ts
T

93 lines
5.1 KiB
TypeScript

import assert from 'node:assert/strict';
import test from 'node:test';
import { botActions } from '../game/bots';
import { viewGame, type Game } from '../game/engine';
import {
actMentatBribe as act, createMentatBribeFixture as fixture,
finishMentatBribe as finish, mentatBribeInventory as inventory, mentatBribePlayer as player,
nextMentatBribeOpening as nextMentat,
} from './fixture-mentat-bribes';
function offer(f: ReturnType<typeof fixture>, game: Game, amount: number, dueTurn: number) {
game = act(game, f.asker, { type: 'dealOffer', target: f.target, terms: 'Pay the agreed spice.',
payment: { kind: 'deferredBribe', payer: f.target, recipient: f.asker, amount, dueTurn } });
const deal = game.deals!.at(-1)!.id;
game = act(game, f.target, { type: 'dealRespond', deal, accept: true });
return { game, deal };
}
void test('human autopilot waits for the actual due turn before paying a future deal', () => {
const f = fixture();
const accepted = offer(f, f.game, 3, f.game.turn + 1);
let game = act(accepted.game, f.target, { type: 'setAutopilot', difficulty: 'Easy' });
assert.equal(player(game, f.target).autopilot, 'Easy');
assert.equal(viewGame(game, f.target).deferredDealPaymentOptions[accepted.deal]?.blocked, null);
assert.ok(!botActions(viewGame(game, f.target)).some(action => action.type === 'dealPay'));
assert.equal(player(game, f.target).spice, 10);
assert.equal(game.deals!.find(row => row.id === accepted.deal)!.paymentStatus, 'pending');
assert.deepEqual(inventory(game), f.originalCards);
game = finish(game);
assert.equal(game.turn, f.game.turn + 1);
assert.equal(game.phase, 0);
assert.equal(player(game, f.target).spice, 10);
const action = botActions(viewGame(game, f.target))[0];
assert.deepEqual(action, { type: 'dealPay', deal: accepted.deal });
game = act(game, f.target, action);
assert.equal(game.deals!.find(row => row.id === accepted.deal)!.paymentStatus, 'paid');
assert.equal(player(game, f.target).spice, 7);
assert.equal(player(game, f.asker).bribes, 3);
assert.deepEqual(inventory(game), f.originalCards);
});
void test('an insolvent Mentat payer cannot sacrifice an individually payable current deal for a future one', () => {
const f = fixture();
const first = offer(f, f.game, 6, f.game.turn + 1);
const second = offer(f, first.game, 6, f.game.turn + 1);
const future = offer(f, second.game, 2, f.game.turn + 2);
const smallerFuture = offer(f, future.game, 1, f.game.turn + 2);
let game = act(smallerFuture.game, f.target, { type: 'bribe', target: f.asker, amount: 3 });
game = nextMentat(finish(game));
assert.equal(game.turn, f.game.turn + 1);
assert.equal(game.phase, 8);
assert.equal(player(game, f.target).spice, 7);
for (const deal of [first.deal, second.deal])
assert.equal(game.deals!.find(row => row.id === deal)!.paymentStatus, 'pending');
const before = structuredClone(game);
assert.ok(viewGame(game, f.target).deferredDealPaymentOptions[future.deal]?.blocked);
assert.throws(() => act(game, f.target, { type: 'dealPay', deal: future.deal }));
assert.deepEqual(game, before);
assert.equal(viewGame(game, f.target).deferredDealPaymentOptions[smallerFuture.deal]?.blocked, null);
const sparePaid = act(game, f.target, { type: 'dealPay', deal: smallerFuture.deal });
assert.equal(player(sparePaid, f.target).spice, 6);
assert.equal(sparePaid.deals!.find(row => row.id === smallerFuture.deal)!.paymentStatus, 'paid');
for (const deal of [first.deal, second.deal])
assert.equal(sparePaid.deals!.find(row => row.id === deal)!.paymentStatus, 'pending');
const paid = act(game, f.target, { type: 'dealPay', deal: first.deal });
assert.equal(player(paid, f.target).spice, 1);
assert.equal(paid.deals!.find(row => row.id === first.deal)!.paymentStatus, 'paid');
assert.equal(paid.deals!.find(row => row.id === second.deal)!.paymentStatus, 'void');
assert.equal(paid.deals!.find(row => row.id === future.deal)!.paymentStatus, 'pending');
assert.deepEqual(inventory(paid), f.originalCards);
});
void test('early future payment cannot spend the funds needed for a currently due deal', () => {
const f = fixture();
const due = offer(f, f.game, 8, f.game.turn);
const future = offer(f, due.game, 3, f.game.turn + 1);
const before = structuredClone(future.game);
assert.ok(viewGame(before, f.target).deferredDealPaymentOptions[future.deal]?.blocked);
assert.throws(() => act(future.game, f.target, { type: 'dealPay', deal: future.deal }));
assert.deepEqual(future.game, before);
assert.equal(player(future.game, f.target).spice, 10);
assert.equal(future.game.deals!.find(row => row.id === due.deal)!.paymentStatus, 'pending');
assert.equal(future.game.deals!.find(row => row.id === future.deal)!.paymentStatus, 'pending');
const paid = act(future.game, f.target, { type: 'dealPay', deal: due.deal });
assert.equal(player(paid, f.target).spice, 2);
assert.equal(player(paid, f.asker).bribes, 8);
assert.equal(paid.deals!.find(row => row.id === due.deal)!.paymentStatus, 'paid');
assert.equal(paid.deals!.find(row => row.id === future.deal)!.paymentStatus, 'pending');
assert.deepEqual(inventory(paid), f.originalCards);
});