Inside the Lab · Field note 001 · Kicker
We Taught a Small AI Model to Play a Character, Using 120 Million Simulated Hands
How a bot-coding arena became an experiment in simulation, synthetic training data, fine-tuning and verifiable computation.
What if the interesting part of a card game wasn’t the cards, but the intelligence sitting behind them?
What if people could build their own bots, give them personalities, write their strategies, and watch them compete against other people’s creations? And what if every decision could be reproduced and checked?
That was the idea behind Kicker, a six-seat hold’em arena for coding bots, built by OptimaTech Labs and Appently. The game is the test the bots take. The work is in the bots.
But letting other people’s programs compete is an unusual engineering problem. The bots have to make their own decisions. Their behaviour has to be consistent. They can’t be allowed to interfere with the environment. And when a game ends, the system has to offer more than a claim that it was fair. We wanted the evidence.
That led us into simulation engines, sandboxed execution, deterministic replay, cryptographic proofs, and eventually a small model trained on decisions our own engine produced.
The first problem: predictable bots
Ordinary game AI lives inside the game. The developer controls the rules, the logic and where it runs. Kicker lets people bring their own logic: describe how a bot should play, write exact rules in PBOT (plain-English rules that compile to TypeScript, then to a WebAssembly module of about 1 KB), or write a whole bot in TypeScript or Rust.
That raises a question: how do you let anyone’s strategy compete without giving it any control over the system? Our answer is WebAssembly. Every bot runs in a sandbox with a fixed instruction budget. It has no network, no clock and no randomness of its own. It gets the game state, returns a decision, and that’s all it can do.
That gives us a controlled environment and, just as important, exact replay. The same game state, the same bot and the same inputs produce the same decision. Which matters, because the next problem is proving what happened.
The second problem: don’t ask anyone to trust the result
A fair game shouldn’t depend on believing whoever runs it. Kicker keeps several layers of evidence:
- Seed first. Before a batch of hands is dealt, the hash of its random seed is published. The seed is revealed afterwards, so anyone can deal the hands again.
- Chained hands. Each hand’s record is hashed into the next. Change one card and every hand after it breaks.
- A proof for every hand. Each batch has a Merkle root, and every hand has a proof that it belongs to that batch.
- Signed batches. Optima Prime, our model server, signs every batch root with the time it arrived, without seeing the hands behind it. We fetched one of those receipts and checked it: valid, with both its Ed25519 and its post-quantum ML-DSA-87 signature.
Each layer does a different job. The seed commitment shows the randomness wasn’t chosen after the fact. Replay shows the record follows from the inputs. The chain and the Merkle proofs make changes or omissions visible. The signature ties each batch to an identifiable system at a point in time.
None of this proves the software is free of bugs. It gives people concrete evidence they can examine for themselves, which is a far more useful goal than promising fairness.
Then a different problem: personality
We didn’t just want bots that could play. We wanted characters. A cautious player should behave differently from a reckless one, and those differences had to change actual decisions, not just the words used to describe them.
Kicker already supported style sliders, plain-English descriptions and PBOT rules. We wanted to know whether a small model could turn a character’s written style into a complete, consistent playbook: what that character does in each of 2,880 situations (street, whether there’s a bet to face, position, hand strength, price and the kind of opponent).
The approach: let the engine teach the model. Instead of asking a general-purpose AI to improvise strategy, we generated the answers ourselves.
120 million simulated hands
Our engine sampled 42,700 real spots from play between the house characters, then replayed each one 150 to 200 times with fold, call and raise each forced as the first move, playing every hand to the end. That measured what each choice was worth. A pass took about two minutes on the Mac Pro’s 24 CPU cores, and we ran two.
Opponents were read honestly. When a rival’s hidden cards were dealt again for those playouts, we only kept hands that rival’s own style would have played the way they did.
One honest detail: only 895 of the 2,880 situations came up in that play. The rest can’t occur against these opponents, so they were filled from the nearest situation that did.
Then came the personality. A documented set of rules bends those values by the character’s dials: tightness adds folds, calling down turns folds into calls, aggression turns calls into raises, bluffing adds raises with weak hands, and tilt loosens everything after a big loss. Ten situational quirks, like slow-playing or chasing draws, add shifts where they apply.
Finally, words. About 2,800 characters were written as prompts exactly the way Kicker asks in production. Together that made 168,000 lessons, plus 2,000 held back for calibration. Nobody hand-labelled a single decision; the engine wrote the lessons.
A 421-million-parameter model
We started from Laya, an open-source decision model from Convai Innovations (Apache 2.0), built on ModernBERT-large. A decision model doesn’t write text. It reads a situation and answers typed questions with a probability for every option, which is exactly the shape of this job.
Rather than training anything from scratch, we fine-tuned that compact model for one narrow task: read a character and a situation, and reproduce the playbook our engine worked out. One pass over the data took 2 hours 26 minutes on our Mac Pro’s M2 Ultra GPU, at low priority so the products running on the same machine stayed responsive.
What changed after fine-tuning
We tested the model against the engine’s answers, using character descriptions written in wording it had never seen.
| Same top choice as the engine, unseen wording | 40% before → 92% after |
|---|---|
| Same top choice, familiar wording | 42% before → 97% after |
| Folds a set on the river facing a bet | 60–80% before → about 1% |
| Raises with aces facing a raise | 8–22% before → 83–97% |
| Folds when it costs nothing to stay in | 16–30% before → 0% |
The personalities came through too. Facing a big raise with seven-two, tight Brann folds 94% of the time, while Odile, who calls almost everything, folds 45%. On tilt, everyone loosens up.
And it changed how the bots play. We built Brann twice, once from his sliders alone and once compiled by the model, and sat each at a table against the five house characters for 500,000 hands. The compiled Brann won by a wide margin, far outside the error range of either result.
What those numbers mean. The model learned to reproduce our engine. It did not prove it became an expert player. The engine defines the target and the model approximates it. If the engine has blind spots, the model inherits them. And because it learned against the house characters, “tight” is relative to them: compiled Brann plays 73% of hands, tight at this table and loose by normal standards. Retraining against real owners’ bots will fix that. For Kicker, though, faithfully reproducing a character is the point. Consistency is part of the product.
From experiment to running system
The tuned model now runs in production on Optima Prime 0.10, as a signed record: its weights fingerprint, a signed manifest and the key that signed it. When an owner saves a new playing style, Kicker asks the model for all 5,760 decisions (2,880 situations, calm and on tilt). A full character compiles in about 2 minutes 25 seconds, and every one of those decisions carries its own signed receipt.
The same server signs every batch of hands. So the chain runs end to end: which model shaped a bot’s style, what it was asked, which hands were dealt, and that the record hasn’t changed since.
One more detail we checked: repeated identical questions give byte-identical answers, on the GPU and on the CPU. Between the two, about one probability in twenty differs in the fourth decimal place, so a receipt should be replayed on the device it names.
Why this matters beyond cards
Hold’em made a good test bed: the rules are explicit, the choices are limited, and outcomes can be simulated at scale. But the questions are broader. Can a simulation produce useful teaching data for a specialised model? Can plain-English instructions become bounded, executable behaviour? Can agents from different people run safely side by side? Can their decisions be recorded, replayed, and carry provenance you can check?
Those questions matter for autonomous systems, simulations, agent evaluation and interactive characters, anywhere an AI decision has to be both useful and accountable. And it backs something we believe: a small model with a well-defined job and a good source of training data can beat asking a much larger model to improvise everything.
What we learned, and what’s next
The interesting result wasn’t the number of hands or the size of the dataset. It was how the pieces fit. The engine produced the reference behaviour. The model learned to approximate it. The sandbox kept other people’s code inside its limits. Replay made every game reproducible. Optima Prime tied decisions and results to signed, identifiable records. Separate pieces solving separate problems, which together make a test bed for building and evaluating autonomous software.
There is still work to do. Agreement with a reference isn’t the same as performance against unfamiliar strategies. Signatures aren’t a substitute for reviewing the game logic. A deterministic sandbox still has to be tested for correctness and security.
Next we’ll retrain against real owners’ bots, measure against new opponents, and keep improving the step that turns a character’s description into a playable style. Poker is where we’re testing it. It won’t necessarily be where we stop.
Try it. Watch the six characters play live: each one’s PBOT rules are on screen, and the rule that fired lights up after every hand. Visit the Kicker arena. Or talk to us about simulation engines, custom models and agent infrastructure.