Files
Dune/tests/ghola-leader-truth-runtime.test.ts
T
Quintine c1ffa1c28a
Verify and publish checkpoint container / container (push) Successful in 10m45s
Prototype named Ghola leader Truthtrance promises
2026-10-29 17:34:55 +11:00

121 lines
6.1 KiB
TypeScript

import assert from 'node:assert/strict';
import test from 'node:test';
import { viewGame, type Action, type Game } from '../game/engine';
import { botActions } from '../game/bots';
import { DIFFICULTIES } from '../game/bot-profiles';
import {
actTruthLeaderRevival as act, answerTruthLeaderRevival as answer,
createTruthLeaderRevivalFixture as fixture, declareTruthLeaderRevival as declare,
settleTruthLeaderRevival as settle, truthLeaderRevivalInventory as inventory,
truthLeaderRevivalPlayer as player,
} from './fixture-truth-leader-revival';
function rejected(game: Game, actor: string, action: Action) {
const before = structuredClone(game);
assert.throws(() => act(game, actor, action));
assert.deepEqual(game, before);
}
function askGhola(f: ReturnType<typeof fixture>) {
const declared = declare(f);
const game = act(declared.game, f.asker, { type: 'truthAsk', question: {
kind: 'gholaLeader', target: f.target, turn: declared.game.turn, leader: f.leader,
} });
return game;
}
for (const advanced of [false, true]) {
void test(`${advanced ? 'Advanced' : 'Basic'} named Ghola Yes is fulfilled by an actual Ghola leader return`, () => {
const f = fixture({ advanced, ghola: true });
assert.ok(f.ghola);
const asked = askGhola(f);
assert.ok(viewGame(asked, f.target).truthGholaLeaderAnswers?.includes('yes'));
let game = answer(asked, f.target, 'yes');
const promise = game.gholaLeaderPromises!.find(row => row.player === f.target)!;
assert.equal(promise.leader, f.leader);
assert.ok(!promise.fulfilled);
// Spending the only dealt Ghola on a force return cannot satisfy the named leader.
rejected(game, f.target, { type: 'card', card: f.ghola, amount: 1, elite: 0 });
game = settle(act(game, f.target, { type: 'card', card: f.ghola, leader: f.leader }));
assert.equal(player(game, f.target).leaders.find(leader => leader.id === f.leader)!.dead, false);
assert.equal(player(game, f.target).leaderRevived, false);
assert.equal(game.gholaLeaderPromises!.find(row => row.event === promise.event)!.fulfilled, true);
assert.equal(game.discard.filter(card => card.id === f.ghola).length, 1);
assert.deepEqual(inventory(game), f.originalCards);
});
}
void test('named Ghola No rejects the matching leader return but allows its independent force return', () => {
const f = fixture({ ghola: true });
assert.ok(f.ghola);
const asked = askGhola(f);
assert.ok(viewGame(asked, f.target).truthGholaLeaderAnswers?.includes('no'));
let game = answer(asked, f.target, 'no');
const promise = game.gholaLeaderPromises!.find(row => row.player === f.target)!;
rejected(game, f.target, { type: 'card', card: f.ghola, leader: f.leader });
game = settle(act(game, f.target, { type: 'card', card: f.ghola, amount: 1, elite: 0 }));
assert.equal(player(game, f.target).leaders.find(leader => leader.id === f.leader)!.dead, true);
assert.ok(!game.gholaLeaderPromises!.find(row => row.event === promise.event)!.fulfilled);
assert.equal(game.discard.filter(card => card.id === f.ghola).length, 1);
assert.deepEqual(inventory(game), f.originalCards);
});
void test('two Truthtrances cannot bind contradictory answers about the same Ghola leader', () => {
const f = fixture({ ghola: true });
let game = declare(f, f.game, { copies: 2 }).game;
const question = { kind: 'gholaLeader', target: f.target, turn: game.turn, leader: f.leader };
game = act(game, f.asker, { type: 'truthAsk', question });
game = answer(game, f.target, 'yes');
game = act(game, f.asker, { type: 'truthAsk', question });
assert.ok(viewGame(game, f.target).truthGholaLeaderAnswers?.includes('yes'));
assert.ok(!viewGame(game, f.target).truthGholaLeaderAnswers?.includes('no'));
rejected(game, f.target, { type: 'truthAnswer', answer: 'no' });
game = answer(game, f.target, 'yes');
assert.equal(game.gholaLeaderPromises!.filter(row => row.player === f.target).length, 2);
});
void test('prior ordinary-revival Yes prevents a conflicting Ghola leader Yes', () => {
const f = fixture({ ghola: true });
let game = declare(f, f.game, { copies: 2 }).game;
game = act(game, f.asker, { type: 'truthAsk', question: {
kind: 'leaderRevival', target: f.target, turn: game.turn, leader: f.leader,
} });
game = answer(game, f.target, 'yes');
game = act(game, f.asker, { type: 'truthAsk', question: {
kind: 'gholaLeader', target: f.target, turn: game.turn, leader: f.leader,
} });
assert.ok(!viewGame(game, f.target).truthGholaLeaderAnswers?.includes('yes'));
assert.ok(viewGame(game, f.target).truthGholaLeaderAnswers?.includes('no'));
rejected(game, f.target, { type: 'truthAnswer', answer: 'yes' });
game = answer(game, f.target, 'no');
assert.ok(game.leaderRevivalPromises!.some(row => row.player === f.target && row.answer));
assert.ok(game.gholaLeaderPromises!.some(row => row.player === f.target && !row.answer));
});
void test('all bot profiles choose the native named Ghola guide', () => {
const f = fixture({ ghola: true });
const game = answer(askGhola(f), f.target, 'yes');
const view = viewGame(game, f.target);
const guide = view.gholaLeaderPromiseGuide;
assert.ok(guide);
assert.equal(guide.action.type, 'card');
assert.equal(guide.action.leader, f.leader);
for (const difficulty of DIFFICULTIES) {
const botView = viewGame(game, f.target);
botView.players.find(seat => seat.id === f.target)!.bot = difficulty;
assert.deepEqual(botActions(botView)[0], guide.action);
}
const after = settle(act(game, f.target, guide.action));
assert.ok(after.gholaLeaderPromises!.find(row => row.player === f.target)?.fulfilled);
});
for (const advanced of [false, true]) {
void test(`${advanced ? 'Advanced' : 'Basic'} ordinary revival cannot consume the leader promised for Ghola`, () => {
const f = fixture({ advanced, ghola: true });
const game = answer(askGhola(f), f.target, 'yes');
assert.ok(game.gholaLeaderPromises!.some(row => row.player === f.target && row.answer && !row.fulfilled));
rejected(game, f.target, f.revivalAction);
assert.ok(player(game, f.target).leaders.find(leader => leader.id === f.leader)!.dead);
});
}