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, detector in 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

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

Downloads last month

-

Downloads are not tracked for this model. How to track
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support