Exercise Ecaz Nexus expansion games

This commit is contained in:
Quintine committed 2026-09-27 22:56:49 +10:00
1 parent 99c6ab5863
commit d77aa5472b
5 files changed
+75 -40

No files matched your search

+4
View File
@@ -1,5 +1,9 @@
# Dune implementation status # Dune implementation status
## 27 September 2026 — Ecaz/Moritani with Nexus Cards
The paired-faction Nexus audit setup now includes genuine Ecaz/Moritani games, retaining the CHOAM/Richese profile and both public start gates. At seed 20260927 all ten two-through-six-seat Basic/Advanced [samples](VERIFICATION_WORKFLOW.md#reusable-base-and-faction-sample-games) completed: 3,827 accepted actions, 98 JSON restores, five unallied card choices and no rejected actions. A three-seat Basic trace reaches Ecaz Ambassador decisions, battles and Nexus drawing; an interrupted four-seat Advanced game resumes as its own Ecaz/Nexus roster. The source-bound report is `/tmp/dune-ecaz-nexus-checkpoint-20260927/report.json`. None of those draws activated a Nexus card effect; card-family and combined-mode completion remain gated.
## 27 September 2026 — CHOAM/Richese with Nexus Cards ## 27 September 2026 — CHOAM/Richese with Nexus Cards
The offline [sample runner](VERIFICATION_WORKFLOW.md#reusable-base-and-faction-sample-games) now admits genuine CHOAM/Richese setup with the physical Nexus module at two through six seats in Basic and Advanced. At seed 20260928 all ten games completed with 3,481 accepted actions, 90 periodic JSON restores, no rejected actions and one unallied Nexus Card choice. A separate seed 20260927 completed nine games with three choices, then stopped in six-seat Advanced Bidding after 1,863 accepted actions at the existing unresolved exhausted-Richese-cache normal-auction-count guard; the failed report is `/tmp/dune-choam-nexus-trial-20260927/report.json`. The passing source-bound checkpoint report is `/tmp/dune-choam-nexus-checkpoint-20260928/report.json`. No drawn card effect happened in these samples, so they do not certify the CHOAM Betrayal panel, all Nexus effects or public module starts. The offline [sample runner](VERIFICATION_WORKFLOW.md#reusable-base-and-faction-sample-games) now admits genuine CHOAM/Richese setup with the physical Nexus module at two through six seats in Basic and Advanced. At seed 20260928 all ten games completed with 3,481 accepted actions, 90 periodic JSON restores, no rejected actions and one unallied Nexus Card choice. A separate seed 20260927 completed nine games with three choices, then stopped in six-seat Advanced Bidding after 1,863 accepted actions at the existing unresolved exhausted-Richese-cache normal-auction-count guard; the failed report is `/tmp/dune-choam-nexus-trial-20260927/report.json`. The passing source-bound checkpoint report is `/tmp/dune-choam-nexus-checkpoint-20260928/report.json`. No drawn card effect happened in these samples, so they do not certify the CHOAM Betrayal panel, all Nexus effects or public module starts.
+15 -13
View File
@@ -105,6 +105,7 @@ node --import tsx tools/faction-games.ts --out /tmp/dune-new-choam-roster --prof
node --import tsx tools/faction-games.ts --out /tmp/dune-new-ecaz-roster --profile ecaz-roster node --import tsx tools/faction-games.ts --out /tmp/dune-new-ecaz-roster --profile ecaz-roster
node --import tsx tools/faction-games.ts --out /tmp/dune-new-ix-roster --profile ix-roster node --import tsx tools/faction-games.ts --out /tmp/dune-new-ix-roster --profile ix-roster
node --import tsx tools/faction-games.ts --out /tmp/dune-new-choam-nexus --profile choam-nexus node --import tsx tools/faction-games.ts --out /tmp/dune-new-choam-nexus --profile choam-nexus
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-nexus-three --profile nexus --players 3 --rules advanced 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 node --import tsx tools/faction-games.ts --out /tmp/dune-new-resume --resume /private/failed-combined-advanced.json
``` ```
@@ -113,19 +114,20 @@ Default and `--profile all` retain the original six fixed CHOAM/Richese,
Ecaz/Moritani and combined Basic/Advanced samples. `--profile base` selects ten Ecaz/Moritani and combined Basic/Advanced samples. `--profile base` selects ten
additional samples: Basic and Advanced with two through six players. additional samples: Basic and Advanced with two through six players.
`--players 2..6` narrows the base, Homeworld, Nexus, combined `--players 2..6` narrows the base, Homeworld, Nexus, combined
`homeworld-nexus`, `choam-roster`, `ecaz-roster`, `ix-roster` or `choam-nexus` `homeworld-nexus`, `choam-roster`, `ecaz-roster`, `ix-roster`, `choam-nexus`
profile; omission or `all` retains all five counts. The fixed base roster adds Atreides, or `ecaz-nexus` profile; omission or `all` retains all five counts. The fixed
Harkonnen, Fremen, Emperor, Guild and Bene Gesserit in that order. These are base roster adds Atreides, Harkonnen, Fremen, Emperor, Guild and Bene Gesserit
sample rosters, not an assertion that they are the only rules-permitted player-count in that order. These are sample rosters, not the only rules-permitted
configurations. Each optional module profile runs two through six base factions player-count configurations. Each optional module profile runs two through
under Basic and Advanced using the actual setup seams. The three expansion-roster six base factions under Basic and Advanced using the actual setup seams. The
profiles also run Basic and Advanced with their respective physical expansion three expansion-roster profiles also run Basic and Advanced with their
enabled: CHOAM/Richese start with CHOAM then Richese, Ecaz/Moritani with respective physical expansion enabled: CHOAM/Richese start with CHOAM then
Moritani then Ecaz, Ixian/Tleilaxu with Ixians then Tleilaxu; subsequent seats Richese, Ecaz/Moritani with Moritani then Ecaz, Ixian/Tleilaxu with Ixians
extend each to six without changing existing fixed four-seat samples. Their then Tleilaxu. Subsequent seats extend each to six without changing existing
seed offsets are 68–77, 78–87 and 88–97. `choam-nexus` reuses the CHOAM/Richese fixed four-seat samples. Their seed offsets are 68–77, 78–87 and 88–97.
roster with both the physical expansion and Nexus Cards enabled at offsets `choam-nexus` and `ecaz-nexus` reuse the respective expansion rosters with Nexus
98–107; it does not alter the fixed six default samples. Cards enabled at seed offsets 98–107 and 108–117, respectively. The original
six default samples are unchanged.
Output names include the player count. Homeworld's original four-seat seed 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 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 remain 38–39; its smaller rosters use 40–47. The combined module uses
+13 -8
View File
@@ -6360,15 +6360,20 @@ export function initializeNexusGameForAudit(state: Game): Game {
'Enable Nexus cards in a fresh audit lobby first.'); 'Enable Nexus cards in a fresh audit lobby first.');
return initializeSetupGameForAudit(state, !!state.homeworlds, true); return initializeSetupGameForAudit(state, !!state.homeworlds, true);
} }
/** Offline CHOAM/Richese with Nexus Cards; public expansion/module starts remain gated. */ /** Offline paired-faction Nexus setup; public expansion/module starts remain gated. */
export function initializeChoamNexusGameForAudit(state: Game): Game { export function initializePairedNexusGameForAudit(state: Game): Game {
requireRule(state.expansions.length === 1 && state.expansions[0] === 'choam' && const expansion = state.expansions[0];
state.players.some(player => player.faction === 'choam') && requireRule(state.expansions.length === 1 &&
state.players.some(player => player.faction === 'richese'), ((expansion === 'choam' &&
'The CHOAM Nexus sample requires both expansion factions and their deck.'); state.players.some(player => player.faction === 'choam') &&
state.players.some(player => player.faction === 'richese')) ||
(expansion === 'ecaz' &&
state.players.some(player => player.faction === 'ecaz') &&
state.players.some(player => player.faction === 'moritani'))),
'The paired Nexus sample requires both selected expansion factions and their deck.');
requireRule(!!state.nexusCards && state.nexusCards.cards === null && state.nexusCards.phase === null && requireRule(!!state.nexusCards && state.nexusCards.cards === null && state.nexusCards.phase === null &&
!state.homeworlds && !state.leaderSkills && !state.discoveryEnabled && !state.ecazTreachery, !state.homeworlds && !state.leaderSkills && !state.discoveryEnabled && !state.ecazTreachery,
'Enable only Nexus Cards in a fresh CHOAM expansion lobby.'); 'Enable only Nexus Cards in a fresh paired expansion lobby.');
return initializeSetupGameForAudit(state, false, true, false, false, false, false, true); return initializeSetupGameForAudit(state, false, true, false, false, false, false, true);
} }
@@ -6464,7 +6469,7 @@ function initializeSetupGameForAudit(state: Game, homeworlds: boolean, nexus = f
), ),
), ),
factions && nexus factions && nexus
? 'The CHOAM Nexus audit needs a fresh expansion lobby without other optional modules.' ? 'The paired expansion Nexus audit needs a fresh lobby without other optional modules.'
: factions : factions
? 'The faction prototype supports base factions and the selected expansion factions without optional modules.' ? 'The faction prototype supports base factions and the selected expansion factions without optional modules.'
: choam : choam
+21 -1
View File
@@ -255,7 +255,7 @@ void test('combined modules reach saved Nexus draws and actual Homeworld shipmen
void test('expansion-roster snapshots resume their own Advanced games rather than the fixed four-seat sample', (t) => { void test('expansion-roster snapshots resume their own Advanced games rather than the fixed four-seat sample', (t) => {
const area = temporary(t); const area = temporary(t);
for (const [profile, seed] of [['choam-roster', '20260928'], ['ecaz-roster', '20260927'], ['ix-roster', '20260927'], ['choam-nexus', '20260928']] as const) { for (const [profile, seed] of [['choam-roster', '20260928'], ['ecaz-roster', '20260927'], ['ix-roster', '20260927'], ['choam-nexus', '20260928'], ['ecaz-nexus', '20260927']] as const) {
const name = `${profile}-4-advanced`; const name = `${profile}-4-advanced`;
const initial = join(area, `${profile}-initial`); const initial = join(area, `${profile}-initial`);
assert.equal(run(initial, '--profile', profile, '--players', '4', '--rules', 'advanced', '--seed', seed, '--max-actions', '1').status, 1); assert.equal(run(initial, '--profile', profile, '--players', '4', '--rules', 'advanced', '--seed', seed, '--max-actions', '1').status, 1);
@@ -331,6 +331,26 @@ void test('CHOAM/Richese Nexus game reaches an unallied card choice and restores
assert.ok(game.restores > 0); assert.ok(game.restores > 0);
}); });
void test('Ecaz/Moritani Nexus game draws cards and resolves Ambassador battles through JSON restores', (t) => {
const out = join(temporary(t), 'ecaz-nexus-complete');
const result = run(out, '--profile', 'ecaz-nexus', '--players', '3', '--rules', 'basic', '--seed', '20260927');
assert.equal(result.status, 0, result.stderr);
const report = json<CliReport>(join(out, 'report.json'));
const game = json<{ results: (CliResult & {
used: Record<string, number>;
rejected: Record<string, number>;
restores: number;
})[] }>(join(out, 'results.json')).results[0];
assert.equal(report.status, 'passed');
assert.equal(report.sourceUnchanged, true);
assert.equal(game.name, 'ecaz-nexus-3-basic');
assert.equal(game.outcome, 'complete');
assert.ok(game.used.nexusCardChoice > 0);
assert.ok(game.used['decision:ecazAmbassador'] > 0);
assert.deepEqual(game.rejected, {});
assert.ok(game.restores > 0);
});
void test('three-seat Nexus samples preserve their module and roster across saved continuation', (t) => { void test('three-seat Nexus samples preserve their module and roster across saved continuation', (t) => {
const area = temporary(t); const area = temporary(t);
for (const [rules, ordinal] of [['basic', 42], ['advanced', 43]] as const) { for (const [rules, ordinal] of [['basic', 42], ['advanced', 43]] as const) {
+22 -18
View File
@@ -10,10 +10,10 @@ import {
applyAction, applyAction,
createGame, createGame,
initializeBaseGameForAudit, initializeBaseGameForAudit,
initializeChoamNexusGameForAudit,
initializeFactionExpansionsGameForAudit, initializeFactionExpansionsGameForAudit,
initializeHomeworldGameForAudit, initializeHomeworldGameForAudit,
initializeNexusGameForAudit, initializeNexusGameForAudit,
initializePairedNexusGameForAudit,
initializeLeaderSkillsGameForAudit, initializeLeaderSkillsGameForAudit,
joinGame, joinGame,
newPlayer, newPlayer,
@@ -29,7 +29,7 @@ import { privateOutputDirectory, sourceSnapshot } from './verification';
const DEFAULT_SEED = 20_260_926; const DEFAULT_SEED = 20_260_926;
const DEFAULT_MAX_ACTIONS = 3_500; const DEFAULT_MAX_ACTIONS = 3_500;
const DIFFICULTIES = ['Easy', 'Medium', 'Hard', 'Brutal'] as const; const DIFFICULTIES = ['Easy', 'Medium', 'Hard', 'Brutal'] as const;
type Profile = 'base' | 'choam' | 'ecaz' | 'combined' | 'homeworld' | 'nexus' | 'homeworld-nexus' | 'choam-roster' | 'ecaz-roster' | 'ix-roster' | 'choam-nexus' | 'moritani-skills' | 'tleilaxu-skills' | 'ix-skills' | 'choam-skills'; type Profile = 'base' | 'choam' | 'ecaz' | 'combined' | 'homeworld' | 'nexus' | 'homeworld-nexus' | 'choam-roster' | 'ecaz-roster' | 'ix-roster' | 'choam-nexus' | 'ecaz-nexus' | 'moritani-skills' | 'tleilaxu-skills' | 'ix-skills' | 'choam-skills';
type Rules = 'basic' | 'advanced'; type Rules = 'basic' | 'advanced';
type Scenario = { type Scenario = {
@@ -144,9 +144,13 @@ const EXPANSION_ROSTER_SCENARIOS: readonly Scenario[] = [
})), })),
), ),
); );
const CHOAM_NEXUS_SCENARIOS: readonly Scenario[] = EXPANSION_ROSTER_SCENARIOS const PAIRED_NEXUS_SCENARIOS: readonly Scenario[] = EXPANSION_ROSTER_SCENARIOS
.filter(scenario => scenario.profile === 'choam-roster') .filter(scenario => scenario.profile === 'choam-roster' || scenario.profile === 'ecaz-roster')
.map(scenario => ({ ...scenario, profile: 'choam-nexus', ordinal: scenario.ordinal + 30 })); .map(scenario => ({
...scenario,
profile: scenario.profile === 'choam-roster' ? 'choam-nexus' : 'ecaz-nexus',
ordinal: scenario.ordinal + 30,
}));
const MORITANI_SKILLS_ROSTER: readonly FactionId[] = [ const MORITANI_SKILLS_ROSTER: readonly FactionId[] = [
@@ -219,10 +223,10 @@ type Result = {
function usage() { function usage() {
return ( return (
'Usage: node --import tsx tools/faction-games.ts --out NEW_PRIVATE_DIR ' + 'Usage: node --import tsx tools/faction-games.ts --out NEW_PRIVATE_DIR ' +
'[--seed UINT32] [--profile all|base|choam|ecaz|combined|homeworld|nexus|homeworld-nexus|choam-roster|ecaz-roster|ix-roster|choam-nexus|moritani-skills|tleilaxu-skills|ix-skills|choam-skills] ' + '[--seed UINT32] [--profile all|base|choam|ecaz|combined|homeworld|nexus|homeworld-nexus|choam-roster|ecaz-roster|ix-roster|choam-nexus|ecaz-nexus|moritani-skills|tleilaxu-skills|ix-skills|choam-skills] ' +
'[--rules both|basic|advanced] [--players all|2|3|4|5|6] [--max-actions POSITIVE] ' + '[--rules both|basic|advanced] [--players all|2|3|4|5|6] [--max-actions POSITIVE] ' +
'[--resume FAILED_GAME.json]\n' + '[--resume FAILED_GAME.json]\n' +
'Runs genuine setup and gameplay offline. Default/all keeps the six expansion samples; base, Homeworld, Nexus, combined Homeworld-Nexus, expansion roster, CHOAM 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.' 'Runs genuine setup and gameplay offline. Default/all keeps the six expansion samples; base, Homeworld, Nexus, combined Homeworld-Nexus, expansion roster, paired expansion 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.'
); );
} }
@@ -254,15 +258,15 @@ function positive(value: string | undefined) {
function scenarioName(scenario: Scenario) { function scenarioName(scenario: Scenario) {
return scenario.profile === 'base' || scenario.profile === 'homeworld' || scenario.profile === 'nexus' || scenario.profile === 'homeworld-nexus' || return scenario.profile === 'base' || scenario.profile === 'homeworld' || scenario.profile === 'nexus' || scenario.profile === 'homeworld-nexus' ||
scenario.profile === 'choam-roster' || scenario.profile === 'ecaz-roster' || scenario.profile === 'ix-roster' || scenario.profile === 'choam-nexus' || skillProfile(scenario.profile) scenario.profile === 'choam-roster' || scenario.profile === 'ecaz-roster' || scenario.profile === 'ix-roster' || scenario.profile === 'choam-nexus' || scenario.profile === 'ecaz-nexus' || skillProfile(scenario.profile)
? `${scenario.profile}-${scenario.roster.length}-${scenario.rules}` ? `${scenario.profile}-${scenario.roster.length}-${scenario.rules}`
: `${scenario.profile}-${scenario.rules}`; : `${scenario.profile}-${scenario.rules}`;
} }
function parseProfile(value: string | undefined) { function parseProfile(value: string | undefined) {
const profile = value ?? 'all'; const profile = value ?? 'all';
if (!['all', 'base', 'choam', 'ecaz', 'combined', 'homeworld', 'nexus', 'homeworld-nexus', 'choam-roster', 'ecaz-roster', 'ix-roster', 'choam-nexus', 'moritani-skills', 'tleilaxu-skills', 'ix-skills', 'choam-skills'].includes(profile)) if (!['all', 'base', 'choam', 'ecaz', 'combined', 'homeworld', 'nexus', 'homeworld-nexus', 'choam-roster', 'ecaz-roster', 'ix-roster', 'choam-nexus', 'ecaz-nexus', 'moritani-skills', 'tleilaxu-skills', 'ix-skills', 'choam-skills'].includes(profile))
throw new Error('--profile must be all, base, choam, ecaz, combined, homeworld, nexus, homeworld-nexus, choam-roster, ecaz-roster, ix-roster, choam-nexus, moritani-skills, tleilaxu-skills, ix-skills or choam-skills.'); throw new Error('--profile must be all, base, choam, ecaz, combined, homeworld, nexus, homeworld-nexus, choam-roster, ecaz-roster, ix-roster, choam-nexus, ecaz-nexus, moritani-skills, tleilaxu-skills, ix-skills or choam-skills.');
return profile as Profile | 'all'; return profile as Profile | 'all';
} }
@@ -308,10 +312,10 @@ function freshGame(scenario: Scenario) {
} }
if (scenario.profile === 'homeworld' || scenario.profile === 'homeworld-nexus') if (scenario.profile === 'homeworld' || scenario.profile === 'homeworld-nexus')
game.homeworlds = { custody: null }; game.homeworlds = { custody: null };
if (scenario.profile === 'nexus' || scenario.profile === 'homeworld-nexus' || scenario.profile === 'choam-nexus') if (scenario.profile === 'nexus' || scenario.profile === 'homeworld-nexus' || scenario.profile === 'choam-nexus' || scenario.profile === 'ecaz-nexus')
game.nexusCards = { cards: null, phase: null }; game.nexusCards = { cards: null, phase: null };
if (scenario.profile === 'choam-nexus') if (scenario.profile === 'choam-nexus' || scenario.profile === 'ecaz-nexus')
return initializeChoamNexusGameForAudit(game); return initializePairedNexusGameForAudit(game);
return skillProfile(scenario.profile) return skillProfile(scenario.profile)
? initializeLeaderSkillsGameForAudit(game) ? initializeLeaderSkillsGameForAudit(game)
: scenario.profile === 'homeworld' : scenario.profile === 'homeworld'
@@ -370,10 +374,10 @@ function resumedGame(path: string) {
game.moritaniAssassinateCallEvents game.moritaniAssassinateCallEvents
) )
throw new Error('--resume sample scenarios exclude unsupported optional modules.'); throw new Error('--resume sample scenarios exclude unsupported optional modules.');
const scenario = [...SCENARIOS, ...BASE_SCENARIOS, ...MODULE_SCENARIOS, ...EXPANSION_ROSTER_SCENARIOS, ...CHOAM_NEXUS_SCENARIOS, ...MORITANI_SKILLS_SCENARIOS, ...TLEILAXU_SKILLS_SCENARIOS, ...IX_SKILLS_SCENARIOS, ...CHOAM_SKILLS_SCENARIOS].find( const scenario = [...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) =>
(candidate.profile === 'homeworld' || candidate.profile === 'homeworld-nexus') === !!game.homeworlds && (candidate.profile === 'homeworld' || candidate.profile === 'homeworld-nexus') === !!game.homeworlds &&
(candidate.profile === 'nexus' || candidate.profile === 'homeworld-nexus' || candidate.profile === 'choam-nexus') === !!game.nexusCards && (candidate.profile === 'nexus' || candidate.profile === 'homeworld-nexus' || candidate.profile === 'choam-nexus' || candidate.profile === 'ecaz-nexus') === !!game.nexusCards &&
skillProfile(candidate.profile) === !!game.leaderSkills && skillProfile(candidate.profile) === !!game.leaderSkills &&
candidate.rules === (game.advanced ? 'advanced' : 'basic') && candidate.rules === (game.advanced ? 'advanced' : 'basic') &&
JSON.stringify(candidate.expansions) === JSON.stringify(candidate.expansions) ===
@@ -567,15 +571,15 @@ async function main() {
const profile = parseProfile(values.profile); const profile = parseProfile(values.profile);
const rules = parseRules(values.rules); const rules = parseRules(values.rules);
const players = parsePlayers(values.players); const players = parsePlayers(values.players);
if (supplied('players') && !['base', 'homeworld', 'nexus', 'homeworld-nexus', 'choam-roster', 'ecaz-roster', 'ix-roster', 'choam-nexus'].includes(profile) && !skillProfile(profile)) if (supplied('players') && !['base', 'homeworld', 'nexus', 'homeworld-nexus', 'choam-roster', 'ecaz-roster', 'ix-roster', 'choam-nexus', 'ecaz-nexus'].includes(profile) && !skillProfile(profile))
throw new Error('--players requires --profile base, homeworld, nexus, homeworld-nexus, choam-roster, ecaz-roster, ix-roster, choam-nexus or a skill profile.'); throw new Error('--players requires --profile base, homeworld, nexus, homeworld-nexus, choam-roster, ecaz-roster, ix-roster, choam-nexus, ecaz-nexus or a skill profile.');
if (skillProfile(profile) && rules === 'advanced') if (skillProfile(profile) && rules === 'advanced')
throw new Error('Expansion Leader Skills profiles currently support only Basic samples.'); throw new Error('Expansion Leader Skills profiles currently support only Basic samples.');
const resume = values.resume ? resumedGame(values.resume) : null; const resume = values.resume ? resumedGame(values.resume) : null;
const requestedSamples = profile === 'homeworld' || profile === 'nexus' || profile === 'homeworld-nexus' const requestedSamples = profile === 'homeworld' || profile === 'nexus' || profile === 'homeworld-nexus'
? MODULE_SCENARIOS ? MODULE_SCENARIOS
: profile === 'choam-roster' || profile === 'ecaz-roster' || profile === 'ix-roster' ? EXPANSION_ROSTER_SCENARIOS : profile === 'choam-roster' || profile === 'ecaz-roster' || profile === 'ix-roster' ? EXPANSION_ROSTER_SCENARIOS
: profile === 'choam-nexus' ? CHOAM_NEXUS_SCENARIOS : profile === 'choam-nexus' || profile === 'ecaz-nexus' ? PAIRED_NEXUS_SCENARIOS
: profile === 'base' ? BASE_SCENARIOS : profile === 'base' ? BASE_SCENARIOS
: profile === 'moritani-skills' ? MORITANI_SKILLS_SCENARIOS : profile === 'moritani-skills' ? MORITANI_SKILLS_SCENARIOS
: profile === 'tleilaxu-skills' ? TLEILAXU_SKILLS_SCENARIOS : profile === 'tleilaxu-skills' ? TLEILAXU_SKILLS_SCENARIOS