Gen 2 V4 — Monte Carlo Multi-Way Implementation

# Gen 2 V4 — Monte Carlo Multi-Way Implementation

**Date:** 2026-06-15
**Status:** ✅ COMPLETE

## Summary

Implemented Gen 2 V4: V3 production strategy with Monte Carlo multi-way equity engine. Dropped Gen 2 V2 (replaced by V4 in the registry — V4 supersedes both V2 and V3 conceptually, though V3 remains as the exact-equity baseline).

## Design

V3 and V4 share **identical decision logic**. The only difference is equity computation:

| | V3 | V4 |
|---|---|---|
| HU equity | Exhaustive enumeration (exact) | Exhaustive enumeration (exact) |
| Multi-way equity | Exhaustive enumeration (single-opp approximation) | Monte Carlo — true multi-way (vs strongest opp) |
| Decision logic | V3 F1+F4+F7 fixes | Identical (shared via base) |

### Refactor: Shared Decision Logic
Moved V3's decision methods (`get_first_in_action`, `would_check_first_in`, `get_raised_pot_action`, `draw_quality`, `adjusted_ppot`, etc.) into `RolloutPostFlopBase` as `pub(crate)` methods. V3 and V4 are now thin wrappers (~100 lines each) that compute equity differently then delegate to `base.get_first_in_action_v3()` / `base.get_raised_pot_action_v3()`.

## MC Equity Dispatch

`RolloutPostFlopBase::compute_potential_mc(ctx)`:
```
if num_opponents <= 1:
    hp.compute(hole, board)                  // exhaustive (exact)
else:
    hp.compute_multiway_uniform(hole, board, num_opps, mc_samples, mc_seed)
```

Uses `HandPotential::compute_multiway_uniform` — a specialized fast path that deals random cards from a shuffled deck (partial Fisher-Yates). No `HandRange` or weight-map machinery needed since every opponent hand is equally likely.

### Initial Approach (slow) → Fast Path
The initial implementation used `compute_vs_range_multiway_shared` with `HandRange::init_all(1.0)`. This was 20× **slower** than V3 because the sparse-pair MC machinery (designed for Gen 3's narrow ranges) scans all ~990 candidate pairs per opponent per sample — with a uniform range there's no sparsity benefit.

**Fix:** Added `compute_multiway_uniform` which skips the `HandRange`/`sample_sparse` machinery entirely and just does partial Fisher-Yates + sequential dealing. Per-sample cost dropped from O(opps × pairs) to O(cards_needed).

## Changes

### New Files
- `holdem_bots/src/cash/rollout_postflop_v4.rs` — V4 strategy (~100 lines, thin wrapper)
- `configs/bots/cash_gen2_v4.toml` — V4 bot config

### Modified Files
- `holdem_bots/src/cash/thresholds.rs` — Added `mc_samples` (default 20,000) and `mc_seed` (default 42) to `PostflopThresholds` (now 82 fields)
- `holdem_bots/src/cash/rollout_postflop_base.rs` — Moved V3 decision logic here. Added `compute_potential_mc()` using fast uniform path.
- `holdem_bots/src/cash/rollout_postflop_v3.rs` — Simplified to thin wrapper delegating to base
- `holdem_bots/src/common/hand_potential.rs` — Added `compute_multiway_uniform()` fast path + 5 tests
- `holdem_bots/src/cash/mod.rs` — Removed V2 module/re-export, added V4
- `holdem_bots/src/assembly/registrations.rs` — Removed `g2_postflop_rollout_v2`, added `g2_postflop_rollout_v4`
- `holdem_gui/src/api.rs` — Updated strategy list (removed V2, added V4) + added MC params
- `README.md` — Updated strategy variants table and bot assemblies

### Deleted Files
- `holdem_bots/src/cash/rollout_postflop_v2.rs` — V2 strategy (superseded by V4)

## Test Results
- **All 345 tests pass** (5 new uniform MC tests + 24 range MC tests + 13 Gen 3 strategy tests)
- Clean build, 0 warnings

## Performance (10-max, 1000 hands, release build)

| Config | Wall time |
|--------|-----------|
| 10× V3 (exhaustive) | **2.3s** |
| 10× V4 (uniform MC, 20K samples) | **2.6s** |

V4 is now performance-competitive with V3 (~10% slower). Initial implementation with sparse-pair MC was 50.6s — the fast path gave a **20× speedup**.

## Play Quality (5v5 V3 vs V4, 10 seeds × 1000 hands)

V4 wins **7 of 10 seeds** with an average edge of **+1.05 BB/hand per seat**.

| Seed | V4 edge/seat | Winner |
|------|:-:|:-:|
| 42 | +1.93 | V4 |
| 1337 | -0.20 | V3 |
| 9999 | +1.12 | V4 |
| 7777 | -0.25 | V3 |
| 2024 | +1.85 | V4 |
| 555 | +0.31 | V4 |
| 314 | +0.71 | V4 |
| 271 | +2.00 | V4 |
| 8675 | +3.10 | V4 |
| 11 | -0.01 | tie |

V4's edge comes from true multi-way equity (evaluating vs the strongest opponent), while V3's exhaustive `compute()` evaluates single-opponent equity and approximates multi-way with threshold exponents.

## Next Steps
1. Consider V4 as new production baseline (better play quality, comparable speed)
2. Parameter sweep `mc_samples` — could reduce to 10K for ~2× speedup with ±0.5% SE
3. SNG-specific parameter tuning

id: 8469c381b4d14e03a2252a4423aae956
parent_id: 1246bbc3bb4948fc8329079b84b4ae3d
created_time: 2026-06-15T06:41:20.114Z
updated_time: 2026-06-15T07:16:47.575Z
is_conflict: 0
latitude: 0.00000000
longitude: 0.00000000
altitude: 0.0000
author: 
source_url: 
is_todo: 0
todo_due: 0
todo_completed: 0
source: joplin-desktop
source_application: net.cozic.joplin-desktop
application_data: 
order: 1781505680114
user_created_time: 2026-06-15T06:41:20.114Z
user_updated_time: 2026-06-15T07:16:47.575Z
encryption_cipher_text: 
encryption_applied: 0
markup_language: 1
is_shared: 0
share_id: 
conflict_original_id: 
master_key_id: 
user_data: 
deleted_time: 0
type_: 1