/** * Shared dice-modifier semantics — the single source of truth that both the * sampling tier (`Roller`) and the exact-distribution tier (`DiceStats`) must * agree on. Historically each modifier was re-encoded per tier, which let the * sampler and the analyzer silently diverge (see the emphasis center / reroll * tie-break bugs). Anything that defines *what a modifier means* belongs here, * imported by both tiers, rather than copied. * * This module is being grown incrementally as mechanics are unified; emphasis * is the first migrated modifier. */ /** * The reference point for an `emphasis` ("furthest from X") roll. For * `'average'` this is the die's true mean `(sides + 1) / 2` — a half-integer * for even-sided dice. (The earlier sampler used `floor(sides / 2)`, which is * not the mean and broke symmetry across the center, so the sampler and the * exact distribution disagreed.) */ export declare function emphasisCenter(sides: number, furthestFrom: number | 'average'): number; /** * Resolve which of two rolls is "furthest" from `center`. Returns `'first'` / * `'second'` for a clear winner; on an equal-distance tie it applies the * tie-breaker, returning `'tie'` for `'reroll'` (the caller decides how to * reroll — the sampler rolls a fresh pair, the analyzer renormalises over * non-tie outcomes). */ export declare function pickFurthest(first: number, second: number, center: number, tieBreaker: 'low' | 'high' | 'reroll'): 'first' | 'second' | 'tie'; /** * How a chained modifier combines successive rolls: `'sum'` adds each rolled * face to a running total (explode / compound), `'replace'` keeps only the * latest face (reroll). */ export type ChainMode = 'sum' | 'replace'; /** * Upper bound on the number of (live-state × face) steps {@link walkChain} will * process before declining. The walk is polynomial, but the polynomial still * grows without limit as the per-step continuation probability approaches 1 * (e.g. `1d50 explode on >= 2`, which triggers on 49 of 50 faces — the * ε-truncation depth `unboundedDepthCap` returns is ~1354, and the live * accumulated-value set widens with depth). Such a distribution is genuinely * heavy-tailed and impractical to enumerate exactly; past this budget the * exact tier declines (`exact-not-supported`) and the caller falls back to * Monte Carlo, which samples it correctly. The bound is sized so every * ordinary explode/reroll/compound (small dice, or large dice exploding on a * narrow trigger range such as `explode on max`) stays well under it. */ export declare const MAX_CHAIN_WORK = 10000000; /** * Exact-enumeration kernel for a single die's modifier chain (explode / * compound / reroll), shared by `DiceStats`. Walks every face `1..sides` * probability-weighted; a face that satisfies `trigger` (and is within the * depth `cap`) extends the chain, otherwise the chain terminates and the * outcome is emitted via `emit(value, prob)`. `mode` selects how successive * rolls combine (see {@link ChainMode}). * * `start` seeds the walk for composition — e.g. the explode phase of a * `combined` reroll-then-explode functor begins from an already-settled face * at depth 1 (the settled die is the first link of the chain). This single * kernel replaces the previously separate explode/compound, reroll, and * combined enumerators, so the trigger/cap/accumulation semantics live in one * place and cannot drift between modifiers. * * The walk propagates a *distribution* over accumulated values one depth at a * time rather than enumerating each chain path individually. Two live chains * that reach the same accumulated value at the same depth have identical * futures (continuation depends only on the next rolled face, never on the * path taken to get here), so they are merged into a single weighted entry. * This is what keeps the kernel polynomial: a naive path enumeration branches * by the number of triggering faces at every depth, so a partial trigger like * `explode on >= 4` of a d6 (3 of 6 faces continue) with the unbounded * tail-mass cap of ~40 would walk `3^40 ≈ 1e19` paths and never return. Here * each depth carries at most O(cap * sides) distinct accumulated values, so * the whole walk is O(cap^2 * sides^2) regardless of how many faces trigger. */ export declare function walkChain(sides: number, trigger: (face: number) => boolean, cap: number, mode: ChainMode, emit: (value: number, prob: number) => void, start?: { acc: number; prob: number; depth: number; }): void;