Initial commit
This commit is contained in:
commit
8c709fb52f
771 files changed
+222983
No files matched your search
@@ -0,0 +1,107 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import {
|
||||
applyAction,
|
||||
createGame,
|
||||
newPlayer,
|
||||
viewGame,
|
||||
type Game,
|
||||
} from '../game/engine';
|
||||
import { baseDeck, ixBattleCards } from '../game/cards';
|
||||
import { botActions } from '../game/bots';
|
||||
import { DIFFICULTIES } from '../game/bot-profiles';
|
||||
|
||||
const cards = [...baseDeck(), ...ixBattleCards()];
|
||||
const card = (kind: string) => cards.find((c) => c.kind === kind)!;
|
||||
function fixture() {
|
||||
let g = createGame('PLANFIX', newPlayer('p', 'Planner', 'emperor'));
|
||||
g.players.push(newPlayer('q', 'Inspector', 'atreides'));
|
||||
Object.assign(g, {
|
||||
status: 'playing',
|
||||
phase: 6,
|
||||
turn: 2,
|
||||
storm: 18,
|
||||
active: 'p',
|
||||
order: ['p', 'q'],
|
||||
});
|
||||
for (const p of g.players)
|
||||
Object.assign(p, {
|
||||
hand: [],
|
||||
spice: 10,
|
||||
forces: { 'arrakeen:10': 4 },
|
||||
reserves: 16,
|
||||
traitors: [],
|
||||
});
|
||||
g.players[0].hand = ['worthless', 'projectile', 'snooper', 'chemistry'].map(
|
||||
card,
|
||||
);
|
||||
g = applyAction(g, 'p', {
|
||||
type: 'chooseBattle',
|
||||
territory: 'arrakeen',
|
||||
target: 'q',
|
||||
});
|
||||
assert.equal(g.battle!.preparation!.kind, 'prescience');
|
||||
return g;
|
||||
}
|
||||
function asked(field: string) {
|
||||
return applyAction(fixture(), 'q', { type: 'prescience', field });
|
||||
}
|
||||
function offered(g: Game) {
|
||||
return DIFFICULTIES.map((difficulty) => {
|
||||
const v = viewGame(g, 'p');
|
||||
v.players[0].bot = difficulty;
|
||||
const before = structuredClone(v),
|
||||
actions = botActions(v);
|
||||
assert.deepEqual(v, before);
|
||||
assert.ok(actions.length, difficulty);
|
||||
for (const action of actions)
|
||||
assert.doesNotThrow(
|
||||
() => applyAction(g, 'p', action),
|
||||
`${difficulty}: ${JSON.stringify(action)}`,
|
||||
);
|
||||
return actions;
|
||||
});
|
||||
}
|
||||
void test('Prescience fixed weapon and defense retain physical slot uniqueness and valid alternate roles for every profile', () => {
|
||||
for (const [field, value] of [
|
||||
['weapon', card('worthless').id],
|
||||
['defense', card('chemistry').id],
|
||||
] as const) {
|
||||
const g = applyAction(asked(field), 'p', {
|
||||
type: 'prescienceAnswer',
|
||||
value,
|
||||
});
|
||||
for (const actions of offered(g)) {
|
||||
assert.ok(actions.every((a) => a[field] === value));
|
||||
assert.ok(actions.every((a) => !a.weapon || a.weapon !== a.defense));
|
||||
}
|
||||
}
|
||||
});
|
||||
void test('Prescience committed leader and dial constrain the final plans without inventing leaderless combinations', () => {
|
||||
for (const [field, value] of [
|
||||
['leader', 'emperor-4'],
|
||||
['dial', 2],
|
||||
] as const) {
|
||||
const g = applyAction(asked(field), 'p', {
|
||||
type: 'prescienceAnswer',
|
||||
value,
|
||||
});
|
||||
for (const actions of offered(g)) {
|
||||
assert.ok(actions.every((a) => a[field] === value));
|
||||
assert.ok(actions.every((a) => a.leader !== null));
|
||||
}
|
||||
}
|
||||
});
|
||||
void test('every offered Prescience answer has an authoritative completion and ignores unrevealed opposing cards', () => {
|
||||
for (const field of ['weapon', 'defense', 'leader', 'dial']) {
|
||||
const g = asked(field),
|
||||
changed = structuredClone(g);
|
||||
changed.players[1].hand = [card('poison'), card('shield')];
|
||||
changed.players[1].spice = 999;
|
||||
const a = offered(g),
|
||||
b = offered(changed);
|
||||
assert.deepEqual(a, b);
|
||||
for (const actions of a)
|
||||
assert.ok(actions.every((action) => action.type === 'prescienceAnswer'));
|
||||
}
|
||||
});
|
||||
Reference in new issue
Block a user