What our crew keeps answering in the dev chat
By Raul Moriarty ·Poker Software Expert
Every couple of months a new developer joins the project and asks the same questions. We pinned the answers. Twenty-one of them: solver compilation, opponent-model convergence under anonymous tables, latency budgets that bit us last year, detection topology, and the LLM questions we keep getting that we have to politely shut down.
What this FAQ covers
- Solver theory and compilation: GTO baselines, action/state abstraction, multiway compression.
- Opponent modelling: convergence under anonymous tables, Bayesian priors, archetype bucketing.
- Engineering: latency budgets, multi-table state isolation, UI automation, mobile-vs-desktop tradeoffs.
- Detection: behavioural shaping, action-timing distributions, adversarial-classification frame.
- Open research problems in 2026.
A GTO solver calculates a pre-determined strategy for a particular game tree (preflop range, a given board, a given betting options), through iteration of CFR or a variant (PioSolver and GTO+ for Heads Up / Six Max, MonkerSolver for other situations). It creates a large "lookup" table with frequencies for each possible action. A real-time bot uses this table as a base and can quickly query (under 1ms latency) it to determine what action(s) best fit in current live-game conditions. The bot uses GTO Solver results for action choices and overlays an opponent modeling system that identifies an exploitable weakness in how the GTO Solver has generated actions. That will cause the bot to move its chosen action further away than would be otherwise optimal from those provided by the GTO Solver.
Model architecture depends on the number of players at the table. In heads up (HU), it uses a fine grained model for each board texture to be able to process all of an opponents actions throughout the session as well as to update the same opponent's probabilities during every hand. In 6 max, there are five other opponents. Each of those five opponents can only have their model updated once each time he enters a hand. The model has to create broad categories of behavior for all of the other players at the table (such as loose-passive or tight-aggressive) and then refine which category fits best for each of them over the course of the session.
At GGPoker, rake is 6-8 percent on NL10 six-max, which is significantly mitigated by rakeback; it is roughly 4-5 percent on NL50 and about 3 percent net on NL200. At higher stakes, expected value per hour drops as regs study solvers more closely, so your EV-per-hour graph will likely peak in the range of NL25-NL100 based on the specific game format. Spin & Gold math differs from other formats: the rake structure is flat and variance is skewed forward. The €5 and €10 buys have a positive expected value when played frequently enough, however they take thousands of hands to achieve this.
By shape, not by mean or variance alone. Human action-timing distributions are log-normal-ish with state-dependent location and heavy right tails: snap-folds in 600-1200ms, hard rivers in 5-30 seconds, occasional 8-25 second "distraction" tails that are state-independent. Adding uniform noise around a centroid gives a bot the wrong distributional shape. Drawing from a fixed log-normal also fails because the location parameter needs to condition on decision difficulty. detection note.
Two reasons, both rooted in branching factor. Chess has roughly 35 legal moves in a typical position; No-Limit Hold'em has a continuous bet-size space layered on top of fold/check/call/raise, so the action set is effectively unbounded. To solve it you have to discretise bet sizes into a finite abstraction, and every abstraction throws away information. Second, poker is a game of imperfect information (you never see the opponent's cards), so the "solution" is a probability distribution over actions at every node rather than a single best move. Computing and storing that object takes far more space than a chess tablebase.
None (or virtually none) for real-time decision making; some utility for subsequent hands or adjacent hands. Frontier LLMs generate range estimates that do not relate to ICM and make poor use of prior knowledge about relative frequencies. They have little-to-no understanding of how to size bets for the current game state. Frontier LLMs can be beaten by a $5 buy-in heads up bot built off solver output. They help off the table by annotating hand histories, generating candidate exploit hypotheses worth solver-checking, producing synthetic training data, and summarising solver output into plain language
The empirical floor for a useful exploitable deviation in joint observations is about 80-150 hands of play, which is enough to estimate VPIP and PFR to within ±5 percent with confidence intervals overlaid on c-betting responses and 3-betting frequencies. In theory a Bayesian prior that conditions on population characteristics can reduce this to 40-60 hands of play; the prior's stability remains an open research question when moving from a fish vs reg bimodal distribution (as present at older European poker sites) to the much tighter bimodal distribution found at GGPoker.
Each table runs its own state machine with its own decision queue. A naive implementation shares a CPU and clocks decisions sequentially, which creates observable cross-table action latency correlations: table A's hard river decision delays table B's snap-fold, and the correlation is detectable. With preemptive scheduling, a tough decision on table A surrenders the CPU and rejoins the queue when easier decisions are pending, with each table's behavioural-timing distribution sampled independently. Production systems also add a per-table "alertness" parameter that drifts down over long sessions to mimic human fatigue.
On a modern Android device (Snapdragon 8 Gen 2 class), 30-80ms for solver-table lookup, 5-15ms for opponent-model update, 10-30ms for UI inspection on the rendered client. Total compute budget around 100-150ms per action. The human-perceptible action latency is the behaviourally-shaped sample on top of that, which is typically 600ms to several seconds. The compute budget is comfortable; the constraint is power consumption over long sessions.
Three streets of betting, a board that has revealed most of its information, and 2-4 opponents whose ranges have narrowed materially. The game tree under reasonable bet-sizing abstraction blows up faster than any other street. MonkerSolver's depth-limited subgame solving handles the math but only for specific tree shapes; producing a general-purpose multiway turn policy that compiles to a mobile inference budget remains an open problem. Current production systems solve for high-frequency tree shapes and fall back to heuristic-plus-opponent-model for the rest.
Three-handed hyper turbos have completely different math from 6-max cash: short stacks, fast blind levels, multiplier-aware ICM at the high end (a 1000x spin has different bubble dynamics than a flat 2x). GG says starting chips, blind levels, and time bank depend on the multiplier, so there is no single stack depth. Push-fold charts dominate from about 12bb down; the equilibrium changes shape entirely. Using a cash engine on a Spin table loses money fast because the cash engine over-deepens its lines into stack depths that do not exist. The operator's published rules for the format are summarised in Spin and Go bots on GGPoker.
Equity distributions flatten because each player has six two-card combinations rather than one. Preflop equity differences between hands shrink, blockers matter more, and turn play has higher EV-per-decision than river play. The solver outputs are larger and harder to compress. Opponent-modelling features differ: 3-bet frequency by hand class is less informative; reraise frequency given board texture is more. The UI automation layer needs to handle four hole cards instead of two.
Compilation uses two compression methods. State abstraction compresses the game state into smaller equivalence classes by board texture, SPR, position, plus prior actions to reduce the lookup key from the full state space down to an integer index. Action abstraction discretises the possible bet sizes into a finite set (for example 33%, 66%, 100%, 150% etc.), then solves for that abstraction level. Together, the two compress the data by four to five orders of magnitude compared to the original CFR output, with bounded EV loss compared to using no compression.
Android is the production target because the application sandbox is permissive enough to support accessibility-service-driven input automation with relatively stable behavioural shaping. iOS is significantly harder because the sandbox blocks the input-automation surface and the available alternatives (jailbreak, MDM exploitation) are unstable across OS versions. Most production poker AI runs Android-only as a result, which has the side effect of concentrating detection-side analysis on Android-typical telemetry, a small but useful population shift.
Hardware level entropy is the most difficult for an operator to create. Sensor information can be collected by an operator for a mobile device (accelerometer drift, gyroscope movement while gaming) as well as information about the CPU's clock frequency variation and the discharge rate of its battery. For example, a device that sits on a desktop and runs for 12 hours has a measurable difference in its sensor readings than does a device being carried around by a person. One implementation includes placing the device into a powered cradle and adds low level perturbations to the sensor reading programmatically, this may counteract the inexpensive version of this test but it is unclear if it will prevent an advanced operator using side channel analysis.
Bans are not immediate. The visible sequence is quiet flag → soft restriction (withdrawal limits drop, KYC re-verification requested) → structured interview from support → confiscation and closure. The cycle takes 2 weeks to 9 months depending on review-queue capacity and triggering events; the most common accelerator is a large first withdrawal. review pipeline in more depth.
Yes, though they lag the commercial frontier. Classical benchmarking ran through the Annual Computer Poker Competition and its successors; agents such as Tartanian, Slumbot, or Pluribus were evaluated against fixed opponent pools. Academic work today sits mainly in the Brown-Sandholm lineage at Carnegie Mellon and the DeepStack / DeepMind directions. Over 2023-2026, commercial poker AI has pulled well ahead of any public benchmark, because commercial systems are tuned against real player populations rather than research baselines.
As a rough estimate, the solver baseline provides an almost zero EV strategy against solid opposition and a positive EV strategy against your overall population. Modeling of your opponent will add 1-4 BB / 100 vs. average opponents based on their exploitability. Behavioral shaping with detection awareness will cost you 0.5-1.5 BB / 100 over pure GTO output. Net result will be 2-5 BB / 100 at NL25-NL100 on GGPoker, which scales with HPPH (60-80 per table, multi-tabling) and Effective Stake. All numbers depend on your pool and decrease as the regulars in your pool become aware of improved solver work from them.
Reliability in UI Automation. Solvers and opponent models attract interest, but the product has failed almost every time at the screen scrape and action emit layers. GGPoker sends out client updates 4-6 times per year, with about 50% of those breaking some part that the input layer is dependent upon. Shipping these less interesting parts of the system each year takes far more development effort than improving the mathematics.
Three research problems remain: online opponent model convergence under anonymous identity rotation, detection aware policy combiners in the adversarial classification frame, and compiling Pluribus scale multi-way output into a mobile inference budget with no acceptable EV loss. These problems have been open for a long time. There is also a team chat where some of the discussion occurs.
Please read the articles listed at the bottom of this webpage (the detection page) along with information from the home page (the open questions). If you are able to share an example of how you've used any of these open issues in your own work or have access to related data, use the chat function at the bottom of this article. This chat is generally quiet as it is monitored solely by the development team.
Prefer a test run to another FAQ entry?
The team sets one up for free in the chat and sends the price. New questions still make it into the FAQ once they come up twice.