Check Advanced victory after Mentat actions
Verify and publish checkpoint container / container (push) Successful in 10m42s
Verify and publish checkpoint container / container (push) Successful in 10m42s
This commit is contained in:
1 parent
c608d6fea6
commit
64706cc7e0
12 files changed
+339
-27
No files matched your search
@@ -0,0 +1,97 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
import { applyAction, type Game } from '../game/engine';
|
||||
import { territory } from '../game/board';
|
||||
import { createMoritaniStrongholdsFixture } from './fixture-moritani-strongholds';
|
||||
|
||||
/** This inherited fixture uses real Advanced Moritani/Guild/Emperor setup and
|
||||
* native phase actions through the opening Moritani placement decision, with
|
||||
* the Stronghold Cards module. Only physical board groups are staged after
|
||||
* that opening to expose a rare three-stronghold Guild victory. The prevention
|
||||
* case also transfers one original Karama to Guild's hand without duplication.
|
||||
* It is not a naturally earned full game or module-combination certification. */
|
||||
function position() {
|
||||
const fixture = createMoritaniStrongholdsFixture();
|
||||
const game = structuredClone(fixture.beforePlacement);
|
||||
assert.equal(game.phase, 8); assert.equal(game.decision?.kind, 'moritaniPlacement');
|
||||
for (const player of game.players) {
|
||||
player.reserves += Object.values(player.forces).reduce((sum, count) => sum + count, 0);
|
||||
player.forces = {};
|
||||
if (player.elites) {
|
||||
player.elites.reserves += Object.values(player.elites.forces).reduce((sum, count) => sum + count, 0);
|
||||
player.elites.forces = {};
|
||||
}
|
||||
}
|
||||
const winner = game.players.find(player => player.faction === 'guild')!;
|
||||
for (const name of ['arrakeen', 'carthag', 'sietch_tabr'])
|
||||
winner.forces[`${name}:${territory(name).sectors[0]}`] = 1;
|
||||
winner.reserves -= 3;
|
||||
const owner = game.players.find(player => player.faction === 'moritani')!;
|
||||
return { game, winner: winner.id, owner: owner.id };
|
||||
}
|
||||
|
||||
function pending(game: Game) {
|
||||
assert.equal(game.phase, 8); assert.equal(game.status, 'playing');
|
||||
assert.deepEqual(game.winner, []); assert.ok(!game.victoryContinuation);
|
||||
}
|
||||
function finish(game: Game, winner: string) {
|
||||
const turn = game.turn;
|
||||
for (const player of game.players) game = applyAction(game, player.id, { type: 'ready' });
|
||||
assert.equal(game.turn, turn); assert.equal(game.status, 'finished');
|
||||
assert.deepEqual(game.winner, [winner]);
|
||||
assert.equal(game.strongholdCards?.claimedTurn, turn);
|
||||
}
|
||||
function passPlacement(game: Game) {
|
||||
while (game.response) {
|
||||
const actor = game.players.find(player => !game.response!.passed.includes(player.id));
|
||||
assert.ok(actor);
|
||||
game = applyAction(game, actor.id, { type: 'passResponse' });
|
||||
}
|
||||
return game;
|
||||
}
|
||||
function stageKarama(game: Game, id: string) {
|
||||
const player = game.players.find(seat => seat.id === id)!;
|
||||
const held = player.hand.find(card => card.effect === 'karama');
|
||||
if (held) return held.id;
|
||||
const deckIndex = game.deck.findIndex(card => card.effect === 'karama');
|
||||
let card = deckIndex >= 0 ? game.deck.splice(deckIndex, 1)[0] : undefined;
|
||||
if (!card) {
|
||||
const donor = game.players.find(seat => seat.id !== id && seat.hand.some(candidate => candidate.effect === 'karama'));
|
||||
assert.ok(donor, 'One original Karama is still in the unplayed supply');
|
||||
card = donor.hand.splice(donor.hand.findIndex(candidate => candidate.effect === 'karama'), 1)[0];
|
||||
}
|
||||
if (player.hand.length >= 4) game.deck.push(player.hand.pop()!);
|
||||
player.hand.push(card);
|
||||
return card.id;
|
||||
}
|
||||
|
||||
void test('Advanced Moritani declining placement leaves the winner pending until Mentat closes', () => {
|
||||
const f = position();
|
||||
const game = applyAction(f.game, f.owner, { type: 'decision', decline: true });
|
||||
pending(game); assert.equal(game.decision, null);
|
||||
assert.equal(game.moritaniTerror!.placementTurn, game.turn);
|
||||
finish(game, f.winner);
|
||||
});
|
||||
|
||||
void test('Advanced Moritani placement completes before the later victory check', () => {
|
||||
const f = position();
|
||||
const token = f.game.moritaniTerror!.tokens.find(candidate => candidate.status === 'available')!;
|
||||
let game = applyAction(f.game, f.owner, { type: 'decision', token: token.id, territory: 'tueks_sietch' });
|
||||
game = passPlacement(game);
|
||||
pending(game); assert.equal(game.moritaniTerror!.placementTurn, game.turn);
|
||||
assert.equal(game.moritaniTerror!.tokens.find(candidate => candidate.id === token.id)?.location, 'tueks_sietch');
|
||||
finish(game, f.winner);
|
||||
});
|
||||
|
||||
void test('Advanced Moritani placement canceled by Karama still leaves victory until Mentat closes', () => {
|
||||
const f = position(), card = stageKarama(f.game, f.winner);
|
||||
const token = f.game.moritaniTerror!.tokens.find(candidate => candidate.status === 'available')!;
|
||||
const original = structuredClone(f.game.moritaniTerror!.tokens);
|
||||
let game = applyAction(f.game, f.owner, { type: 'decision', token: token.id, territory: 'tueks_sietch' });
|
||||
assert.equal(game.response?.kind, 'moritaniPlacement');
|
||||
game = applyAction(game, f.winner, { type: 'card', mode: 'cancel', card });
|
||||
pending(game); assert.equal(game.moritaniTerror!.placementTurn, game.turn);
|
||||
assert.deepEqual(game.moritaniTerror!.tokens, original);
|
||||
assert.ok(game.discard.some(candidate => candidate.id === card));
|
||||
finish(game, f.winner);
|
||||
});
|
||||
@@ -0,0 +1,118 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
import { applyAction, viewGame, type Game } from '../game/engine';
|
||||
import { territory } from '../game/board';
|
||||
import { botActions } from '../game/bots';
|
||||
import { DIFFICULTIES } from '../game/bot-profiles';
|
||||
import { createTechTokens } from '../game/tech-tokens';
|
||||
import { bindTruthBribe, createTruthBribeFixture, truthBribePlayer } from './fixture-truth-bribe';
|
||||
|
||||
/** Original Advanced Harkonnen/Atreides/Emperor setup and native actions reach
|
||||
* Collection. Only the conserved force locations are staged to expose a rare
|
||||
* victory board; the agreement case also stages a complete physical Tech set,
|
||||
* and the final-turn case stages turn ten. This is not a naturally earned
|
||||
* late-game board, a save/recovery test, or full module acceptance. */
|
||||
function position(options: { holds?: number; simultaneous?: boolean; finalTurn?: boolean } = {}) {
|
||||
const source = createTruthBribeFixture({ advanced: true, phase: 7 });
|
||||
const game = structuredClone(source.game);
|
||||
for (const player of game.players) {
|
||||
player.reserves += Object.values(player.forces).reduce((sum, count) => sum + count, 0);
|
||||
player.forces = {};
|
||||
if (player.elites) {
|
||||
player.elites.reserves += Object.values(player.elites.forces).reduce((sum, count) => sum + count, 0);
|
||||
player.elites.forces = {};
|
||||
}
|
||||
}
|
||||
const assign = (id: string, names: string[]) => {
|
||||
const player = truthBribePlayer(game, id);
|
||||
for (const name of names) player.forces[`${name}:${territory(name).sectors[0]}`] = 1;
|
||||
player.reserves -= names.length;
|
||||
};
|
||||
const holds = options.holds ?? 3;
|
||||
assign('atreides', ['arrakeen', 'carthag', 'sietch_tabr'].slice(0, holds));
|
||||
if (options.simultaneous) {
|
||||
assign('emperor', ['tueks_sietch', 'habbanya_ridge_sietch']);
|
||||
game.techTokens = createTechTokens();
|
||||
for (const token of Object.values(game.techTokens)) token.owner = 'emperor';
|
||||
}
|
||||
if (options.finalTurn) game.turn = 10;
|
||||
return { source, game };
|
||||
}
|
||||
|
||||
function enterMentat(initial: Game) {
|
||||
let game = initial;
|
||||
for (const player of game.players) game = applyAction(game, player.id, { type: 'ready' });
|
||||
assert.equal(game.phase, 8);
|
||||
assert.equal(game.status, 'playing');
|
||||
assert.deepEqual(game.winner, []);
|
||||
assert.equal(game.victoryContinuation, undefined);
|
||||
return game;
|
||||
}
|
||||
function finishMentat(initial: Game) {
|
||||
let game = initial;
|
||||
for (const player of game.players) game = applyAction(game, player.id, { type: 'ready' });
|
||||
return game;
|
||||
}
|
||||
|
||||
void test('Advanced winner waits through a real Truthtrance and its required Mentat payment', () => {
|
||||
const f = position();
|
||||
let game = enterMentat(f.game);
|
||||
const bound = bindTruthBribe(f.source, 'yes', 3, f.source.recipient, game);
|
||||
game = bound.game;
|
||||
assert.throws(() => applyAction(game, f.source.payer, { type: 'ready' }), /Truthtrance|bribe|payment/i);
|
||||
const guide = viewGame(game, f.source.payer).bribePromiseGuide;
|
||||
assert.ok(guide);
|
||||
const payerSpice = truthBribePlayer(game, f.source.payer).spice;
|
||||
game = applyAction(game, f.source.payer, guide.action);
|
||||
assert.equal(truthBribePlayer(game, f.source.payer).spice, payerSpice - 3);
|
||||
assert.equal(truthBribePlayer(game, f.source.recipient).bribes, 3);
|
||||
game = finishMentat(game);
|
||||
assert.equal(game.status, 'finished');
|
||||
assert.deepEqual(game.winner, ['atreides']);
|
||||
});
|
||||
|
||||
void test('Advanced Mentat with no winner advances once only after every player is ready', () => {
|
||||
let game = enterMentat(position({ holds: 2 }).game);
|
||||
const turn = game.turn;
|
||||
for (const player of game.players.slice(0, -1)) {
|
||||
game = applyAction(game, player.id, { type: 'ready' });
|
||||
assert.equal(game.phase, 8); assert.equal(game.turn, turn);
|
||||
}
|
||||
game = applyAction(game, game.players.at(-1)!.id, { type: 'ready' });
|
||||
assert.equal(game.status, 'playing'); assert.equal(game.turn, turn + 1); assert.equal(game.phase, 0);
|
||||
assert.deepEqual(game.winner, []);
|
||||
});
|
||||
|
||||
void test('Advanced final-turn fallback is checked after Mentat opportunities', () => {
|
||||
const game = enterMentat(position({ holds: 2, finalTurn: true }).game);
|
||||
const settled = finishMentat(game);
|
||||
assert.equal(settled.turn, 10); assert.equal(settled.status, 'finished');
|
||||
assert.deepEqual(settled.winner, ['atreides']);
|
||||
});
|
||||
|
||||
void test('Advanced simultaneous qualifiers vote after Mentat and a unanimous agreement advances once', () => {
|
||||
const game = finishMentat(enterMentat(position({ simultaneous: true }).game));
|
||||
const offer = game.victoryContinuation;
|
||||
assert.ok(offer); assert.equal(offer.resume, 'advance');
|
||||
assert.deepEqual(new Set(offer.voters), new Set(['atreides', 'emperor']));
|
||||
let next = applyAction(game, offer.voters[0], { type: 'victoryContinuation', event: offer.event, mode: 'continue', extra: 1 });
|
||||
for (const voter of offer.voters.slice(1))
|
||||
next = applyAction(next, voter, { type: 'victoryContinuation', event: offer.event, mode: 'accept' });
|
||||
assert.equal(next.status, 'playing'); assert.equal(next.turn, game.turn + 1); assert.equal(next.phase, 0);
|
||||
assert.equal(next.victoryExtraStrongholds, 1); assert.ok(!next.victoryContinuation);
|
||||
});
|
||||
|
||||
void test('all four minimal bot profiles can finish Advanced Mentat before victory', () => {
|
||||
const initial = enterMentat(position().game);
|
||||
for (const difficulty of DIFFICULTIES) {
|
||||
let game = structuredClone(initial);
|
||||
for (const player of game.players) {
|
||||
const view = viewGame(game, player.id);
|
||||
view.players.find(seat => seat.id === player.id)!.bot = difficulty;
|
||||
const action = botActions(view).find(candidate => candidate.type === 'ready');
|
||||
assert.ok(action, `${difficulty} can finish ${player.id}'s Mentat opportunity`);
|
||||
game = applyAction(game, player.id, action);
|
||||
}
|
||||
assert.equal(game.status, 'finished'); assert.deepEqual(game.winner, ['atreides']);
|
||||
}
|
||||
});
|
||||
@@ -34,6 +34,12 @@ function actual(g: Game) {
|
||||
let result = structuredClone(g);
|
||||
result.phase = 7;
|
||||
for (const p of result.players) result = applyAction(result, p.id, { type: 'ready' });
|
||||
if (result.advanced) {
|
||||
assert.equal(result.phase, 8);
|
||||
assert.equal(result.status, 'playing');
|
||||
assert.ok(!result.victoryContinuation);
|
||||
for (const p of result.players) result = applyAction(result, p.id, { type: 'ready' });
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -39,6 +39,12 @@ function originalMentat(game: Game) {
|
||||
assert.equal(game.phase, 8);
|
||||
return game;
|
||||
}
|
||||
function finishMentat(game: Game) {
|
||||
assert.equal(game.phase, 8);
|
||||
for (const player of game.players) if (!game.ready.includes(player.id))
|
||||
game = applyAction(JSON.parse(JSON.stringify(game)), player.id, { type: 'ready' });
|
||||
return game;
|
||||
}
|
||||
|
||||
void test('actual original Mentat awards the high Ecaz two-native-world alliance win with one joint stronghold and no ordinary point inflation', () => {
|
||||
const fixture = ecazHomeworldFixture(true);
|
||||
@@ -59,11 +65,19 @@ void test('actual original Mentat awards the high Ecaz two-native-world alliance
|
||||
const ready = botActions(view).find(action => action.type === 'ready') ?? { type: 'ready' as const };
|
||||
result = applyAction(result, player.id, ready);
|
||||
}
|
||||
assert.equal(result.phase, 8); assert.equal(result.status, 'playing');
|
||||
for (const player of result.players) {
|
||||
const view = viewGame(result, player.id); view.players.find(seat => seat.id === player.id)!.bot = difficulty;
|
||||
const ready = botActions(view).find(action => action.type === 'ready') ?? { type: 'ready' as const };
|
||||
result = applyAction(result, player.id, ready);
|
||||
}
|
||||
assert.equal(result.status, 'finished');
|
||||
assert.deepEqual(new Set(result.winner), new Set(['ec', 'al']));
|
||||
assert.deepEqual(biddingPhysicalIds(result), cards);
|
||||
}
|
||||
game = originalMentat(game);
|
||||
assert.equal(game.status, 'playing'); assert.deepEqual(game.winner, []);
|
||||
game = finishMentat(game);
|
||||
assert.equal(game.status, 'finished'); assert.deepEqual(new Set(game.winner), new Set(['ec', 'al']));
|
||||
assert.deepEqual(biddingPhysicalIds(game), cards);
|
||||
assert.deepEqual(game.players.map(player => ({ id: player.id, forces: player.forces, reserves: player.reserves, tanks: player.tanks })), forces);
|
||||
@@ -77,6 +91,8 @@ void test('native population6 does not award this win despite the same actual jo
|
||||
assert.equal(viewGame(game, 'ec').ecazHomeworldVictory!.high, false);
|
||||
game = originalMentat(game);
|
||||
assert.equal(game.status, 'playing'); assert.deepEqual(game.winner, []);
|
||||
game = finishMentat(game);
|
||||
assert.equal(game.status, 'playing'); assert.deepEqual(game.winner, []);
|
||||
});
|
||||
|
||||
for (const predicted of ['ecaz', 'guild'] as const) void test(`actual BG prediction of ${predicted} replaces the printed Ecaz alliance win without changing public progress`, () => {
|
||||
@@ -85,6 +101,8 @@ for (const predicted of ['ecaz', 'guild'] as const) void test(`actual BG predict
|
||||
ecazSeat(game, 'observer').prediction = { faction: predicted, turn: game.turn };
|
||||
assert.equal(viewGame(game, 'al').ecazHomeworldVictory!.qualifies, progress.qualifies);
|
||||
game = originalMentat(game);
|
||||
assert.equal(game.status, 'playing'); assert.deepEqual(game.winner, []);
|
||||
game = finishMentat(game);
|
||||
assert.deepEqual(game.winner, ['observer']); assert.equal(game.status, 'finished');
|
||||
});
|
||||
|
||||
|
||||
@@ -48,9 +48,14 @@ function reachMentat(game: Game): Game {
|
||||
g = applyAction(g, g.decision.player, { type: 'decision', done: true });
|
||||
return g;
|
||||
}
|
||||
function finishMentat(game: Game): Game {
|
||||
let g = game;
|
||||
for (const p of g.players) g = applyAction(g, p.id, { type: 'ready' });
|
||||
return g;
|
||||
}
|
||||
function offer(game: Game): NonNullable<Game['victoryContinuation']> {
|
||||
const pending = game.victoryContinuation;
|
||||
assert.ok(pending, 'The original Collection and Mentat transition offers a public agreement');
|
||||
assert.ok(pending, 'The Mentat victory check offers a public agreement');
|
||||
return pending;
|
||||
}
|
||||
function act(game: Game, player: string, mode: 'finish' | 'continue' | 'accept', extra?: number): Game {
|
||||
@@ -72,10 +77,12 @@ function completeVotes(game: Game, extra: number): Game {
|
||||
}
|
||||
|
||||
void test('Advanced public simultaneous victory waits for each qualifying participant and rejects other actions', () => {
|
||||
const game = reachMentat(position());
|
||||
const opened = reachMentat(position());
|
||||
assert.equal(opened.phase, 8); assert.ok(!opened.victoryContinuation);
|
||||
const game = finishMentat(opened);
|
||||
assert.equal(game.phase, 8); assert.equal(game.status, 'playing'); assert.deepEqual(game.winner, []);
|
||||
const pending = offer(game);
|
||||
assert.equal(pending.resume, 'mentat');
|
||||
assert.equal(pending.resume, 'advance');
|
||||
assert.deepEqual(pending.sides, [['ix'], ['at', 'em']]);
|
||||
assert.deepEqual(pending.voters, ['ix', 'at', 'em']);
|
||||
assert.deepEqual(pending.accepted, []);
|
||||
@@ -91,7 +98,7 @@ void test('Advanced public simultaneous victory waits for each qualifying partic
|
||||
});
|
||||
|
||||
void test('declining the common proposal settles the original winners and correct private prediction', () => {
|
||||
const game = reachMentat(position({ prediction: true }));
|
||||
const game = finishMentat(reachMentat(position({ prediction: true })));
|
||||
const vote = offer(game);
|
||||
assert.deepEqual(game.winner, []);
|
||||
const publicIx = viewGame(game, 'ix').victoryContinuation;
|
||||
@@ -105,11 +112,11 @@ void test('declining the common proposal settles the original winners and correc
|
||||
assert.ok(!next.victoryContinuation);
|
||||
});
|
||||
|
||||
void test('unanimous continuation raises the ordinary solo and alliance targets without losing Mentat', () => {
|
||||
const original = reachMentat(position());
|
||||
void test('unanimous continuation raises targets and advances once after completed Mentat', () => {
|
||||
const original = finishMentat(reachMentat(position()));
|
||||
const beforeForces = original.players.map(player => ({ id: player.id, forces: player.forces, reserves: player.reserves }));
|
||||
const next = completeVotes(original, 2);
|
||||
assert.equal(next.status, 'playing'); assert.equal(next.phase, 8); assert.equal(next.turn, original.turn);
|
||||
assert.equal(next.status, 'playing'); assert.equal(next.phase, 0); assert.equal(next.turn, original.turn + 1);
|
||||
assert.deepEqual(next.winner, []); assert.ok(!next.victoryContinuation);
|
||||
assert.equal(next.victoryExtraStrongholds, 2);
|
||||
assert.deepEqual(next.players.map(player => ({ id: player.id, forces: player.forces, reserves: player.reserves })), beforeForces);
|
||||
@@ -117,15 +124,11 @@ void test('unanimous continuation raises the ordinary solo and alliance targets
|
||||
assert.equal(progress.find(row => row.player === 'ix')?.target, 5);
|
||||
assert.equal(progress.find(row => row.player === 'at')?.target, 6);
|
||||
assert.equal(progress.find(row => row.player === 'at')?.qualifies, false);
|
||||
let completed = next;
|
||||
for (const p of completed.players) completed = applyAction(completed, p.id, { type: 'ready' });
|
||||
assert.equal(completed.turn, original.turn + 1);
|
||||
assert.equal(completed.victoryExtraStrongholds, 2);
|
||||
});
|
||||
|
||||
void test('zero-increase agreement preserves ordinary targets for the next turn', () => {
|
||||
const next = completeVotes(reachMentat(position()), 0);
|
||||
assert.equal(next.status, 'playing'); assert.equal(next.phase, 8);
|
||||
const next = completeVotes(finishMentat(reachMentat(position())), 0);
|
||||
assert.equal(next.status, 'playing'); assert.equal(next.phase, 0);
|
||||
assert.equal(next.victoryExtraStrongholds ?? 0, 0);
|
||||
const progress = viewGame(next, 'ix').victoryProgress;
|
||||
assert.equal(progress.find(row => row.player === 'ix')?.target, 3);
|
||||
@@ -138,13 +141,28 @@ void test('Basic multiple qualifiers can agree while one qualifying side still w
|
||||
assert.ok(basic.victoryContinuation);
|
||||
const settled = act(basic, basic.victoryContinuation.voters[0], 'finish');
|
||||
assert.equal(settled.status, 'finished');
|
||||
const single = reachMentat(position({ onlyAlliance: true }));
|
||||
const single = finishMentat(reachMentat(position({ onlyAlliance: true })));
|
||||
assert.equal(single.status, 'finished'); assert.ok(single.winner.length > 0);
|
||||
assert.ok(!single.victoryContinuation);
|
||||
});
|
||||
|
||||
void test('a saved legacy Advanced agreement resumes its original Mentat opportunity', () => {
|
||||
// Stage the old opening-time receipt using the still-supported Basic path.
|
||||
// This controlled saved frame is not a historical-room recovery proof.
|
||||
const game = reachMentat(position({ advanced: false }));
|
||||
assert.equal(offer(game).resume, 'mentat');
|
||||
game.advanced = true;
|
||||
assert.equal(game.mentatVictoryAfterActions, undefined);
|
||||
const next = completeVotes(game, 1);
|
||||
assert.equal(next.status, 'playing'); assert.equal(next.phase, 8);
|
||||
assert.equal(next.turn, game.turn); assert.ok(!next.victoryContinuation);
|
||||
const advanced = finishMentat(next);
|
||||
assert.equal(advanced.turn, game.turn + 1);
|
||||
assert.equal(advanced.phase, 0);
|
||||
});
|
||||
|
||||
void test('all four minimal bot profiles produce a legal response for the public continuation', () => {
|
||||
const game = reachMentat(position());
|
||||
const game = finishMentat(reachMentat(position()));
|
||||
for (const level of DIFFICULTIES) {
|
||||
const view = viewGame(game, 'ix');
|
||||
view.players.find(player => player.id === 'ix')!.bot = level;
|
||||
|
||||
@@ -53,6 +53,11 @@ function enterMentat(source: Game): Game {
|
||||
g = applyAction(g, g.decision.player, { type: 'decision', done: true });
|
||||
assert.equal(g.decision?.kind, 'choamMentat');
|
||||
g = applyAction(g, g.decision.player, { type: 'decision', done: true });
|
||||
} else if (g.advanced) {
|
||||
// Fresh Advanced Mentat evaluates victory after all ordinary opportunities.
|
||||
assert.equal(g.status, 'playing');
|
||||
assert.ok(!g.victoryContinuation);
|
||||
for (const p of g.players) g = applyAction(g, p.id, { type: 'ready' });
|
||||
}
|
||||
return g;
|
||||
}
|
||||
@@ -110,7 +115,7 @@ void test('unanimous turn-ten agreement opens real turn eleven without replaying
|
||||
const opening = enterMentat(original);
|
||||
const before = opening.players.map(p => ({ id: p.id, forces: p.forces, reserves: p.reserves }));
|
||||
const agreed = accept(opening, 1);
|
||||
assert.equal(agreed.turn, 10); assert.equal(agreed.phase, 8);
|
||||
assert.equal(agreed.turn, 11); assert.equal(agreed.phase, 0);
|
||||
assert.equal(agreed.victoryFinalTurn, 11); assert.equal(agreed.victoryExtraStrongholds, 1);
|
||||
assert.deepEqual(agreed.players.map(p => ({ id: p.id, forces: p.forces, reserves: p.reserves })), before);
|
||||
const next = finishMentat(agreed);
|
||||
@@ -151,6 +156,9 @@ void test('accepted turn eleven follows native phases through its next Mentat ch
|
||||
}
|
||||
assert.equal(game.turn, 11);
|
||||
assert.equal(game.phase, 8, 'The original phase sequence reaches the next Mentat check');
|
||||
assert.equal(game.status, 'playing');
|
||||
assert.ok(!game.victoryContinuation);
|
||||
for (const p of game.players) game = actAdvancedRevivalOrder(game, p.id, { type: 'ready' }, actions);
|
||||
assert.ok(game.status === 'finished' || game.victoryContinuation,
|
||||
'The extended final-turn check settles a winner or offers the next agreement');
|
||||
assert.equal(game.victoryFinalTurn, 11);
|
||||
|
||||
@@ -62,6 +62,10 @@ function actual(g: Game) {
|
||||
g = structuredClone(g);
|
||||
g.phase = 7;
|
||||
for (const p of g.players) g = applyAction(g, p.id, { type: 'ready' });
|
||||
if (g.advanced && g.phase === 8 && g.status === 'playing' && !g.victoryContinuation) {
|
||||
// Fresh Advanced games check the quote after every Mentat opportunity.
|
||||
for (const p of g.players) g = applyAction(g, p.id, { type: 'ready' });
|
||||
}
|
||||
return g;
|
||||
}
|
||||
function compare(g: Game, winner: string[]) {
|
||||
|
||||
Reference in new issue
Block a user