Building an exact video poker solver in the browser
Video poker is one of the few casino games that is completely solvable — small enough to enumerate, big enough that nobody bothers to. Here's the architecture, the correctness proofs, and what happened when we pointed the solver at the strategy charts everyone memorizes.
The problem is smaller than it looks
A dealt hand has 2⁵ = 32 possible holds; a hold of k cards has C(47, 5−k) possible draws — at worst 1,533,939 (discard everything). Solving one hand exactly means evaluating every draw of every hold against the paytable: a few million 5-card evaluations. With a fast evaluator (rank/suit bit tricks, no allocations in the hot loop) that’s single-digit milliseconds in a browser Web Worker — no lookup tables, no server, no approximation. The engine is ~1,500 lines of dependency-free TypeScript.
Proving it right (the part that matters)
A solver that’s 99.9% correct is worthless — it will confidently misprice edge cases. Two proofs anchor ours. First, the evaluator: classify all 2,598,960 five-card hands and compare category counts against the century-old known frequencies (4 royals, 36 straight flushes, 624 quads…) — exact match required. Second, end-to-end: a game’s total return requires solving every deal. Suits don’t matter up to isomorphism, so the deal space collapses to 134,459 equivalence classes — solve each once, weight by multiplicity:
Hitting published figures to seven decimals validates the evaluator, the enumeration, and the paytables simultaneously. It also hands you a research instrument.
Then we pointed it at the strategy charts
Human players use ordered pattern lists (“first matching line wins”). A list is just data — so score it exactly: for each of the 134,459 classes, record the max EV of every chart line’s matching holds. Any reordering, deletion, or insertion of lines becomes an O(classes) table scan — cheap enough to hill-climb over chart space.
Results: the classic 16-line Jacks or Better “simple strategy” returns exactly 99.4593%; two added lines (inside straights with 3+ high cards, and splitting suited-connector draws by high card) reach 99.5003%, and the search proves the next candidate line isn’t worth its memorization slot (full study). On 10/7 Double Bonus the solver found a line published charts miss — break a high pair for an overlapping Q-J-10/K-Q-J royal draw — verified across all 3,456 such hands (that study). The same machinery rejected every “intuitive” improvement that measured worse, which is most of them.
Product details that fell out for free
Exactness composes. Per-category EV decomposition (where does a hold’s 0.8237 come from?) is the same enumeration with a histogram — that became the analyzer’s breakdown panel. Shareable permalinks (?cards=Qs,Js,10s,Jh,3d&game=db) re-solve on load, so a forum argument can end with a link; the link unfurls with the cards rendered and the best hold ringed, computed server-side by the same engine. Trainer feedback cites chart-line numbers because the classifier and the chart are the same data structure.

Try to break it
Everything is checkable from the browser: the analyzer for any hand, the verification methodology, and the studies with their negative results included. If you find a hand we misprice, that’s a bug report we genuinely want: [email protected].
Open the solver →