Preflight Homeworld-sourced AI arrival overlaps

This commit is contained in:
Quintine committed 2026-09-29 20:29:40 +10:00
1 parent 41fd6fe173
commit b9c8b740ca
6 files changed
+106 -15

No files matched your search

+23 -4
View File
@@ -36,10 +36,14 @@ and Face Dancers are unnecessary. It does not mutate the view or sample
randomness. Only the known typed arrival error excludes a candidate; unexpected
faults propagate. The engine remains authoritative for every actual action.
Homeworlds, explicit Nexus shipments, special movement and unrecognized action
shapes retain their existing adapters and legal fallback search. They are not
classified as illegal by this narrower preview. This is not a complete action
validator or certification that every AI candidate in every module is legal.
Homeworld-sourced ordinary shipments now quote the same public arrival triggers,
including a colocated Ambassador and Terror, after the bot selects its own
physical source. Their off-planet origin is sufficient for the arrival quote;
Homeworld source eligibility, tariffs and any payment-dependent Guild response
remain authoritative engine checks, not duplicated AI rules. Other Homeworld
routes, explicit Nexus shipments, special movement and unrecognized actions
retain their own adapters and legal fallback search. This is not a complete
action validator or certification that every AI candidate is legal.
## Evidence
@@ -53,3 +57,18 @@ stance settlement does not turn a lone advisor group into fighters.
Final checks and same-seed integrated results are recorded with the source-bound
private checkpoint and Git message. Existing saved games remain protected; no
server restart or database change is required.
## 29 September: Homeworld-sourced shipment overlap
A genuine five-seat Advanced combined Homeworld/Nexus/expansion game on
`41fd6fe` completed in 786 actions but proposed two Tleilaxu shipments from
its own Homeworld into unsupported Ambassador/Terror arrival combinations.
Both were rejected without writes before a legal alternative was chosen. The
owner-view preflight formerly skipped every Homeworld shipment. It now passes
off-planet Homeworld-source arrivals through the same public quote without
assuming a price or hidden Terror face. All four AI profiles exclude the
reproduced action while retaining an executable alternative. A same-seed
genuine-setup replay completes in 786 accepted attempts, zero rejections,
21 JSON continuations and the same fourth-turn shared victory. This repairs
candidate legality, not the unsupported arrival-rule composition or public
expansion start gate.
+8
View File
@@ -6,6 +6,14 @@ Expansion starts and full rules/publication acceptance remain gated.**
## Current checkpoint and work
A five-seat Advanced combined Homeworld/Nexus/expansion sample exposed two
known-unsupported Ecaz Ambassador/Moritani Terror shipment candidates from
Tleilaxu's own Homeworld. [AI arrival preflight](BOT_ARRIVAL_PREFLIGHT.md#29-september-homeworld-sourced-shipment-overlap)
now excludes them using only public entry reactions. The same genuine game
completed with 786/786 legal attempts and 21 JSON continuations; this is a
candidate-legality repair, not support for the combined arrival or a public
expansion start. The source is not in the last verified live image.
A bounded [Ecaz Ambassador/Moritani Terror same-entry continuation](MORITANI_ENTRY_TIMING.md#selected-bounded-overlap-order--29-september-2026)
now snapshots both original optional triggers and offers their owners sequential
choices in current storm order on classic plus Ecaz/Moritani Basic/Advanced
+21
View File
@@ -1,5 +1,26 @@
# Dune implementation status
## 29 September 2026 — Homeworld-sourced AI arrival preflight
A genuine five-seat Advanced combined Homeworld/Nexus/expansion game on
`41fd6fe` exposed two rejected Tleilaxu shipments from its own Homeworld
into unsupported Ambassador/Terror entry combinations. The public AI
[preflight](BOT_ARRIVAL_PREFLIGHT.md#29-september-homeworld-sourced-shipment-overlap)
had skipped Homeworld sources. It now quotes their off-planet arrival through
the same authoritative public reaction helper, without sampling private
Terror faces or recreating source pricing. A failing-before/passing-after
real-engine regression exercises all four AI profiles and executable
fallbacks. The identical genuine game completes after the fix with
786 accepted attempts, no rejected candidates, 21 JSON continuations and
a fourth-turn shared victory. Other Homeworld payment-dependent reactions
and unsupported combined arrivals remain gated; no public start was opened.
The initial full check exposed an existing Guild shipment test that asserted
payment before a legitimately opened Karama rate response. That test now
resolves the response before checking the rounded cost. The final
`npm run check` passes typecheck, lint and 5,757/5,757 offline cases. No HTTP
contract, database schema or player-facing control changed.
## 29 September 2026 — Sapho finite-bid boundary and pending source questions
The existing Once Around Juice of Sapho adapter permits an unacted holder to
+20 -9
View File
@@ -14,10 +14,11 @@ import {
import { quoteSmugglerShipment } from './smuggler-shipment';
/**
* Known arrival support for ordinary owner-view candidates, not a complete
* action validator. Homeworld, explicit Nexus and special movement routes keep
* their own adapters and fallbacks. No hidden Terror face or rival hand enters
* this quote, and unexpected failures are never treated as rejected candidates.
* Known arrival support for ordinary owner-view candidates, plus the public
* overlap guard for Homeworld-sourced shipments. This is not a complete action
* validator. Explicit Nexus and special movement routes retain their adapters.
* No hidden Terror face or rival hand enters this quote; unexpected failures
* are never treated as rejected candidates.
*/
export function botArrivalBlock(g: GameView, action: Action): string | null {
if (
@@ -25,10 +26,8 @@ export function botArrivalBlock(g: GameView, action: Action): string | null {
g.phase !== 5 ||
g.active !== g.me ||
!['ship', 'move'].includes(action.type) ||
g.homeworlds ||
action.nexus !== undefined ||
action.source !== undefined ||
action.homeworldSources !== undefined ||
typeof action.territory !== 'string' ||
typeof action.sector !== 'number' ||
!validLocation(action.territory, action.sector)
@@ -45,9 +44,21 @@ export function botArrivalBlock(g: GameView, action: Action): string | null {
paidBox: g.decision?.kind === 'nullentropy',
};
let order: CompletedMovementArrivalInput['order'];
if (action.type === 'ship') {
if (g.moritaniAtomics?.territory === to)
return 'Atomics Aftermath permanently blocks shipments into this territory.';
if (action.type === 'ship' && g.moritaniAtomics?.territory === to)
return 'Atomics Aftermath permanently blocks shipments into this territory.';
if (g.homeworlds || action.homeworldSources !== undefined) {
if (action.type !== 'ship' || !g.homeworlds || action.homeworldSources === undefined)
return null;
// Off-planet Homeworld pools have the same arrival triggers. Only the
// public reaction overlap is quoted here; the engine owns source and price.
order = {
player: me.id,
origin: 'homeworld',
to,
advisors: arrivalAsAdvisor(g, me, to),
wantsFighters: false,
};
} else if (action.type === 'ship') {
const amount = action.noField === undefined ? action.amount : 1;
if (
typeof amount !== 'number' ||
+32
View File
@@ -3,6 +3,9 @@ import test from 'node:test';
import { botArrivalBlock } from '../game/bot-arrival';
import { botActions } from '../game/bots';
import { applyAction, viewGame, type Action, type Game } from '../game/engine';
import { createHomeworldCustody } from '../game/homeworld-custody';
import { homeworldContext } from '../game/homeworld-game';
import { createTerrorState } from '../game/moritani-terror';
import {
arrivalMove,
arrivalPlayer,
@@ -52,6 +55,35 @@ void test('all four profiles avoid the reproduced Ambassador/Terror entries and
}
});
void test('Homeworld-sourced AI shipments avoid unsupported paired entry reactions', () => {
const game = movementArrivalGame();
game.homeworlds = { custody: createHomeworldCustody(homeworldContext(game)) };
arrivalPlayer(game, 'tleilaxu').shipped = false;
const action: Action = {
type: 'ship',
territory: 'carthag',
sector: 11,
amount: 1,
elite: 0,
homeworldSources: { 'homeworld:tleilaxu': { normal: 1, elite: 0 } },
};
assert.throws(() => applyAction(game, 'tleilaxu', action), overlap);
const before = JSON.stringify(game);
for (const profile of profiles) {
const view = ownView(game, profile);
assert.match(botArrivalBlock(view, action) ?? '', overlap);
const choices = botActions(view);
assert.ok(choices.length, profile);
assert.equal(choices.some(candidate => candidate.type === 'ship' &&
candidate.territory === 'carthag'), false, profile);
assert.ok(applyAction(game, view.me, choices[0]));
}
assert.equal(JSON.stringify(game), before);
game.moritaniTerror = createTerrorState(() => 0.2);
assert.equal(botArrivalBlock(ownView(game), action), null);
assert.ok(applyAction(game, 'tleilaxu', action));
});
void test('single reactions and matching-faction Ambassador immunity remain playable', () => {
for (const tokens of ['ambassador', 'terror'] as const) {
const game = movementArrivalGame('tleilaxu', tokens);
+2 -2
View File
@@ -882,10 +882,10 @@ void test('Guild groups from several sectors pay one rounded shipment cost', ()
type: 'guildShip',
forces: { 'pasty_mesa:5': 1, 'pasty_mesa:6': 2 },
};
const returned = applyAction(g, 'p0', { ...action, territory: 'reserves' });
const returned = passResponses(applyAction(g, 'p0', { ...action, territory: 'reserves' }));
assert.equal(returned.players[0].spice, before - 2);
assert.equal(returned.players[0].reserves, 18);
g = applyAction(g, 'p0', { ...action, territory: 'sietch_tabr', sector: 14 });
g = passResponses(applyAction(g, 'p0', { ...action, territory: 'sietch_tabr', sector: 14 }));
assert.equal(g.players[0].spice, before - 2);
assert.equal(g.players[0].forces['sietch_tabr:14'], 3);
conservation(g);