Preflight Homeworld-sourced AI arrival overlaps
This commit is contained in:
1 parent
41fd6fe173
commit
b9c8b740ca
6 files changed
+106
-15
No files matched your search
@@ -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.
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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' ||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in new issue
Block a user