Exercise Homeworld games across two to six seats
This commit is contained in:
1 parent
628c536218
commit
6124618d42
3 files changed
+52
-23
No files matched your search
@@ -97,7 +97,7 @@ node --import tsx tools/faction-games.ts --out /tmp/dune-new-moritani-skills --p
|
||||
node --import tsx tools/faction-games.ts --out /tmp/dune-new-tleilaxu-skills --profile tleilaxu-skills
|
||||
node --import tsx tools/faction-games.ts --out /tmp/dune-new-base-three --profile base --players 3 --rules advanced
|
||||
node --import tsx tools/faction-games.ts --out /tmp/dune-new-combined --profile combined --rules advanced --seed 20260926
|
||||
# Homeworld (four base factions) and Nexus (two through six), Basic and Advanced.
|
||||
# Homeworld and Nexus with two through six base factions, Basic and Advanced.
|
||||
node --import tsx tools/faction-games.ts --out /tmp/dune-new-homeworld --profile homeworld
|
||||
node --import tsx tools/faction-games.ts --out /tmp/dune-new-nexus --profile nexus
|
||||
node --import tsx tools/faction-games.ts --out /tmp/dune-new-nexus-three --profile nexus --players 3 --rules advanced
|
||||
@@ -107,15 +107,16 @@ node --import tsx tools/faction-games.ts --out /tmp/dune-new-resume --resume /pr
|
||||
Default and `--profile all` retain the original six fixed CHOAM/Richese,
|
||||
Ecaz/Moritani and combined Basic/Advanced samples. `--profile base` selects ten
|
||||
additional samples: Basic and Advanced with two through six players.
|
||||
`--players 2..6` narrows the base or Nexus profile; omission or `all` retains
|
||||
all five counts. The fixed base roster adds Atreides, Harkonnen, Fremen,
|
||||
Emperor, Guild and Bene Gesserit in that order. These are sample rosters, not
|
||||
an assertion that these are the only rules-permitted player-count
|
||||
configurations. The `homeworld` profile runs four base factions; `nexus` runs
|
||||
two through six, each under Basic and Advanced using the actual
|
||||
optional-module setup seams. Nexus output names include the player count;
|
||||
the six-seat random seed offsets remain 38–39, and smaller rosters use 40–47.
|
||||
They do not open public module starts or assert complete module interaction
|
||||
`--players 2..6` narrows the base, Homeworld or Nexus profile; omission or
|
||||
`all` retains all five counts. The fixed base roster adds Atreides, Harkonnen,
|
||||
Fremen, Emperor, Guild and Bene Gesserit in that order. These are sample
|
||||
rosters, not an assertion that these are the only rules-permitted player-count
|
||||
configurations. Homeworld and Nexus each run two through six base factions
|
||||
under Basic and Advanced using the actual optional-module setup seams.
|
||||
Their output names include the player count. Homeworld's original four-seat
|
||||
seed offsets remain 36–37; its other rosters use 48–51 and 54–57. Nexus's
|
||||
six-seat offsets remain 38–39; its smaller rosters use 40–47. These samples
|
||||
do not open public module starts or assert complete module interaction
|
||||
coverage.
|
||||
|
||||
All-AI Nexus samples now make public reciprocal offers as well as accepting
|
||||
@@ -128,6 +129,12 @@ three-seat Advanced sample and five-seat Basic sample reached actual
|
||||
the subsequent draw when a seat remains unallied. These samples do not
|
||||
establish complete card-effect coverage.
|
||||
|
||||
At the 20260927 seed, all ten Homeworld samples completed with no rejected
|
||||
actions and repeated JSON restores. The six-seat Basic game reached actual
|
||||
Junction offers/shipments and a Caladan reinforcement decision; these
|
||||
observations do not certify every Homeworld occupation or expansion
|
||||
interaction.
|
||||
|
||||
`--profile moritani-skills` adds five Basic samples with Moritani, then Emperor,
|
||||
Guild, Harkonnen, Fremen and Bene Gesserit. `--players` also narrows this profile;
|
||||
Advanced requests fail before running. Its seed offsets are 16–20, leaving
|
||||
|
||||
@@ -180,8 +180,8 @@ void test('Homeworld and Nexus sample snapshots resume only their matching modul
|
||||
['nexus', 39, 'nexusCards', 'homeworlds'],
|
||||
] as const) {
|
||||
const out = join(area, profile);
|
||||
const name = profile === 'nexus' ? 'nexus-6-advanced' : 'homeworld-advanced';
|
||||
const playerFilter = profile === 'nexus' ? ['--players', '6'] : [];
|
||||
const name = profile === 'nexus' ? 'nexus-6-advanced' : 'homeworld-4-advanced';
|
||||
const playerFilter = ['--players', profile === 'nexus' ? '6' : '4'];
|
||||
assert.equal(run(out, '--profile', profile, '--rules', 'advanced', ...playerFilter, '--seed', '1000', '--max-actions', '1').status, 1);
|
||||
assertFailedEvidence(out, name, 1000 + ordinal, false);
|
||||
const snapshot = join(out, `failed-${name}.json`);
|
||||
@@ -200,6 +200,26 @@ void test('Homeworld and Nexus sample snapshots resume only their matching modul
|
||||
}
|
||||
});
|
||||
|
||||
void test('Homeworld samples cover each two-to-six-seat roster under both rules and resume the two-seat configuration', (t) => {
|
||||
const area = temporary(t);
|
||||
const out = join(area, 'homeworld');
|
||||
assert.equal(run(out, '--profile', 'homeworld', '--seed', '1000', '--max-actions', '1').status, 1);
|
||||
assert.deepEqual(json<{ results: CliResult[] }>(join(out, 'results.json')).results.map(({ name, seed }) => [name, seed]), [
|
||||
['homeworld-2-basic', 1048], ['homeworld-2-advanced', 1049],
|
||||
['homeworld-3-basic', 1050], ['homeworld-3-advanced', 1051],
|
||||
['homeworld-4-basic', 1036], ['homeworld-4-advanced', 1037],
|
||||
['homeworld-5-basic', 1054], ['homeworld-5-advanced', 1055],
|
||||
['homeworld-6-basic', 1056], ['homeworld-6-advanced', 1057],
|
||||
]);
|
||||
const snapshot = join(out, 'failed-homeworld-2-advanced.json');
|
||||
const game = json<{ players: unknown[]; homeworlds: unknown }>(snapshot);
|
||||
assert.equal(game.players.length, 2);
|
||||
assert.ok(game.homeworlds);
|
||||
const resumed = join(area, 'resumed');
|
||||
assert.equal(run(resumed, '--resume', snapshot, '--seed', '1000', '--max-actions', '1').status, 1);
|
||||
assertFailedEvidence(resumed, 'homeworld-2-advanced', 1049, true);
|
||||
});
|
||||
|
||||
void test('three-seat Nexus samples preserve their module and roster across saved continuation', (t) => {
|
||||
const area = temporary(t);
|
||||
for (const [rules, ordinal] of [['basic', 42], ['advanced', 43]] as const) {
|
||||
|
||||
+13
-11
@@ -103,13 +103,15 @@ const BASE_SCENARIOS: readonly Scenario[] = [2, 3, 4, 5, 6].flatMap((players) =>
|
||||
})),
|
||||
);
|
||||
const MODULE_SCENARIOS: readonly Scenario[] = [
|
||||
...(['basic', 'advanced'] as const).map((rules, index) => ({
|
||||
ordinal: 36 + index,
|
||||
profile: 'homeworld' as const,
|
||||
rules,
|
||||
expansions: [],
|
||||
roster: BASE_ROSTER.slice(0, 4),
|
||||
})),
|
||||
...[2, 3, 4, 5, 6].flatMap((players) =>
|
||||
(['basic', 'advanced'] as const).map((rules, index) => ({
|
||||
ordinal: players === 4 ? 36 + index : 48 + (players - 2) * 2 + index,
|
||||
profile: 'homeworld' as const,
|
||||
rules,
|
||||
expansions: [],
|
||||
roster: BASE_ROSTER.slice(0, players),
|
||||
})),
|
||||
),
|
||||
...[2, 3, 4, 5, 6].flatMap((players) =>
|
||||
(['basic', 'advanced'] as const).map((rules, index) => ({
|
||||
ordinal: players === 6 ? 38 + index : 40 + (players - 2) * 2 + index,
|
||||
@@ -195,7 +197,7 @@ function usage() {
|
||||
'[--seed UINT32] [--profile all|base|choam|ecaz|combined|homeworld|nexus|moritani-skills|tleilaxu-skills|ix-skills|choam-skills] ' +
|
||||
'[--rules both|basic|advanced] [--players all|2|3|4|5|6] [--max-actions POSITIVE] ' +
|
||||
'[--resume FAILED_GAME.json]\n' +
|
||||
'Runs genuine setup and gameplay offline. Default/all keeps the six expansion samples; base, Nexus and skill profiles default to their 2–6-player samples. Homeworld uses four base factions. --players requires --profile base, nexus or a skill profile. Output must be a new private directory outside the checkout.'
|
||||
'Runs genuine setup and gameplay offline. Default/all keeps the six expansion samples; base, Homeworld, Nexus and skill profiles default to their 2–6-player samples. --players requires one of these profiles. Output must be a new private directory outside the checkout.'
|
||||
);
|
||||
}
|
||||
|
||||
@@ -226,7 +228,7 @@ function positive(value: string | undefined) {
|
||||
}
|
||||
|
||||
function scenarioName(scenario: Scenario) {
|
||||
return scenario.profile === 'base' || scenario.profile === 'nexus' || skillProfile(scenario.profile)
|
||||
return scenario.profile === 'base' || scenario.profile === 'homeworld' || scenario.profile === 'nexus' || skillProfile(scenario.profile)
|
||||
? `${scenario.profile}-${scenario.roster.length}-${scenario.rules}`
|
||||
: `${scenario.profile}-${scenario.rules}`;
|
||||
}
|
||||
@@ -538,8 +540,8 @@ async function main() {
|
||||
const profile = parseProfile(values.profile);
|
||||
const rules = parseRules(values.rules);
|
||||
const players = parsePlayers(values.players);
|
||||
if (supplied('players') && profile !== 'base' && profile !== 'nexus' && !skillProfile(profile))
|
||||
throw new Error('--players requires --profile base, nexus or a skill profile.');
|
||||
if (supplied('players') && !['base', 'homeworld', 'nexus'].includes(profile) && !skillProfile(profile))
|
||||
throw new Error('--players requires --profile base, homeworld, nexus or a skill profile.');
|
||||
if (skillProfile(profile) && rules === 'advanced')
|
||||
throw new Error('Expansion Leader Skills profiles currently support only Basic samples.');
|
||||
const resume = values.resume ? resumedGame(values.resume) : null;
|
||||
|
||||
Reference in new issue
Block a user