90 lines
5.6 KiB
TypeScript
90 lines
5.6 KiB
TypeScript
import assert from 'node:assert/strict';
|
|
import test from 'node:test';
|
|
import { viewGame } from '../game/engine';
|
|
import { botActions, runBots } from '../game/bots';
|
|
import { DIFFICULTIES } from '../game/bot-profiles';
|
|
import { createExtortionBotPriority, extortionAct, extortionEvent, extortionPayment, extortionSeat,
|
|
finishExtortionGiftResponse } from './emperor-extortion-bot-priority-fixture';
|
|
|
|
for (const advanced of [false, true]) for (const autopilot of [false, true]) void test(
|
|
`${advanced ? 'Advanced' : 'Basic'} human Emperor gets Extortion gift priority before ${autopilot ? 'autopilot' : 'bot'} ally`, () => {
|
|
const {game: initial} = createExtortionBotPriority(advanced, {autopilot});
|
|
let game = initial;
|
|
const event = extortionEvent(game);
|
|
assert.deepEqual(viewGame(game, 'e').emperorExtortionPriority, {event, recipient:'a', canContinue:true});
|
|
for (const id of ['a','h','m'])
|
|
assert.deepEqual(viewGame(game, id).emperorExtortionPriority, {event, recipient:'a', canContinue:false});
|
|
const e = extortionSeat(game, 'e').spice, a = extortionSeat(game, 'a').spice;
|
|
for (const difficulty of DIFFICULTIES) {
|
|
const view = viewGame(game, 'a');
|
|
const actor = view.players.find(player => player.id === 'a')!;
|
|
if (autopilot) actor.autopilot = difficulty; else actor.bot = difficulty;
|
|
assert.ok(!botActions(view).some(action => action.type === 'decision' && action.event === event), difficulty);
|
|
}
|
|
const waiting = runBots(game, 5);
|
|
assert.equal(waiting.decision?.kind, 'moritaniExtortion');
|
|
assert.equal(extortionEvent(waiting), event);
|
|
assert.equal(extortionSeat(waiting, 'e').spice, e);
|
|
assert.equal(extortionSeat(waiting, 'a').spice, a);
|
|
const original = structuredClone(game);
|
|
assert.throws(() => extortionAct(game, 'a', extortionPayment(game, false)));
|
|
assert.throws(() => extortionAct(game, 'a', {type:'emperorExtortionContinue', event}));
|
|
assert.throws(() => extortionAct(game, 'e', {type:'emperorExtortionContinue', event:'stale'}));
|
|
assert.deepEqual(game, original);
|
|
game = extortionAct(game, 'e', {type:'emperorExtortionContinue', event});
|
|
assert.ok(!viewGame(game, 'e').emperorExtortionPriority);
|
|
assert.deepEqual(game.emperorExtortionGiftRelease, {event, emperor:'e', recipient:'a'});
|
|
const action = botActions(viewGame(game, 'a')).find(row => row.type === 'decision' && row.event === event);
|
|
assert.ok(action); assert.equal(action.pay, false);
|
|
game = runBots(game, 1);
|
|
assert.ok(game.decision?.kind !== 'moritaniExtortion' || game.decision.event !== event);
|
|
});
|
|
|
|
for (const advanced of [false, true]) for (const canceled of [false, true]) void test(
|
|
`${advanced ? 'Advanced' : 'Basic'} ${canceled ? 'canceled' : 'successful'} Emperor gift preserves priority until Continue`, () => {
|
|
const fixture = createExtortionBotPriority(advanced);
|
|
let game = fixture.game;
|
|
const event = extortionEvent(game), emperor = extortionSeat(game, 'e').spice;
|
|
game = extortionAct(game, 'e', {type:'emperorGift', amount:1});
|
|
assert.equal(game.response?.kind, 'emperorGift');
|
|
game = canceled ? extortionAct(game, 'h', {type:'card', card:fixture.karama, mode:'cancel'}) :
|
|
finishExtortionGiftResponse(game);
|
|
assert.equal(game.response, null);
|
|
assert.equal(game.decision?.kind, 'moritaniExtortion');
|
|
assert.equal(extortionEvent(game), event);
|
|
assert.deepEqual(viewGame(game, 'e').emperorExtortionPriority, {event, recipient:'a', canContinue:true});
|
|
assert.equal(extortionSeat(game, 'a').spice, canceled ? 2 : 3);
|
|
assert.equal(extortionSeat(game, 'e').spice, emperor - (canceled ? 0 : 1));
|
|
const before = runBots(game, 5);
|
|
assert.equal(extortionEvent(before), event);
|
|
game = extortionAct(game, 'e', {type:'emperorExtortionContinue', event});
|
|
const action = botActions(viewGame(game, 'a')).find(row => row.type === 'decision' && row.event === event);
|
|
// The current minimal bot policy declines even when it can afford to pay.
|
|
assert.ok(action); assert.equal(action.pay, false);
|
|
game = runBots(game, 1);
|
|
assert.ok(game.decision?.kind !== 'moritaniExtortion' || game.decision.event !== event);
|
|
assert.equal(game.moritaniTerror!.tokens.find(row => row.id === fixture.token)?.status, 'extortion');
|
|
});
|
|
|
|
for (const advanced of [false, true]) void test(
|
|
`${advanced ? 'Advanced' : 'Basic'} no-spice Emperor still has neutral Continue priority`, () => {
|
|
const {game} = createExtortionBotPriority(advanced, {emperorSpice:0});
|
|
const event = extortionEvent(game);
|
|
assert.deepEqual(viewGame(game, 'e').emperorExtortionPriority, {event, recipient:'a', canContinue:true});
|
|
assert.deepEqual(viewGame(game, 'a').emperorExtortionPriority, {event, recipient:'a', canContinue:false});
|
|
assert.ok(!botActions(viewGame(game, 'a')).some(action => action.type === 'decision'));
|
|
const continued = extortionAct(game, 'e', {type:'emperorExtortionContinue', event});
|
|
assert.ok(botActions(viewGame(continued, 'a')).some(action => action.type === 'decision' && action.pay === false));
|
|
});
|
|
|
|
for (const advanced of [false, true]) void test(
|
|
`${advanced ? 'Advanced' : 'Basic'} unaffiliated bot pays or declines without Emperor priority`, () => {
|
|
const {game} = createExtortionBotPriority(advanced, {unallied:true});
|
|
const event = extortionEvent(game);
|
|
assert.ok(!viewGame(game, 'e').emperorExtortionPriority);
|
|
const action = botActions(viewGame(game, 'a')).find(row => row.type === 'decision' && row.event === event);
|
|
assert.ok(action); assert.equal(action.pay, false);
|
|
const next = runBots(game, 1);
|
|
assert.ok(next.decision?.kind !== 'moritaniExtortion' || next.decision.event !== event);
|
|
});
|