Video Poker Payout Calculator Obsession
Or, how I got from several years to seven seconds.

I have spent much of the past few months obsessing over the payouts of two video poker machines: Deuces Wild and Jacks or Better.


The following tables were generated by my Video Poker Payout Calculator.

Full Pay Jacks or Better (FPJOB) Perfect Play Payout
Payout Award Frequency EV Variance
royal straight flush 800 0.00248% 0.0198 15.8059
straight flush 50 0.01093% 0.0055 0.2625
four of a kind 25 0.23625% 0.0591 1.3613
full house 9 1.15122% 0.1036 0.7376
flush 6 1.10145% 0.0661 0.2759
straight 4 1.12294% 0.0449 0.1014
three of a kind 3 7.44487% 0.2233 0.2992
two pair 2 12.92789% 0.2586 0.1305
jacks or better 1 21.45850% 0.2146 0.0000
nothing 0 54.54347% 0.0000 0.5405
Totals 100.00000% 0.9954 19.5147

Full Pay Deuces Wild (FPDW) Perfect Play Payout
Payout Award Frequency EV Variance
natural royal straight flush 800 0.00221% 0.0177 14.0981
four deuces 200 0.02037% 0.0407 8.0662
wild royal straight flush 25 0.17958% 0.0449 1.0337
five of a kind 15 0.32016% 0.0480 0.6268
straight flush 9 0.41748% 0.0376 0.2667
four of a kind 5 6.49382% 0.3247 1.0351
full house 3 2.12291% 0.0637 0.0843
flush 2 1.68964% 0.0338 0.0166
straight 2 5.59189% 0.1118 0.0551
three of a kind 1 28.47093% 0.2847 0.0000
nothing 0 54.69100% 0.0000 0.5553
Totals 100.00000% 1.0076 25.8379


With an EV of 1.0076, Full Pay Deuces Wild on average pays back 100.76% of what you put in, as long as you make all the right discard decisions. That's an average, of course -- your mileage may vary, especially in shorter sessions.

Let's say you are a casino game designer and you're looking to model a game that's more in the casino's favor, say something with a 99.99% payout. You might try ratcheting down some of the payout awards to see what happens. Or maybe you were in the casino and came across such a machine, curious to know the new payout. It's just a few numbers, doesn't look that much work, does it? The tricky one is that middle column...

So I set out to write a program that could quickly calculate the payout of either machine, allowing for arbitrary changes to the payout table. At first, this seemed like a tall task. There are 52x51x50x49x48 possible ways to get your first five cards in draw poker (52-card deck, so, 52 possibilities for the 1st card, 51 for the 2nd, etc).

That's almost 312 million equally probable hands. Then, each hand has 32 (=25) possible discards that each have to be evaluated for expected value. In order to pick the discard with the best promise in each case, all 32 discards must be considered individually. The discard with the best expected value is picked as the optimal discard strategy for that hand, and is then used in figuring the total payout.

The "expected value" (EV - this is statistician-speak for average payout) depends on the number of final outcomes that deliver each payout, ie it depends on how often each outcome is reached or in what ratio*. Each discard scenario and all of its possible draw outcomes must be examined.

* Here's the formula:
EV = Σ ( payout ·  payout_hits 
total draws
)
where "payout" is the amount paid divided by the amount bet.

Example Analysis of Jacks Or Better hand KC,JD,2D,KH,3H
  Optimal discard (best EV) is highlighted in green.
Held Total
Draws
J+ Pair 2 Pair Trip Str Flush Full Hse 4 of Kind StrFl Royal EV
_ _ _ _ _ 1533939 164229 73683 33075 7269 2976 2271 387 26 1 0.3194
_ _ _ _ 3H 178365 11787 9033 4177 637 327 297 53 3 0 0.2861
_ _ _ KH _ 178365 34677 6993 2529 382 328 141 9 1 1 0.3481
_ _ _ KH 3H 16215 2175 552 206 0 165 9 1 0 0 0.3079
_ _ 2D _ _ 178365 11787 9033 4177 382 328 297 53 2 0 0.2802
_ _ 2D _ 3H 16215 597 717 283 128 0 18 2 0 0 0.2222
_ _ 2D KH _ 16215 2175 552 206 0 0 9 1 0 0 0.2468
_ _ 2D KH 3H 1081 93 21 7 0 0 0 0 0 0 0.1443
_ JD _ _ _ 178365 41775 9033 4177 764 326 297 53 3 1 0.4615
_ JD _ _ 3H 16215 2769 717 283 0 0 18 2 0 0 0.3246
_ JD _ KH _ 16215 4461 552 206 128 0 9 1 0 0 0.4194
_ JD _ KH 3H 1081 207 21 7 0 0 0 0 0 0 0.2497
_ JD 2D _ _ 16215 2769 717 283 0 165 18 2 0 0 0.3856
_ JD 2D _ 3H 1081 127 27 9 0 0 0 0 0 0 0.1924
_ JD 2D KH _ 1081 207 21 7 0 0 0 0 0 0 0.2497
_ JD 2D KH 3H 47 5 0 0 0 0 0 0 0 0 0.1063
KC _ _ _ _ 178365 34677 6993 2529 382 493 141 9 1 1 0.3536
KC _ _ _ 3H 16215 2175 552 206 0 0 9 1 0 0 0.2468
KC _ _ KH _ 16215 11559 2592 1854 0 0 165 45 0 0 1.5365
KC _ _ KH 3H 1081 801 186 84 0 0 9 1 0 0 1.4162
KC _ 2D _ _ 16215 2175 552 206 0 0 9 1 0 0 0.2468
KC _ 2D _ 3H 1081 93 21 7 0 0 0 0 0 0 0.1443
KC _ 2D KH _ 1081 801 186 84 0 0 9 1 0 0 1.4162
KC _ 2D KH 3H 47 39 6 2 0 0 0 0 0 0 1.2127
KC JD _ _ _ 16215 4461 552 206 128 0 9 1 0 0 0.4194
KC JD _ _ 3H 1081 207 21 7 0 0 0 0 0 0 0.2497
KC JD _ KH _ 1081 801 186 84 0 0 9 1 0 0 1.4162
KC JD _ KH 3H 47 39 6 2 0 0 0 0 0 0 1.2127
KC JD 2D _ _ 1081 207 21 7 0 0 0 0 0 0 0.2497
KC JD 2D _ 3H 47 5 0 0 0 0 0 0 0 0 0.1063
KC JD 2D KH _ 47 39 6 2 0 0 0 0 0 0 1.2127
KC JD 2D KH 3H 1 1 0 0 0 0 0 0 0 0 1.0000

Some of these discard scenarios are tougher than others. For example, in the case where you hold every card there's only one outcome to evaluate. If you get rid of all five cards there are 47x46x45x44x43 (184 million) ways to replace the discards. Adding up all the draws from all 32 discards, there are 206,471,236 paths from a particular five-card hand to a final set of five cards. Each of these must be considered for potential payout.

But remember, there are 312 million initial five-card hands. That makes 312M x 206M = 64.4 quadrillion (15 zeros) total possible outcomes to examine. No problem (sarcasm)! My dual 450 MHz processors can handle 2 billion instructions per second (1000 MIPS each), so it would take about a year for them just to count this high. Even if I could evaluate each poker hand outcome in one "instruction" (actually it takes a few hundred), and/or buy a much faster computer, this still would not be acceptable.

Fortunately, there are a great number of redundant calculations in this strategy. First, the order of the cards does not matter. This reduces the true number of pre-discard hands by a factor of 5x4x3x2x1 = 120. The order does not matter in the final draw either. That's why the "total draws" column in the table actually maxes out around 1.5 million instead of the 184 million I was originally worried about. It turns out there are only 2.6 million deals with 2.6 million draws each, for a new combined total of "only" 6.7 trillion outcomes to evaluate.

Still too big -- I couldn't stop there. Well, for a while I did, but then I had one of those lightbulb-over-the-head experiences and I rejoined the hunt.

MUSICAL SUITS

The suits don't matter! Okay, for flushes the suits of the cards do matter a little, but for the most part suits are not nearly as important as the ranks. Since none of the payouts require a particular suit (ie royal flush spades pays the same as diamonds), it turns out you can take advantage of this.

Simply put, whether you are dealt 3H-6H-4H-8S-7C or 3C-6C-4C-8D-7H, the math that dictates which cards to keep remains the same. That's because if the suits are rotated around like musical chairs (all hearts turn to clubs, all clubs turn to diamonds, and so on) and the ranks stay put, the outcome distributions remain the same.

For example, in hands with three different suits represented in a 3:1:1 ratio, this can result in twelve identical results. For those hands I was able to do the computation once and then substitute those results the other 11 times. This reduces the number of initial deals to consider down to 331,682 from 2.6 million. Not bad.

Then, instead of brute-force counting the number of this or that payout among all the draw outcomes, I came up with efficient algorithms for determining the exact probability of each payout, discard by discard. Strict tests of the algorithms were established, spot-checking them for accuracy against the slower "brute-force" counts.

This turned out to be a great application of the probability theory course I took in college, but ultimately was time I could have better spent at the beach. As I fine-tuned my algorithms with the aid of a code profiling tool (let me just say that Borland's OptimizeIt Suite kicks major butt) and a lot of sneaky shortcuts (like using threads to divide the work between the two CPUs), the total running time to payout calculation was reduced to about five minutes. While this was a huge improvement over the brute force counts (which took several days), and way better than the original estimate of several years, I still felt it was unacceptable.

On the plus side, while still too slow for analyzing the entire data set, the algorithms are lightning-fast at picking the best discard for individual hands. This came in handy when I put together my Video Poker Trainer. The "draw" button lights up when you have chosen the best discard. The trainer does its analysis using my algorithm-driven Discard Wizard.

REDUNDANCY HUNT

Still convinced that the payout calculator could be faster, it occurred to me that I could pre-calculate much of the data. For sufficiently hairy computations such as these, it's a lot quicker to read a large number or result set from a file/database than it is to actually compute it. I had thought of this early on but until now the amount of data was way too large for even my 70gig drive.

Now that I had eliminated a lot of the redundancy, the dataset of outcomes had finally been reduced to a manageable size. Roughly 300 megs of data per game were produced, representing the probability ratios of all payouts among all 32 discards in all 331,682 distinct deals. For the first time I could run comparisons on the dataset as a whole, instead of considering just one part at a time in the relatively small space of active memory.

I combed the database of outcomes for any remaining redundancy. My analysis revealed that there are only about 127,000 unique discard "fingerprints" (payout probability counts across all 32 discards) for Jacks or Better and just over 100,000 for Deuces Wild. As before, these were involved computations wherein the data could now be read in and processed just once, and then the results could be substituted when it came time to do that calculation for any matching hands.

So, the number of significant deals was cut again - this time by a factor of three. A table of all significant deal fingerprints was saved to a file for each game. Each fingerprint has around 350 payout counts (32 discards, 10-12 payout counts each). Once compressed, each game produced a file about 11 megs in size, which I call a strategy file.

When the final program gets fed a new paytable, the appropriate game strategy file is processed to determine perfect play in all scenarios. As the strategy unfolds, the new total payout is broken out and calculated.

New running time (once I optimized all those slow Strings out of the Java I/O code): seven seconds. This I can live with.

Of course, total payout measured in EV is only one factor in determining how you'll do in the casino. Variance and bankroll can play a big part too... (more to come).


Here are the 12 ways to mix the suits in a five card hand when three suits are present in a 3:1:1 ratio (note that order doesn't matter but relative suit ratio does):

SSSHD SSSHC SSSDC
HHHSD HHHSC HHHDC
CCCHS CCCHD CCCSD
DDDHC DDDHS DDDSC

For five-card hands there are only six suit ratio profiles:

    AAAAA, AAAAB, AAABB, AAABC, AABBC, and AABCD.

For a particular profile, the formula for total suit substitutions is the permutations P(4,x) (where x is the number of different suits in the profile) adjusted for the number of suits that are represented in the same ratio (since within each band the order of assignment doesn't matter).

For example, in the AABCD case (2:1:1:1) you must divide P(4,4) (ways to assign four suits to four letters, 24) by P(3,3) (redundant ways to assign the last three suits, 6) for a final redundancy factor of 24 / 6 = 4.

A breakdown of the number of calculations saved:
suit ratio profile total hands redundancy factor reduced total
AAAAA 5,148 4x 1,287
AAAAB 111,540 12x 9,295
AAABB 267,696 12x 22,308
AAABC 580,008 12x 48,334
AABBC 949,104 12x 79,092
AABCD 685,404 4x 171,366
2,598,960 7.8x 331,682


All Articles:
  • Java meets Flash (server/client)
  • Freedom of Position (dhtml)
  • Freedom of Position II: Date Picker (dhtml)
  • Video Poker Obsession (optimization)
  • Tag, You're It! Chunk, the All-Purpose Template Engine (java)
  • Look, Ma! Real Scroll Bars! (flash 5)
  • Perl, curl, and HTTPS Post for XML


  • All content © 2002 Tom J. McClure. Some Rights Reserved.