Prototype Tleilaxu special Karama Truthtrance
Verify and publish checkpoint container / container (push) Successful in 11m6s
Verify and publish checkpoint container / container (push) Successful in 11m6s
This commit is contained in:
1 parent
5b8d510470
commit
cfccfe5acb
15 files changed
+391
-20
No files matched your search
@@ -0,0 +1,117 @@
|
||||
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 { windowCardOpportunity } from '../game/window-card-play';
|
||||
import {
|
||||
createTleilaxuSpecialKaramaFixture,
|
||||
actTleilaxuSpecialKarama as act,
|
||||
tleilaxuSpecialKaramaPlayer as player,
|
||||
tleilaxuSpecialKaramaInventory as inventory,
|
||||
} from './fixture-tleilaxu-special-karama';
|
||||
|
||||
function fixture() {
|
||||
const f = createTleilaxuSpecialKaramaFixture();
|
||||
const game = structuredClone(f.game), asker = f.observer;
|
||||
const hand = player(game, asker).hand;
|
||||
// Stage one original Truthtrance by exchanging physical cards with an
|
||||
// existing card zone. Neither Karama nor Ghola is displaced or duplicated.
|
||||
let truth = hand.find(card => card.effect === 'truthtrance');
|
||||
if (!truth) {
|
||||
const zones = [game.deck, game.discard, player(game, f.target).hand];
|
||||
const zone = zones.find(cards => cards.some(card => card.effect === 'truthtrance'));
|
||||
assert.ok(zone);
|
||||
const index = zone.findIndex(card => card.effect === 'truthtrance');
|
||||
assert.ok(hand.length > 0);
|
||||
[hand[0], zone[index]] = [zone[index], hand[0]];
|
||||
truth = hand[0];
|
||||
}
|
||||
assert.deepEqual(inventory(game), f.inventory);
|
||||
const source = JSON.stringify(['karamaTleilaxuSpecial', game.turn, f.owner]);
|
||||
assert.equal(windowCardOpportunity(game, 'karamaTleilaxuSpecial')?.source, source);
|
||||
return { ...f, game, asker, truth: truth.id, windowSource: source };
|
||||
}
|
||||
type Fixture = ReturnType<typeof fixture>;
|
||||
function question(f: Fixture, target = f.owner, source = f.windowSource) {
|
||||
return { kind: 'turnActions', target, turn: f.game.turn, mixed: { kind: 'action', action: {
|
||||
kind: 'windowCardPlay', effect: 'karamaTleilaxuSpecial', source,
|
||||
} } };
|
||||
}
|
||||
function declare(f: Fixture) {
|
||||
let game = act(f.game, f.asker, { type: 'card', card: f.truth });
|
||||
for (let count = 0; game.truthtrance?.stage === 'priority' && count < game.players.length; count++) {
|
||||
const actor = game.players.find(seat => !game.truthtrance!.passed.includes(seat.id));
|
||||
assert.ok(actor);
|
||||
game = act(game, actor.id, { type: 'truthPass' });
|
||||
}
|
||||
assert.equal(game.truthtrance?.stage, 'ask');
|
||||
return game;
|
||||
}
|
||||
function ask(f: Fixture) {
|
||||
return act(declare(f), f.asker, { type: 'truthAsk', question: question(f) });
|
||||
}
|
||||
function reject(game: Game, actor: string, action: Action) {
|
||||
const before = structuredClone(game);
|
||||
assert.throws(() => act(game, actor, action));
|
||||
assert.deepEqual(game, before);
|
||||
}
|
||||
|
||||
void test('Tleilaxu special Karama Yes spends the physical card and prevents a real revival', () => {
|
||||
const f = fixture(), asked = ask(f);
|
||||
assert.ok(viewGame(asked, f.owner).truthTurnActionAnswers?.includes('yes'));
|
||||
assert.equal(viewGame(asked, f.target).truthTurnActionAnswers, null);
|
||||
let game = act(asked, f.owner, { type: 'truthAnswer', answer: 'yes' });
|
||||
const guide = viewGame(game, f.owner).turnActionGuide; assert.ok(guide);
|
||||
assert.equal(guide.action.type, 'card');
|
||||
assert.equal(guide.action.mode, 'special');
|
||||
assert.equal(guide.action.card, f.karama);
|
||||
for (const difficulty of DIFFICULTIES) {
|
||||
const bot = viewGame(game, f.owner);
|
||||
bot.players.find(seat => seat.id === f.owner)!.bot = difficulty;
|
||||
assert.deepEqual(botActions(bot)[0], guide.action);
|
||||
}
|
||||
game = act(game, f.owner, f.special);
|
||||
assert.equal(player(game, f.owner).specialKaramaUsed, true);
|
||||
assert.equal(player(game, f.owner).hand.some(card => card.id === f.karama), false);
|
||||
assert.equal(game.discard.filter(card => card.id === f.karama).length, 1);
|
||||
assert.deepEqual(game.revivalPrevention, { player: f.target, turn: game.turn });
|
||||
const promise = game.turnActionPromises!.find(row => row.player === f.owner);
|
||||
assert.equal(promise?.windowCards?.karamaTleilaxuSpecial?.played, true);
|
||||
assert.equal(promise?.fulfilled, true);
|
||||
reject(game, f.target, f.ordinaryRevival);
|
||||
reject(game, f.target, f.gholaAction);
|
||||
assert.equal(viewGame(game, f.asker).players.find(seat => seat.id === f.owner)!.hand, undefined);
|
||||
assert.deepEqual(viewGame(game, f.asker).turnActionPromises, []);
|
||||
assert.deepEqual(inventory(game), f.inventory);
|
||||
});
|
||||
|
||||
void test('Tleilaxu special Karama No prevents its use and preserves the card', () => {
|
||||
const f = fixture(), asked = ask(f);
|
||||
assert.ok(viewGame(asked, f.owner).truthTurnActionAnswers?.includes('no'));
|
||||
const game = act(asked, f.owner, { type: 'truthAnswer', answer: 'no' });
|
||||
reject(game, f.owner, f.special);
|
||||
reject(game, f.owner, { ...f.special, target: f.owner });
|
||||
assert.ok(player(game, f.owner).hand.some(card => card.id === f.karama));
|
||||
assert.notEqual(player(game, f.owner).specialKaramaUsed, true);
|
||||
assert.deepEqual(inventory(game), f.inventory);
|
||||
});
|
||||
|
||||
void test('Tleilaxu may fulfill Yes by preventing its own revival', () => {
|
||||
const f = fixture();
|
||||
let game = act(ask(f), f.owner, { type: 'truthAnswer', answer: 'yes' });
|
||||
game = act(game, f.owner, { ...f.special, target: f.owner });
|
||||
assert.deepEqual(game.revivalPrevention, { player: f.owner, turn: game.turn });
|
||||
assert.equal(game.turnActionPromises!.find(row => row.player === f.owner)?.fulfilled, true);
|
||||
assert.equal(game.discard.filter(card => card.id === f.karama).length, 1);
|
||||
assert.deepEqual(inventory(game), f.inventory);
|
||||
});
|
||||
|
||||
void test('Tleilaxu special source rejects another respondent and forged turns or owners', () => {
|
||||
const f = fixture(), game = declare(f);
|
||||
reject(game, f.asker, { type: 'truthAsk', question: question(f, f.target) });
|
||||
reject(game, f.asker, { type: 'truthAsk', question: question(f, f.owner,
|
||||
JSON.stringify(['karamaTleilaxuSpecial', game.turn + 1, f.owner])) });
|
||||
reject(game, f.asker, { type: 'truthAsk', question: question(f, f.owner,
|
||||
JSON.stringify(['karamaTleilaxuSpecial', game.turn, f.target])) });
|
||||
});
|
||||
Reference in new issue
Block a user