Strategy agents
Built-in styles generate synthetic poker behavior and provide baseline opponents.
Project Case Study · Machine Learning · Product Systems
I always loved poker home games with my friends, but I started losing more than I won. Instead of guessing which parts of my strategy were bad, I wanted a system where I could write down a style of play, simulate it, measure it, and refine it like an experiment.
Python · scikit-learn · FastAPI · Next.js · PostgreSQL
Poker Sandbox became a strategy testing lab: a place to turn vague poker ideas into executable agents, run those agents through simulated hands, and diagnose which decisions were losing value. The project is part product and part ML system. The product layer makes strategy experimentation feel natural. The ML layer estimates expected value for decisions using supervised data generated by the simulator.
The system is designed as one linear feedback loop. First, agents create behavior. Then the simulator records decisions. Feature engineering turns those decisions into training vectors. Random Forest models learn action value. Finally, a custom strategy is simulated, diagnosed, patched, and rerun.
Built-in styles generate synthetic poker behavior and provide baseline opponents.
Hands are played; every decision state, legal action, and outcome is logged.
Raw game context becomes structured state-action inputs for supervised learning.
Trees learn nonlinear interactions and estimate expected profit for each action.
Plain-English strategy becomes typed policy parameters and an executable agent.
The custom agent is benchmarked against baselines to identify weak patterns.
Recommended parameter changes are tested through another simulation pass.
The first problem was data. I did not have a giant hand-history dataset, so I created synthetic data through built-in agents. Each agent has a recognizable style: Tight-Aggressive plays fewer hands but applies pressure, Loose-Aggressive enters more pots and bluffs more, Calling Station over-calls, and Random creates noisy baseline behavior.
These agents are not meant to be perfect poker players. They are controlled behavior generators. By letting them play many hands, the simulator creates repeated examples of how different styles act across position, stack size, pot size, board texture, and betting pressure.
At every decision point, the simulator snapshots the table before the agent acts. That snapshot includes the legal actions, current street, board cards, pot size, amount to call, stack depth, prior betting behavior, position, and eventual result. This turns a poker hand into a sequence of decision records rather than one final win/loss outcome.
That matters because the model needs to learn from individual choices. A player might lose a hand but still make a good decision, or win a hand after making a bad one. Logging state-action examples gives the ML layer a more granular training signal.
The Random Forest does not see raw cards the way a human does. It receives structured feature vectors. I engineered features that describe the strategic situation: hand strength, pot odds, stack-to-pot ratio, board wetness, position, facing-bet status, aggression history, continuation-bet opportunity, and action encoding.
For EV prediction, the important training example is a state-action pair. The same poker state can be copied across several legal actions; only the action feature changes. The model then learns which actions tend to produce better long-run value in similar situations.
I chose Random Forest regression because the data is structured, tabular, and full of nonlinear interactions. Poker decisions are rarely explained by one feature alone. A call might be good with strong pot odds, bad with a weak hand, better in position, and worse against high aggression. Random Forests handle those feature interactions well without requiring a neural network or massive dataset.
Random Forests are also interpretable, robust to noisy synthetic data, and useful for both classification and regression. In this project, classifiers support hand-strength buckets, opponent action tendencies, and bluff detection. The EV model is a Random Forest regressor that predicts expected profit for each candidate action.
[state features + candidate action]
Predicted EV: +0.30
Each tree learns different splits over the feature vector. One tree might split first on hand strength, another on pot odds, another on whether the agent is facing a bet. The forest averages those outputs to produce a more stable EV estimate than a single decision tree.
When MLAgent reaches a decision, it does not ask the model for a single action directly. It enumerates all legal actions, creates a feature vector for each one, predicts EV for each candidate, and chooses the highest predicted value.
This makes the MLAgent a model-backed decision policy. The model supplies value estimates; the agent turns those estimates into actions.
The strategy lab adds a product layer on top of the simulator. A user can write a strategy in plain English, such as "play loose aggressive, bluff rivers often, and do not overfold to small bets." The parser maps that sentence into a typed configuration: tightness, aggression, bluff frequency by street, call thresholds, continuation-bet frequency, and fold-to-pressure behavior.
Parsing matters because it turns subjective strategy language into parameters the simulator can execute. Instead of leaving a strategy as a note, the system converts it into a ConfigPolicyAgent that can play hands, produce metrics, and be compared against baseline opponents.
After a custom strategy runs through simulations, the diagnostic layer summarizes how it behaved: win rate, big blinds per 100 hands, aggression rate, fold rate, showdown frequency, and matchup performance against each baseline. The leak detector looks for patterns like over-folding to aggression, calling too wide, bluffing too frequently, or failing to apply pressure in profitable spots.
The patching step turns those findings into parameter changes. For example, if a strategy folds too often against small bets, the patch might lower fold-to-aggression and raise the call threshold. If it bluffs too much on later streets, the patch might reduce river bluff frequency. The system then reruns the diagnostic with the patched configuration so the recommendation is tested, not just suggested.
The project became more than a poker toy, even though it helped me a lot at home games. It used synthetic data generation, feature engineering, supervised learning, model-backed decisioning, natural-language configuration, simulation-based evaluation, and automated iteration. The same pattern applies to any domain where a user wants to describe a strategy, test it in a controlled environment, and improve it with evidence.