Connect printed high Ecaz Homeworld alliance victory
This commit is contained in:
1 parent
cfa0fe3952
commit
17bc4537c3
14 files changed
+878
-8
No files matched your search
@@ -0,0 +1,92 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import { applyAction, createGame, initializeHomeworldOccupationGameForAudit, joinGame, newPlayer, type Game } from '../game/engine';
|
||||
import { FACTIONS, type FactionId } from '../game/catalog';
|
||||
import { TERRITORIES } from '../game/board';
|
||||
import { homeworldContext, homeworldGameIntegrity } from '../game/homeworld-game';
|
||||
import { homeworldForceGroups } from '../game/homeworld-custody';
|
||||
import { observeHomeworldOccupation } from '../game/homeworld-occupation-history';
|
||||
import { advanceOriginal } from './fixture-homeworld-occupied-producers';
|
||||
|
||||
export const ecazSeat = (game: Game, id: string) => game.players.find(player => player.id === id)!;
|
||||
|
||||
/** Controlled positions are conserved test placements, NOT naturally achieved
|
||||
* games. Setup and source history are original; no synthetic source facts,
|
||||
* replacement deck, optional cards or fabricated occupation receipt is used. */
|
||||
export function conserveEcazPosition(game: Game): void {
|
||||
const groups = homeworldForceGroups(homeworldContext(game), game.homeworlds!.custody!);
|
||||
for (const player of game.players) {
|
||||
const abroad = groups.filter(home => home.native !== player.id)
|
||||
.reduce((n, home) => n + (home.forces[player.id]?.normal ?? 0) + (home.forces[player.id]?.elite ?? 0), 0);
|
||||
player.tanks = 20 - player.reserves - abroad - Object.values(player.forces).reduce((a, n) => a + n, 0);
|
||||
if (player.elites) {
|
||||
const total = player.faction === 'fremen' ? 3 : player.faction === 'ixians' ? 7 : 5;
|
||||
const abroadElite = groups.filter(home => home.native !== player.id)
|
||||
.reduce((n, home) => n + (home.forces[player.id]?.elite ?? 0), 0);
|
||||
player.elites.tanks = total - player.elites.reserves - abroadElite - Object.values(player.elites.forces).reduce((a, n) => a + n, 0);
|
||||
}
|
||||
}
|
||||
homeworldGameIntegrity(game);
|
||||
}
|
||||
|
||||
export function observeEcazPosition(game: Game, event: string, cause: 'change' | 'turnStart' | 'turnEnd' = 'change'): void {
|
||||
conserveEcazPosition(game);
|
||||
game.homeworldOccupationHistory = observeHomeworldOccupation(game.homeworldOccupationHistory!,
|
||||
homeworldContext(game), game.homeworlds!.custody!, game.turn, cause, event);
|
||||
}
|
||||
|
||||
export function ecazHomeworldFixture(advanced = true, ally: FactionId = 'guild', holders: readonly ['ec' | 'al', 'ec' | 'al'] = ['ec', 'al']) {
|
||||
const factions = ['ecaz', ally, 'atreides', 'emperor', ...(ally === 'beneGesserit' ? ['guild'] : ['beneGesserit'])] as FactionId[];
|
||||
const expansions = [...new Set(factions.map(faction => FACTIONS.find(row => row.id === faction)!.expansion)
|
||||
.filter(expansion => expansion !== 'base'))];
|
||||
let game = createGame('ECAZHOMEWORLDVICTORY', newPlayer('ec', 'Ecaz', 'ecaz'), advanced, expansions);
|
||||
for (const [index, faction] of factions.slice(1).entries())
|
||||
joinGame(game, newPlayer(['al', 'at', 'em', 'observer'][index], faction, faction));
|
||||
game = applyAction(game, 'ec', { type: 'homeworlds', enabled: true });
|
||||
for (const player of game.players) game = applyAction(game, player.id, { type: 'ready' });
|
||||
game = initializeHomeworldOccupationGameForAudit(game);
|
||||
game = advanceOriginal(game, current => current.status === 'playing');
|
||||
const setup = structuredClone(game);
|
||||
game = advanceOriginal(game, current => current.phase === 5 && !current.response && !current.decision);
|
||||
const movement = structuredClone(game);
|
||||
const cards = [...game.deck, ...game.discard, ...(game.richeseCache ?? []), ...(game.richeseRemoved ?? []),
|
||||
...game.players.flatMap(player => player.hand)].map(card => card.id).sort();
|
||||
const originalTanks = Object.fromEntries(game.players.map(player => [player.id, {
|
||||
normal: player.tanks, elite: player.elites?.tanks ?? 0,
|
||||
}]));
|
||||
for (const player of game.players) {
|
||||
Object.assign(player, { forces: {}, reserves: 20 - player.tanks, ally: null });
|
||||
delete player.advisors;
|
||||
if (player.elites) Object.assign(player.elites, {
|
||||
reserves: (player.faction === 'ixians' ? 7 : player.faction === 'fremen' ? 3 : 5) - player.elites.tanks,
|
||||
forces: {},
|
||||
});
|
||||
}
|
||||
const joint = TERRITORIES.find(site => site.type === 'stronghold' && !site.sectors.includes(game.storm))!;
|
||||
assert.ok(joint, 'Original Movement must have a non-storm joint stronghold.');
|
||||
const jointStronghold = joint.id;
|
||||
const jointLocation = `${joint.id}:${joint.sectors[0]}`;
|
||||
const custody = game.homeworlds!.custody!;
|
||||
custody.visitors = {
|
||||
'homeworld:atreides': { [holders[0]]: { normal: 1, elite: 0 } },
|
||||
'homeworld:emperor': { [holders[1]]: { normal: 1, elite: 0 } },
|
||||
};
|
||||
if (advanced) custody.salusa = { normal: 0, elite: 0 };
|
||||
ecazSeat(game, 'ec').ally = 'al';
|
||||
ecazSeat(game, 'al').ally = 'ec';
|
||||
ecazSeat(game, 'ec').forces = { [jointLocation]: 1 };
|
||||
ecazSeat(game, 'al').forces = { [jointLocation]: 1 };
|
||||
ecazSeat(game, 'ec').reserves = 7;
|
||||
ecazSeat(game, 'al').reserves = 20 - originalTanks.al.normal - 1 - holders.filter(holder => holder === 'al').length;
|
||||
for (const id of ['at', 'em']) {
|
||||
ecazSeat(game, id).reserves = 0;
|
||||
if (ecazSeat(game, id).elites) ecazSeat(game, id).elites!.reserves = 0;
|
||||
}
|
||||
observeEcazPosition(game, 'controlled-conserved-ecaz-two-native-worlds');
|
||||
for (const player of game.players) {
|
||||
assert.ok(player.tanks >= originalTanks[player.id].normal, 'Controlled position cannot resurrect original Tanks.');
|
||||
assert.ok((player.elites?.tanks ?? 0) >= originalTanks[player.id].elite, 'Controlled position cannot resurrect original special Tanks.');
|
||||
}
|
||||
assert.deepEqual([...game.deck, ...game.discard, ...(game.richeseCache ?? []), ...(game.richeseRemoved ?? []),
|
||||
...game.players.flatMap(player => player.hand)].map(card => card.id).sort(), cards);
|
||||
return { game, setup, movement, cards, jointStronghold, jointLocation };
|
||||
}
|
||||
Reference in new issue
Block a user