The simulator has been made public again after a large body of work and validation has been added.
Blackjack Wonk — what the simulator actually does
A research-grade blackjack simulator and analysis framework. Apache-2.0. https://github.com/ether-ore/blackjack_wonk
Game rules
All major shoe-game blackjack rule combinations:
- Decks: 1–8.
- Dealer soft-17: S17 or H17.
- Doubling: Double-on-any-two (DOA), or restricted (e.g., D10/D11).
- Splitting: Configurable max splits (typically 3 → 4 hands). DAS (double after split). RSA (resplit aces). Optional non-standard combinations: hit after split aces, double after split aces.
- Surrender: Late surrender (LS), allowed or not. Optional surrender-after-split.
- Dealer peek modes: A_only, A_or_T, none.
- Penetration: Configurable percentage [0, 1].
- Burn cards: Configurable count at start of shoe.
- Blackjack payout: Configurable (3:2 = 1.5, 6:5 = 1.2, etc.).
Counting systems
12 system families across 60 shipped configurations under data/rules/counting/:
- Hi-Lo family (level-1, balanced, RC→TC) — including checkpoint, cap-lock, trigger variants
- KO (level-1, unbalanced, RC-based) — plus trigger-checkpoint and cap-lock variants
- Ace-Five (level-1, balanced) — including +-variants and trigger/cap-lock variants
- Ace-Ten Front (level-2, S17 and H17 variants)
- KISS-2 / KISS-3 (level-1, balanced)
- Hi-Opt I (unbalanced, level-1) and Hi-Opt II (level-2)
- Canfield Master and Expert
- Halves (level-2, balanced)
- Red Seven (color-aware)
- High-Only (unbalanced, simple)
- Compositional systems (track per-rank composition)
- Custom TC formulas — arbitrary expression-based true-count math via counting.tc_formula
True-count divisor modes: full_deck, half_deck, quarter_deck, continuous. Choice of divisor basis: cards_remaining or cards_dealt.
Insurance modeling: configurable per-system threshold (take when TC ≥ threshold).
Bet spreads and bet policies (Betting VM)
Bets are executed by a bytecode VM in src/typescript/simulator_lite/src/betting_vm.ts. Supported modes:
- Flat — fixed unit bet
- Bucket spread — count-tiered (RC-based or TC-based) with arbitrary buckets
- Progression: sequence — Labouchere, 1-3-2-6, etc. with configurable advance/reset rules
- Progression: martingale — exponential multiplier on loss, optional max steps and reset
- Progression: percentage — increase by % on win, optional cap and reset
- State machine — conditional state transitions on win/loss with custom unit formulas
- Current Loss Balance (CLB) — loss-chasing variant of state machine
- Kelly criterion — fractional or full Kelly with configurable edge tables, edge slope/cap, variance assumptions, and bankroll scaling
Configurable push behavior per policy: no_change, repeat_last, count_as_win/loss, no_advance/advance/reset.
Shipped examples include flat, half-Kelly, full-Kelly, half-Kelly-with-KO, full-Kelly-with-KO, Martingale, Labouchere, 1-3-2-6, Plus 50%, CLB, multiple Ace-Five spread widths (1:8, 1:16, 1:32), and counting-spread variants for each shipped system.
Play strategies
- Basic strategy — compiled per rule cell (deck count + S17/H17 + DAS + LS) by the configurator. Decision maps embedded in the manifest.
- Composition-dependent overrides — strategies that vary by hand composition (e.g., 6-5 vs. 7-4 hard 11), authored in policy files.
- Index deviations — count-threshold rules that override basic strategy. Shipped packs:
- Hi-Lo: BJA Chapter 10 (full and Illustrious-18-only subset), Illustrious 18, full deviation set
- KO: Preferred, Full, trigger-checkpoint variants
- Ace-Five, Ace-Ten Front, High-Only, Red Seven, Compositional (non-count-based)
- Compositional deviations — hand_contains_any/all and hand_is_exactly rule conditions
- Unconditional rules — apply regardless of count (e.g., always-surrender hands)
- Deviation stacking — multiple packs with priority ordering; last match wins
Multi-seat tables
environment.table supports 1–7 seat tables with explicit seat geometry:
- Total players configurable; primary seat (the player under analysis) at any seat 1..total_players
- Co-seats deal and act in seat order; primary's decisions are the focus of analysis
- All co-seat policies must be count-blind (this is enforced at manifest-build time)
Per-seat play policies (Phase 2)
Each co-seat can be assigned a different play style. Shipped seat-style policies:
- basic_strategy (default)
- dealer_mimic_s17 and dealer_mimic_h17 — co-seat mirrors the dealer's strategy
- never_bust — co-seat stands at hard 12+
These behave like NPC players at your table. Useful for studying how shadow play affects card consumption.
Per-seat mistake models (Phase 3)
Co-seats (and optionally the primary) can be assigned probabilistic error rates. Configurable per-action-class error rates [0, 1]:
- hard_hit_stand, soft_hit_stand, hard_double, soft_double, pair_split, surrender, insurance
Error decisions are seeded via BLAKE3 hashing of (manifest_seed, shuffle_PRNG_variant, binary_deck_partition, round, seat, hand, decision_index, mistake_model_key). Order-independent and reproducible — same seed plus same model produces identical mistake decisions across runs.
Per-seat telemetry reports both intended and realized error rates per class.
PRNG, shuffle, determinism
Two independent PRNG streams: shoe (card deals) and mistakes (error decisions).
Shoe PRNGs:
- xoshiro256ss (Blackman & Vigna, 2021) — current default, 256-bit state, SplitMix64 warmup
- mulberry32 — 32-bit, simpler, used for legacy reproducibility
Binary-deck mode: random_source.type: 'preshuffled_binary' loads pre-shuffled binary decks from disk. Workers leapfrog non-overlapping deck partitions for parallel runs. Same binary deck + same manifest = bit-identical results.
Full determinism with same seed, PRNG variant, and random-source type.
Telemetry collected
The result file is the heart of the simulator. Every run emits:
Volume: rounds, hands total, hand wins/losses/pushes, round wins/losses/pushes, doubles, splits (events and resulting hands), surrenders, blackjacks.
EV by count bucket: for every RC or TC bucket, the simulator emits rounds, hands, sum of net units, sum of squared net units, total bet units, derived EV/hand, average bet, SD/hand, dealer hole card distribution, dealer final hand distribution, first-action histogram, average remaining aces/tens/low/neutral cards, and per-rank composition.
Count state: RC/TC histogram at decision time, decision count by (TC bucket × initial state key), decision-action distribution per state-and-upcard cell.
Action histograms: H/S/D/P/SU counts indexed by (player state, dealer upcard).
Variance (Welford running stats): per-hand and per-round outcome variance. Selectable risk basis (per-hand or per-round). Derived from this: SD, variance, Desirability Index (DI), and N0 (rounds-to-ruin estimate).
Betting diagnostics: bet-units histogram, average bet, total bet, bet-policy mode + source spread key. For Kelly: fraction used, % at cap, % at min, model edge by TC, realized EV by TC.
Wonging diagnostics (when entry policy active): rounds played vs. sat out, % time at table.
Insurance: offers, takes, wins, bet units, payout units, net units.
Deviation execution: count of deviations applied, count skipped (illegal), per-rule fire histogram. Deviation precompiler stats (rules compiled, composition entries, wildcard rules, compile time).
Bankroll (when enabled): starting, final, net, stopped reason, risk of ruin.
Bankroll recommendations (when no bankroll provided): minimum/recommended/aggressive sizing.
Per-seat aggregation: for every seat — policy key, mistake model key, hands played, cards consumed, rounds active, action histogram by class, baseline action histogram, mistake counts by class, realized error rate by class.
Performance: elapsed ms, rounds per second.
Reproducibility metadata: counting system + key + bucketing + TC mode/basis/rounding, table geometry, seat profiles, mistake-RNG hash and keying constants, run config (rounds requested, termination reason, wonging flags, bankroll config, binary-deck shoes consumed).
Validation framework
tests/python/integration/verify_accuracy_metrics.py — rule-aware accuracy validator. Checks include:
- Win/loss/push rate sanity against rule-cell baselines
- Standard deviation within expected range (rule-aware)
- Hand count consistency vs. action sums
- Bet distribution histogram consistency vs. average bet
- Per-bucket EV monotonicity (count direction → EV direction)
- Deviation rule legality
- Wonging round-accounting consistency
- Count histogram occupancy
- Dealer outcome histogram (~28% bust rate, expected 17-21 distribution)
- Variance positivity
- Kelly model edge vs. realized edge correlation
- Bankroll trajectory and RoR sanity
- Composition-dependent decision consistency
- Split, double, surrender, blackjack accounting
Multi-worker parallelism
--workers N flag spawns N worker processes. Each worker simulates an independent slice; results merge through a declarative aggregation system with 140+ aggregation rules keyed by result-field path. Rule strategies include:
- sum (counters and net units)
- histogram (count distributions)
- array_by_key (EV bucket merging with derived-field recalculation)
- custom (per-seat data, streak stats)
- first (run-wide constants — table geometry, count system metadata)
- derived (post-merge recalculation — acceptance rate, percent time at table)
Binary-deck mode supports leapfrogging: workers consume non-overlapping deck partitions for true parallel reproducibility.
Operationally real features worth knowing
Bankroll-conditioned runs: --bankroll N, --bankroll-goal N, --bankroll-stop-loss N. Termination reason recorded in result.
Cut-card vs. fixed-round penetration: the simulator uses cut-card semantics (reshuffle when penetration % reached). Fixed-round is not a separate mode.
Wonging / table entry policies: TC or RC threshold for entry and exit. When sitting out: sit_out (shadow players advance shoe), flat_min (bet minimum, count), or reshuffle (exit only at reshuffle). Recheck dynamically per round or once per shoe.
Forced-state sampling: restrict simulation to hands within a count range. Used for focused accuracy studies. Results include acceptance rate.
Continuous Shuffle Machine (CSM): not in production. A CSM prototype was implemented and validated end-to-end but archived as csm-prototype-v1 — A/B testing showed a measurable perf cost on the dominant shoe-mode workload. Documented. The simulator only supports shoe games.
Composition-dependent BJ payouts: insurance is a separate side bet (2:1 if dealer has BJ, loses otherwise), no other side bets.
No European No-Hole-Card (ENHC): in the schema but not executed in the lite engine.
Where to look
- Code: https://github.com/ether-ore/blackjack_wonk
- Runtime bundle (download and run, 1.8 MB): https://github.com/ether-ore/blackjack_wonk/releases/download/v1.0.0/blackjack-wonk-1.0.0-runtime.zip
- User Manual: docs/handoff/USER_MANUAL.md
- Owner's Manual (when BJW is and isn't the right tool): docs/project/BJW_OWNERS_MANUAL.md
Apache-2.0. Comments and PRs welcome.