Book of Odin — Math Spec, book-of-odin-v2
Draft
draft-v0.1. DEV ONLY. NOT FOR PRODUCTION. NOT FOR CERTIFICATION. Every figure here was measured from the pack named below. Internal measurement is not external certification, and this version is not frozen.
1. Identity
The canonical config hash is computed by libs/mathengine/canonical, the repository's single
canonicalisation-and-hash implementation. The artefact SHA-256 identifies the source file; the
canonical hash is the identity the runtime records on every durable feature row. They answer
different questions and both are recorded.
book-of-odin-v1 is untouched. Migration 008 still hashes
e87b3e29396ac8ff05196ae6d56fe46e7282a53b77388c1e0fc9af5d33346990. Both versions share one
game_id and coexist as separate game_configs rows.
2. Reel model
Strips are weighted entries, not stops. One weighted draw per reel picks an entry index and the visible column is that entry plus the next two, wrapping. Rows on a reel are therefore correlated, and only 20 columns are reachable per reel.
The layout is design-locked so the trigger frequencies stay exactly analysable:
So P(BOOK visible on a reel) = (w8 + w9 + w10) / W, and the COIN count per reel takes only
five values. Both totals are exact integer convolutions of five per-reel distributions — no
sampling. Pinned by TestReelLayoutIsDesignLocked and TestReelWindowCounts.
3. Paytable
Payouts are multipliers of the line stake; a round total divides by the 10 paylines.
BOOK declares only a 5-of-a-kind, and that is structural rather than an omission. The line
evaluator takes the first non-wild symbol as the match symbol, so leading BOOKs always fold
into the symbol that follows them; BOOK is the match symbol only when the whole line is BOOK.
Declaring 2-, 3- and 4-entries would advertise rows no grid can reach. Pinned by
TestWildShortRunsAreStructurallyUnreachable.
COIN has no line payout at any count. A COIN line pay would add base value to exactly the
rounds that trigger the Hold & Win, and no later measurement could separate the two.
4. Features
4.1 Free Spins
3+ BOOK award 10 free spins. One of A–I is selected by the server before the series and
expands for its whole duration. Retrigger is disabled in this version. The nine variants are
equally weighted, so the selection-weighted aggregate is a plain mean.
expanding_symbol.TriggerSymbolID is deliberately empty. The selected symbol reaches the
executor as an override; the configured value is what applies when there is no override — that
is, in the base game. game-engine keeps expanding_symbol on the paid base path, so naming a
real symbol there makes it expand on ordinary paid spins. Naming BOOK — the obvious choice,
and what v1 does — takes the base game from 39.51% to 148.66% RTP. Measured, not estimated;
pinned by TestBaseGameDoesNotExpand.
4.2 Hold & Win (hold_and_win/v2)
6+ COIN open the feature, from a base spin or a free spin. Triggering coins carry into their
own cells. 3 respins; any new coin resets to 3; a miss decrements. Termination at 0 respins or
15 locked cells. Ordinary award is the sum of locked coin multipliers; a full grid pays that sum
plus the GRAND.
The v1 mechanic cannot express either the regular-only pool (it requires all four kinds with
positive weight) or the additive GRAND (it replaces the sum). That is why hold_and_win/v2
exists. v1 is unchanged and still frozen behind magic-vault-5x3's and hold_and_win_reference's
golden vectors.
5. RTP decomposition
Whole-game target 98.00%, tolerance ±0.10 pp.
5.1 Exact terms
Full enumeration of the reel-stop state space (20⁵ = 3 200 000 grids per reelset) and integer convolutions of the reel windows. No sampling error.
Per-variant free-spin value and ceiling:
5.2 Sampled terms
Seeded Monte Carlo through RunHoldAndWinToCompletion — the same Start/Step state machine
the durable runtime steps through one request at a time.
5.3 Reconciliation
Target 98.0000%, deviation −0.0021 pp, tolerance ±0.10 pp → within tolerance.
GRAND is not a fifth addend. Under v2 it is paid on top of the coin sum inside the feature, so it is already contained in the two Hold & Win terms; it is reported separately for auditability only.
5.4 Why the tolerance is ±0.10 pp
Three things bound how precisely this game can be placed on a target:
- Measurement. The line terms are exact, so the whole game's uncertainty is the Hold & Win conditional means', scaled by their trigger probabilities: ±0.0241 pp at 95% on 5M samples. A tolerance below about ±0.05 pp would assert a difference the evidence cannot resolve.
- The integer lattice. Payouts and reel weights are integers, so reachable RTP values form a lattice. The finest useful lever moves the total by 0.0124 pp per unit.
- Model residual. The free-spins term is
E[max(pre-expansion, post-expansion)], because the expansion never reduces a win. That is linear in the paytable only where expansion helps, which is why the final tuning step was verified by re-enumerating rather than by arithmetic.
±0.10 pp is roughly four times the measurement uncertainty and eight times the lattice step.
6. Feature frequencies and nesting
Each free spin is an independent trial at the same probability — the nested feature consumes no free spin and does not change the variant reelset — so the count per series is exactly Binomial(10, p). The modal series launches none; two or more is a genuine tail at 1 in 491.
7. Max win
The Hold & Win ceiling is an analytic bound rather than a witnessed value, and the reason is
recorded: all fifteen locked cells would have to draw the 100× coin, which is (1/102)¹⁵ ≈ 7.4 × 10⁻³¹ before even accounting for filling the grid. No seed search can exhibit it, and the
feature is a stochastic state machine rather than a fixed state space, so enumeration is
impossible. Per the change request, a documented reason is recorded in place of a proof.
No single round can reach the 5000× cap. That is what makes the decomposition valid: the cap never redistributes value between terms, so the four terms are genuinely additive.
8. Buy Free Spins
Priced per variant, because the player chooses which symbol expands and the variants are not equally rich. Each price is that variant's own expected value divided by the 98% target and rounded up, so no option can return more than the game does. There is no direct Buy Hold & Win in this version.
A bought series is an ordinary free-spins series: it opens Hold & Win on the same 6+ COIN rule, and that term is included in every EV above.
9. Which driver may be trusted for which term
RunBatch cannot measure this pack's whole-game RTP, and the reason is a real limitation of
the atomic free-spins driver rather than a property of these reels. FreeSpinsExecutor spins the
base reel set and expands the configured symbol; it ignores FreeSpinsConfig.Variants
entirely. Book of Odin's free spins run on the selected variant's reelset and expand the selected
symbol, which is what the runtime does through RunFreeSpinRoundVariantExt.
mathengine is consistent about this elsewhere — the exact path refuses variant configs by name
(ReasonFSVariants) rather than approximating them. The atomic driver does not refuse; it
silently substitutes different reels. Proven, not described, by
reference_driver_test.go:TestAtomicFreeSpinsDriverIgnoresVariants.
Every term above was therefore measured on a driver that is faithful to the runtime. Nothing in
this spec comes from RunBatch.
10. Open items
- The pack is Draft and unfrozen; no freeze evidence has been assembled.
- Volatility metrics beyond the win-distribution ceilings are not yet recorded.
- Whole-game simulation remains unavailable until the atomic free-spins driver learns variants (see §9). The decomposition is more precise than a simulation would be, but a simulation is still the independent cross-check a freeze would want.