74 lines
3.3 KiB
TypeScript
74 lines
3.3 KiB
TypeScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { applyAction, createGame, newPlayer, viewGame, type Action, type Game } from '../game/engine';
|
|
import { botActions } from '../game/bots';
|
|
import { guildTransportQuote } from '../game/transport-quote';
|
|
import { guildShipmentCost } from '../game/shipment-price';
|
|
|
|
function fixture(fremen = false): Game {
|
|
const g = createGame('GUILDTRANSPORTPRICE', newPlayer('p', 'Shipper', fremen ? 'fremen' : 'guild'), false);
|
|
g.players.push(newPlayer('q', 'Ally', fremen ? 'guild' : 'atreides'));
|
|
Object.assign(g, {
|
|
status: 'playing', phase: 5, turn: 2, active: 'p',
|
|
order: ['p', 'q'], movementRemaining: ['p', 'q'], storm: 18,
|
|
});
|
|
const shipper = g.players[0];
|
|
shipper.ally = 'q';
|
|
g.players[1].ally = 'p';
|
|
shipper.hand = [];
|
|
shipper.spice = 3;
|
|
shipper.forces = fremen ? {} : { 'red_chasm:7': 5 };
|
|
shipper.reserves = fremen ? 20 : 15;
|
|
return g;
|
|
}
|
|
|
|
const cross = (territory: string): Action => ({
|
|
type: 'guildShip', from: 'red_chasm:7', amount: 2,
|
|
territory, sector: territory === 'carthag' ? 11 : 0,
|
|
});
|
|
const quote = (g: Game, action: Action) => guildTransportQuote(viewGame(g, 'p'), action);
|
|
const botTransport = (g: Game) => botActions(viewGame(g, 'p')).filter(a => a.type === 'guildShip');
|
|
|
|
void test('a canceled Guild rate doubles cross and return quotes, with purchased Karama restoring the half rate', () => {
|
|
const g = fixture();
|
|
const returned = cross('reserves');
|
|
const desertDestination: Action = {
|
|
type: 'guildShip', from: 'red_chasm:7', amount: 2,
|
|
territory: 'imperial_basin', sector: 10,
|
|
};
|
|
assert.equal(quote(g, returned).quote?.cost, 1);
|
|
assert.equal(quote(g, desertDestination).quote?.cost, 2);
|
|
g.guildRateBlocked = { turn: 2, player: 'p' };
|
|
assert.equal(quote(g, returned).quote?.cost, 2);
|
|
assert.deepEqual(quote(g, returned).unavailableReasons, []);
|
|
assert.equal(quote(g, desertDestination).quote?.cost, 4);
|
|
assert.ok(quote(g, desertDestination).unavailableReasons.some(reason => reason.includes('spice')));
|
|
assert.equal(guildShipmentCost('reserves', 2, false), 2);
|
|
assert.equal(guildShipmentCost('other', 2, false), 4);
|
|
g.karamaShipping = { player: 'p', owner: 'q' };
|
|
assert.equal(quote(g, returned).quote?.cost, 1);
|
|
assert.equal(quote(g, desertDestination).quote?.cost, 2);
|
|
assert.deepEqual(quote(g, desertDestination).unavailableReasons, []);
|
|
});
|
|
|
|
void test('after an unaffordable Guild rate cancellation bots omit discounted-only cross and allied southern transports', () => {
|
|
const guild = fixture();
|
|
guild.players[0].bot = 'Medium';
|
|
const discounted = botTransport(guild)[0];
|
|
assert.ok(discounted);
|
|
assert.equal(applyAction(guild, 'p', discounted).players[0].shipped, true);
|
|
guild.guildRateBlocked = { turn: 2, player: 'p' };
|
|
assert.deepEqual(botTransport(guild), []);
|
|
|
|
const fremen = fixture(true);
|
|
fremen.players[0].bot = 'Medium';
|
|
fremen.players[0].spice = 2;
|
|
const southern = botTransport(fremen).find(a => a.from === 'reserves');
|
|
assert.ok(southern);
|
|
assert.equal(applyAction(fremen, 'p', southern).players[0].shipped, true);
|
|
fremen.guildRateBlocked = { turn: 2, player: 'p' };
|
|
assert.deepEqual(botTransport(fremen), []);
|
|
fremen.karamaShipping = { player: 'p', owner: 'q' };
|
|
assert.ok(botTransport(fremen).some(a => a.from === 'reserves'));
|
|
});
|