Connect Basic shared Ecaz Orgiz Collection settlement

This commit is contained in:
Quintine committed 2026-10-14 20:07:59 +11:00
1 parent bcd1b1041a
commit 1f38f12beb
11 files changed
+196 -73

No files matched your search

+2 -2
View File
@@ -95,8 +95,8 @@ export function DiscoveryPanel({ game, act, busy }: Props) {
})}
</div>
<p className="fine">Inspection and reveal are optional. Next-turn free entry, Cistern income for every fighter occupant and later-turn Ornithopter movement are available in the development prototype. Jacurutu income, Testing Station storm choices and Shrine card conversion are prototyped. {game.advanced
? 'Each Orgiz fighter occupant takes one spice per qualifying territory from the first actual foreign collector in storm order. Shared Ecaz lots wait for the actual allocation: Ecaz first, then ally, excluding zero shares and the occupant itself.'
: 'Each Orgiz fighter occupant keeps the provisional one-spice transfer per positive board deposit from the first actual foreign collector in storm order. Basic shared Ecaz allocation remains pending.'} Actual ordinary Collection receipts identify each payer; co-present forces or old wallets alone never qualify. An empty purse never creates debt. Mixed physical Jacurutu dials use a defeated-player choice before settlement. Testing Station occupants each choose once in original storm order, cumulatively. These ordering/allocation interpretations are provisional.</p>
? 'Each Orgiz fighter occupant takes one spice per qualifying territory from the first actual foreign ordinary collector in storm order.'
: 'Each Orgiz fighter occupant keeps the provisional one-spice transfer per positive board deposit from the first actual foreign ordinary collector in storm order.'} Shared Ecaz lots wait for actual allocation, then consider positive foreign recipients Ecaz first, ally second, excluding zero shares and the occupant itself. Basic counts actual positive deposits; Advanced counts the territory once. Actual ordinary receipts identify each payer; co-present forces or old wallets alone never qualify. Empty purses never create debt. Mixed Jacurutu dials use a defeated-player choice. Testing Station occupants each choose once in original storm order, cumulatively. These ordering/allocation interpretations are provisional.</p>
</section>
);
}
+23 -3
View File
@@ -29,7 +29,27 @@ optional skill bands, combinations and public expansion starts remain open.
Rules implementation continues; comprehensive assurance and administration
remain deferred until all rules are implemented.
**Canonical Orgiz Collection sources — 14 October.**
**Basic shared Orgiz settlement — 14 October.**
Both modes now wait for the original Ecaz/ally allocation before Orgiz.
`quoteSharedOrgiz` replaces the Advanced-only path: Advanced charges once per
territory; Basic counts actually reduced positive deposits in the original
saved source map for that territory. Positive foreign recipients are considered
Ecaz first, then ally, skipping zero shares, self and empty purses. Every
transfer updates available balances and checks cumulative overflow. The
original allocation event prevents replay; no saved schema is added.
The native Basic equal split reproduced the missing payment before the fix.
Now equal/full/zero shares, acceptance timing, wrong actors and stale events
pass in both modes; multi-source/fallback/overflow boundaries also pass.
Final six-file union80/80, types/lint/build pass. Actual390px Basic Red Chasm
lot4: Use equal split changes Ecaz7/Guild8→8/11 with one transfer, no decision
and Ready restored. No horizontal overflow or browser errors. All four legal
policies independently complete that Basic split through real engine actions.
Basic's per-deposit mapping and ordering remain provisional; broader module,
rules-mode and deployed acceptance remain open.
[Contract](DISCOVERY_PROTOTYPE.md#multi-occupant-and-shared-cisternorgiz-collection--14-october-2026).
**Earlier canonical Orgiz Collection sources (`bcd1b10`) — 14 October.**
The original ordinary quote now records positive player/location/amount deposit
receipts before Orgiz. Both modes use actual collectors, not co-present force
inference: Basic groups by positive deposit; Advanced groups by territory.
@@ -45,8 +65,8 @@ the other taking0. Face1 is selected at the actual turn-two forecast shuffle,
not assigned to a later source. Final six-file union72/72; types/lint and app
build pass. Actual390px Finish shipment & movement turns phase5→7,
Ecaz7/Guild10→Ecaz8/Guild14 with one Orgiz transfer and Ready restored.
No horizontal overflow or browser errors. Basic shared-allocation settlement,
complete modules and deployed acceptance remain open.
No horizontal overflow or browser errors. Basic shared settlement was pending
at that checkpoint and is now connected above; full/deployed acceptance remains open.
[Contract](DISCOVERY_PROTOTYPE.md#multi-occupant-and-shared-cisternorgiz-collection--14-october-2026).
**Earlier Orgiz payer ordering and native proof (`bc09ac3`) — 14 October.**
+31 -25
View File
@@ -75,13 +75,15 @@ recipients in storm order, skipping self and empty purses: once per territory
in Advanced, once per positive deposit under Basic's provisional mapping.
Co-present forces and old wallets alone do not establish collection.
Advanced shared Ecaz desert lots wait for actual bilateral allocation. Each
occupant considers positive foreign recipients Ecaz first, then ally, charging
at most one spice from an available current balance. A zero share or an old
wallet alone cannot establish eligibility; the owner never pays itself. Basic
shared-allocation Orgiz settlement remains pending. Occupant/payer ordering
is a recommended provisional interpretation, not a new publisher ruling or
separately answered numerical user question.
Shared Ecaz desert lots in both modes wait for actual bilateral allocation.
Each occupant considers positive foreign recipients Ecaz first, then ally,
skipping zero shares, self and empty purses. Advanced charges once per
territory. Basic applies its provisional positive-deposit count to the original
source-map reductions for that territory, excluding unchanged, empty and
unrelated keys. Available balances track every transfer and cumulative
overflow. Original allocation events prevent replay; no saved schema is added.
Occupant/payer ordering and Basic's mapping remain provisional recommended
interpretations, not new publisher rulings or separate numerical user answers.
Current native fixtures preserve original armies, wallets, hands, alliances
and phases. They use real setup, token placement, paid shipments, inspection,
@@ -96,13 +98,15 @@ An exhausted pile leaves the later collector ineligible even with old spice.
`tests/fixture-discovery-occupied-collection.ts`,
`tests/discovery-occupied-collection-runtime.test.ts`,
`tests/fixture-discovery-shared-orgiz.ts` and
`tests/discovery-shared-orgiz-runtime.test.ts`. Focused helper regressions cover
original storm-order payer selection, depleted purses, advisor exclusion and
zero actual collection. The six-file related union72/72, types/lint and app
build pass. Actual390px Finish shipment & movement produces phase7,
Ecaz8/Guild14 from7/10, two actual2-spice source receipts, one Orgiz transfer,
no decision, enabled Ready, no overflow and no browser errors. Basic shared
allocation, broader combined modules and full acceptance remain open.
`tests/discovery-shared-orgiz-runtime.test.ts`. Native Basic/Advanced cases
cover equal/full/zero Ecaz shares, acceptance timing, wrong actors and stale
allocation. Focused boundaries cover multiple Basic source keys, one Advanced
territory, exhausted recipients, foreign-ally fallback and cumulative overflow.
Final six-file union80/80, types/lint and app build pass. Actual390px Basic
Use equal split credits a real Red Chasm lot4, then transfers1: Ecaz7/Guild8
become8/11; phase7 has no decision and Ready is enabled. No overflow or browser
errors. All four minimally configured policies complete the Basic equal split
through actual engine actions. Broader combinations/full acceptance remain open.
## Mixed E1/E2 and paired E3 Nexus — 7 October 2026
@@ -763,10 +767,11 @@ positive territory Collection. Multiple collected source keys do not multiply
the transfer. Canonical positive ordinary deposit receipts identify actual
foreign payers in original storm order, including multiple collectors in one
sector; self and empty purses are skipped. No presence-based ambiguity gate
remains. Advanced shared Ecaz desert lots wait for actual bilateral allocation,
then consider positive foreign recipients Ecaz first, ally second. Basic
shared settlement remains pending. Ordering is provisional under the
recommended-option priority; the Advanced territory trigger itself is sourced.
remains. Shared Ecaz desert lots in both modes wait for actual allocation,
then consider positive foreign recipients Ecaz first, ally second. Advanced
charges once per territory; Basic uses its provisional count of actually
reduced source deposits in that territory. Ordering and the Basic mapping are
provisional; the Advanced territory trigger itself is sourced.
Basic retains its recorded provisional one-transfer-per-positive-deposit
interpretation. The context requires its existing public `advanced` boolean:
@@ -880,9 +885,10 @@ conversion was fabricated.
Basic provisionally charges once per positive deposit. The first actual
foreign collector in storm order with available spice pays; mere presence,
self and zero actual collection do not qualify. Empty purses never create
debt. Advanced shared Ecaz lots wait for allocation, then consider positive
foreign recipients Ecaz first, ally second. Basic shared settlement remains
pending; occupant/payer ordering remains provisional.
debt. Shared Ecaz lots in both modes wait for allocation, then consider actual
positive foreign recipients Ecaz first, ally second. Advanced charges once
per territory; Basic counts its actually reduced positive source deposits.
Owner/payer ordering and the Basic mapping remain provisional.
- **Jacurutu:** one bank spice per opposing undialed physical counter actually
sent to the Tanks. Unique readings remain automatic. For different possible
@@ -1028,10 +1034,10 @@ to Spice Blow. Refresh restored the same station forces and private Snooper.
This is targeted control/recovery evidence, not an unmodified complete game.
The 187 opening saved rooms remain preserved; no server restart was needed.
1. Connect Basic shared-allocation Orgiz settlement and refine combined
Discovery interactions. Canonical ordinary receipts now close the guessed
shared-sector payer gap. Other recommended Discovery interpretations remain
visibly provisional; the Basic positive-deposit mapping is not certified.
1. Refine combined Discovery interactions. Canonical ordinary receipts and
actual bilateral shared settlement now cover both modes without guessed
payers or withheld Basic allocation. Recommended interpretations remain
visibly provisional; focused evidence is not complete-module certification.
2. Integrate combined modules and complete games, then refine interaction,
strategy and presentation coverage before opening normal start gates.
3. Use the existing prototype starter and saved-room verification for further
+28
View File
@@ -1,5 +1,33 @@
# Dune implementation status
## 14 October 2026 — Native Basic shared Ecaz Orgiz settlement
BEFORE: a genuine Basic equal split paid the real lot but withheld Orgiz:
Ecaz9/Guild10 instead of8/11. AFTER: `quoteSharedOrgiz` replaces the
Advanced-only quote and hook. Both modes use actual bilateral receipts.
Advanced charges once per territory. Basic counts original positive source-map
reductions in that territory, excluding unchanged/empty/unrelated keys.
Positive foreign recipients are considered Ecaz first, then ally; self, zero
shares and empty purses are skipped. Every transfer updates available balances
and checks cumulative income overflow. Existing allocation events retain their
one-time ownership; no saved schema or compatibility alias is introduced.
Genuine Basic/Advanced fixtures preserve original armies, wallets, cards,
alliance and phases. Cases cover equal/full/zero shares, acceptance timing,
wrong actor and stale replay. Pure boundaries cover multiple Basic source
deposits versus one Advanced territory, exhausted-recipient fallback, self
exclusion, zero-share old wallets and cumulative overflow without mutation.
One invalid empty-key test fixture was corrected to an actual board sector.
Final six-file union80/80, types/lint and app build pass. Actual390×844 Basic
table: Use equal split settles the real Red Chasm lot4 and then transfers1,
changing Ecaz7/Guild8→8/11, leaving phase7 with no decision and enabled Ready.
No horizontal overflow or browser errors. Easy/Medium/Hard/Brutal each complete
that Basic split through a real legal engine action; this is not calibration.
Owned hosts/tabs/scaffolding removed; proof outside Git. Existing build
warnings remain. Ordering and Basic's positive-deposit mapping are provisional;
combined modules, full modes, assurance and deployed acceptance remain open.
## 14 October 2026 — Canonical positive Collection receipts for Orgiz
The original ordinary quote now emits `deposits` with actual positive
+1 -1
View File
@@ -549,7 +549,7 @@ These are unresolved in the cited records. Do not infer an answer from elapsed t
| Nexus responses and composition | Betrayal private-response timing, partial revival/payment choices, Emperor fewer-than-five, fresh advisor/storm cases, Duke restoration and remaining faction-specific combinations. Tleilaxu Secret Ally force accounting, leader slot/eligibility and force-only use remain unresolved after requested publisher/designer source searches; do not infer answers from native Tleilaxu revival or Ghola. | [Common boundaries](NEXUS_CARD_RULES.md#remaining-source-questions-and-release-boundaries), [Tleilaxu source search](NEXUS_TLEILAXU_RULES.md#source-search-follow-up) and individual faction source/runtime documents |
| Moritani Atomics after alliance change | The publisher's persistent −1 hand-limit text names Moritani and “your ally,” but not whether the penalty follows a later ally, remains on the original ally or affects both. Requested online source searches found no clarification; the alliance-change case remains gated. | [Terror source boundary](MORITANI_TERROR_RULES.md#material-gaps-and-ordinary-compositions) |
| Moritani Sneak Attack entry classification | The printed verb is “Send”; no retrieved ruling classifies the off-planet reserve transfer as a shipment for other arrival powers. The engine now **explicitly adopts** the supported shipment composition: a positive Sneak Attack in Shipment and Movement accrues the Heighliners token once for its owner, then offers the Bene Gesserit Intrusion (Advanced, with fighters present) and the free spiritual-advisor shipment, with the committed Terror entry resolving first; a zero-force resolution accrues nothing and opens no reaction. This order is an implementation interpretation, not a publisher ruling. | [Source boundary and adopted classification](MORITANI_ASSASSINATION_SNEAK.md#adopted-shipment-classification-7-october-2026) |
| Orgiz collection | Basic retains the publisher-p13 per-blow ambiguity and provisional one-transfer-per-positive-deposit mapping. Authorized supplied Advanced rulebook physical24 says each time Collection occurs in a territory containing spice: one transfer per actual positive territory Collection per fighter occupant, never extra transfers for multiple source keys. Canonical positive ordinary deposit receipts now identify actual payers, including co-present collectors; force presence and old wallets alone do not qualify. Under choose-recommended-option priority, owners and eligible foreign ordinary payers resolve in original storm order, skipping self and empty purses. Advanced shared Ecaz lots wait for actual allocation and consider positive foreign recipients Ecaz first, ally second; Basic shared settlement remains pending. Ordering is provisional implementation interpretation, not a separate numerical user answer or publisher ruling. No debt is created. | [Advanced source cutover and Basic boundary](DISCOVERY_PROTOTYPE.md#authorized-advanced-collection-wording) |
| Orgiz collection | Basic retains the publisher-p13 per-blow ambiguity and provisional one-transfer-per-positive-deposit mapping. Authorized supplied Advanced rulebook physical24 says each time Collection occurs in a territory containing spice: one transfer per actual positive territory Collection per fighter occupant, never extra transfers for multiple source keys. Canonical ordinary deposit receipts identify actual payers, including co-present collectors; force presence and old wallets alone do not qualify. Under choose-recommended-option priority, owners and eligible foreign ordinary payers resolve in original storm order. Shared Ecaz lots in both modes wait for actual bilateral allocation, then consider positive foreign recipients Ecaz first, ally second; Advanced charges once per territory, Basic counts actually reduced positive source deposits in that territory. Self, zero shares and empty purses are skipped; balances track every transfer without debt. Ordering and the Basic mapping are provisional implementations, not separate numerical user answers or publisher rulings. | [Advanced source cutover and Basic boundary](DISCOVERY_PROTOTYPE.md#authorized-advanced-collection-wording) |
| Jacurutu physical dial | Under the 14 October choose-recommendation priority, the prototype selects the defeated-player exact ordinary/elite/paid reading before settlement. Unique readings remain automatic; actual returned counters never earn income. This replaces the earlier unpaid gap without claiming a separate numerical user answer or publisher ruling. | [Recommended runtime and source](DISCOVERY_PROTOTYPE.md#recommended-physical-dial-and-cumulative-storm-choices--14-october-2026) |
| Testing Station contest/timing | The 14 October recommended prototype gives each non-advisor occupant one cumulative adjustment in original storm order, after the ordinary card window and before a single traversal. Original source/base and Weather immunity stay unchanged. Ordering/stacking is explicit provisional interpretation, not a newly found publisher answer. | [Recommended runtime and source](DISCOVERY_PROTOTYPE.md#recommended-physical-dial-and-cumulative-storm-choices--14-october-2026) |
| CHOAM Secret Ally battle timing | The printed after-victory inspection is optional and the Nexus identity stays secret. The connected prototype chooses a uniform use-or-continue window for every winner when Nexus is enabled and CHOAM absent; nonholders pass, while a holder may decide after the result. This privacy-preserving prompt is a product inference, not publisher text. A holder-only pause would reveal custody and a precommitment would remove the printed choice. Ordinary Karama cancellation remains unestablished by retrieved sources; no response is invented. | [After-victory inspection boundary](NEXUS_CHOAM_SECRET_ALLY.md#after-victory-inspection-audit) |
+34 -22
View File
@@ -286,35 +286,47 @@ export function quoteDiscoveryCollection(
const SHARED_COLLECTION_RELEASES: readonly AdvisorRelease[] = [];
/** Advanced's one-territory event waits for the original bilateral allocation.
* Charge the first actual positive foreign recipient (Ecaz, then ally), never
* a guessed share, the owner itself, a city award or unavailable spice. */
export function quoteAdvancedSharedOrgiz(
/** Shared collection waits for its original bilateral allocation. Advanced
* charges once per territory; Basic retains one event per positive deposit.
* Only actual positive foreign recipients qualify; current purses cannot owe. */
export function quoteSharedOrgiz(
context: DiscoveryCollectionContext,
allocation: SpiceAllocationReceipt,
balances: Readonly<Record<string,number>>,
originalSpice: Readonly<Record<string,number>>,
): OrgizCollectionReceipt[] {
requireCollection(context.advanced && allocation.ecaz !== allocation.ally &&
requireCollection(typeof context.advanced === 'boolean' && allocation.ecaz !== allocation.ally &&
context.order.includes(allocation.ecaz) && context.order.includes(allocation.ally) &&
whole(allocation.ecazAmount) && whole(allocation.allyAmount) &&
record(balances) && context.order.every(id => whole(balances[id])),
'Shared Orgiz needs the actual Advanced territory allocation and available balances.');
record(balances) && context.order.every(id => whole(balances[id])) &&
record(originalSpice) && Object.entries(originalSpice).every(([key,amount]) =>
whole(amount) && whole(context.spice[key]) && context.spice[key] <= amount),
'Shared Orgiz needs its actual allocation, original board deposits and available balances.');
const owners = discoveryCollectionOccupants(context,ORGIZ_PROCESSING_STATION,SHARED_COLLECTION_RELEASES);
if (!owners.length) return [];
const locations = context.advanced ? [allocation.territory] : Object.keys(originalSpice)
.filter(key => splitLocation(key).territory === allocation.territory &&
originalSpice[key] > context.spice[key]).sort();
requireCollection(locations.length > 0,
'Shared Orgiz must retain the original positive Collection source.');
const effects: OrgizCollectionReceipt[] = [];
let ecazDebited=0, allyDebited=0;
for (const owner of owners) {
let payer: string;
if (owner.id !== allocation.ecaz && allocation.ecazAmount > 0 &&
balances[allocation.ecaz] > ecazDebited) {
payer=allocation.ecaz; ecazDebited++;
} else if (owner.id !== allocation.ally && allocation.allyAmount > 0 &&
balances[allocation.ally] > allyDebited) {
payer=allocation.ally; allyDebited++;
} else continue;
requireCollection(Number.isSafeInteger(balances[owner.id]+1),
'Shared Orgiz income would overflow the spice balance.');
effects.push({kind:'orgiz',player:owner.id,from:payer,location:allocation.territory,
amount:1,source:'player'});
}
const available = {...balances};
for (const location of locations)
for (const owner of owners) {
let payer: string;
if (owner.id !== allocation.ecaz && allocation.ecazAmount > 0 &&
available[allocation.ecaz] > 0)
payer = allocation.ecaz;
else if (owner.id !== allocation.ally && allocation.allyAmount > 0 &&
available[allocation.ally] > 0)
payer = allocation.ally;
else continue;
requireCollection(Number.isSafeInteger(available[owner.id]+1),
'Shared Orgiz income would overflow the spice balance.');
available[payer]--;
available[owner.id]++;
effects.push({kind:'orgiz',player:owner.id,from:payer,location,
amount:1,source:'player'});
}
return effects;
}
+9 -10
View File
@@ -305,7 +305,7 @@ import {
} from './board-resolution-quote';
import {
quoteDiscoveryCollection,
quoteAdvancedSharedOrgiz,
quoteSharedOrgiz,
DiscoveryCollectionError,
type DiscoveryCollectionQuote,
} from './discovery-collection';
@@ -19690,15 +19690,14 @@ function decideSharedSpice(g: Game, id: string, action: Action) {
`${territory(receipt.territory).name} shared collection: Ecaz received ${receipt.ecazAmount} spice and ${faction(getPlayer(g, receipt.ally).faction).name} received ${receipt.allyAmount}, ${receipt.method === 'agreement' ? 'by agreement' : 'using the equal split with any odd spice going to the ally'}.`,
{ faction: 'ecaz', name: 'Shared collection' },
);
if (g.advanced) {
const balances = Object.fromEntries(g.players.map(player => [player.id,player.spice]));
const transfers = nexusRule(() => quoteAdvancedSharedOrgiz(g,receipt,balances));
for (const transfer of transfers) {
getPlayer(g,transfer.from).spice -= transfer.amount;
getPlayer(g,transfer.player).spice += transfer.amount;
log(g, `${faction(getPlayer(g,transfer.player).faction).name} took ${transfer.amount} spice from ${faction(getPlayer(g,transfer.from).faction).name} through Orgiz Processing Station after the original shared territory allocation.`,
{faction:getPlayer(g,transfer.player).faction,name:'Orgiz Processing Station'});
}
const balances = Object.fromEntries(g.players.map(player => [player.id,player.spice]));
const transfers = nexusRule(() => quoteSharedOrgiz(g,receipt,balances,pending.sourceSpice));
for (const transfer of transfers) {
getPlayer(g,transfer.from).spice -= transfer.amount;
getPlayer(g,transfer.player).spice += transfer.amount;
recordFremenCollection(g,transfer.player,`orgiz:shared:${transfer.location}:${transfer.player}`,transfer.amount);
log(g, `${faction(getPlayer(g,transfer.player).faction).name} took ${transfer.amount} spice from ${faction(getPlayer(g,transfer.from).faction).name} through Orgiz Processing Station after the original shared territory allocation.`,
{faction:getPlayer(g,transfer.player).faction,name:'Orgiz Processing Station'});
}
} else
log(
+4 -4
View File
@@ -4226,7 +4226,7 @@ export const RULE_TOPICS: RuleTopic[] = [
'The five revealed location tokens become separate, initially empty territories inside their surroundings. A private peek does not open the location. Normal ground movement enters through the surrounding territory, shipment uses stronghold prices, at most two factions may occupy it, and forces inside are protected from storms and sandworms. Only Jacurutu Sietch counts toward stronghold victory.',
'Native typed forces enter as their actual ordinary and elite groups, never as passengers from the mobile stronghold or the concealed value of a No-Field. Richese may ship or move a concealed marker into an actually revealed nested location at sector zero; it remains one effective presence until its original reveal materializes available reserves. CHOAM Card Stash uses its five-card limit, drawing first and choosing any held discard afterward.',
'At the start of the next turn, before the storm and mobile stronghold movement, each eligible faction may move any positive subset of its non-advisor groups from the surrounding territory into the newly revealed location. The signed choice preserves ordinary and elite forces across sectors, normal source storm and occupancy restrictions, and costs no spice, shipment or normal movement. The printed text gives no sequence and does not restate the source-storm restriction; processing tokens in reveal order and factions in storm order while excluding storm sources are source inferences.',
'During Collection, after advisor releases, every Cistern fighter occupant receives two bank spice. Orgiz occupants resolve in original storm order using canonical positive ordinary deposit receipts, not force presence or old wallets. Advanced charges once per territory; Basic provisionally charges once per positive deposit. The first actual foreign collector in storm order with available spice pays; self and zero actual collection do not qualify. Advanced shared Ecaz desert lots wait for their real split, then consider positive foreign recipients Ecaz first, ally second. Basic shared settlement remains pending. Payer/occupant ordering is provisional; empty purses never create debt.',
'After advisor releases, every Cistern fighter occupant receives two bank spice. Orgiz occupants resolve in original storm order using actual ordinary deposit receipts, not mere force presence or old wallets. Advanced charges once per territory; Basic provisionally charges once per positive deposit. The first actual foreign ordinary collector in storm order with available spice pays. Shared Ecaz lots in both modes wait for the actual split, then consider positive foreign recipients Ecaz first, ally second; Advanced counts the territory once and Basic counts actually reduced source deposits. Self, zero shares and empty purses do not qualify. Balances track each transfer without debt; ordering and Basic mapping remain provisional.',
'Jacurutu pays one bank spice for each opposing undialed physical force actually sent to the Tanks. Where an Advanced dial admits different physical counts, the defeated player chooses an exact ordinary/elite/paid reading before support, casualties or rewards settle; unique readings remain automatic. This choice timing is a visibly provisional implementation interpretation. Original Harass returns bind their physical dial and actual withdrawal/retreat counters do not earn spice.',
'Each non-advisor Testing Station occupant may decrease ordinary storm movement by one, keep it or increase it by one. The provisional order is the original storm order, once per owner, with cumulative adjustments on current distance. Choose after the ordinary card window; traversal/protection starts only after all choices. Weather Control is unaffected. Original natural source/base and existing sole-frame history remain authoritative; legacy movement without a source is not invented.',
'A current non-advisor Shrine occupant may play a physical Truthtrance card as Karama, or a physical Karama as Truthtrance, within the implemented timing and effect paths. The printed identity and actual card custody remain unchanged; a committed Truthtrance conversion survives departure. This does not create missing Karama powers or settle their pending rules.',
@@ -4236,7 +4236,7 @@ export const RULE_TOPICS: RuleTopic[] = [
checklist: [
{
area: 'Implementation', status: 'Partial',
detail: 'Original classic and supported native Skills/Nexus/Discovery entries retain their selected modules and rule controls. Mixed Jacurutu uses an exact defeated-player physical reading before native settlement. Testing Station queues signed cumulative storm-order choices before one traversal. Cistern pays each fighter occupant; Orgiz consumes actual positive ordinary deposit receipts in original storm order, grouping by territory in Advanced and deposit in Basic without guessing co-present payers or creating debt. Advanced shared lots wait for bilateral allocation; Basic shared settlement and broader mode acceptance remain open.',
detail: 'Original classic and supported native Skills/Nexus/Discovery entries retain their selected modules and controls. Jacurutu uses an exact defeated-player reading before native settlement; Testing Station queues cumulative choices before one traversal. Cistern pays every fighter occupant. Orgiz uses actual ordinary receipts and bilateral shared allocations in both modes, grouping by territory in Advanced and positive source deposit in Basic without guessed payers or debt. Available balances track all transfers and cumulative overflow. Broader mode/module acceptance remains open.',
evidence: ['game/discovery-module-profile.ts', 'game/richese-no-field.ts', 'game/discoveries.ts', 'game/discovery-actions.ts', 'game/discovery-entry.ts', 'game/discovery-collection.ts', 'game/board-resolution-quote.ts', 'game/discovery-flight.ts', 'game/discovery-battle.ts', 'game/discovery-storm.ts', 'game/shrine.ts', 'game/great-maker.ts', 'game/board.ts', 'game/engine.ts'],
},
{
@@ -4251,12 +4251,12 @@ export const RULE_TOPICS: RuleTopic[] = [
},
{
area: 'Documentation', status: 'Partial',
detail: 'Sourced identities and native/classic-skill boundaries are recorded. Advanced Orgiz follows territory Collection and actual ordinary receipts; Basic retains its provisional positive-deposit mapping. Recommended Discovery occupant/payer ordering, Jacurutu allocation and cumulative Testing Station choices are visible interpretations, not publisher rulings. Great Maker, entry/source-storm inferences and Basic shared Orgiz settlement remain open.',
detail: 'Sourced identities and native/classic-skill boundaries are recorded. Advanced Orgiz uses territory Collection; Basic retains its provisional deposit mapping, including actual source reductions after bilateral shared settlement. Recommended occupant/payer ordering, Jacurutu allocation and cumulative Testing Station choices are visible interpretations, not publisher rulings. Great Maker and entry/source-storm inferences remain explicit; full acceptance stays open.',
evidence: ['docs/DISCOVERY_COMPONENTS.md', 'docs/DISCOVERY_PROTOTYPE.md'],
},
{
area: 'Verification', status: 'Partial',
detail: 'Current canonical Collection batch passes72/72 in6 related files, types/lint and build. Genuine Basic/Advanced co-present Sihaya Ridge collectors take2 each or exhaust the6-spice pile in actual storm order; zero actual collection cannot qualify through old wallets. Actual390px Finish shipment & movement produces phase7, Ecaz8/Guild14 from7/10 and one Orgiz transfer with Ready restored, no overflow or errors. Earlier shared-allocation proof passes27/27 and Jacurutu/Testing Station proof104/104, each source-bound. Basic shared settlement, combinations, assurance, strategy and deployed acceptance remain open.',
detail: 'Current Collection batch passes80/80 in6 related files, types/lint and build. Genuine Basic/Advanced cases cover shared equal/full/zero splits, acceptance timing, wrong actors and stale events; helper boundaries cover multiple deposits, exhausted recipients, self/zero-share exclusion and cumulative overflow. Actual390px Basic Red Chasm lot4 changes Ecaz7/Guild8 to8/11 with one transfer and Ready restored, no overflow or errors. Four legal policies complete that Basic allocation via actual actions. Earlier canonical-source and Jacurutu/Station evidence remains source-bound; combinations, assurance, strategy and deployed acceptance remain open.',
evidence: ['tests/mixed-nexus-modules-runtime.test.ts', 'tests/paired-e3-nexus-modules-runtime.test.ts', 'tests/sample-custody.test.ts', 'tests/ambassador-terror-overlap-engine.test.ts', 'tests/e3-nexus-ecaz-runtime.test.ts', 'tests/e3-nexus-moritani-runtime.test.ts', 'tests/single-nexus-e1-runtime.test.ts', 'tests/single-nexus-e2-runtime.test.ts', 'tests/homeworld-paired-nexus-runtime.test.ts', 'tests/homeworld-standalone-nexus-runtime.test.ts',
'tests/fixture-jacurutu-mixed-dial.ts', 'tests/jacurutu-mixed-dial-runtime.test.ts', 'tests/fixture-testing-station-contest.ts', 'tests/testing-station-contest-runtime.test.ts',
'tests/fixture-discovery-occupied-collection.ts', 'tests/discovery-occupied-collection-runtime.test.ts', 'tests/fixture-discovery-shared-orgiz.ts', 'tests/discovery-shared-orgiz-runtime.test.ts',
+56
View File
@@ -13,6 +13,7 @@ import {
ORGIZ_PROCESSING_STATION,
DiscoveryCollectionError,
quoteDiscoveryCollection,
quoteSharedOrgiz,
type DiscoveryCollectionContext,
type DiscoveryCollectionQuote,
} from '../game/discovery-collection';
@@ -676,3 +677,58 @@ void test('two Orgiz owners resolve available collector spice in original storm
assert.equal(balance(discovery,'h'),0);
}
});
function sharedOrgizBoundary(advanced:boolean) {
const game=fixture(advanced,[['a','atreides'],['e','ecaz'],['g','guild']]);
reveal(game,ORGIZ_PROCESSING_STATION);
game.players[0].forces={[`${ORGIZ_PROCESSING_STATION}:0`]:1};
game.players[1].forces={'hagga_basin:12':1};
game.players[2].forces={'hagga_basin:13':1,'red_chasm:7':1};
game.players[1].ally='g';game.players[2].ally='e';
game.spice={'hagga_basin:12':2,'hagga_basin:13':2,'imperial_basin:9':0,'red_chasm:7':3};
const ordinary=quoteSpiceCollection(game);
return {game,context:{...game,spice:ordinary.spice},originalSpice:game.spice,
allocation:{territory:'hagga_basin',ecaz:'e',ally:'g',ecazAmount:2,allyAmount:2,method:'equal' as const}};
}
void test('shared Orgiz counts actual positive source deposits in Basic, one territory in Advanced, excluding unrelated and empty keys',()=>{
for(const advanced of [false,true]) {
const f=sharedOrgizBoundary(advanced);
const before=structuredClone(f);
const effects=quoteSharedOrgiz(f.context,f.allocation,{a:0,e:2,g:4},f.originalSpice);
assert.deepEqual(effects.map(effect=>[effect.player,effect.from,effect.location]),
advanced?[['a','e','hagga_basin']]:
[['a','e','hagga_basin:12'],['a','e','hagga_basin:13']]);
assert.deepEqual(f,before);
}
});
void test('Basic shared Orgiz exhausts the first recipient then considers the actual ally, never self',()=>{
const f=sharedOrgizBoundary(false);
f.context.players[2].forces[`${ORGIZ_PROCESSING_STATION}:0`]=1;
const allocation={...f.allocation,ecazAmount:1,allyAmount:3,method:'agreement' as const};
const effects=quoteSharedOrgiz(f.context,allocation,{a:0,e:1,g:5},f.originalSpice);
assert.deepEqual(effects.map(effect=>[effect.player,effect.from,effect.location]),
[['a','e','hagga_basin:12'],['a','g','hagga_basin:13']]);
});
void test('Basic shared Orgiz skips a zero-share Ecaz wallet and charges only the positive foreign ally',()=>{
const f=sharedOrgizBoundary(false);
const allocation={...f.allocation,ecazAmount:0,allyAmount:4,method:'agreement' as const};
const balances={a:0,e:20,g:6},before=structuredClone(balances);
const effects=quoteSharedOrgiz(f.context,allocation,balances,f.originalSpice);
assert.deepEqual(effects.map(effect=>effect.from),['g','g']);
assert.deepEqual(balances,before);
});
void test('shared Orgiz checks cumulative income overflow without mutating either purse or original source',()=>{
const f=sharedOrgizBoundary(false);
const allocation={...f.allocation,ecazAmount:4,allyAmount:0,method:'agreement' as const};
const balances={a:Number.MAX_SAFE_INTEGER-1,e:4,g:2};
const before=structuredClone({f,balances});
assert.throws(()=>quoteSharedOrgiz(f.context,allocation,balances,f.originalSpice),DiscoveryCollectionError);
assert.deepEqual({f,balances},before);
f.context.advanced=true;
assert.deepEqual(quoteSharedOrgiz(f.context,allocation,balances,f.originalSpice),
[{kind:'orgiz',player:'a',from:'e',location:'hagga_basin',amount:1,source:'player'}]);
});
+6 -4
View File
@@ -7,9 +7,10 @@ import {prepareSharedOrgizCollection,settleSharedOrgiz} from './fixture-discover
const wallets=(game:Game)=>Object.fromEntries(game.players.map(p=>[p.id,p.spice]));
const transfers=(game:Game)=>game.log.filter(entry=>entry.automatic?.name==='Orgiz Processing Station');
for(const advanced of [false,true])
for(const split of ['equal','all-ecaz','all-guild'] as const) {
void test(`Advanced native shared Collection: ${split} respects the actual foreign recipient and sole Orgiz owner`,()=>{
const position=prepareSharedOrgizCollection();
void test(`${advanced?'Advanced':'Basic'} native shared Collection: ${split} respects the actual foreign recipient and sole Orgiz owner`,()=>{
const position=prepareSharedOrgizCollection(advanced);
const original=structuredClone(position.game);
const before=wallets(original);
const choice=split==='equal'?{kind:'equal' as const}:
@@ -35,8 +36,9 @@ for(const split of ['equal','all-ecaz','all-guild'] as const) {
});
}
void test('Native shared Orgiz waits for ally acceptance and rejects the wrong actor without changing wallets',()=>{
const position=prepareSharedOrgizCollection();
for(const advanced of [false,true])
void test(`${advanced?'Advanced':'Basic'} shared Orgiz waits for ally acceptance and rejects the wrong actor without changing wallets`,()=>{
const position=prepareSharedOrgizCollection(advanced);
const before=structuredClone(position.game);
const event=before.ecazCollection!.event;
const action={type:'decision' as const,event,allocation:{kind:'propose' as const,ecazShare:position.lot.amount}};
+2 -2
View File
@@ -69,8 +69,8 @@ function prepareOrgizCollectors(advanced:boolean,terrainType:'sand'|'rock',exhau
}
/** Desert collectors enter the original shared allocation before any credit. */
export function prepareSharedOrgizCollection() {
const position=prepareOrgizCollectors(true,'sand');
export function prepareSharedOrgizCollection(advanced=true) {
const position=prepareOrgizCollectors(advanced,'sand');
const game=applyAction(position.game,position.finalAction.actor,position.finalAction.action);
assert.equal(game.decision?.kind,'ecazSpice');
const pending=game.ecazCollection!.allocation!;