id: 980ec603ac9a4091a36b85efaad86561
parent_id: 
item_type: 1
item_id: 4d53750895be4a30af66ec1c2aea161e
item_updated_time: 1782797680132
title_diff: "[{\"diffs\":[[1,\"Session Audit & Sweep Results — 2026-06-28\"]],\"start1\":0,\"start2\":0,\"length1\":0,\"length2\":42}]"
body_diff: "[{\"diffs\":[[1,\"# Session Audit & Sweep Results — 2026-06-28\\\n\\\n> Full-session work: live server audit, S3 fix (preflop looseness wiring),\\\n> Gen 2/3/4 loose/tight configs, Gen 4 opponent model sweep, Gen 3 config\\\n> comparison, Gen 5 architecture review.\\\n\\\n---\\\n\\\n## 1. Live Server Status\\\n\\\n- **Server**: `live_server` on `127.0.0.1:8088` (rebuilt with S3 fix)\\\n- **Default bot**: `cash_nl_g4_live` (Gen 4 adaptive)\\\n- **Registered configs**: 8 total (g4_live, g4, g4_loose, g4_tight, normal, loose, tight, g2)\\\n- **Profile persistence**: working — table + per-player profiles saved/loaded on restart\\\n- **S3 fix active**: preflop looseness now wired into NG preflop thresholds\\\n\\\n---\\\n\\\n## 2. S3 Fix: Preflop Looseness Wired (CODE CHANGE)\\\n\\\n**Issue**: `g4_adaptive_preflop` computed a VPIP-driven looseness factor but\\\ndelegated directly to NG preflop without applying it. The looseness functions\\\nwere `#[cfg(test)]` only.\\\n\\\n**Fix applied**:\\\n1. Added `looseness_mult: f64` field to `NgPreFlopConfig` (default 1.0, parsed from TOML)\\\n2. NG preflop multiplies open-raise and limp thresholds by `looseness_mult` (clamped 0.5–2.0)\\\n3. `AdaptivePreflopGen4::get_action` now computes the looseness factor from table VPIP and applies it via a per-decision config clone\\\n4. Looseness mapping: VPIP 0.15→0.85, 0.30→1.0, 0.50→1.15, 0.70→1.30\\\n\\\n**Files changed**:\\\n- `holdem_bots/src/cash/ng_preflop.rs` — config field + threshold application + `config()` accessor\\\n- `holdem_bots/src/gen4/adaptive_preflop.rs` — removed `#[cfg(test)]`, wired into `get_action`\\\n\\\n**Tests**: all 10 existing tests pass (looseness monotonicity, clamping, cold-start)\\\n\\\n---\\\n\\\n## 3. Baseline Gen 4 Audit (10k hands, seed 42)\\\n\\\n6-max (2 flock + 2 chump + 1 test bot), 50k MC samples:\\\n\\\n| Player | Won | Profit/Hand | ROI% |\\\n|--------|-----|-------------|------|\\\n| G4Live | 1202 | **+144.65** | +72325% |\\\n| FlockBot-1 | 820 | +100.61 | +50307% |\\\n| ChumpBot-1 | 3141 | -200.99 | -100493% |\\\n\\\n**Range prediction accuracy**: 87.9% hit rate (12892/14661 showdowns)\\\n- Flop: 88.7% | Turn: 88.6% | River: 87.8%\\\n- All opponents classified as Loose (>=0.45 VPIP) — no tight opponents in this field\\\n\\\n---\\\n\\\n## 4. Gen 4 Opponent Model Sweep (14 variants × 3 seeds × 20k hands)\\\n\\\n**Methodology**: Fixed ALL decision thresholds at `cash_nl_g4_live.toml` values.\\\nSwept ONLY the weight map + narrowing curve parameters to test opponent model\\\naccuracy without changing when the bot calls/bets/raises.\\\n\\\n### Results (profit/hand vs chump+flock field)\\\n\\\n| Variant | Param Changed | Profit/Hand | Δ vs Baseline |\\\n|---------|--------------|-------------|---------------|\\\n| **offset030** | narrow_center_offset=0.3 | **152** | **+2** |\\\n| frac035 | strong_hands_fraction=0.35 | 151 | +1 |\\\n| scurve05 | scurve_steepness_mult=0.5 | 151 | +1 |\\\n| combo_wide | frac=0.25, looseness=0.10, offset=0.3, str=1.2 | 151 | +1 |\\\n| **baseline** | live config | **150** | — |\\\n| frac025 | strong_hands_fraction=0.25 | 149 | -1 |\\\n| frac020 | strong_hands_fraction=0.20 | 150 | 0 |\\\n| loose010 | range_looseness=0.10 | 148 | -2 |\\\n| loose015 | range_looseness=0.15 | 150 | 0 |\\\n| offset010 | narrow_center_offset=0.1 | 148 | -2 |\\\n| strength08 | narrow_strength_mult=0.8 | 149 | -1 |\\\n| strength12 | narrow_strength_mult=1.2 | 149 | -1 |\\\n| scurve02 | scurve_steepness_mult=0.2 | 150 | 0 |\\\n| combo_tight | frac=0.20, looseness=0.02, offset=0.1, str=0.8 | 148 | -2 |\\\n\\\n### Key Findings\\\n\\\n1. **Weight map parameters have minimal impact (±2/hand)** when thresholds are\\\n   fixed. The opponent model is already accurate at ~88%.\\\n\\\n2. **Narrowing center offset is the strongest lever**: 0.3 (+2) > 0.2 (baseline)\\\n   > 0.1 (-2). Wider offsets = less aggressive range narrowing = better.\\\n\\\n3. **Strong_hands_fraction is nearly flat**: 0.20-0.35 all within ±1/hand.\\\n   The adaptive observer's per-opponent VPIP-based adjustment already handles this.\\\n\\\n4. **Range looseness above 0.10 hurts**: adding too many medium-tier hands to the\\\n   weight map dilutes range accuracy (148-150 vs baseline 150).\\\n\\\n5. **Narrowing strength doesn't matter** (confirms Gen 4 deep sweep): 0.8, 1.0,\\\n   1.2 all give 149-150. The S-curve shape absorbs the difference.\\\n\\\n**Conclusion**: The opponent model is well-calibrated. The real performance\\\nlever is the **threshold scaling** (`range_tightness_scale`), not the weight map\\\nshape. This validates the design philosophy: \\\"make the opponent model accurate,\\\nlet thresholds stay fixed.\\\"\\\n\\\n---\\\n\\\n## 5. Gen 3/4 Config Comparison (6 configs × 2 fields × 3 seeds × 20k hands)\\\n\\\n| Config | Chump Field | Flock Field | Sum |\\\n|--------|------------|------------|-----|\\\n| **cash_nl_g4_loose** | **150** | 0 | **150** |\\\n| cash_nl_g4_live | 148 | 1 | 149 |\\\n| cash_nl_g4_tight | 148 | 2 | 150 |\\\n| cash_nl_loose (G3) | 86 | 4 | 90 |\\\n| cash_nl_normal (G3) | 72 | 3 | 75 |\\\n| cash_nl_tight (G3) | 69 | 3 | 72 |\\\n\\\n### Key Findings\\\n\\\n1. **Gen 4 dominates Gen 3 by ~70%** (148 vs 86 vs chumps). The adaptive\\\n   observer + range narrowing + threshold scaling is the single biggest\\\n   improvement in the framework.\\\n\\\n2. **Loose/tight differentiation matters more for Gen 3** than Gen 4.\\\n   Gen 3 loose (86) vs tight (69) = 17-point spread. Gen 4 loose (150) vs tight\\\n   (148) = 2-point spread. The adaptive layer absorbs the table-type differences.\\\n\\\n3. **`cash_nl_g4_loose`** is marginally best vs calling stations (+2/hand over live).\\\n   Its starting thresholds favour thin value betting, which the observer further\\\n   refines per-opponent.\\\n\\\n4. **All configs struggle vs flock field** (0-4/hand). Flock_bot (tight-passive)\\\n   is a near-break-even opponent. This is expected — beating tight opponents\\\n   consistently is much harder than beating calling stations.\\\n\\\n---\\\n\\\n## 6. New Configs Created\\\n\\\n| Config | Generation | Purpose |\\\n|--------|-----------|---------|\\\n| `cash_nl_gen2_loose.toml` | Gen 2 | Calling-station tables (value_bet_floor=0.55, wider calls) |\\\n| `cash_nl_gen2_tight.toml` | Gen 2 | Skilled tables (value_bet_floor=0.70, tighter calls, more bluffs) |\\\n| `cash_nl_tight.toml` | Gen 3 | Very tight tables (bet_flop=0.25, call_base=0.42, fold_freq=1.0) |\\\n| `cash_nl_g4_loose.toml` | Gen 4 | Loose starting point (bet_flop=0.35, call_base=0.50, narrow_strength=1.2) |\\\n| `cash_nl_g4_tight.toml` | Gen 4 | Tight starting point (bet_flop=0.25, call_base=0.42, bluff_hs=0.03) |\\\n\\\nAll registered in `configs/live_bots.toml` for A/B testing.\\\n\\\n---\\\n\\\n## 7. Gen 5 Architecture Review\\\n\\\n### What's Well-Designed\\\n\\\n1. **280-dim feature vector** — comprehensive (equity, board, betting, opponents, history)\\\n2. **11-action discrete space** — good bet sizing granularity with proper legality masking\\\n3. **Teacher-fallback pattern** — gracefully delegates to Gen 3/4 when no model loaded\\\n4. **JSONL transition recorder** — thread-safe, for imitation learning data collection\\\n\\\n### Architectural Concerns\\\n\\\n| # | Issue | Severity | Fix |\\\n|---|-------|----------|-----|\\\n| G5-1 | **Opponent features not bridged** — `compute_features()` passes `&[]` for opponent_stats. The entire point of Gen 5 (using the opponent model) is disconnected. | High | Bridge Gen 4 `PlayerStats` → `OpponentFeatures` in `RlStrategy::compute_features()` |\\\n| G5-2 | **No reward assignment** — `Transition` struct lacks a reward field. Self-play RL (PPO) is impossible without it. Only imitation learning works. | High | Add reward field + `on_hand_end` callback to assign outcomes |\\\n| G5-3 | **`win_prob` missing from equity features** — features include hs/ppot/npot/nutpot/rpot but NOT `win_prob` (EHS), the most important call-decision metric. | Medium | Add win_prob as 6th equity feature (FEATURE_DIM bump to 281) |\\\n| G5-4 | **No showdown hand recording** — can't train range-prediction NN (Phase 2) without labeled showdown data. | Medium | Record revealed hole cards at showdown |\\\n| G5-5 | **`pot_after_bb` always 0.0** in action history features. | Low | Compute from wagered amounts |\\\n| G5-6 | **Bet sizing too coarse** — only 5 bet fractions + 2 raise sizes. Limits nuanced sizing. | Low (Phase 2) | Consider hybrid continuous action (discrete + size parameter) |\\\n| G5-7 | **MC equity bottleneck** — 50k samples per decision (~15ms) limits training throughput. | Low | Cache per (hole, board, range_hash) or use faster estimator |\\\n\\\n### Overall Assessment\\\n\\\nThe architecture is **sound and pragmatic**. The MLP actor-critic with teacher\\\nfallback is the right starting point. The two-phase approach (imitation →\\\nself-play) is standard and well-justified. The recommendation to train in\\\nPython/PyTorch while generating data in Rust is correct — candle for inference only.\\\n\\\n**Immediate priorities for Gen 5 activation:**\\\n1. Fix G5-1 (bridge opponent features) — trivial, ~30 min code change\\\n2. Fix G5-2 (reward assignment) — needed for any RL training\\\n3. Fix G5-3 (win_prob feature) — needed for call-decision quality\\\n4. Wire Gen5 recorder into live server alongside Gen 4 decisions\\\n\\\n---\\\n\\\n## 8. Summary of Open Issues Status\\\n\\\n| ID | Issue | Status |\\\n|----|-------|--------|\\\n| **S1** | Passive postflop play | Partially addressed — `bet_turn_base=0.58`, `strong_hands_fraction=0.30`. Further improvement needs Gen 5 (learned betting frequencies). |\\\n| **S2** | Cold start waste | **Resolved** — `update_table_info()` loads table profile from disk; `on_hand_start()` loads player profiles. Verified in code. |\\\n| **S3** | Preflop looseness not wired | **Fixed** — looseness factor now applied to NG preflop open-raise + limp thresholds. |\\\n| **S4** | Range prediction 13% miss | Open — 87.9% hit rate. Weight map sweep shows minimal improvement possible via parameter tuning. Needs Gen 5 range-prediction NN. |\\\n| **U5** | clickWhenReady hand ID validation | Open (userscript side) |\\\n| **D1** | Missing ante events | Open (userscript side) |\\\n\\\n---\\\n\\\n## 9. Recommendations for Next Session\\\n\\\n1. **Deploy `cash_nl_g4_loose` as the default** for Torn (calling-station field).\\\n   Switch to `cash_nl_g4_tight` if the table VPIP drops below 0.25.\\\n\\\n2. **Activate Gen 5 data collection** — fix G5-1/G5-2/G5-3, wire recorder into\\\n   live server, start collecting transitions for imitation learning.\\\n\\\n3. **Sweep EMA half-life** — current `ema_half_life=100` may be too slow for\\\n   live adaptation. Try 50 and 75 to see if faster opponent adaptation helps.\\\n\\\n4. **Focus Gen 4 improvement on threshold scaling**, not weight map shape.\\\n   The sweep proves the model is accurate; the bottleneck is how thresholds\\\n   compensate for narrowed ranges.\\\n\"]],\"start1\":0,\"start2\":0,\"length1\":0,\"length2\":10350}]"
metadata_diff: {"new":{"id":"4d53750895be4a30af66ec1c2aea161e","parent_id":"e13f1845de9b4b6392ad866354fbd562","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":1782632783064,"user_updated_time":1782632783064,"markup_language":1,"is_shared":0,"share_id":"","conflict_original_id":"","master_key_id":"","user_data":"","deleted_time":1782797680132},"deleted":[]}
encryption_cipher_text: 
encryption_applied: 0
updated_time: 2026-06-30T05:37:46.378Z
created_time: 2026-06-30T05:37:46.378Z
type_: 13