Felt Dojo

Felt Dojo: Duplicate poker

Companion to the project brief. What it is, why it's hard, what it measured, and what it can't do.


The idea

Poker is a game where you can play well and lose, repeatedly, for a long time. That's what makes it worth playing and what makes it almost impossible to learn from. If you and I sit at different tables for an evening and you finish up and I finish down, we have learned nothing about which of us played better.

Duplicate bridge solved this in 1914: deal the same hands to every table, and score players against each other on identical cards. Card luck stops mattering because everyone gets the same luck.

Duplicate poker applies that to Hold'em. Two or more players play the identical run of hands: same hole cards, same flops, turns and rivers, same bots in the same seats, same blinds, same starting stacks. The only thing that differs is how each of you plays them. You play on your own schedule, asynchronously; nobody has to be online at the same time.

As far as I know, nobody else has built this for poker.

What you actually get

A leaderboard with three scores, because they answer different questions:

A hand-by-hand review, which is the actual product. The leaderboard is the hook; the review is why the feature exists. It finds the first decision where your line and another player's diverged, opens at that exact moment with the identical history folded away above it, and then plays both lines out side by side to their separate endings. It also surfaces the hands that decided the match , the ones where somebody finished furthest from everyone else (which is rarely the biggest pot), the hands where you were the only one to fold or the only one to get your stack in, and which street your hands tend to be won and lost on.

A style comparison across everyone in the match: how often each of you played a hand, raised first in, three-bet, went to showdown, and how aggressively you bet, over the identical deals.

Free accounts get a 24-hand 6-max match seating five people, fully scored with the complete review. Play one alone and it is scored against a single Intermediate benchmark line instead, so it still closes with a leaderboard. Plus adds the Advanced benchmark, matches up to 1,000 hands, 2–9 seats, custom stakes, longer deadlines, and fields of ten rather than five.

You bring people in either by emailing them an invitation or by sending a share link. Both routes go through the same gate, so a match that has closed its field cannot be joined by either, which matters, because a share link leaves no record you could otherwise check against.

Why this is harder than it sounds

The whole feature rests on one claim: every participant faced exactly the same thing. If that is false anywhere, every number the leaderboard shows is meaningless, and it fails silently, because a slightly different bot decision just looks like a slightly different hand.

Fixing the deck is the easy half. A full 52-card permutation is generated per hand and dealt by fixed index, so one player folding preflop cannot shift the turn card the way it would if cards were dealt on demand.

The bots were the hard half, and the original plan under-estimated it by an order of magnitude. The design assumed the risk lived in one per-hand random roll. An audit before any of it was built measured the truth: replaying identical hands with identical inputs, 15% of all-Intermediate hands diverged, 37% of all-Beginner ones. The plan had estimated 2%. The cause was the Monte Carlo equity estimators, which sample a few hundred runouts per decision and had no seeded source.

Worse, and completely invisible: the preflop equity table was computed per-process and sampled on first use, so it drifted up to 2.4 equity points between server restarts. Two people who played the same match on either side of a deploy faced measurably different opponents, and nothing anywhere would have reported it.

Both are closed. Every bot random draw is now addressed by its coordinates, which hand, which seat, which street, which action ordinal for that seat, rather than drawn from a stream, because a stream desynchronises the instant one player's line creates one extra decision. The preflop table is a committed 7,605-entry resource rather than something each server computes for itself.

And the obvious test for this does not work. Replaying a scripted hand in a fresh JVM passes even with the frozen equity table deleted, because a scripted hand makes the cache keys arrive in the same order in every process. The test that actually works reads preflop equity directly across processes: without the committed table, three JVMs give three different answers; with it, the same answer three times. That distinction was found by deliberately breaking the fix and watching the test stay green.

What it measured, and why it paid for itself before shipping

Duplicate poker was built to let players compare themselves. Its first real use was measuring the bots, and it immediately found a defect that had been live for months.

Playing the identical spot four ways (a human plus three bot benchmarks, same cards, same seats) makes a bot's behaviour directly comparable in a way no amount of aggregate statistics does. A 300-hand run produced 3,357 preflop decisions, and among them the finding that facing a raise before the flop, the Intermediate bot folded 92% of the time and three-bet almost as often as it called. It had no calling range at all; the tier could only fold or re-raise. One absent range explained every symptom that had been separately noticed and never connected: the 4% call rate, a limp that folded 100% of the time to any raise, a price curve that was flat and slightly inverted, and over-bluffing that the same table's over-folding made profitable.

Fixed, and verified twice on independent deals:

facing a preflop raisebeforeafter
fold / call / raise92.0 / 4.2 / 3.881.7 / 16.0 / 2.4
call : raise ratio1.1 : 16.7 : 1
continues after limping0.4%17.2%
sees a flop9.6%19.9%

The feature did not create that defect; cash and tournament behaviour was verified unchanged. It made it visible.

The honest limit: what a short match can and cannot tell you

This is the part I'd want stated plainly if someone else were making these claims.

Most hands of poker are identical no matter who plays them. In a real 24-hand heads-up match, 20 of the 24 ended the same way: both players folded, nothing happened. Those hands contribute exactly zero to any comparison, so the effective sample is the divergence rate times the hand count, not the hand count.

Consequences, measured rather than assumed:

None of that is a defect in the format; raw-chip variance without matched deals would be far larger. It's a limit worth being loud about, so nobody reads a leaderboard from a 24-hand match as a verdict. The leaderboard is the hook; the decisions are the instrument, and they're what made every bot finding above conclusive off a single run.

Integrity, and the one hole that can't be closed

Results, hand histories, and even progress stay sealed until a match closes:

The sealing used to have one blunt edge: a match opened at its deadline, not when everyone had finished. A match that four friends all played on the same evening could sit sealed and unscored for up to a week. Found in the first production play-test; fixed and deployed 2026-08-19: a match now closes as soon as nobody new can join and nobody in it is still outstanding, so the worst case a player experiences went from seven days to about five minutes. The reason it was not a one-line change is worth stating: the obvious version (just look at matches earlier) hands a match that is not actually due to a routine that forfeits everyone still outstanding, which would silently burn players who had days left. The early path is therefore a separate route that structurally cannot reach the forfeit loop, and each of its negative tests was verified to fail against the naive version before shipping.

The hole that redaction cannot close, found in play-testing: every participant plays the identical deal, so anyone who has played hand 12 can describe it completely to anyone who hasn't. Same cards, same seats, same bots, no showdown required. A participant is themselves the leak. Asynchronous duplicate is therefore a trust boundary, fine among friends and not a basis for ranked play. The structural fix is simultaneous play, which is exactly what duplicate bridge relies on, and it's specified but not built.

Where it stands

Built across nine milestones plus an operator benchmark harness and a bot probe harness, and live on feltdojo.com. Load-tested with concurrent runs (2026-08-19, on the dev machine): 12 and 24 simultaneous runs behaved identically (p95 action latency 7 ms at both); the cap refused the 25th cleanly, and identical conditions held across every participant, which is the promise the format cannot break. The honest caveat: that was the dev machine, so it says "the design holds", not "here is what the production box can take."

Open, and all of it product work rather than defects:

What's next

1. Heads-up scoring rework: at two players the relative board is arithmetically identical to raw chips, so it earns no space; matchpoints move to divergent hands only, and the divergence count becomes a first-class statistic rather than a footnote. 2. Think-time totals, and the divergent board beyond heads-up.

(Early close, scoring a match as soon as nothing further can happen to it, used to head this list, and shipped 2026-08-19.)

The one thing deliberately not planned is a fix for the participant-is-the-leak problem above. Simultaneous play would close it, and asynchronous play is the reason anyone can use this feature at all.