id: 5ee0e61867a04f4f8d09197a0049662c
parent_id: 667381444ce943c48cbc3a27ba08ec9d
item_type: 1
item_id: 659da65d964344fc89e6b4eafac0098c
item_updated_time: 1781510518662
title_diff: "[]"
body_diff: "[{\"diffs\":[[0,\"ect.\"],[-1,\" Uses a **strategy framework** with hand analysis, modular strategy composition, and advanced cash-game bots — while keeping proprietary bot logic in a separate private crate.\\\n\\\n---\\\n\\\n## Goals\\\n\\\n1. **Public/Private separation** — Advanced bots live in a private crate excludable from public mirrors\\\n2. **Framework in `holdem_core`** — Traits, helpers, analysis tools remain public and reusable\\\n3. **Idiomatic Rust** — Traits instead of interfaces, enums instead of inheritance, `Option` instead of null\\\n4. **Minimal disruption** — Existing bots and engine code continue to work unchanged\\\n\\\n---\\\n\\\n## Workspace Structure\\\n\\\n```\\\nsuper_marvin/                    # project root\\\n├── Cargo.toml                   # workspace (holdem_bots + holdem_gui)\\\n├── holdem_bots/                  # PRIVATE crate — advanced bots + strategies\\\n│   └── src/\\\n│       ├── lib.rs                # register_bots()\\\n│       ├── assembly/             # strategy registry, config parsing, bot assembly\\\n│       ├── cash/                 # preflop + postflop strategies (Gen 1 + Gen 2 + Gen 3)\\\n│       │   ├── rollout_postflop_base.rs   # shared base (thresholds, context helpers, V3+ decision logic)\\\n│       │   ├── rollout_postflop_v3.rs     # Gen 2 V3 production (thin wrapper over base)\\\n│       │   ├── rollout_postflop_v4.rs     # Gen 2 V4 (V3 + MC multi-way, thin wrapper)\\\n│       │   ├── rollout_postflop_gen3.rs   # Gen 3 range-aware strategy\\\n│       │   └── thresholds.rs              # PostflopThresholds (82 configurable fields incl. mc_samples/mc_seed)\\\n│       ├── sng/                  # SNG/tournament strategies\\\n│       └── common/               # shared utilities\\\n│           ├── hand_potential.rs          # EHS, MC multi-way engine, PotentialResult\\\n│           ├── hand_range.rs              # HandRange (2704-entry, weighted_union, sparse sampling)\\\n│           ├── hand_analyzer.rs           # board texture, draw detection\\\n│           └── player_model.rs            # opponent model (GameStage-based defaults)\\\n├── holdem_gui/                   # Web GUI for parameter sweeps & experiments\\\n│   └── src/\\\n│       ├── main.rs               # axum server on 127.0.0.1:3000\\\n│       ├── api.rs                # REST API (11 endpoints)\\\n│       ├── config_gen.rs         # dynamic TOML generation\\\n│       ├── runner.rs             # subprocess simulation runner\\\n│       ├── types.rs              # shared data types\\\n│       └── static/index.html     # SPA frontend (dark theme, Chart.js)\\\n└── configs/\\\n    ├── bots/                     # bot assembly TOML configs\\\n    │   ├── cash_nl.toml          # Gen 2 V3 production\\\n    │   ├── cash_gen2_v4.toml     # Gen 2 V4 (MC multi-way)\\\n    │   └── cash_gen3.toml        # Gen 3 range-aware\\\n    └── *.toml                    # simulation configs\\\n```\\\n\\\n---\\\n\\\n## Key Traits and Relationships\\\n\\\n### Strategy Trait\\\n```rust\\\npub trait Strategy: Send + Sync {\\\n    fn is_applicable(&self, context: &StrategyContext) -> bool;\\\n    fn get_action(&self, context: &StrategyContext) -> Action;\\\n    fn consider_fold_as_not_applicable(&self) -> bool { false }\\\n    fn weight(&self) -> u32 { 1 }\\\n}\\\n```\\\n\\\n### VotingMode Enum\\\n```rust\\\npub enum VotingMode {\\\n    FirstApplicable,  // First applicable wins\\\n    Democratic,       // Majority vote\\\n    Random,           // Weighted random\\\n}\\\n```\\\n\\\n### ModularStrategy (Composite Pattern)\\\nContains `Vec<Box<dyn Strategy>>` + `VotingMode`. Implements `Strategy` trait.\\\n\\\n### StrategyBot (Adapter)\\\nBridges `Strategy` trait → `HoldemPlayer` trait. Manages per-hand state (hole cards, action recorder).\\\n\\\n### StrategyContext\\\nAggregated decision state: `GameInfo`, `HoleCards`, `ActionRecorder`, `aggressor_id`, `num_raises`, `num_callers`, `num_active`.\\\n\\\n---\\\n\\\n## Strategy Registry (15\"],[1,\"\\\n\\\n---\\\n\\\n## Strategy Registry (14\"],[0,\" typ\"]],\"start1\":103,\"start2\":103,\"length1\":3719,\"length2\":39},{\"diffs\":[[0,\"ollout_v\"],[-1,\"3\"],[1,\"4\"],[0,\"` | Gen \"]],\"start1\":917,\"start2\":917,\"length1\":17,\"length2\":17},{\"diffs\":[[0,\"op (\"],[-1,\"F1 \"],[0,\"trap +\"],[-1,\" F4\"],[0,\" dra\"]],\"start1\":954,\"start2\":954,\"length1\":20,\"length2\":14},{\"diffs\":[[0,\"ll +\"],[-1,\" F7\"],[0,\" rai\"]],\"start1\":972,\"start2\":972,\"length1\":11,\"length2\":8},{\"diffs\":[[0,\"ty) \"],[-1,\"— **cash production** |\\\n| `g2_postflop_rollout_v4` | Gen 2 | V3 + Monte Carlo multi-way equity (HU exhaustive, multi-way MC)\"],[1,\"+ MC multi-way equity — **cash production**\"],[0,\" |\\\n|\"]],\"start1\":987,\"start2\":987,\"length1\":132,\"length2\":51},{\"diffs\":[[0,\" |\\\n\\\n\"],[-1,\"**Note:** `chump_bot` and `flock_bot` are listed in the GUI API but were never implemented. Test opponents use Gen 1 rules or Gen 2 V3 as baselines.\\\n\\\n---\\\n\\\n\"],[0,\"## Gen 2\"],[-1,\" V3 /\"],[0,\" V4 \"]],\"start1\":1202,\"start2\":1202,\"length1\":176,\"length2\":16},{\"diffs\":[[0,\"gy\\\n\\\n\"],[-1,\"V3 and V4 share **identical decision logic** (moved to `RolloutPostFlopBase` in round 3 refactor). The only difference is equity computation:\\\n\\\n- **V3:** Exhaustive enumeration against uniform opponent hands (exact)\\\n- **V4:** HU → exhaustive enumeration (exact); Multi-way (2+ opp) → Monte Carlo with `HandRange::init_all(1.0)` uniform range, true multi-way equity against strongest opponent\\\n\\\n### V3 / V4 Production Fixes (3 promoted)\\\n1. **F1: Check-raise trap protection** — rope-a-dope suppressed when `would_check_first_in()` is true\\\n2. **F4: OTB draw-based call** — `adj_ppot > 0.25` call condition in short-handed raised pots, disabled in true HU (2-seat tables)\\\n3. **F7: Draw safety gate on raises** — non-nut draws blocked from raising when `npot > npot_raise_ceiling` (default 0.25)\\\n\\\n### V4 Monte Carlo Parameters\\\n- `mc_samples` (default 20,000, ±0.35% std error) — configured via `PostflopThresholds`\\\n- `mc_seed` (default 42) — RNG seed for reproducibility\\\n- Uses `compute_vs_range_multiway_shared` with `HandRange::init_all(1.0)` (uniform range = exhaustive sampling)\\\n\\\n---\\\n\\\n## Gen 3 — Range-Aware \"],[1,\"**File:** `holdem_bots/src/cash/rollout_postflop_v4.rs` (~100 lines, thin wrapper)\\\n\\\n### Equity Computation (hybrid dispatch in `Rollout\"],[0,\"Post\"],[-1,\"flop Strategy\\\n\\\n**File:** `holdem_bots/src/cash/rollo\"],[1,\"FlopBase::comp\"],[0,\"ut\"],[1,\"e\"],[0,\"_po\"],[-1,\"stflop_gen3.rs` (~1600 lines)\\\n\\\n### Equity Computation\"],[1,\"tential_mc`)\"],[0,\"\\\n- *\"]],\"start1\":1242,\"start2\":1242,\"length1\":1228,\"length2\":179},{\"diffs\":[[0,\":** \"],[-1,\"`compute_vs_range` — e\"],[1,\"E\"],[0,\"xhau\"]],\"start1\":1443,\"start2\":1443,\"length1\":30,\"length2\":9},{\"diffs\":[[0,\"ion \"],[-1,\"with\"],[1,\"(exact,\"],[0,\" trie\"],[-1,\" \"],[1,\"-\"],[0,\"optimiz\"],[-1,\"ation, range-weight\"],[0,\"ed\"],[1,\")\"],[0,\"\\\n- *\"]],\"start1\":1466,\"start2\":1466,\"length1\":46,\"length2\":31},{\"diffs\":[[0,\"ute_\"],[-1,\"vs_range_\"],[0,\"mult\"]],\"start1\":1531,\"start2\":1531,\"length1\":17,\"length2\":8},{\"diffs\":[[0,\"ultiway_\"],[-1,\"shared\"],[1,\"uniform\"],[0,\"` — Mont\"]],\"start1\":1536,\"start2\":1536,\"length1\":22,\"length2\":23},{\"diffs\":[[0,\"ith \"],[-1,\"range-weighted importance samp\"],[1,\"partial Fisher-Yates dea\"],[0,\"ling\"]],\"start1\":1568,\"start2\":1568,\"length1\":38,\"length2\":32},{\"diffs\":[[0,\"ent\\\n\"],[-1,\"- **Config:** `mc_samples` (default 20,000, ±0.35% std error), `mc_seed` (default 42)\\\n\\\n### Opponent Range\\\nBuilt via `HandRange::init_opponent_range(eval, board, excluded, strong_hands_fr\"],[1,\"\\\n### Decision Logic\\\nAll decision logic lives in `RolloutPostFlopBase` (`get_first_in_\"],[0,\"action\"],[1,\"`\"],[0,\", \"],[-1,\"range_draw_weight)`:\\\n- **Strong made hands:** top `strong_hands_fraction` (default 0.35) by hand strength\\\n- **Strong draws:** flush draws, OESDs, overcards — scaled by `range_draw_weight` (default 1.0)\\\n- Combined via `weighted_union` (max of scaled weights)\\\n\\\n### Weighted Decision Scoring (OR-chain refactored)\\\nReplaces OR-chain call/raise logic with dynamic threshold adjustments. Each factor (draw quality, pot odds, commitment, bluff equity) subtracts from the base threshold, making every parameter effective:\\\n\\\n**`should_call(ctx, equity, model)`:**\\\n- Base threshold from `call_base` + bet size + model modifier\\\n- Lowered by: `call_w_draw` (draw quality), `call_w_pot_odds` (favorable odds, clamped 0.3), `call_w_commitment` (chips invested)\\\n- **Floor:** `threshold.max(required_equity * 0.5)` — prevents degenerate over-calling\\\n- Decision: `win_prob.powf(call_adj) > threshold`\\\n\\\n**`should_raise(ctx, equity, model)`:**\\\n- Base threshold from `raise_base` + bet size + model modifier\\\n- Lowered by: `raise_w_draw` (draw quality), `raise_w_bluff` (bluff equity)\\\n- Decision: `win_prob.powf(raise_adj) > threshold`\\\n\\\n### Gen3Config Parameters (29 total)\\\n\\\n| Category | Parameters |\\\n|----------|-----------|\\\n| Range | `strong_hands_fraction`, `bluff_factor`, `range_draw_weight` |\\\n| Equity | `mc_samples`, `mc_seed` |\\\n| Bet | `bet_flop_base`, `bet_turn_base` |\\\n| Raise | `raise_base`, `raise_cheap_flop`, `raise_ppot_base`, `raise_w_draw`, `raise_w_bluff` |\\\n| Call | `call_base`, `call_ppot_base`, `call_w_draw`, `call_w_pot_odds`, `call_w_commitment` |\\\n| Draws | `draw_call_ppot_thresh` |\\\n| Traps | `check_raise_flop`, `check_raise_turn`, `rope_a_dope_flop`, `rope_a_dope_turn` |\\\n| Bluffs | `semi_bluff_hs_base`, `semi_bluff_ppot_base`, `bluff_success_floor`, `conti_bet_fold_thresh` |\\\n| Runtime | `sweep_mode` (bool, default true — deterministic RNG for sweeps; false = system entropy for live) |\\\n\\\n### Deterministic Decision RNG\\\n`decision_rng()` seeds `StdRng` from `mc_seed + win_prob.to_bits() + pot_size + num_active`. When `sweep_mode = false`, mixes in `rand::random::<u64>()` for non-reproducible play.\\\n\\\n### Benchmark (10K hands, 6-max, seed 42)\\\n| Bot | Avg BB/Hand |\\\n|-----|-------------|\\\n| Gen 3 | **+2.53** |\\\n| Gen 2 V3 | +1.02 |\\\n| Gen 1 | -3.55 |\\\n\\\n---\\\n\\\n## holdem_gui — Experiment GUI\\\n\\\nWeb-based tool for parameter sweeps running on `http://127.0.0.1:3000`.\\\n\\\n### Architecture\\\n- **Backend**: axum REST API, spawns `holdem_bots` binary as subprocess\\\n- **Frontend**: single-page HTML/JS app with Chart.js for result visualization\\\n- **No recompilation needed** — parameter overrides are injected into dynamically generated TOML configs\\\n\\\n### Features\\\n- Load/edit/save bot and simulation TOML configs\\\n- Two sweep modes: VsBaseline (tuned vs baseline + filler) and RoundRobin (different values compete)\\\n- Parameter selection from configurable fields, grouped by category\\\n- Range input: steps mode or ±% mode with preview\\\n- Flexible table composition: pick any bots for any seats\\\n- Full game settings editor (seats, hands, blinds, ante, etc.)\\\n- Live progress tracking with auto-polling\\\n- Results table with delta colouring + Chart.js line chart\\\n\\\n---\\\n\\\n## Design Decisions\\\n\\\n- **Strategy separate from HoldemPlayer:** Strategy is a decision interface; HoldemPlayer is a lifecycle interface. Allows composition and isolated testing.\\\n- **StrategyBot adapter over ModularStrategy implementing HoldemPlayer:** Keeps strategy framework pure, manages per-hand state in one place.\\\n- **Plugin registry over hardcoded match:** Allows holdem_bots to register without holdem_core knowing about it.\\\n- **Subprocess over library call for GUI:** The GUI spawns the `holdem_bots` binary as a subprocess rather than calling `run_cash_game()` directly. This decouples the GUI from engine internals and avoids blocking the async runtime.\\\n- **Dynamic TOML generation over static config files:** Parameter overrides are injected into dynamically generated TOML configs stored in temp directories.\\\n- **toml::Value construction over format! templates:** Config generation uses the `toml` crate's value types instead of string formatting, preventing TOML injection attacks.\\\n- **Weighted threshold scoring over OR-chains:** Gen 3 uses dynamic threshold adjustments instead of OR-chain conditions. Each parameter (draw weight, pot odds, commitment) shifts the call/fold boundary for all hands, making every parameter effective in parameter sweeps.\\\n- **V3/V4 shared decision logic in base:** V3 and V4\"],[1,\"`get_raised_pot_action`, `would_check_first_in`, etc.). V4 is a thin wrapper that computes equity then delegates.\\\n\\\n### Production Fixes (inherited from V3, validated via A/B testing)\\\n1. **F1: Check-raise trap protection** — rope-a-dope suppressed when `would_check_first_in()` is true\\\n2. **F4: OTB draw-based call** — `adj_ppot > 0.25` call condition, disabled in true HU (2-seat tables)\\\n3. **F7: Draw safety gate on raises** — non-nut draws blocked when `npot > npot_raise_ceiling`\\\n\\\n### MC Parameters\\\n- `mc_samples`: 10,000 default (quick tests), 20,000 (sweeps), 50,000 (production configs)\\\n- `mc_seed`: 42 (reproducibility)\\\n- See \\\"MC Sample Count Guidelines\\\" note for accuracy/speed tradeoffs\\\n\\\n### V4 vs V3 Benchmark (10K hands, 5v5, 10 seeds)\\\nV4 wins **6/10 seeds**, average edge **+0.34 BB/hand per seat**. V3 removed — V4 is the new production baseline.\\\n\\\n### History\\\n- **V2** (dropped): Nut-aware draw quality, hardcoded constants\\\n- **V3** (dropped, superseded by V4): V2 + F1/F4/F7 fixes, exhaustive equity, 80 sweepable thresholds\\\n- **V4** (current production): V3\"],[0,\" dec\"]],\"start1\":1647,\"start2\":1647,\"length1\":4672,\"length2\":1174},{\"diffs\":[[0,\"ion \"],[-1,\"f\"],[0,\"lo\"],[-1,\"w (`get_first_in_action_v3`, `get_raised_pot_action_v3`, etc.) lives in `RolloutPostFlopBase`. V3 (exhaustive equity) and V4 (MC equity) are thin wrappers that compute equity differently then delegate to the same decision methods.\"],[1,\"gic + MC multi-way equity, 82 thresholds (incl. mc_samples/mc_seed)\"]],\"start1\":2823,\"start2\":2823,\"length1\":237,\"length2\":73}]"
metadata_diff: {"new":{},"deleted":[]}
encryption_cipher_text: 
encryption_applied: 0
updated_time: 2026-06-15T08:06:54.930Z
created_time: 2026-06-15T08:06:54.930Z
type_: 13