138 lines
7.7 KiB
TypeScript
138 lines
7.7 KiB
TypeScript
import assert from 'node:assert/strict';
|
|
import test from 'node:test';
|
|
import { applyAction, viewGame, type Action, type Game } from '../game/engine';
|
|
import { botActions } from '../game/bots';
|
|
import { DIFFICULTIES } from '../game/bot-profiles';
|
|
import {
|
|
betrayalMentatResources, betrayalMentatSeat, createBetrayalMentatQuestionFixture,
|
|
prepareBetrayalMentatPlans, reloadBetrayalMentat,
|
|
} from './fixture-betrayal-mentat-question';
|
|
|
|
function rejectBetrayalMentat(game: Game, actor: string, action: Action) {
|
|
const before = structuredClone(game);
|
|
assert.throws(() => applyAction(game, actor, action));
|
|
assert.deepEqual(game, before);
|
|
}
|
|
|
|
void test('native Betrayal Mentat exact weapon shows privately before original posture and accepts card-free native plans', () => {
|
|
const fixture = createBetrayalMentatQuestionFixture();
|
|
const { owner, target, observer, leader } = fixture;
|
|
let game = reloadBetrayalMentat(fixture.battle);
|
|
assert.equal(game.decision?.kind, 'mentatQuestion');
|
|
assert.equal(game.decision?.player, owner);
|
|
assert.equal(game.battle!.preparation, undefined);
|
|
assert.equal(game.battle!.leaderSkillHidden, undefined);
|
|
assert.equal(viewGame(game, owner).mentat.pending!.stage, 'name');
|
|
const event = game.battle!.mentatQuestionEvent!;
|
|
const resources = betrayalMentatResources(game);
|
|
assert.deepEqual(resources, betrayalMentatResources(fixture.paid));
|
|
assert.ok(fixture.shipmentGateEvents.length);
|
|
const ask: Action = { type: 'decision', event, weapon: 'Crysknife' };
|
|
rejectBetrayalMentat(game, target, ask);
|
|
rejectBetrayalMentat(game, owner, { ...ask, event: 'stale' });
|
|
game = applyAction(game, owner, ask);
|
|
assert.equal(game.decision?.kind, 'mentatQuestion');
|
|
assert.equal(game.decision?.player, target);
|
|
assert.equal(game.battle!.preparation, undefined);
|
|
const held = betrayalMentatSeat(game, target).hand;
|
|
const weapon = held.find(card => card.name === 'Crysknife')!;
|
|
const other = held.find(card => card.name === 'Snooper')!;
|
|
assert.deepEqual(viewGame(game, target).mentat.pending!.cards,
|
|
held.filter(card => card.name === 'Crysknife'));
|
|
for (const id of [owner, observer, 'shipper'])
|
|
assert.deepEqual(viewGame(game, id).mentat.pending!.cards, []);
|
|
const show: Action = { type: 'decision', event, card: weapon.id };
|
|
rejectBetrayalMentat(game, owner, show);
|
|
rejectBetrayalMentat(game, target, { ...show, event: 'stale' });
|
|
rejectBetrayalMentat(game, target, { ...show, card: other.id });
|
|
game = applyAction(reloadBetrayalMentat(game), target, show);
|
|
assert.equal(game.decision?.kind, 'leaderSkillVisibility');
|
|
assert.equal(game.battle!.preparation, undefined);
|
|
assert.deepEqual(betrayalMentatResources(game), resources);
|
|
for (const id of [owner, target])
|
|
assert.equal(viewGame(game, id).mentat.history[0].card.id, weapon.id);
|
|
for (const id of [observer, 'shipper']) {
|
|
assert.deepEqual(viewGame(game, id).mentat.history, []);
|
|
assert.ok(!JSON.stringify(viewGame(game, id)).includes(weapon.id));
|
|
}
|
|
assert.ok(!JSON.stringify(game.log).includes(weapon.id));
|
|
rejectBetrayalMentat(game, target, show);
|
|
game = prepareBetrayalMentatPlans(game);
|
|
assert.equal(game.decision, null);
|
|
assert.equal(game.battle!.preparation, undefined);
|
|
assert.equal(game.battle!.leaderSkillHidden![owner], true);
|
|
assert.deepEqual(betrayalMentatResources(game), resources);
|
|
const targetSkill = game.leaderSkills!.assignments.find(a => a.owner === target)!.leader;
|
|
const targetLeader = betrayalMentatSeat(game, target).leaders.find(l =>
|
|
!l.dead && !l.capturedBy && l.id !== targetSkill)!;
|
|
game = applyAction(game, owner, { type: 'battlePlan', dial: 0, leader });
|
|
game = applyAction(game, target, { type: 'battlePlan', dial: 0, leader: targetLeader.id });
|
|
assert.equal(game.battle!.plans[owner].leader, leader);
|
|
assert.equal(game.battle!.plans[target].leader, targetLeader.id);
|
|
assert.equal(game.battle!.plans[target].weapon, null);
|
|
assert.equal(game.battle!.plans[target].defense, null);
|
|
assert.ok(betrayalMentatSeat(game, target).hand.some(card => card.id === weapon.id));
|
|
assert.deepEqual(betrayalMentatResources(game), resources);
|
|
});
|
|
|
|
void test('native Betrayal absent weapon offers only actual held alternatives and retains the freely shown defense', () => {
|
|
const fixture = createBetrayalMentatQuestionFixture();
|
|
const { owner, target, observer } = fixture;
|
|
const before = betrayalMentatResources(fixture.battle);
|
|
let game = applyAction(reloadBetrayalMentat(fixture.battle), owner,
|
|
{ type: 'decision', event: fixture.battle.battle!.mentatQuestionEvent, weapon: 'Lasgun' });
|
|
assert.equal(game.decision?.kind, 'mentatQuestion');
|
|
assert.equal(game.decision?.player, target);
|
|
const pending = viewGame(game, target).mentat.pending!;
|
|
assert.deepEqual(pending.cards, betrayalMentatSeat(game, target).hand);
|
|
const shown = pending.cards.find(card => card.name === 'Snooper')!;
|
|
rejectBetrayalMentat(game, observer, { type: 'decision', event: pending.event, card: shown.id });
|
|
rejectBetrayalMentat(game, target, { type: 'decision', event: pending.event, decline: true });
|
|
rejectBetrayalMentat(game, target, { type: 'decision', event: pending.event, card: 'not-held' });
|
|
game = applyAction(reloadBetrayalMentat(game), target,
|
|
{ type: 'decision', event: pending.event, card: shown.id });
|
|
assert.equal(viewGame(game, owner).mentat.history[0].card.id, shown.id);
|
|
assert.deepEqual(viewGame(game, observer).mentat.history, []);
|
|
assert.deepEqual(betrayalMentatResources(game), before);
|
|
game = prepareBetrayalMentatPlans(game);
|
|
const targetSkill = game.leaderSkills!.assignments.find(a => a.owner === target)!.leader;
|
|
const targetLeader = betrayalMentatSeat(game, target).leaders.find(l =>
|
|
!l.dead && !l.capturedBy && l.id !== targetSkill)!;
|
|
game = applyAction(game, owner, { type: 'battlePlan', dial: 0, leader: fixture.leader });
|
|
game = applyAction(game, target, { type: 'battlePlan', dial: 0, leader: targetLeader.id });
|
|
assert.equal(game.battle!.plans[target].defense, null);
|
|
assert.ok(betrayalMentatSeat(game, target).hand.some(card => card.id === shown.id));
|
|
assert.deepEqual(betrayalMentatResources(game), before);
|
|
});
|
|
|
|
void test('all four legal policies apply the composed private question and response through the original next battle preparation', () => {
|
|
const fixture = createBetrayalMentatQuestionFixture();
|
|
for (const difficulty of DIFFICULTIES) {
|
|
let game = reloadBetrayalMentat(fixture.battle);
|
|
const resources = betrayalMentatResources(game);
|
|
const own = viewGame(game, fixture.owner);
|
|
own.players.find(p => p.id === fixture.owner)!.bot = difficulty;
|
|
assert.deepEqual(botActions(viewGame(game, fixture.target)), []);
|
|
assert.deepEqual(botActions(viewGame(game, fixture.observer)), []);
|
|
const ask = botActions(own)[0];
|
|
assert.ok(own.mentat.pending!.weapons.includes(ask.weapon as string));
|
|
game = applyAction(game, fixture.owner, ask);
|
|
const target = viewGame(game, fixture.target);
|
|
target.players.find(p => p.id === fixture.target)!.bot = difficulty;
|
|
const show = botActions(target)[0];
|
|
assert.ok(target.mentat.pending!.cards.some(card => card.id === show.card));
|
|
game = applyAction(game, fixture.target, show);
|
|
assert.equal(viewGame(game, fixture.owner).mentat.history.length, 1);
|
|
assert.deepEqual(viewGame(game, fixture.observer).mentat.history, []);
|
|
assert.equal(game.decision?.kind, 'leaderSkillVisibility');
|
|
const actor = game.decision!.player;
|
|
const posture = viewGame(game, actor);
|
|
posture.players.find(p => p.id === actor)!.bot = difficulty;
|
|
game = applyAction(game, actor, botActions(posture)[0]);
|
|
game = prepareBetrayalMentatPlans(game);
|
|
assert.equal(game.decision, null);
|
|
assert.equal(game.battle!.preparation, undefined);
|
|
assert.deepEqual(betrayalMentatResources(game), resources);
|
|
}
|
|
});
|