Gen 4 — Adaptive Opponent Modelling (State of the Art)

# Gen 4 — Adaptive Opponent Modelling (State of the Art)

> **Current state, 2026-07-31.** Gen 4.8 (G47 preflop + G48 postflop) is the last Gen 4 strategy — config: `cash_nl_g48`. Production is now **Gen 5** (G48 formula + RangeNet pure NN) — config: `cash_nl_g50`.

## Gen 4.8 — Formula-Based Postflop + G47 Preflop

G48 replaces the 80+ parameter Gen 3/4 categorical decision tree with a **unified score formula** + threshold comparisons (~21 parameters). Same equity computation (MC rollouts), only the decision logic changes.

| Priority | Strategy | When | Role |
|---|---|---|---|
| 1 | `g2_ke_hu_nash` | HU, 15-25 BB | Nash equilibrium (unchanged) |
| 2 | `g47_equity_preflop` | All preflop | PEM2 matrix equity + VPIP/PFR-adaptive thresholds |
| 3 | `g48_formula_postflop` | Postflop | Formula-based decisions with observer ranges (+ RangeNet in Gen 5) |

### G48 Unified Score Formulas

**value_score** (for betting/raising):
```
value = hs × (1 − npot_w × npot) + nutredraw_w × nutpot × (1 − hs) − behind_penalty_w × (1 − hs) × (1 − ppot)
```

**draw_score** (for semi-bluffing and calling with draws):
```
draw_quality = nutpot / (nutpot + rpot + ε)
draw_score = ppot × draw_quality × ppot_weight
```

**combined = value_score + draw_score** — single threshold comparison replaces 7 first-in branches and 5 raised-pot branches.

### G48 Key Features
- **Behind-penalty** (`behind_penalty_weight=0.15`): continuous penalty for hands unlikely to improve when behind
- **Board-hit boost** (`narrow_board_hit_boost=3.0`): range narrowing boosts hands matching top board card (×3 weight), penalizes without (×1/3)
- **Draw-heavy board trap guard**: `board_is_drawy()` guards ALL trap paths
- **Per-player looseness**: `aggressor_vvip(ctx)` keys off bettor's individual VPIP
- **Observer-based range equity**: shares range computation with G45

### G48 Key Parameters (live)

| Parameter | Value | Description |
|-----------|-------|-------------|
| `sizing_base` | 0.33 | Base bet/raise fraction of pot |
| `ppot_weight` | 0.75 | Draw bonus weight in combined score |
| `npot_weight` | 0.10 | Negative potential penalty |
| `call_base` | 0.42 | Base call threshold |
| `raise_base_bb` | 3.0 | Preflop standard open |
| `range_alpha_min` | 1.0 | Min NN blend weight (Gen 5: pure NN) |
| `range_alpha_max` | 1.0 | Max NN blend weight (Gen 5: pure NN) |
| MC samples | 10,000 (sim) / 40,000 (live) | Divided by opponent count, floor 8K |

## Gen 4.7 — Matrix-Based Equity Preflop

G47 replaces NG ranking-table preflop with **pure equity decisions** from a precomputed 169×169 PEM2 distribution matrix.

### PEM2 Distribution Matrix

- **Format**: 10 metrics × 8 bins × 169×169 pairs × f32 = ~8.7 MB binary (`data/preflop_equity_matrix.bin`)
- **10 metrics**: equity, hs, ppot, npot, nutpot, rpot, draw, board, status, hs_random
- **Generation**: Full C(48,3)×C(45,2) enumeration per pair, rayon parallelized
- **Binary**: `holdem_bots/src/bin/gen_preflop_equity.rs`
- **API**: `load_or_generate()`, `equity_vs_vpip_range()`, `ppot/npot/nutpot_vs_vpip_range()`, `quartiles_vs_vpip_range()`

## Configs

| Config | Gen | Purpose |
|---|---|---|
| `cash_nl_g50.toml` | 5 | ke_hu → g47 → **g48 + RangeNet (alpha=1.0)** — Gen 5 sim (production) |
| `cash_nl_g50_live.toml` | 5 | Same, live settings — Gen 5 live config |
| `cash_nl_g48.toml` | 4 | ke_hu → g47 → **g48 (heuristic ranges)** — last Gen 4 config |
| `g48_{fish,station,nit,tag,lag,maniac,reg}.toml` | 4 | G48 personality variants |

## Key Files

| File | Content |
|---|---|
| `holdem_bots/src/gen4/formula_postflop.rs` | **G48 strategy** — unified score formula, Gen48Config with range_alpha/model params |
| `holdem_bots/src/gen4/equity_preflop.rs` | **G47 strategy** — PEM2 equity decisions |
| `holdem_bots/src/gen4/preflop_equity_matrix.rs` | PEM2 matrix — load, generate, equity lookups |
| `holdem_bots/src/gen4/range_builder.rs` | Heuristic range construction (Gen 3/4 only, unused when alpha=1.0) |
| `holdem_bots/src/gen4/range_predictor.rs` | `RangePredictor` — loads RangeNet, predicts ranges, `probs_to_hand_range()`, `blend_alpha()` |
| `holdem_bots/src/gen4/observer.rs` | Observer (EMA tracking, per-opponent stats) |
| `holdem_bots/src/gen4/profile.rs` | PlayerStats, PlayerProfile, three-tier blend |
| `holdem_bots/src/gen4/mod.rs` | `build_ranges_and_equity()` — NN + heuristic blending |
| `holdem_bots/src/cash/rollout_postflop_base.rs` | Shared postflop base |

## Generation Lineage

```
Gen 1 (static rules) → Gen 2 (EHS rollout) → Gen 3 (range-aware equity)
→ Gen 4 (G45/G47/G48: adaptive observer + formula, config: cash_nl_g48)
→ Gen 5 (G48 + RangeNet pure NN, config: cash_nl_g50)
→ Gen 6 (future: RL self-play)
```

## Where This Goes Next

- **Gen 5** (declared 2026-07-31): G48 formula + RangeNet pure NN. Currently in sanity check. See Gen 5 notebook.
- **Gen 5 tuning**: formula parameter sweep with NN ranges, faster NN inference
- **Gen 6** (future): RL self-play for continuous improvement

id: de72ff3ebcbd45689c988e50038e80b0
parent_id: e13f1845de9b4b6392ad866354fbd562
created_time: 2026-06-28T05:16:57.134Z
updated_time: 2026-07-31T18:41:32.251Z
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: 1782623817134
user_created_time: 2026-06-28T05:16:57.134Z
user_updated_time: 2026-07-31T18:41:32.251Z
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
is_locked: 0
extracted_resource_ids: 
type_: 1