Poker bots are a well-documented problem space and a genuinely instructive one: imperfect information, stochastic outcomes, an adversary, a state space too large to enumerate, and a hard real-time perception problem bolted to the front of it. Very little software has to do all of that at once.
Stated plainly at the top: automating play on a real-money site violates the terms of every operator that exists, and taking money from other players by concealed automation is fraud. Operators confiscate balances and close accounts, and the conduct has attracted criminal attention in several jurisdictions. What follows is an analysis of how the system is structured and why it is difficult — not instructions for deploying one. The interesting research is done in environments that permit it: self-play, open research frameworks, and the academic competitions that ran for years precisely so this work could happen without defrauding anybody.
Three subsystems
Every such system decomposes the same way.
Perception. Reconstruct the current state of the table from whatever the client exposes.
Decision. Given the state, choose an action.
Actuation. Deliver that action to the client.
Popular accounts spend all their attention on the middle box. In practice the middle box is the part that has been solved in public, repeatedly, by researchers who published their methods — and the first box is where the engineering actually lives.
Perception is the hard part
A real-money client offers no API. It offers pixels. So the perception layer is a computer-vision pipeline with unusually unforgiving requirements, and it must reconstruct not a snapshot but a history.
What has to be recovered on every frame: the hole cards, the board, the pot, every player's stack, who holds the button, whose turn it is, the current bet to call, the minimum raise, the blind level, the number of players still in the hand, and the complete sequence of actions this street. That last one is the killer. Screen scraping samples state; poker is defined by events. If a player raises and folds within one sampling interval, you did not see a raise, and your model of the hand is now wrong in a way that will not correct itself.
The subsidiary problems compound:
- Cards are easy, numbers are hard. Card faces are a fixed sprite set and template-match reliably. Stack sizes are anti-aliased proportional text over a variable background, rendered in a locale-dependent format, sometimes abbreviated, sometimes in big-blind units, sometimes mid-animation as chips slide into the pot.
- The layout is not fixed. Tables resize. Themes change sprite sets and colour ranges. A client update reflows the felt and every hard-coded offset becomes wrong at once. Anything built on absolute coordinates is a maintenance treadmill.
- Animation is state ambiguity. During a chip slide the pot on screen is neither the old value nor the new one. A naive reader will sample a transient and act on it.
- Occlusion and elision. All-in side pots, disconnected players, sit-outs, straddles, run-it-twice: each is a rule the table renders in a special case that the general-case parser has never seen.
The result is a system whose failure mode is silent. A parser that misreads a stack does not crash; it produces a plausible state that is wrong, and every downstream decision is confidently incorrect. Anyone who has debugged an OCR pipeline against live rendering knows the shape of this: you are not fighting an algorithm, you are fighting a long tail of presentation cases, and the tail never ends.
The far cheaper alternative — reading the hand-history files the client writes to disk — gives clean, unambiguous, parseable state. It also gives it after the hand is over, which is useful for analysis and useless for acting.
The decision engine, in three generations
Rule-based. A decision table over a small feature set: hand class, position, pot odds, number of opponents, and a handful of thresholds. Fast, comprehensible, and beatable by any competent human within a session, because it is deterministic and its thresholds are discoverable.
Equity-based. Estimate the probability of winning by Monte Carlo roll-out — deal the unknown cards many times, evaluate the showdown, count. This needs a very fast hand evaluator, which is why ranking seven cards quickly is a real subfield rather than a trivia question. Equity plus pot odds plus some model of implied odds produces a solid, tight, exploitable player. Its weakness is structural: it reasons about the current hand, not about the opponent's strategy, so it does not bluff coherently and does not defend against being bluffed.
Game-theoretic. The published research line is counterfactual regret minimisation and its descendants. CFR iterates self-play over an abstracted version of the game, accumulating regret for actions not taken, and converges toward an equilibrium strategy. Because the real game is far too large, it is played on an abstraction: card abstractions bucket similar hands together, action abstractions restrict betting to a small set of sizes. The published milestones are public — heads-up limit hold'em was essentially solved in 2015, and heads-up and six-player no-limit programs beat professional players in 2017 and 2019 respectively.
The gap between "there is a solver" and "there is a bot" is translation. Your abstraction knows about a bet of half the pot and a bet of the full pot. The opponent bets 0.63 of the pot. Mapping that observed action into the abstraction, choosing a response, and mapping the response back out is lossy, and the loss is exploitable. A large part of the practical difficulty is that translation error, not the equilibrium computation.
There is also a strategic objection to equilibrium play in a soft game: an equilibrium strategy is unexploitable, which means it does not lose, but it also does not maximally punish bad opponents. Against weak players an exploitative model makes more money. Against strong ones it gets counter-exploited.
Actuation gives you away
Suppose perception and decision both work. The system still has to click, and clicking is where it becomes visible.
Human action timing has structure: it varies with decision difficulty, it has a long tail, it correlates with pot size and street, and it degrades over a session as the player tires. Constant-latency actions, or latency drawn from a symmetric distribution around a mean, look nothing like that. Nor do mouse paths generated by an easing function, which are smooth in a way human motor control is not.
Operators have every advantage in this contest. They see the full action log for every account, across sessions and across accounts, and they are looking for statistical signatures rather than proof of any particular mechanism:
- timing distributions that are too regular, or too independent of decision complexity
- input paths with no tremor, overshoot or correction
- decision similarity between accounts far above what independent players produce
- superhuman consistency in marginal spots, sustained over volume no human plays
- session lengths and break patterns no person sustains
- environment fingerprints — virtual machines, known automation frameworks, unusual client instrumentation
And crucially, the operator holds the money. They do not need to win an arms race; they can freeze a balance and require identification. That asymmetry is why this is not a technically interesting adversarial game so much as a rigged one, on top of being dishonest.
The part worth keeping
Strip the poker off and what remains is a general lesson about a class of system.
You have a rich, well-understood decision problem sitting behind a narrow, lossy, adversarially-changing interface. Almost all of the engineering effort — and essentially all of the failure — occurs at the interface, not in the decision logic. The decision logic is testable in isolation, has a clear correctness criterion, and can be improved offline. The perception layer is untestable except against live inputs, has no ground truth, fails silently, and breaks whenever someone else ships a UI change.
That pattern recurs constantly: RPA against enterprise web apps, scraping for price intelligence, medical device integration over screen output, legacy terminal automation. The advice is the same everywhere. Get a real interface if one exists at any price. If you cannot, invest in validating the reconstructed state rather than in producing it — cross-check invariants (do the stacks and pot sum to the amount that entered the hand?), and make disagreement fail loudly rather than continuing on a state you have no confidence in.
That, and pick a target where automating it is not fraud.