Round CHOAM income after totaling eligible support

This commit is contained in:
Quintine committed 2026-10-27 10:59:21 +11:00
1 parent 54787c7f7b
commit dff0c77a5b
8 files changed
+161 -26

No files matched your search

+50
View File
@@ -0,0 +1,50 @@
# CHOAM total support income
27 October 2026. **Prototyped / Partial.** The adopted Advanced source,
supplied physical29/printed27 F, gives CHOAM half the total eligible support
spice with one final rounding down. Two other combatants supporting with3 and5
therefore yield4, and3 plus3 yield3. Earlier per-combatant flooring underpaid
those battles. See the [source authority](RULE_DECISIONS.md#authorized-source-amendment--1-october-2026).
This correction changes aggregation only. Each non-CHOAM side still contributes
`plan.support - plan.allyPayment`. That includes its own debit and the Arrakeen
Bank contribution. Publisher E2 physical/printed10's Arrakeen Q&A explicitly
includes the Bank's two support spice; replacing eligibility with wallet debits
would incorrectly remove that benefit. The
[publisher E2 book](https://www.gf9games.com/dune/wp-content/uploads/2021/11/CHOAM-Rulebook-low-res.pdf#page=10)
supplies that omitted detail. No combined publisher worked example for every
subsidy/funding mixture is claimed.
CHOAM's own side remains excluded. CHOAM-funded allied support goes entirely to
the Bank under supplied G. Salusa free Sardaukar, Fremen free support, fixed
Homeworld strength and separate Banker strength spending add no paid support.
The current native pledge admits only a mutual CHOAM donor; future third-party
funding or generic discounts are not certified by this correction. Existing
Banker/Bureaucrat per-payer/per-invoice rulings remain separate.
The shared `game/battle-resolution-quote.ts` calculation supplies the original
engine settlement. Native wallet debits, escrow, casualty/card cleanup, any
revealed-Traitor suppression, explosion income, Karama redirection and once-only
collection keep their paths. Basic has no Advanced force-payment income.
Already queued income retains its recorded receipt; no saved field, migration,
recalculation of settled payments or collection confirmation is added.
Focused quote and actual engine payment regressions cover odd totals. Runtime
verification uses a clearly labelled controlled battle source with real native
plan, Traitor, response and market actions; it is not a fresh setup/full-game
claim. Full modes, wider compositions, comprehensive assurance and deployed
acceptance remain open.
Frozen **4 affected files, 71/71**, types/lint/build and two controlled battle
programmes pass. 13 actual native packets and 6 GameTable SSR renders verify
one automatic4 receipt and one pending/canceled4 receipt, unchanged3/5 debits,
original pending response text and once-only closing behavior. The explicit
controlled fixture assigns phase, purses, armies and leader strength; it is not
a genuine starting-game journey. No control callbacks are operated. Quote and
engine regressions cover3+5/3+3; the existing subsidy, aid, own support, free
support, Traitor, explosion and cancellation cases remain in the related union.
The existing server returned HTTP200 without restart. No saved-game/database operation.
Proof: `/home/quintine/.local/share/dune/checkpoints/20261027-choam-support-income-proof-v1/`;
HTML/journeys: adjacent `20261027-choam-support-income-runtime/run/`.
Full modes, broader combinations and final assurance remain open.
+29 -12
View File
@@ -20,6 +20,29 @@ open mode gates.
Keep identified Basic-function gaps ahead of Advanced and wider interactions.
Prototype labels and final acceptance gates remain.
**CHOAM total support income — 27 October. Prototyped / Partial.**
Advanced income now adds eligible support before rounding down once:3+5 yields4
and3+3 yields3. Original `support - allyPayment` eligibility retains Arrakeen's
Bank contribution and CHOAM-own/ally exclusions. Payments, free support, Traitor
suppression, native aftermath and Karama paths are unchanged; this is separate
from Banker/Bureaucrat per-invoice rules. Already queued receipts retain their
recorded amounts. [Source and scope](CHOAM_SUPPORT_INCOME.md).
Frozen **4 affected files, 71/71**, types/lint/build and two controlled battle
programmes pass. 13 actual choose/plan/Traitor/response/closing packets and
6 GameTable SSR renders verify automatic4 once and cancelable4 with unchanged
combatant debits3/5. The existing unit source assigns phase/wallets/forces and
zero-strength leaders, with a conserved original Karama in one case; this is
explicitly controlled evidence, not native original setup/full-game proof.
No control callbacks are claimed. New quote/engine odd-total regressions retain
the original subsidy, aid, Traitor, explosion and free-support scenarios.
The existing server returned HTTP200 without restart. No database or saved-game
operation. Full modes, wider compositions and final rules/recovery/privacy/
browser/deployed acceptance remain. Proof:
`/home/quintine/.local/share/dune/checkpoints/20261027-choam-support-income-proof-v1/`;
journeys/HTML: adjacent `20261027-choam-support-income-runtime/run/`.
**Advanced Charity player order — 27 October. Prototyped / Partial.**
New Advanced phases proceed from the First Player counterclockwise. Only the
current seat may claim or Finish; CHOAM income and wealthy BG responses retain
@@ -72,18 +95,12 @@ Proof: `/home/quintine/.local/share/dune/checkpoints/20261027-ix-starting-pool-p
journeys/HTML: adjacent `20261027-ix-starting-pool-runtime/run/`.
[Source contract](IX_PROTOTYPE.md#advanced-starting-draft-source-cutover--27-october-2026).
**Next source-identified functions:** CHOAM's total-support rounding
(supplied physical29 F) and Family Atomics' full timing/sector/removal contract
(physical39; the revision's stricter requirements remain Advanced-only after
bounded Basic comparison). The bounded CHOAM source comparison supports a
rounding-only correction: retain eligible `support - allyPayment`, including
Arrakeen's expressly sourced Bank contribution; preserve CHOAM-own/CHOAM-aid
exclusions and free-force support. Aggregate before flooring; unrelated
Banker/Bureaucrat per-invoice rulings stay distinct. This is next implementation,
not a passed new effect. Charity Sapho composition waits for the interaction pass.
These findings are concrete divergences, not a complete rules audit. The bounded
six-classic-faction pass found connected independent functions; optional modules,
interactions and final acceptance remain open.
**Next source-identified function:** Family Atomics' full timing/sector/removal
contract (supplied physical39). The revision's stricter requirements remain
Advanced-only after bounded Basic comparison. Charity Sapho composition remains
for the interaction pass. These findings are concrete divergences, not a complete
rules audit. The bounded six-classic-faction pass found connected independent
functions; optional modules, interactions and final acceptance remain open.
**Native E3 trained capture — 27 October. Prototyped / Partial.**
Fresh bare Advanced E3/all14 setups now connect Harkonnen with actual Ecaz
+24
View File
@@ -1,5 +1,29 @@
# Dune implementation status
## 27 October 2026 — CHOAM aggregate support rounding
Prototyped / Partial. Advanced income now adds eligible support before rounding down once:3+5 yields4
and3+3 yields3. Original `support - allyPayment` eligibility retains Arrakeen's
Bank contribution and CHOAM-own/ally exclusions. Payments, free support, Traitor
suppression, native aftermath and Karama paths are unchanged; this is separate
from Banker/Bureaucrat per-invoice rules. Already queued receipts retain their
recorded amounts. [Source and scope](CHOAM_SUPPORT_INCOME.md).
Frozen **4 affected files, 71/71**, types/lint/build and two controlled battle
programmes pass. 13 actual choose/plan/Traitor/response/closing packets and
6 GameTable SSR renders verify automatic4 once and cancelable4 with unchanged
combatant debits3/5. The existing unit source assigns phase/wallets/forces and
zero-strength leaders, with a conserved original Karama in one case; this is
explicitly controlled evidence, not native original setup/full-game proof.
No control callbacks are claimed. New quote/engine odd-total regressions retain
the original subsidy, aid, Traitor, explosion and free-support scenarios.
The existing server returned HTTP200 without restart. No database or saved-game
operation. Full modes, wider compositions and final rules/recovery/privacy/
browser/deployed acceptance remain. Proof:
`/home/quintine/.local/share/dune/checkpoints/20261027-choam-support-income-proof-v1/`;
journeys/HTML: adjacent `20261027-choam-support-income-runtime/run/`.
## 27 October 2026 — Ordered Advanced Charity
Prototyped / Partial. New Advanced phases proceed from the First Player counterclockwise. Only the
+10
View File
@@ -1,5 +1,15 @@
# Rule decision index
## CHOAM total support rounding — 27 October 2026
Supplied Advanced physical29/printed27 F calls for half the total eligible
force-support spice, rounded down once. Move the floor outside the existing
non-CHOAM `support - allyPayment` sum. Retain Arrakeen's publisher-confirmed Bank
contribution, CHOAM's own/ally exclusions, free support and original payment/
Traitor/cancellation paths. This is the adopted source correction, distinct
from existing Banker/Bureaucrat per-invoice rulings. Wider hypothetical funding
and discount combinations remain unverified. [Source and scope](CHOAM_SUPPORT_INCOME.md).
## Advanced Charity player order — 27 October 2026
Supplied physical8/printed6 expressly begins Charity with the First Player and
+11 -8
View File
@@ -670,16 +670,19 @@ function calculate(input: BattleResolutionInput): BattleResolutionQuote {
}
const choam = input.participants.find((p) => p.faction === 'choam');
// Preserve existing source-backed income, including Stronghold bank support;
// CHOAM's prepaid ally contribution is excluded from this receipt.
// CHOAM's prepaid ally contribution is excluded from this receipt. The
// adopted Advanced source rounds once after totaling the eligible support.
const choamAmount =
input.advanced && choam && !ac && !dc
? [a, d].reduce(
(sum, s) =>
sum +
(s.id === choam.id
? 0
: Math.floor((s.plan.support - (s.plan.allyPayment ?? 0)) / 2)),
0,
? Math.floor(
[a, d].reduce(
(sum, s) =>
sum +
(s.id === choam.id
? 0
: s.plan.support - (s.plan.allyPayment ?? 0)),
0,
) / 2,
)
: 0;
const result =
+10 -2
View File
@@ -4747,16 +4747,17 @@ export const RULE_TOPICS: RuleTopic[] = [
title: 'CHOAM combat funding and force income',
category: 'Advanced & expansions',
coverage: 'Partial',
developmentStage: 'Prototyped',
summary:
'Finance an ally’s supported forces and receive a share of other players’ combat payments.',
steps: [
'In advanced play, CHOAM may pay some or all of its ally’s force support. At the start of an allied battle, choose the amount of unspent spice to reserve for the phase. CHOAM can update this pledge during ordinary Battle controls. Only a current mutual ally may use it.',
'A Karama response can prevent allied funding for the current battle. Reserved spice stays with the pledge, but the ally must plan with its own funds. Later battles get a fresh response and may use the remaining pledge.',
'When sealing a plan, choose total spice support and optionally CHOAM’s share. With no explicit share, your own spice is used first and the pledge covers the shortfall. The chosen amounts remain reserved until resolution; CHOAM cannot withdraw a committed share. Unspent pledged spice returns at the end of Battle, after the closing market.',
'When other players pay for battle forces, CHOAM receives half of each player’s actual payment, rounded down. Its own force payments and any spice it supplies for its ally go to the bank without generating a rebate. For example, payments of three and five from two other players produce one plus two spice for CHOAM.',
'When other factions support battle forces, add the eligible support from both sides, then halve the total and round down once. Payments of three and five produce four spice for CHOAM. Retain the Arrakeen Bank contribution, while excluding CHOAM’s own support and the spice it supplies for its ally. Free-supported forces and separate battle-strength spending add no support spice.',
'Any revealed traitor prevents all CHOAM force-payment income from that battle. A sole traitor winner pays no support, including its pledged allied share. If both sides reveal traitors, both pay their support but CHOAM still gets no income. A lasgun–shield explosion without a traitor still pays support and generates income.',
'After casualty and winner-card choices, a separate Karama response precedes CHOAM’s income. Cancellation sends the amount to the bank and does not refund the combatants. Complete the response before post-battle technology, capture and Face Dancer rewards continue.',
'Pledges and unrevealed payment splits stay private to their authorized viewers. Battle guides and all four AI levels account for reserved allied funds when finding legal plans. The precise ordering with later expansion powers, rounding across simultaneous payers, allied-payment interpretation and the alliance cancellation window remain under final audit. CHOAM starts stay disabled pending its remaining powers.',
'Pledges and unrevealed payment splits stay private to their authorized viewers. Battle guides and all four AI levels account for reserved allied funds when finding legal plans. The precise ordering with later expansion powers, wider allied-payment compositions and the alliance cancellation window remain under final audit. CHOAM starts remain previews; complete faction and combination acceptance is unfinished.',
],
related: [
'faction-choam',
@@ -4766,6 +4767,13 @@ export const RULE_TOPICS: RuleTopic[] = [
'card-karama',
'card-truthtrance',
],
checklist: [
{ area: 'Implementation', status: 'Partial', detail: 'Support income floors the eligible battle total once; original support, Arrakeen contribution, allied exclusions, Traitor and aftermath rules remain.', evidence: ['game/battle-resolution-quote.ts', 'game/engine.ts'] },
{ area: 'Player controls', status: 'Partial', detail: 'Existing funding, plan and cancellation controls retain automatic uncontested income; no extra collection confirmation.', evidence: ['components/choam-battle-funding.tsx', 'components/game-table.tsx'] },
{ area: 'AI', status: 'Partial', detail: 'Existing minimal policies use shared resolution and payment rules; strategy/calibration remain deferred.', evidence: ['game/bots.ts', 'tests/choam-combat.test.ts'] },
{ area: 'Documentation', status: 'Partial', detail: 'Adopted total-rounding source and unchanged native eligible support are explicit.', evidence: ['docs/CHOAM_SUPPORT_INCOME.md'] },
{ area: 'Verification', status: 'Partial', detail: 'Focused quote/payment regressions cover odd support sums and existing exclusions; full modes, wider compositions and deployed acceptance remain.', evidence: ['tests/battle-resolution-quote.test.ts', 'tests/choam-combat.test.ts'] },
],
},
{
id: 'choam-karama',
+11
View File
@@ -297,6 +297,17 @@ void test('Habbanya selects ordinary and Stone ties; canceled free-support and e
{ normal: 2, elite: 1, paidNormal: 0, paidElite: 0 },
]);
});
void test('CHOAM quotes half of the total other-faction support with one final floor', () => {
const g = input();
g.participants = [...g.participants, { id: 'c', faction: 'choam' }];
g.attacker.plan.dial = g.attacker.plan.support = 3;
g.defender.plan.dial = g.defender.plan.support = 5;
const before = structuredClone(g), q = quoteBattleResolution(g);
assert.deepEqual(q.choamIncome, { owner: 'c', amount: 4 });
assert.deepEqual(q.payments.map(payment => payment.ownPayment), [3, 5]);
assert.deepEqual(q.payments.map(payment => payment.bankSupport), [0, 0]);
assert.deepEqual(g, before);
});
void test('funding consumes explicit current escrow and includes Stronghold bank support in CHOAM income without debiting the bank or donor twice', () => {
const g = input();
g.participants = [
+16 -4
View File
@@ -196,16 +196,28 @@ void test('the ally can choose CHOAM to pay despite having its own spice; forged
assert.equal(result.response, null);
assert.equal(player(result, 'c').spice, 7); // Six after funding, plus one from opponent; no self rebate.
});
void test('income rounds down each other player payment and is collected only once after combat choices', () => {
void test('income rounds down the combined support and is collected only once after combat choices', () => {
let g = settle(plan(plan(start(fixture(false)), 'g', 3), 'e', 3));
assert.equal(g.response, null);
assert.equal(player(g, 'c').spice, 12); // No cancellation choice: income settles immediately.
assert.equal(player(g, 'c').spice, 13); // Half of 3 + 3; uncontested income settles immediately.
assert.equal(player(g, 'g').spice, 7);
assert.equal(player(g, 'e').spice, 7);
g = allow(g);
assert.equal(player(g, 'c').spice, 12);
assert.equal(player(g, 'c').spice, 13);
assert.equal(g.pendingChoamBattleIncome, null);
assert.equal(g.decision?.kind, 'choamMarket');
g = send(g, 'c', { type: 'decision', done: true });
assert.equal(player(g, 'c').spice, 12);
assert.equal(player(g, 'c').spice, 13);
});
void test('two other combatants paying three and five yield four CHOAM income without changing either debit', () => {
let g = settle(plan(plan(start(fixture(false)), 'g', 3), 'e', 5));
assert.equal(player(g, 'c').spice, 14);
assert.equal(player(g, 'g').spice, 7);
assert.equal(player(g, 'e').spice, 5);
assert.equal(g.pendingChoamBattleIncome, null);
assert.equal(g.decision?.kind, 'choamMarket');
g = send(g, 'c', { type: 'decision', done: true });
assert.equal(player(g, 'c').spice, 14);
});
void test('Karama redirects just this battle income to the bank without refunding force payments', () => {
const initial = fixture(false);