Exercise all-expansion Homeworld and Nexus games

This commit is contained in:
Quintine committed 2026-09-28 20:54:52 +10:00
1 parent 068da48734
commit 88829b7057
5 files changed
+62 -25

No files matched your search

+8
View File
@@ -135,6 +135,14 @@ and five Nexus Card draw/keep decisions in the five-seat games. No Nexus
effect was played in those samples; card-specific combinations and public
expansion/Nexus starts remain gated.
Four more [all-expansion Homeworld/Nexus samples](IMPLEMENTATION_STATUS.md)
completed five/six-seat Basic and Advanced games with 2,800 accepted
actions, 74 JSON/private-view restores and unchanged source fingerprints.
Five-seat tables drew/kept three Nexus Cards and used real Homeworld
deployment decisions. Eight proposed entries were rejected by the existing
Ecaz Ambassador/Moritani Terror overlap gate; bots found alternate actions
and completed. Their unresolved priority and all public mode gates remain.
The bounded [native Richese Nexus Cunning shipment](NEXUS_RICHESE_RULES.md#bounded-native-cunning-prototype-27-september-2026) now uses two distinct physical No-Field tokens in one priced shipment in genuine Richese/CHOAM paired Nexus games. One token immediately reveals a reserve-capped public force group; the other remains concealed. Owner-only controls, four legal AI profiles, Karama/Guild responses, signed saved receipts and concurrent in-memory SQLite settlement are connected in Basic/Advanced. A read-only local browser fixture displayed the owner selector and no rival private panel. Same-shipment nonrepeat is a documented product interpretation; faceup-component artwork, combined modules, Betrayal and full Nexus acceptance remain gated. This code is local and not in the last verified production image.
The independent [Reinforcements Battle Plan prototype](REINFORCEMENTS_RUNTIME.md)
+18
View File
@@ -1,5 +1,23 @@
# Dune implementation status
## 28 September 2026 — all-expansion Homeworld/Nexus samples
The offline combined audit setup can also enable genuine Homeworld custody with
Nexus Cards and all three expansion decks; public starts remain unchanged.
Five- and six-seat Basic/Advanced games all completed: 2,800 accepted actions,
74 JSON/private-view restores and unchanged source fingerprints. Five-seat
tables reached three Nexus Card draw/keep choices and six actual Homeworld
revival-deployment decisions. Eight proposed shipments/moves into public
Ecaz Ambassador and Moritani Terror overlaps were rejected without mutation;
bot continuations found other legal actions. The published arrival-priority
question remains unresolved. An intentionally capped ten-action Advanced save
resumed to turn-five victory with 877 accepted actions and 23 more restores.
The source-bound matrix and replay reports are in
`/tmp/dune-combined-homeworld-nexus-matrix/report.json` and
`/tmp/dune-homeworld-nexus-resume-finish/report.json`. These sample wins do not
certify individual card effects, every Homeworld entitlement or combined
expansion publication.
## 28 September 2026 — five/six-seat all-expansion Nexus integration samples
The offline-only `combined-nexus` audit setup selects all three physical
+9 -4
View File
@@ -115,6 +115,7 @@ node --import tsx tools/faction-games.ts --out /tmp/dune-new-choam-nexus --profi
node --import tsx tools/faction-games.ts --out /tmp/dune-new-ecaz-nexus --profile ecaz-nexus
node --import tsx tools/faction-games.ts --out /tmp/dune-new-ix-nexus --profile ix-nexus
node --import tsx tools/faction-games.ts --out /tmp/dune-new-combined-nexus --profile combined-nexus
node --import tsx tools/faction-games.ts --out /tmp/dune-new-combined-homeworld-nexus --profile combined-homeworld-nexus
node --import tsx tools/faction-games.ts --out /tmp/dune-new-nexus-three --profile nexus --players 3 --rules advanced
node --import tsx tools/faction-games.ts --out /tmp/dune-new-resume --resume /private/failed-combined-advanced.json
```
@@ -124,8 +125,8 @@ 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, Homeworld, Nexus, combined
`homeworld-nexus`, expansion-roster or paired-Nexus profiles. The
`combined-nexus` profile supports `--players 5` or `6` only. Omission or `all`
retains every roster in a selected profile. The fixed
`combined-nexus` and `combined-homeworld-nexus` profiles support `--players 5`
or `6` only. Omission or `all` retains every roster in a selected profile. The fixed
base roster adds Atreides, Harkonnen, Fremen, Emperor, Guild and Bene Gesserit
in that order. These are sample rosters, not the only rules-permitted
player-count configurations. Each optional module profile runs two through
@@ -140,8 +141,12 @@ rosters with Nexus Cards enabled at seed offsets 98–107, 108–117 and 118–1
`combined-nexus` selects five or six actual expansion factions and all three
expansion decks with Nexus Cards in Basic and Advanced. Five seats leave Richese
absent so a player can remain unallied and draw; six seats contain every expansion
faction. Their seed offsets are 130–131 and 128–129 respectively. The original
six default samples are unchanged. Output names include the player count.
faction. Their seed offsets are 130–131 and 128–129 respectively.
`combined-homeworld-nexus` reuses the same four rosters with physical Homeworld
custody added, at offsets 132–135. Its public arrival-priority gate can reject a
route where Ecaz Ambassadors and Moritani Terror both react; the bot must choose
another legal route. Neither profile establishes those pending source priorities.
The original six default samples are unchanged. 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. The combined module uses offsets 58–67. These samples
+5 -5
View File
@@ -6716,8 +6716,8 @@ export function initializePairedNexusGameForAudit(state: Game): Game {
return initializeSetupGameForAudit(state, false, true, false, false, false, false, true);
}
/** Offline-only all-expansion Nexus composition; not a public start gate. */
export function initializeCombinedNexusGameForAudit(state: Game): Game {
/** Offline-only all-expansion Nexus composition, optionally with Homeworlds; not a public start gate. */
export function initializeCombinedNexusGameForAudit(state: Game, homeworlds = false): Game {
requireRule(state.expansions.length === 3 &&
['ix', 'choam', 'ecaz'].every(expansion => state.expansions.includes(expansion)) &&
[5, 6].includes(state.players.length) &&
@@ -6726,10 +6726,10 @@ export function initializeCombinedNexusGameForAudit(state: Game): Game {
.every(faction => state.players.some(player => player.faction === faction)),
'The combined Nexus sample requires five or six expansion factions and their three decks.');
requireRule(!!state.nexusCards && state.nexusCards.cards === null && state.nexusCards.phase === null &&
!state.homeworlds && !state.leaderSkills && !state.discoveryEnabled &&
!!state.homeworlds === homeworlds && !state.leaderSkills && !state.discoveryEnabled &&
!state.ecazTreachery && !state.techTokens && !state.strongholdCards,
'Enable only Nexus Cards in a fresh combined expansion lobby.');
return initializeSetupGameForAudit(state, false, true, false, false, false, false, true);
'Enable only the selected Nexus and Homeworld modules in a fresh combined expansion lobby.');
return initializeSetupGameForAudit(state, homeworlds, true, false, false, false, false, true);
}
/** Offline prototype entry through real Ix setup, including both expansion factions.
+22 -16
View File
@@ -30,7 +30,7 @@ import { privateOutputDirectory, sourceSnapshot } from './verification';
const DEFAULT_SEED = 20_260_926;
const DEFAULT_MAX_ACTIONS = 3_500;
const DIFFICULTIES = ['Easy', 'Medium', 'Hard', 'Brutal'] as const;
type Profile = 'base' | 'choam' | 'ecaz' | 'combined' | 'combined-nexus' | 'homeworld' | 'nexus' | 'homeworld-nexus' | 'choam-roster' | 'ecaz-roster' | 'ix-roster' | 'choam-nexus' | 'ecaz-nexus' | 'ix-nexus' | 'moritani-skills' | 'tleilaxu-skills' | 'ix-skills' | 'choam-skills';
type Profile = 'base' | 'choam' | 'ecaz' | 'combined' | 'combined-nexus' | 'combined-homeworld-nexus' | 'homeworld' | 'nexus' | 'homeworld-nexus' | 'choam-roster' | 'ecaz-roster' | 'ix-roster' | 'choam-nexus' | 'ecaz-nexus' | 'ix-nexus' | 'moritani-skills' | 'tleilaxu-skills' | 'ix-skills' | 'choam-skills';
type Rules = 'basic' | 'advanced';
type Scenario = {
@@ -161,6 +161,11 @@ const COMBINED_NEXUS_SCENARIOS: readonly Scenario[] = [
})),
];
const COMBINED_HOMEWORLD_NEXUS_SCENARIOS: readonly Scenario[] =
COMBINED_NEXUS_SCENARIOS.map(scenario => ({
...scenario, profile: 'combined-homeworld-nexus', ordinal: scenario.ordinal + 4,
}));
const MORITANI_SKILLS_ROSTER: readonly FactionId[] = [
'moritani', 'emperor', 'guild', 'harkonnen', 'fremen', 'beneGesserit',
];
@@ -231,10 +236,10 @@ type Result = {
function usage() {
return (
'Usage: node --import tsx tools/faction-games.ts --out NEW_PRIVATE_DIR ' +
'[--seed UINT32] [--profile all|base|choam|ecaz|combined|combined-nexus|homeworld|nexus|homeworld-nexus|choam-roster|ecaz-roster|ix-roster|choam-nexus|ecaz-nexus|ix-nexus|moritani-skills|tleilaxu-skills|ix-skills|choam-skills] ' +
'[--seed UINT32] [--profile all|base|choam|ecaz|combined|combined-nexus|combined-homeworld-nexus|homeworld|nexus|homeworld-nexus|choam-roster|ecaz-roster|ix-roster|choam-nexus|ecaz-nexus|ix-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; combined-nexus adds five/six-seat Basic/Advanced all-expansion Nexus samples. Base, Homeworld, Nexus, Homeworld-Nexus, expansion roster, paired expansion Nexus and skill profiles default to their 2–6-player samples. --players requires a supported profile. Output must be a new private directory outside the checkout.'
'Runs genuine setup and gameplay offline. Default/all keeps the six expansion samples; combined-nexus and combined-homeworld-nexus add five/six-seat Basic/Advanced all-expansion samples. Base, Homeworld, Nexus, Homeworld-Nexus, expansion roster, paired expansion Nexus and skill profiles default to their 2–6-player samples. --players requires a supported profile. Output must be a new private directory outside the checkout.'
);
}
@@ -265,7 +270,7 @@ function positive(value: string | undefined) {
}
function scenarioName(scenario: Scenario) {
return scenario.profile === 'base' || scenario.profile === 'homeworld' || scenario.profile === 'nexus' || scenario.profile === 'homeworld-nexus' || scenario.profile === 'combined-nexus' ||
return scenario.profile === 'base' || scenario.profile === 'homeworld' || scenario.profile === 'nexus' || scenario.profile === 'homeworld-nexus' || scenario.profile === 'combined-nexus' || scenario.profile === 'combined-homeworld-nexus' ||
scenario.profile === 'choam-roster' || scenario.profile === 'ecaz-roster' || scenario.profile === 'ix-roster' || scenario.profile === 'choam-nexus' || scenario.profile === 'ecaz-nexus' || scenario.profile === 'ix-nexus' || skillProfile(scenario.profile)
? `${scenario.profile}-${scenario.roster.length}-${scenario.rules}`
: `${scenario.profile}-${scenario.rules}`;
@@ -273,8 +278,8 @@ function scenarioName(scenario: Scenario) {
function parseProfile(value: string | undefined) {
const profile = value ?? 'all';
if (!['all', 'base', 'choam', 'ecaz', 'combined', 'combined-nexus', 'homeworld', 'nexus', 'homeworld-nexus', 'choam-roster', 'ecaz-roster', 'ix-roster', 'choam-nexus', 'ecaz-nexus', 'ix-nexus', 'moritani-skills', 'tleilaxu-skills', 'ix-skills', 'choam-skills'].includes(profile))
throw new Error('--profile must be all, base, choam, ecaz, combined, combined-nexus, homeworld, nexus, homeworld-nexus, choam-roster, ecaz-roster, ix-roster, choam-nexus, ecaz-nexus, ix-nexus, moritani-skills, tleilaxu-skills, ix-skills or choam-skills.');
if (!['all', 'base', 'choam', 'ecaz', 'combined', 'combined-nexus', 'combined-homeworld-nexus', 'homeworld', 'nexus', 'homeworld-nexus', 'choam-roster', 'ecaz-roster', 'ix-roster', 'choam-nexus', 'ecaz-nexus', 'ix-nexus', 'moritani-skills', 'tleilaxu-skills', 'ix-skills', 'choam-skills'].includes(profile))
throw new Error('--profile must be all, base, choam, ecaz, combined, combined-nexus, combined-homeworld-nexus, homeworld, nexus, homeworld-nexus, choam-roster, ecaz-roster, ix-roster, choam-nexus, ecaz-nexus, ix-nexus, moritani-skills, tleilaxu-skills, ix-skills or choam-skills.');
return profile as Profile | 'all';
}
@@ -318,12 +323,12 @@ function freshGame(scenario: Scenario) {
player.bot = DIFFICULTIES[index % DIFFICULTIES.length];
player.ready = true;
}
if (scenario.profile === 'homeworld' || scenario.profile === 'homeworld-nexus')
if (scenario.profile === 'homeworld' || scenario.profile === 'homeworld-nexus' || scenario.profile === 'combined-homeworld-nexus')
game.homeworlds = { custody: null };
if (scenario.profile === 'nexus' || scenario.profile === 'homeworld-nexus' || scenario.profile === 'choam-nexus' || scenario.profile === 'ecaz-nexus' || scenario.profile === 'ix-nexus' || scenario.profile === 'combined-nexus')
if (scenario.profile === 'nexus' || scenario.profile === 'homeworld-nexus' || scenario.profile === 'choam-nexus' || scenario.profile === 'ecaz-nexus' || scenario.profile === 'ix-nexus' || scenario.profile === 'combined-nexus' || scenario.profile === 'combined-homeworld-nexus')
game.nexusCards = { cards: null, phase: null };
if (scenario.profile === 'combined-nexus')
return initializeCombinedNexusGameForAudit(game);
if (scenario.profile === 'combined-nexus' || scenario.profile === 'combined-homeworld-nexus')
return initializeCombinedNexusGameForAudit(game, scenario.profile === 'combined-homeworld-nexus');
if (scenario.profile === 'choam-nexus' || scenario.profile === 'ecaz-nexus' || scenario.profile === 'ix-nexus')
return initializePairedNexusGameForAudit(game);
return skillProfile(scenario.profile)
@@ -384,10 +389,10 @@ function resumedGame(path: string) {
game.moritaniAssassinateCallEvents
)
throw new Error('--resume sample scenarios exclude unsupported optional modules.');
const scenario = [...SCENARIOS, ...COMBINED_NEXUS_SCENARIOS, ...BASE_SCENARIOS, ...MODULE_SCENARIOS, ...EXPANSION_ROSTER_SCENARIOS, ...PAIRED_NEXUS_SCENARIOS, ...MORITANI_SKILLS_SCENARIOS, ...TLEILAXU_SKILLS_SCENARIOS, ...IX_SKILLS_SCENARIOS, ...CHOAM_SKILLS_SCENARIOS].find(
const scenario = [...SCENARIOS, ...COMBINED_NEXUS_SCENARIOS, ...COMBINED_HOMEWORLD_NEXUS_SCENARIOS, ...BASE_SCENARIOS, ...MODULE_SCENARIOS, ...EXPANSION_ROSTER_SCENARIOS, ...PAIRED_NEXUS_SCENARIOS, ...MORITANI_SKILLS_SCENARIOS, ...TLEILAXU_SKILLS_SCENARIOS, ...IX_SKILLS_SCENARIOS, ...CHOAM_SKILLS_SCENARIOS].find(
(candidate) =>
(candidate.profile === 'homeworld' || candidate.profile === 'homeworld-nexus') === !!game.homeworlds &&
(candidate.profile === 'nexus' || candidate.profile === 'homeworld-nexus' || candidate.profile === 'choam-nexus' || candidate.profile === 'ecaz-nexus' || candidate.profile === 'ix-nexus' || candidate.profile === 'combined-nexus') === !!game.nexusCards &&
(candidate.profile === 'homeworld' || candidate.profile === 'homeworld-nexus' || candidate.profile === 'combined-homeworld-nexus') === !!game.homeworlds &&
(candidate.profile === 'nexus' || candidate.profile === 'homeworld-nexus' || candidate.profile === 'choam-nexus' || candidate.profile === 'ecaz-nexus' || candidate.profile === 'ix-nexus' || candidate.profile === 'combined-nexus' || candidate.profile === 'combined-homeworld-nexus') === !!game.nexusCards &&
skillProfile(candidate.profile) === !!game.leaderSkills &&
candidate.rules === (game.advanced ? 'advanced' : 'basic') &&
JSON.stringify(candidate.expansions) ===
@@ -581,10 +586,10 @@ async function main() {
const profile = parseProfile(values.profile);
const rules = parseRules(values.rules);
const players = parsePlayers(values.players);
if (supplied('players') && !['base', 'homeworld', 'nexus', 'homeworld-nexus', 'combined-nexus', 'choam-roster', 'ecaz-roster', 'ix-roster', 'choam-nexus', 'ecaz-nexus', 'ix-nexus'].includes(profile) && !skillProfile(profile))
if (supplied('players') && !['base', 'homeworld', 'nexus', 'homeworld-nexus', 'combined-nexus', 'combined-homeworld-nexus', 'choam-roster', 'ecaz-roster', 'ix-roster', 'choam-nexus', 'ecaz-nexus', 'ix-nexus'].includes(profile) && !skillProfile(profile))
throw new Error('--players requires a roster or optional-module profile.');
if (profile === 'combined-nexus' && players !== 'all' && ![5, 6].includes(players))
throw new Error('--profile combined-nexus supports only five or six players.');
if ((profile === 'combined-nexus' || profile === 'combined-homeworld-nexus') && players !== 'all' && ![5, 6].includes(players))
throw new Error(`--profile ${profile} supports only five or six players.`);
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;
@@ -593,6 +598,7 @@ async function main() {
: profile === 'choam-roster' || profile === 'ecaz-roster' || profile === 'ix-roster' ? EXPANSION_ROSTER_SCENARIOS
: profile === 'choam-nexus' || profile === 'ecaz-nexus' || profile === 'ix-nexus' ? PAIRED_NEXUS_SCENARIOS
: profile === 'combined-nexus' ? COMBINED_NEXUS_SCENARIOS
: profile === 'combined-homeworld-nexus' ? COMBINED_HOMEWORLD_NEXUS_SCENARIOS
: profile === 'base' ? BASE_SCENARIOS
: profile === 'moritani-skills' ? MORITANI_SKILLS_SCENARIOS
: profile === 'tleilaxu-skills' ? TLEILAXU_SKILLS_SCENARIOS