Connect occupied Homeworld economics and Bidding
This commit is contained in:
1 parent
a6e80c7e23
commit
0774ae6f83
32 files changed
+3220
-111
No files matched your search
@@ -0,0 +1,72 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import { applyAction, createGame, initializeHomeworldOccupationGameForAudit, joinGame, newPlayer, viewGame, type Action, type Game } from '../game/engine';
|
||||
import { botActions } from '../game/bots';
|
||||
|
||||
/** Drive original phase/control producers, never synthesize a paid receipt or
|
||||
* advance the phase clock by assignment. Tests may stop before any choice. */
|
||||
export function advanceOriginal(game: Game, stop: (game: Game) => boolean, limit = 600): Game {
|
||||
for (let step = 0; (game.phaseOpening || !stop(game)) && step < limit; step++) {
|
||||
let choice: { player: string; action: Action } | undefined;
|
||||
if (game.phaseOpening) {
|
||||
const player = game.players.find(player => !game.phaseOpening!.passed.includes(player.id));
|
||||
if (player) choice = { player: player.id, action: { type: 'ready' } };
|
||||
} else if (game.response) {
|
||||
const player = game.players.find(player => !game.response!.passed.includes(player.id));
|
||||
if (player) choice = { player: player.id, action: { type: 'passResponse' } };
|
||||
}
|
||||
if (!choice) for (const player of game.players) {
|
||||
const view = viewGame(game, player.id);
|
||||
view.players.find(seat => seat.id === player.id)!.bot = 'Easy';
|
||||
const action = botActions(view)[0];
|
||||
if (!action) continue;
|
||||
if (action.type === 'stormDial') action.amount = 0;
|
||||
choice = { player: player.id, action };
|
||||
break;
|
||||
}
|
||||
assert.ok(choice, 'original source progression must have a legal next action');
|
||||
game = applyAction(game, choice.player, choice.action);
|
||||
}
|
||||
assert.ok(!game.phaseOpening && stop(game), 'original source progression must reach its settled requested boundary');
|
||||
return game;
|
||||
}
|
||||
|
||||
export function buyOriginalNormalLot(game: Game, buyer: string, amount: number): Game {
|
||||
assert.ok(game.auction && !game.phaseOpening && !game.response && !game.decision);
|
||||
for (let step = 0; game.auction!.active !== buyer && step < game.players.length; step++)
|
||||
game = applyAction(game, game.auction!.active, { type: 'passBid' });
|
||||
assert.equal(game.auction!.active, buyer);
|
||||
game = applyAction(game, buyer, { type: 'bid', amount, allyPayment: 0 });
|
||||
for (let step = 0; !game.currentAuctionSale && step < game.players.length + 2; step++) {
|
||||
if (game.decision?.kind === 'auctionPayment')
|
||||
game = applyAction(game, buyer, { type: 'decision', karama: false });
|
||||
else game = applyAction(game, game.auction!.active, { type: 'passBid' });
|
||||
}
|
||||
assert.ok(game.currentAuctionSale, 'the original purchase producer must own its paid invoice');
|
||||
return game;
|
||||
}
|
||||
|
||||
export function cash(game: Game): Record<string, number> {
|
||||
return Object.fromEntries(game.players.map(player => [player.id, player.spice]));
|
||||
}
|
||||
|
||||
/** Fresh original Collection roster includes Ecaz shared legs and a real
|
||||
* Harkonnen occupier; the ordinary setup inventory is never replaced. */
|
||||
export function freshSouthernProducerGame(): Game {
|
||||
let game = createGame('SOUTHERNPRODUCER', newPlayer('native', 'Fremen', 'fremen'), true, ['ecaz']);
|
||||
for (const [id, faction] of [['occupier', 'harkonnen'], ['ally', 'guild'], ['ecaz', 'ecaz'], ['atreides', 'atreides']] as const)
|
||||
joinGame(game, newPlayer(id, id, faction));
|
||||
game = applyAction(game, 'native', { type: 'homeworlds', enabled: true });
|
||||
for (const player of game.players) game = applyAction(game, player.id, { type: 'ready' });
|
||||
game = initializeHomeworldOccupationGameForAudit(game);
|
||||
return advanceOriginal(game, game => game.status === 'playing');
|
||||
}
|
||||
|
||||
/** Original native Guild payment can coexist with a BG accompaniment choice. */
|
||||
export function freshGuildAdvisorProducerGame(): Game {
|
||||
let game = createGame('GUILDADVISORPRODUCER', newPlayer('native', 'Guild', 'guild'), true);
|
||||
for (const [id, faction] of [['occupier', 'atreides'], ['observer', 'beneGesserit'], ['buyer', 'emperor']] as const)
|
||||
joinGame(game, newPlayer(id, id, faction));
|
||||
game = applyAction(game, 'native', { type: 'homeworlds', enabled: true });
|
||||
for (const player of game.players) game = applyAction(game, player.id, { type: 'ready' });
|
||||
return advanceOriginal(initializeHomeworldOccupationGameForAudit(game), game => game.status === 'playing');
|
||||
}
|
||||
Reference in new issue
Block a user