--- title: Poker decision engine with a calibrated risk knob license: mit tags: - ml-intern - poker - decision-engine - risk-control - monte-carlo --- # poker-risk-engine A poker decision engine where **risk appetite is a single, calibrated scalar knob** `r ∈ [0,1]` — the piece our survey found missing from every public poker agent: levers exist in the literature, but nobody ships an aggression dial with measured, monotone response curves. ## The knob `r` simultaneously and monotonically controls: calling threshold (`pot_odds − 0.20·r`), raise threshold (`0.62 − 0.10·r`), bet threshold (`0.60 − 0.12·r`), and bet sizing (`0.45 + 0.55·r` of pot). Equity is Monte Carlo vs uniform opponents (treys). ## Calibration (120 seeded scenarios, eval_knobs.py) | r | VPIP | PFR | Aggr. rate | Bet/pot | |---|---|---|---|---| | 0.0 | 0.36 | 0.00 | 0.15 | 0.91 | | 0.5 | 0.55 | 0.06 | 0.19 | 1.35 | | 1.0 | 0.72 | 0.16 | 0.22 | 1.82 | All four aggression metrics are monotone in r (script-verified). **Honest caveat:** heuristic mean EV rises monotonically to r=1.0 with no interior peak — under a fold-equity-only EV model, aggression is never penalized, so the EV curve reflects the EV model's known weakness, not an uncalibrated knob. An EV model with value-realization dynamics would likely produce the expected interior peak; that's the next refinement. ## Misread injection: perception noise costs money Corrupt card reads (rank or suit swaps) at probability p, re-decide at r=0.5: | p | Action flip rate | Mean EV loss (chips) | |---|---|---| | 0.00 | 0.025 | 2.19 | | 0.05 | 0.042 | 3.86 | | 0.10 | 0.075 | 8.75 | | 0.20 | 0.083 | 13.12 | | 0.30 | 0.167 | **34.26** | A 30% misread rate costs ~15× more EV than the clean-read baseline — the quantitative bridge between the perception stack ([ThreeSixNine/poker-table-perception-eval](https://huggingface.co/datasets/ThreeSixNine/poker-table-perception-eval), detector in [ThreeSixNine/yolo11n-poker-table-cards](https://huggingface.co/ThreeSixNine/yolo11n-poker-table-cards)) and decision quality. ## Limitations (v0, by design) Uniform random opponent model (no reads/ranges); single-street decisions (no future-street planning, no ICM); heuristic EV used only for policy comparison; preflop is pure equity MC (no charts). See `LIMITATIONS` in decision_engine.py. ## Usage ```python from decision_engine import RiskEngine, TableState eng = RiskEngine(n_sims=1500) st = TableState(hero=["AS", "KD"], board=["AH", "7S", "2D"], pot=160, stack=1000, to_call=40, n_players=3, position="BTN", street="flop") print(eng.decide(st, r=0.7)) # {"action": "raise", "amount": ...} ``` Reproduce: `python eval_knobs.py --n-scenarios 120 --sims 1500`