Support native Truthtrance questions with Basic Kull
This commit is contained in:
1 parent
1059cc1682
commit
f96183f350
15 files changed
+443
-26
No files matched your search
@@ -0,0 +1,64 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
import { viewGame, type Game } from '../game/engine';
|
||||
import { botActions } from '../game/bots';
|
||||
import type { TruthQuestion } from '../game/truthtrance';
|
||||
import { createKullBribeFixture, act, advance, clean, inventory, player } from './fixture-kull-bribe';
|
||||
import { beforeIxSpiceDraw, orderIxSpiceCards } from './fixture-ix-spice-cards';
|
||||
import { nextKullPurposeStep } from './fixture-kull-purpose';
|
||||
|
||||
// The unopened physical cards are ordered by the inherited fixture; only a later
|
||||
// unopened worm and replacement are selected here. No ally or promise is staged.
|
||||
function source() {
|
||||
const f = createKullBribeFixture();
|
||||
let game = advance(structuredClone(f.game), g => g.turn === 2 && beforeIxSpiceDraw(g));
|
||||
orderIxSpiceCards(game, ['worm', 'land']);
|
||||
game = advance(game, g => g.phase === 1 && g.nexus && clean(g) && !g.spiceWindow && !g.spiceResolution);
|
||||
game = act(game, f.counterOwner, { type: 'alliance', target: f.actor });
|
||||
return { f, game };
|
||||
}
|
||||
function close(game: Game): Game {
|
||||
for (let i = 0; i < 300 && game.nexus; i++) {
|
||||
const guide = game.players.map(p => ({ p, guide: viewGame(game, p.id).alliancePromiseGuide }))
|
||||
.find(row => row.guide);
|
||||
const step = guide ? { actor: guide.p.id, action: guide.guide!.action } : nextKullPurposeStep(game);
|
||||
assert.ok(step, 'Native Nexus has a legal continuation');
|
||||
game = act(game, step.actor, step.action);
|
||||
}
|
||||
assert.equal(game.nexus, false); return game;
|
||||
}
|
||||
for (const kind of ['alliance', 'allianceExpression', 'mixedAlliance'] as const) {
|
||||
void test(`printed Kull Basic game resolves native ${kind} Truthtrance in the ordinary worm Nexus`, () => {
|
||||
const { f, game: original } = source(); let game = original;
|
||||
game = act(game, f.asker, { type: 'card', card: f.truth });
|
||||
for (let i = 0; game.truthtrance?.stage === 'priority' && i < game.players.length; i++) {
|
||||
const seat = game.players.find(p => !game.truthtrance!.passed.includes(p.id)); assert.ok(seat);
|
||||
game = act(game, seat.id, { type: 'truthPass' });
|
||||
}
|
||||
const question: TruthQuestion = kind === 'alliance'
|
||||
? { kind, target: f.actor, turn: game.turn, partner: f.counterOwner }
|
||||
: kind === 'allianceExpression'
|
||||
? { kind, target: f.actor, turn: game.turn, claim: { kind: 'partner', partner: f.counterOwner } }
|
||||
: { kind, target: f.actor, turn: game.turn, mixed: { kind: 'and', terms: [
|
||||
{ kind: 'fact', fact: { kind: 'spice', compare: 'gte', value: 0 } },
|
||||
{ kind: 'action', action: { partner: f.counterOwner } },
|
||||
] } };
|
||||
game = act(game, f.asker, { type: 'truthAsk', question });
|
||||
game = act(game, f.actor, { type: 'truthAnswer', answer: 'yes' });
|
||||
const guide = viewGame(game, f.actor).alliancePromiseGuide; assert.ok(guide);
|
||||
assert.equal(guide.action.type, 'alliance'); assert.equal(guide.action.target, f.counterOwner);
|
||||
const bot = viewGame(game, f.actor); bot.players.find(p => p.id === f.actor)!.bot = 'Easy';
|
||||
const botAlliance = botActions(bot).find(action => action.alliancePromiseEvent === guide.action.alliancePromiseEvent); assert.ok(botAlliance);
|
||||
assert.equal(player(act(structuredClone(game), f.actor, botAlliance), f.actor).ally, f.counterOwner);
|
||||
game = act(game, f.actor, guide.action);
|
||||
assert.equal(player(game, f.actor).ally, f.counterOwner);
|
||||
game = close(game);
|
||||
const view = viewGame(game, f.actor);
|
||||
const rows = kind === 'alliance' ? view.alliancePromises : kind === 'allianceExpression'
|
||||
? view.allianceExpressionPromises : view.mixedAlliancePromises;
|
||||
assert.ok(rows.some(row => row.fulfilled));
|
||||
assert.equal(game.discard.filter(card => card.id === f.truth).length, 1);
|
||||
assert.ok(player(game, f.reactor).hand.some(card => card.id === f.kull));
|
||||
assert.deepEqual(inventory(game), f.originalCards);
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
import { alliancePromiseModeSupported as supported } from '../game/alliance-promises';
|
||||
|
||||
const basic = { advanced: false, expansions: ['choam', 'ix'],
|
||||
players: [{ faction: 'choam' }, { faction: 'emperor' }, { faction: 'guild' }], kullPreview: true };
|
||||
|
||||
void test('printed Kull admission reuses the supported Basic CHOAM envelope', () => {
|
||||
assert.equal(supported(basic), true);
|
||||
assert.equal(supported({ ...basic, players: [...basic.players, { faction: 'ixians' }] }), true);
|
||||
assert.equal(supported({ ...basic, players: [...basic.players, { faction: 'tleilaxu' }] }), true);
|
||||
assert.equal(supported({ ...basic, expansions: ['choam', 'ix', 'ecaz'],
|
||||
players: [...basic.players, { faction: 'moritani' }] }), true);
|
||||
assert.equal(supported({ ...basic, advanced: true }), false);
|
||||
assert.equal(supported({ ...basic, expansions: ['choam'] }), false);
|
||||
assert.equal(supported({ ...basic, players: [{ faction: 'emperor' }, { faction: 'guild' }] }), false);
|
||||
assert.equal(supported({ ...basic, players: [...basic.players, { faction: 'guild' }] }), false);
|
||||
assert.equal(supported({ ...basic, kullPreview: false }), true);
|
||||
for (const flag of ['semutaPreview', 'nexusKullPreview', 'richeseBetrayalPreview',
|
||||
'guildBetrayalPreview', 'nexusIxianReplacementPreview', 'nexusIxianBetrayalPreview',
|
||||
'nexusHarkonnenBetrayalPreview', 'moritaniAssassinatePreview', 'nexusCards', 'homeworlds',
|
||||
'leaderSkills', 'techTokens', 'strongholdCards', 'discoveryEnabled', 'ecazTreachery']) {
|
||||
assert.equal(supported({ ...basic, [flag]: true }), false, flag);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,122 @@
|
||||
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 { bidPromiseModeSupported } from '../game/bid-promises';
|
||||
import { phaseBidPromiseModeSupported } from '../game/turn-bid-promises';
|
||||
import { act, advance, clean, createKullBribeFixture, inventory, player } from './fixture-kull-bribe';
|
||||
|
||||
function rejected(game: Game, actor: string, action: Action) {
|
||||
const before = structuredClone(game);
|
||||
assert.throws(() => act(game, actor, action));
|
||||
assert.deepEqual(game, before);
|
||||
}
|
||||
|
||||
void test('Basic printed Kull admits native bid commitments only in its supported envelope', () => {
|
||||
const scope = { advanced: false, expansions: ['choam', 'ix'],
|
||||
players: [{ faction: 'choam' }, { faction: 'emperor' }], kullPreview: true };
|
||||
assert.equal(bidPromiseModeSupported(scope), true);
|
||||
assert.equal(phaseBidPromiseModeSupported(scope), false);
|
||||
assert.equal(bidPromiseModeSupported({ ...scope, advanced: true }), false);
|
||||
assert.equal(bidPromiseModeSupported({ ...scope, expansions: ['choam'] }), false);
|
||||
assert.equal(bidPromiseModeSupported({ ...scope, semutaPreview: true }), false);
|
||||
assert.equal(bidPromiseModeSupported({ ...scope, nexusKullPreview: true }), false);
|
||||
assert.equal(bidPromiseModeSupported({ ...scope, leaderSkills: {} }), false);
|
||||
assert.equal(bidPromiseModeSupported({ ...scope, players: [{ faction: 'emperor' }, { faction: 'guild' }] }), false);
|
||||
});
|
||||
|
||||
void test('a genuine Basic Kull auction preserves the respondent’s real Yes bid and payment', () => {
|
||||
const f = createKullBribeFixture();
|
||||
let game = advance(structuredClone(f.charity), g => g.phase === 3 && clean(g) && g.auction?.active === f.actor);
|
||||
assert.ok(game.auction);
|
||||
const lot = game.auction.cards[game.auction.index].id;
|
||||
game = act(game, f.asker, { type: 'card', card: f.truth });
|
||||
while (game.truthtrance?.stage === 'priority') {
|
||||
const passer = game.players.find(p => !game.truthtrance!.passed.includes(p.id));
|
||||
assert.ok(passer); game = act(game, passer.id, { type: 'truthPass' });
|
||||
}
|
||||
game = act(game, f.asker, { type: 'truthAsk', question: {
|
||||
kind: 'bid', target: f.actor, turn: game.turn, lot: game.auction!.index + 1, minimum: 4,
|
||||
} });
|
||||
assert.ok(viewGame(game, f.actor).truthBidAnswers?.includes('yes'));
|
||||
game = act(game, f.actor, { type: 'truthAnswer', answer: 'yes' });
|
||||
rejected(game, f.actor, { type: 'passBid' });
|
||||
rejected(game, f.actor, { type: 'bid', amount: 3 });
|
||||
const guide = viewGame(game, f.actor).bidPromiseGuide; assert.ok(guide);
|
||||
assert.equal(guide.action.amount, 4);
|
||||
const bot = viewGame(game, f.actor); bot.players.find(p => p.id === f.actor)!.bot = 'Easy';
|
||||
const botBid = botActions(bot).find(action => action.bidPromiseEvent === guide.action.bidPromiseEvent); assert.ok(botBid);
|
||||
assert.equal(act(structuredClone(game), f.actor, botBid).bidPromises?.[0].fulfilled, true);
|
||||
game = act(game, f.actor, guide.action);
|
||||
assert.ok(game.bidPromises?.some(row => row.player === f.actor && row.fulfilled));
|
||||
game = advance(game, g => player(g, f.actor).hand.some(card => card.id === lot));
|
||||
assert.equal(player(game, f.actor).spice, 6);
|
||||
assert.deepEqual(inventory(game), f.originalCards);
|
||||
});
|
||||
|
||||
void test('a Yes bid cannot be evaded by buying its own lot with Karama', () => {
|
||||
const f = createKullBribeFixture();
|
||||
let game = advance(structuredClone(f.charity), g => g.phase === 3 && clean(g) && g.auction?.active === f.actor);
|
||||
game = act(game, f.asker, { type: 'card', card: f.truth });
|
||||
while (game.truthtrance?.stage === 'priority') {
|
||||
const passer = game.players.find(p => !game.truthtrance!.passed.includes(p.id));
|
||||
assert.ok(passer); game = act(game, passer.id, { type: 'truthPass' });
|
||||
}
|
||||
game = act(game, f.asker, { type: 'truthAsk', question: {
|
||||
kind: 'bid', target: f.actor, turn: game.turn, lot: game.auction!.index + 1, minimum: 4,
|
||||
} });
|
||||
game = act(game, f.actor, { type: 'truthAnswer', answer: 'yes' });
|
||||
rejected(game, f.actor, { type: 'card', mode: 'purchase', card: f.karama });
|
||||
});
|
||||
|
||||
void test('a No bid caps the actual current-lot declaration in a Kull game', () => {
|
||||
const f = createKullBribeFixture();
|
||||
let game = advance(structuredClone(f.charity), g => g.phase === 3 && clean(g) && g.auction?.active === f.actor);
|
||||
game = act(game, f.asker, { type: 'card', card: f.truth });
|
||||
while (game.truthtrance?.stage === 'priority') {
|
||||
const passer = game.players.find(p => !game.truthtrance!.passed.includes(p.id));
|
||||
assert.ok(passer); game = act(game, passer.id, { type: 'truthPass' });
|
||||
}
|
||||
game = act(game, f.asker, { type: 'truthAsk', question: {
|
||||
kind: 'bid', target: f.actor, turn: game.turn, lot: game.auction!.index + 1, minimum: 4,
|
||||
} });
|
||||
game = act(game, f.actor, { type: 'truthAnswer', answer: 'no' });
|
||||
rejected(game, f.actor, { type: 'bid', amount: 4 });
|
||||
game = act(game, f.actor, { type: 'bid', amount: 3 });
|
||||
assert.equal(game.auction?.bid, 3);
|
||||
assert.equal(game.bidPromises?.[0].answer, false);
|
||||
assert.deepEqual(inventory(game), f.originalCards);
|
||||
});
|
||||
|
||||
void test('an opposing Karama purchase and Kull response suspend and then reconcile a Yes bid', () => {
|
||||
const f = createKullBribeFixture();
|
||||
let game = advance(structuredClone(f.charity), g => g.phase === 3 && clean(g) && g.auction?.active === f.actor);
|
||||
game = act(game, f.asker, { type: 'card', card: f.truth });
|
||||
while (game.truthtrance?.stage === 'priority') {
|
||||
const passer = game.players.find(p => !game.truthtrance!.passed.includes(p.id));
|
||||
assert.ok(passer); game = act(game, passer.id, { type: 'truthPass' });
|
||||
}
|
||||
game = act(game, f.asker, { type: 'truthAsk', question: {
|
||||
kind: 'bid', target: f.actor, turn: game.turn, lot: game.auction!.index + 1, minimum: 4,
|
||||
} });
|
||||
game = act(game, f.actor, { type: 'truthAnswer', answer: 'yes' });
|
||||
const before = structuredClone(game.bidPromises);
|
||||
const pending = act(game, f.counterOwner, { type: 'card', mode: 'purchase', card: f.counter });
|
||||
assert.equal(pending.pendingKull?.stage, 'offer');
|
||||
assert.deepEqual(pending.bidPromises, before);
|
||||
assert.equal(viewGame(pending, f.actor).bidPromiseGuide, null);
|
||||
const reaction = viewGame(pending, f.reactor).kullReaction; assert.ok(reaction);
|
||||
const declined = advance(act(pending, f.reactor,
|
||||
{ type: 'kullDecision', event: reaction.event, decline: true }), clean);
|
||||
assert.equal(declined.bidPromises?.[0].released, true);
|
||||
assert.ok(player(declined, f.reactor).hand.some(card => card.id === f.kull));
|
||||
assert.deepEqual(inventory(declined), f.originalCards);
|
||||
const declared = act(pending, f.reactor, { type: 'kullDecision', event: reaction.event, source: 'printed', card: f.kull });
|
||||
assert.deepEqual(declared.bidPromises, before);
|
||||
game = advance(declared, clean);
|
||||
assert.deepEqual(game.bidPromises, before);
|
||||
const guide = viewGame(game, f.actor).bidPromiseGuide; assert.ok(guide);
|
||||
game = act(game, f.actor, guide.action);
|
||||
assert.equal(game.bidPromises?.[0].fulfilled, true);
|
||||
assert.deepEqual(inventory(game), f.originalCards);
|
||||
});
|
||||
@@ -0,0 +1,139 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
import { TERRITORIES } from '../game/board';
|
||||
import { viewGame, type Game } from '../game/engine';
|
||||
import { botActions } from '../game/bots';
|
||||
import { revivalPromiseModeSupported } from '../game/revival-promises';
|
||||
import { act, advance, clean, createKullBribeFixture, inventory, player } from './fixture-kull-bribe';
|
||||
|
||||
function source() {
|
||||
const f = createKullBribeFixture();
|
||||
let game = structuredClone(f.game);
|
||||
// The original driver selects the scalar lottery and dials one for each
|
||||
// player in the next Storm. Select a real empty next-Storm sand sector.
|
||||
const sector = game.storm % 18 + 1;
|
||||
const destination = TERRITORIES.find(place => place.type === 'sand' && place.sectors.includes(sector) &&
|
||||
game.players.every(seat => !Object.keys(seat.forces).some(key => key.startsWith(`${place.id}:`))));
|
||||
assert.ok(destination);
|
||||
game = act(game, f.actor, { type: 'ship', territory: destination.id, sector, amount: 3, elite: 0, allyPayment: 0 });
|
||||
game = advance(game, clean);
|
||||
game = act(game, f.actor, { type: 'endMovement' });
|
||||
// Reorder only the still-unopened Spice deck so the next turn has a normal
|
||||
// blow and cannot open an unrelated random Nexus decision.
|
||||
const safe = game.spiceDeck.findIndex(card => 'territory' in card && !card.discovery);
|
||||
assert.ok(safe >= 0); game.spiceDeck.unshift(game.spiceDeck.splice(safe, 1)[0]);
|
||||
game = advance(game, state => state.turn === 2 && state.phase === 3 && clean(state) && state.auction?.active === f.actor);
|
||||
assert.equal(player(game, f.actor).tanks, 3);
|
||||
assert.equal(player(game, f.actor).spice, 4);
|
||||
assert.deepEqual(inventory(game), f.originalCards);
|
||||
return { ...f, game };
|
||||
}
|
||||
|
||||
function ask(f: ReturnType<typeof source>, game: Game) {
|
||||
game = act(game, f.asker, { type: 'card', card: f.truth });
|
||||
while (game.truthtrance?.stage === 'priority') {
|
||||
const passer = game.players.find(seat => !game.truthtrance!.passed.includes(seat.id));
|
||||
assert.ok(passer); game = act(game, passer.id, { type: 'truthPass' });
|
||||
}
|
||||
return act(game, f.asker, { type: 'truthAsk', question: {
|
||||
kind: 'revival', target: f.actor, turn: game.turn, counter: 'total', minimum: 3,
|
||||
} });
|
||||
}
|
||||
|
||||
void test('Basic printed Kull permits native Revival questions while preserving module fences', () => {
|
||||
const native = { advanced: false, expansions: ['choam', 'ix'],
|
||||
players: [{ faction: 'choam' }, { faction: 'emperor' }], kullPreview: true };
|
||||
assert.equal(revivalPromiseModeSupported(native), true);
|
||||
for (const option of ['semutaPreview', 'nexusKullPreview', 'nexusCards', 'leaderSkills',
|
||||
'richeseBetrayalPreview', 'guildBetrayalPreview'] as const)
|
||||
assert.equal(revivalPromiseModeSupported({ ...native, [option]: true }), false, option);
|
||||
assert.equal(revivalPromiseModeSupported({ ...native, advanced: true }), false);
|
||||
assert.equal(revivalPromiseModeSupported({ ...native, players: [{ faction: 'emperor' }, { faction: 'guild' }] }), false);
|
||||
});
|
||||
|
||||
void test('a genuine Kull game binds and completes an ordinary Revival Yes after real Storm losses', () => {
|
||||
const f = source();
|
||||
let game = advance(f.game, state => state.phase === 4 && clean(state));
|
||||
game = ask(f, game);
|
||||
assert.ok(viewGame(game, f.actor).truthRevivalAnswers?.includes('yes'));
|
||||
game = act(game, f.actor, { type: 'truthAnswer', answer: 'yes' });
|
||||
const guide = viewGame(game, f.actor).revivalPromiseGuide; assert.ok(guide);
|
||||
assert.equal(guide.action.type, 'revive'); assert.equal(guide.action.amount, 3);
|
||||
const bot = viewGame(game, f.actor); bot.players.find(p => p.id === f.actor)!.bot = 'Easy';
|
||||
const botRevival = botActions(bot).find(action => action.revivalPromiseEvent === guide.action.revivalPromiseEvent); assert.ok(botRevival);
|
||||
assert.equal(act(structuredClone(game), f.actor, botRevival).decision?.kind, 'choamFreeRevival');
|
||||
game = act(game, f.actor, guide.action);
|
||||
assert.equal(game.decision?.kind, 'choamFreeRevival');
|
||||
assert.equal(game.revivalPromises?.[0].progress, 0);
|
||||
game = advance(game, clean);
|
||||
assert.equal(game.revivalPromises?.[0].progress, 3);
|
||||
assert.equal(game.revivalPromises?.[0].fulfilled, true);
|
||||
assert.equal(player(game, f.actor).tanks, 0);
|
||||
assert.deepEqual(inventory(game), f.originalCards);
|
||||
});
|
||||
|
||||
void test('a successful Kull auction block cannot be forecast as a Karama payment funding Revival', () => {
|
||||
const f = source();
|
||||
let game = act(f.game, f.actor, { type: 'card', mode: 'purchase', card: f.karama });
|
||||
const reaction = viewGame(game, f.reactor).kullReaction; assert.ok(reaction);
|
||||
game = act(game, f.reactor, { type: 'kullDecision', event: reaction.event, source: 'printed', card: f.kull });
|
||||
game = advance(game, clean);
|
||||
assert.ok(viewGame(game, f.actor).karamaBlocked);
|
||||
game = act(game, f.actor, { type: 'bid', amount: 3, allyPayment: 0 });
|
||||
game = ask(f, game);
|
||||
assert.deepEqual(viewGame(game, f.actor).truthRevivalAnswers, ['no']);
|
||||
const before = structuredClone(game);
|
||||
assert.throws(() => act(game, f.actor, { type: 'truthAnswer', answer: 'yes' }));
|
||||
assert.deepEqual(game, before);
|
||||
game = act(game, f.actor, { type: 'truthAnswer', answer: 'no' });
|
||||
assert.equal(game.revivalPromises?.[0].answer, false);
|
||||
assert.deepEqual(inventory(game), f.originalCards);
|
||||
});
|
||||
|
||||
function outstandingPayment() {
|
||||
const f = source();
|
||||
let game = act(f.game, f.actor, { type: 'bid', amount: 3, allyPayment: 0 });
|
||||
game = ask(f, game);
|
||||
assert.ok(viewGame(game, f.actor).truthRevivalAnswers?.includes('yes'));
|
||||
game = act(game, f.actor, { type: 'truthAnswer', answer: 'yes' });
|
||||
const promise = structuredClone(game.revivalPromises);
|
||||
game = advance(game, state => !state.response && !state.phaseOpening &&
|
||||
state.decision?.kind === 'auctionPayment' && state.decision.player === f.actor);
|
||||
const payment = viewGame(game, f.actor).revivalPromiseGuide; assert.ok(payment);
|
||||
assert.equal(payment.action.type, 'decision'); assert.equal(payment.action.karama, true);
|
||||
game = act(game, f.actor, payment.action);
|
||||
assert.equal(game.pendingKull?.stage, 'offer');
|
||||
assert.deepEqual(game.revivalPromises, promise);
|
||||
assert.equal(viewGame(game, f.actor).revivalPromiseGuide, null);
|
||||
const reaction = viewGame(game, f.reactor).kullReaction; assert.ok(reaction);
|
||||
game = act(game, f.reactor, { type: 'kullDecision', event: reaction.event, source: 'printed', card: f.kull });
|
||||
assert.equal(game.pendingKull?.stage, 'counter');
|
||||
assert.deepEqual(game.revivalPromises, promise);
|
||||
assert.equal(viewGame(game, f.actor).revivalPromiseGuide, null);
|
||||
return { f, game, promise };
|
||||
}
|
||||
|
||||
void test('an opposing successful Kull may release a Revival Yes only after preventing its actual payment', () => {
|
||||
const { f, game: offered, promise } = outstandingPayment();
|
||||
const game = advance(offered, clean);
|
||||
assert.equal(promise?.[0].released, undefined);
|
||||
assert.equal(game.revivalPromises?.[0].progress, 0);
|
||||
assert.equal(game.revivalPromises?.[0].released, true);
|
||||
assert.equal(player(game, f.actor).spice, 1);
|
||||
assert.ok(player(game, f.actor).hand.some(card => card.id === f.karama));
|
||||
assert.deepEqual(inventory(game), f.originalCards);
|
||||
});
|
||||
|
||||
void test('a distinct Kull counter preserves the original payment and outstanding Revival Yes', () => {
|
||||
const { f, game: offered, promise } = outstandingPayment();
|
||||
assert.ok(viewGame(offered, f.counterOwner).responseControls?.cancelCards.includes(f.counter));
|
||||
let game = act(offered, f.counterOwner, { type: 'card', mode: 'cancel', card: f.counter });
|
||||
game = advance(game, state => state.phase === 4 && clean(state));
|
||||
assert.deepEqual(game.revivalPromises, promise);
|
||||
assert.equal(player(game, f.actor).spice, 4);
|
||||
const guide = viewGame(game, f.actor).revivalPromiseGuide; assert.ok(guide);
|
||||
game = advance(act(game, f.actor, guide.action), clean);
|
||||
assert.equal(game.revivalPromises?.[0].progress, 3);
|
||||
assert.equal(game.revivalPromises?.[0].fulfilled, true);
|
||||
assert.deepEqual(inventory(game), f.originalCards);
|
||||
});
|
||||
@@ -30,14 +30,14 @@ void test('public host lobby shows the explicitly selected Kull preview and its
|
||||
const f = fixture(), host = viewGame(f.lobby,f.lobby.host), html = table(host);
|
||||
assert.equal(host.lobbyModules?.kullPreview,true);
|
||||
assert.match(html,/Kull Wahad · Basic development preview/);
|
||||
assert.match(html,/does not support structured Truthtrance questions about bids/);
|
||||
assert.match(html,/does not support phase-wide bid or combined turn-action Truthtrance questions/);
|
||||
assert.match(html,/defers unfunded non-CHOAM Karama-dependent bids/);
|
||||
assert.match(html,/When Richese is seated, structured bribe commitments are also unavailable/);
|
||||
assert.match(html,/When Richese is seated, structured bid, alliance, Revival and bribe commitments are unavailable/);
|
||||
const off = act(f.lobby,f.lobby.host,{type:'moduleOption',module:'kullPreview',enabled:false});
|
||||
const unselected = viewGame(off,off.host);
|
||||
assert.equal(unselected.lobbyModules?.kullPreview,false);
|
||||
assert.match(table(unselected),/Kull Wahad · Basic development preview/);
|
||||
assert.doesNotMatch(table(unselected),/does not support structured Truthtrance questions about bids/);
|
||||
assert.doesNotMatch(table(unselected),/does not support phase-wide bid or combined turn-action Truthtrance questions/);
|
||||
const nonhost = table(viewGame(f.lobby,f.actor));
|
||||
assert.match(nonhost,/disabled=""[^>]*type="checkbox"|type="checkbox"[^>]*disabled=""/);
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user