Chromatic Polynomials 15 – Slot Games
- 1 day ago
- 10 min read
Slot games can be evaluated using CPs. There is a great tutorial on slot games available here: Slots 101. That article does a far better job of describing slot games than I can do, so I’m going to assume that you’ve either read it or are familiar with slot games.
Defining a Slot Game
Briefly, a paytable file defines the number of stops on each reel, the symbol associated with each stop, and a range of random number values that select that stop as the final outcome (I’m skipping a lot of steps here, to try to get to the point).
Here is a sample paytable with 3 reels, each with 22 stops. For each reel, the slot machine will draw a Random Number (RN) from 0 to 63 and use it to determine which stop that reel lands on. For example, if the RN for Reel 0 is 6, it falls in stop 0’s range (0-3), so reel 0 will show the Blue7 symbol.
| Reel 0 (RNs) | Reel 1 (RNs) | Reel 2 (RNs) |
Stop 0 | Blank (0-3) | Blank (0-3) | Blank (0-3) |
Stop 1 | Blue7 (4-7) | Blue7 (4) | White7 (4-6) |
Stop 2 | Blank (8-9) | Blank (5-7) | Blank (7-8) |
Stop 3 | WhiteBar (10-13) | RedBar (8-11) | RedBar (9-12) |
Stop 4 | Blank (14) | Blank (12) | Blank (13) |
Stop 5 | BlueBar (15-18) | BlueBar (13-16) | WhiteBar (14-16) |
Stop 6 | Blank (19-22) | Blank(17-20) | Blank (17-20) |
Stop 7 | Red7 (23-24) | White7 (21-22) | Blue7 (21-22) |
Stop 8 | Blank (25-28) | Blank (23-26) | Blank (23-26) |
Stop 9 | BlueBar (29-32) | BlueBar (27-30) | WhiteBar (27-29) |
Stop 10 | Blank (33) | Blank (31) | Blank (30-31) |
Stop 11 | WhiteBar (34-37) | RedBar (32-35) | RedBar (32-35) |
Stop 12 | Blank (38) | Blank (36-37) | Blank (36-37) |
Stop 13 | Blue7 (39-42) | Blue7 (38) | White7 (38-40) |
Stop 14 | Blank (43) | Blank (39-40) | Blank (41-42) |
Stop 15 | WhiteBar (44-47) | RedBar (41-44) | RedBar (43-46) |
Stop 16 | Blank (48) | Blank (45) | Blank (47) |
Stop 17 | BlueBar (49-52) | BlueBar (46-49) | WhiteBar (48-50) |
Stop 18 | Blank (53) | Blank (50-51) | Blank (51-53) |
Stop 19 | White7 (54-55) | Red7 (52-55) | Red7 (54-55) |
Stop 20 | Blank (56-59) | Blank (56-59) | Blank (56-59) |
Stop 21 | RedBar (60-63) | WhiteBar (60-63) | BlueBar (60-63) |
We can directly translate this to CP expressions for each reel. We will have three vertices RN0, RN1, and RN2. We can define one expression for each stop on each reel. For example, the conditions to select stop 1 on Reel 0 will be R0S1.
R0S0 = δRN0{0-3} | R1S0 = δRN1{0-3} | R1S0 = δRN2{0-3} |
R0S1 = δRN0{4-7} | R1S1 = δRN1{4} | R1S1 = δRN2{4-6} |
R0S2 = δRN0{8-9} | R1S2 = δRN1{5-7} | R1S2 = δRN2{7-8} |
R0S3 = δRN0{10-13} | R1S3 = δRN1{8-11} | R1S3 = δRN2{9-12} |
R0S4 = δRN0{14} | R1S4 = δRN1{12} | R1S4 = δRN2{13} |
R0S5 = δRN0{15-18} | R1S5 = δRN1{13-16} | R1S5 = δRN2{14-16} |
R0S6 = δRN0{19-22} | R1S6 = δRN1{17-20} | R1S6 = δRN2{17-20} |
R0S7 = δRN0{23-24} | R1S7 = δRN1{21-22} | R1S7 = δRN2{21-22} |
R0S8 = δRN0{25-28} | R1S8 = δRN1{23-26} | R1S8 = δRN2{23-26} |
R0S9 = δRN0{29-32} | R1S9 = δRN1{27-30} | R1S9 = δRN2{27-29} |
R0S10 = δRN0{33} | R1S10 = δRN1{31} | R1S10 = δRN2{30-31} |
R0S11 = δRN0{34-37} | R1S11 = δRN1{32-35} | R1S11 = δRN2{32-35} |
R0S12 = δRN0{38} | R1S12 = δRN1{36-37} | R1S12 = δRN2{36-37} |
R0S13 = δRN0{39-42} | R1S13 = δRN1{38} | R1S13 = δRN2{38-40} |
R0S14 = δRN0{43} | R1S14 = δRN1{39-40} | R1S14 = δRN2{41-42} |
R0S15 = δRN0{44-47} | R1S15 = δRN1{41-44} | R1S15 = δRN2{43-46} |
R0S16 = δRN0{48} | R1S16 = δRN1{46} | R1S16 = δRN2{47} |
R0S17 = δRN0{49-52} | R1S17 = δRN1{46-49} | R1S17 = δRN2{48-50} |
R0S18 = δRN0{53} | R1S18 = δRN1{50-51} | R1S18 = δRN2{51-53} |
R0S19 = δRN0{54-55} | R1S19 = δRN1{52-55} | R1S19 = δRN2{54-55} |
R0S20 = δRN0{56-59} | R1S20 = δRN1{56-59} | R1S20 = δRN2{56=59} |
R0S21 = δRN0{60-63} | R1S21 = δRN1{60-63} | R1S21 = δRN2{60-63} |
Now we can combine these to define the conditions required to hit each symbol. It’s easiest to write the terms in a say-what-you-mean way. For instance, Reel 0 will show a Blue Bar if it stops on Stops 5, 9, or 17:
R0BlueBar = R0S5 + R0S9 + R0S17
The CP engine will automatically reduce it to a single term:
R0BlueBar = (R0S5 + R0S9 + R0S17)
R0BlueBar = (δRN0{15-18} + δRN0{29-32} + δRN0{49-52})
R0BlueBar = δRN0{15-18,29-32,49-52}
This tells us that Reel 0 will show a Blue Bar if RN 0 is 15, 16, 17, 18, 29, 30, 31, 32, 49, 50, 51, or 52.
Doing this for all reels and symbols will give us the terms:
R0Blank = (R0S0 + R0S2 + R0S4 + R0S6 + R0S8 + R0S10 + R0S12 + R0S14 + R0S16 + R0S18 + R0S20) | R1Blank = (R1S0 + R1S2 + R1S4 + R1S6 + R1S8 + R1S10 + R1S12 + R1S14 + R1S16 + R1S18 + R1S20( | R2Blank = (R2S0 + R2S2 + R2S4 + R2S6 + R2S8 + R2S10 + R2S12 + R2S14 + R2S16 + R2S18 + R2S20) |
R0Red7 = (R0S7) | R1Red7 = (R1S19) | R2Red7 = (R2S19) |
R0White7 = (R0S19) | R1White7 = (R1S7) | R2White7 = (R2S1 + R2S13) |
R0Blue7 = (R0S1 + R0S13) | R1Blue7 = (R1S1 + R1S13) | R2Blue7 = (R2S7) |
R0RedBar = (R0S21) | R1RedBar = (R1S3 + R1S11 + R1S15) | R2RedBar = (R2S3 + R2S11 + R2S15) |
R0WhiteBar = (R0S3 + R0S11 + R0S15) | R1WhiteBar = (R1S21) | R2WhiteBar = (R2S5 + R2S9 + R2S17) |
R0BlueBar = (R0S5 + R0S9 + R0S17) | R1BlueBar = (R1S5 + R1S9 + R1S17) | R2BlueBar = (R2S21) |
The way the symbols like up defines the payout. For this sample game, here are the win categories:
Win Category | Pays |
3 Blanks | 1 |
Any 3 Blue | 2 |
Any 3 White | 2 |
Any 3 Red | 2 |
Any 3 Bars | 5 |
3 Red Bars | 10 |
Any Red, Any White, Any Blue | 20 |
3 White Bars | 25 |
3 Blue Bars | 40 |
Red Bar, White Bar, Blue Bar | 50 |
Any 3 Sevens | 80 |
3 Blue 7s | 150 |
3 White 7s | 200 |
3 Red 7s | 250 |
Red 7, White 7, Blue 7 | 400 |
Those wins are encoded in CPs as the product of the conditions required for each reel:
Blanks = R0Blank R1Blank R2Blank
AnyBlue = (R0BlueBar + R0Blue7) (R1BlueBar + R1Blue7) (R2BlueBar + R2Blue7)
AnyWhite = (R0WhiteBar + R0White7) (R1WhiteBar + R1White7) (R2WhiteBar + R2White7)
AnyRed = (R0RedBar + R0Red7) (R1RedBar + R1Red7) (R2RedBar + R2Red7)
AnyBars = (R0RedBar + R0WhiteBar + R0BlueBar) (R1RedBar + R1WhiteBar + R1BlueBar) (R2RedBar + R2WhiteBar + R2BlueBar)
RedBars = R0RedBar R1RedBar R2RedBar
RedWhiteBlue = (R0RedBar + R0Red7) (R1WhiteBar + R1White7) (R2BlueBar + R2Blue7)
WhiteBars = R0WhiteBar R1WhiteBar R2WhiteBar
BlueBars = R0BlueBar R1BlueBar R2BlueBar
RedWhiteBlueBars = R0RedBar R1WhiteBar R2BlueBar
AnySevens = (R0Red7 + R0White7 + R0Blue7) (R1Red7 + R1White7 + R1Blue7) (R2Red7 + R2White7 + R2Blue7)
BlueSevens = R0Blue7 R1Blue7 R2Blue7
WhiteSevens = R0White7 R1White7 R2White7
RedSevens = R0Red7 R1Red7 R2Red7
RedWhiteBlue7 = R0Red7 R1White7 R2Blue7
Automatically Eliminating Duplicates
Note that many of these overlap. If you get 3 red sevens, it will match Reds, Sevens, and RedSevens. Slot games typically only pay for the highest win that the outcome matches. So lower pays need to exclude the possibility of also hitting a higher pay:
PayBlanks = Blanks
PayAnyBlue = AnyBlue (1 – BlueBars) (1 – BlueSevens)
You might think that we need to explicitly exclude every higher pay, like this:
PayAnyBlue = AnyBlue (1 – AnyWhite) (1 – AnyRed) (1 – AnyBars) (1 – RedBars) (1 – RedWhiteBlue) (1 – WhiteBars) (1 – BlueBars) (1 – RedWhiteBlueBars) (1 – Sevens) (1 – BlueSevens) (1 – WhiteSevens) (1 – RedSevens) * (1 – RedWhiteBlue7)
We could do that – the answer will still be right. But it’s more work than we need to do. Taking one smaller example:
The intersection of AnyBlue and AnyWhite is 0. We can see that by performing the summation:

So this exclusion isn’t necessary:
PayAnyBlue = AnyBlue * (1 – AnyWhite)
We can continue testing others to see what we actually need to exclude. We will find that AnyBlue only intersects with AnyBars, BlueBars, AnySevens, and BlueSevens. This means we can trim PayAnyBlue to this:
PayAnyBlue = AnyBlue (1 – AnyBars) (1 – BlueBars) (1 – AnySevens) (1 – BlueSevens)
Can you see how we can do better than this?
Intuitively, if an outcome matches the AnyBlue win category and the AnyBars win category, then it must also match the BlueBars win category. Likewise, if it matches AnyBlue and BlueBars, it must also match AnyBars. So we only need to exclude either AnyBars or BlueBars. We can do the same thing with AnySevens and BlueSevens to get this:
PayAnyBlue = AnyBlue (1 – BlueBars) (1 – BlueSevens)
We have two cases that simplify our expressions:
An expression, Ex1, that does not overlap with another expression, Ex2, so there’s no need to exclude Ex2 when evaluating Ex1.
An expression, Ex1, that overlaps with another expression, Ex2, the exact same way it overlaps with a third expression, Ex3. That is Ex1 ∩ Ex2 = Ex1 ∩ Ex3, so either Ex2 or Ex3 needs to be excluded, but not both.
We can detect both of these cases with this test:

We only need to exclude candidate expressions for which this test succeeds.
Applying this to the PayAnyBlue case we do the following:
Set PayAnyBlue = AnyBlue.
See that ∑ PayAnyBlue = ∑ PayAnyBlue * (1 – AnyWhite). This fails the test, so there’s no need to exclude AnyWhite.
See that ∑ PayAnyBlue = ∑ PayAnyBlue * (1 – AnyRed), so there’s no need to exclude AnyRed.
See that ∑ PayAnyBlue > ∑ PayAnyBlue (1 – AnyBars), so excluding AnyBars is necessary. Set PayAnyBlue = AnyBlue (1 – AnyBars).
A few steps later, we will test BlueBars, and see that ∑ AnyBlue (1 – AnyBars) = ∑ AnyBlue (1 – AnyBars) * (1 – BlueBars), so there’s no need to exclude BlueBars.
Continue through the rest of the cases to arrive at PayAnyBlue = AnyBlue (1 – AnyBars) (1 – AnySevens)
This provides us with a process that can be done programmatically, so we don’t have to think through all the cases ourselves, and risk human error. Applying this to all the outcomes will give us a set of CP expressions that will tell us exact number of ways each winning outcome can occur.
Determining Payback Percentage
To determine the average payout for the slot game, we multiply the summation of each win cagetory’s CP by the amount paid for that outcome. We add those up, and divide by the number of possible outcomes.
Multiple Paylines
The example above shows how to evaluate the payouts for one payline. Almost all slot games include multiple paylines, to entice the player to bet more money on each game.
Adding more paylines is trivial. The example game from the Slots 101 tutorial has 5 paylines:

It should be fairly trivial to see that each payline shifts the symbols in some or all of the reels. If Reel 0 shows Stop 7 on payline 1, then it is showing Stop 6 on payline 2. All of the expressions above can be labeled as Payline 1 expressions, and similar expressions can be written for each other payline. We can calculate the payback percentage for each payline as a sum product of the win category expressions and their payouts, divided by the total number of possible RN combinations.
This would be a very tedious task, even for this simple example, but it lets us do something that I haven’t seen many slot calc tools do – a true volatility calculation. We can traverse the cartesian product of all possible payouts on all paylines to determine how they combine to form larger pays.
For example, we can start with Blanks on Payline 1, and see how it overlaps with wins on Payline 2:
∑ PayBlanks_Payline1 * PayBlanks_Payline2 = 0
Since this yields 0, we don’t need to bother adding Paylines 3, 4, or 5.
∑ PayBlanks_Payline1 * PayAnyBlue_Payline2 > 0
This shows us it’s possible to hit the Blanks win category on payline 1 and the AnyBlue win category on payline 2 at the same time. We can continue in a depth-first search style to add payline 3:
∑ PayBlanks_Payline1 PayAnyBlue_Payline2 PayAnyBlue_Payline3 = 0
∑ PayBlanks_Payline1 PayAnyBlue_Payline2 PayAnyWhite_Payline3 = 0
∑ PayBlanks_Payline1 PayAnyBlue_Payline2 PayAnyRed_Payline3 = 0
This isn’t a process I would want to do manually, but it would be very easy to automate. The end result will tell us the exact number of ways to get every unique combination of payouts across multiple paylines and allow us to calculate the true volatility of the game.
Scatter Pays
In addition to paylines, more advanced games have “scatter pays”. These are payouts for N symbols occurring anywhere on the screen. These can be calculated the same way that we calculated the placement of fences in Quoridor – by summing outcomes with and without scatter pay symbols separately.
We would define expressions for the occurrence of scatter pay symbols on each reel:
Reel0Scatters0 = δRN0{RNs that produce no scatter symbols on reel 0}
Reel0Scatters1 = δRN0{RNs that produce 1 scatter symbol on reel 0}
Reel0Scatters2 = δRN0{RNs that produce 2 scatter symbols on reel 0}
. ..
Reel1Scatters0 = δRN1{RNs that produce no scatter symbols on reel 1}
Reel1Scatters1 = δRN1{RNs that produce 1 scatter symbol on reel 1}
Reel1Scatters2 = δRN1{RNs that produce 2 scatter symbols on reel 1}
. ..
Now we can perform summations:
After 1 reel:

I’ll stop at 2 scatters per reel, but this can be extended to any number of scatter symbols that can occur.
For the second reel:

Continuing this process for the remaining reels will give the number of ways that every number of scatter symbols can occur on the screen.
Multiple Stages
Slot games also have multiple stages. For example, a specific win can trigger a bonus game – free spins, a wheel spin, a pick game, etc. In this case, we can define CPs for each bonus stage using as many additional RN variables as each bonus stage needs. Each bonus stage CP can be multiplied by CP for the win category that triggers it, to represent the bonus stage’s contribution to the total payout and volatility.
I realize that this article on using Chromatic Polynomials for slot games uses a lot more generalized handwaving than I’ve done in previous sections. I’ve tried to outline enough so that someone who is more interested in it than me can pursue it further. Having professionally worked on slot calculation tools before, I also expect a very few people to be interested.
One CP solution for all phases
The Counting, Completing, and Checking functions of CPs can all be applied to slot games.
The Counting function sums slot CPs over all possible RNs to yield the total number of ways each win can occur. These counts, multiplied by the payout, can be used to calculate the payback percentage and volatility. This can be used in all stages of paytable development.
The Completing function takes explicit values for some variables and sums the rest. When some reels have stopped and other are still spinning, this can be used to tell if certain wins are still possible. Many slot games use a check similar to this to determine when to play “anticipation” sounds or animations - to draw your attention to a big win in progress and build up your hopes that the next reel that stops will build upon or complete the win.
The Checking function takes explicit values for all variables and can be used to determine what win to pay, if any, for a specific slot outcome. This can be used to evaluate slot game results after the reels finish spinning.
One of the biggest challenges that I’ve seen in slot game development is making sure that the game logic faithfully follows the wins and payouts dictated by the paytable. Where I worked, each game required hundreds of hours of testing to ensure that all wins pay what they’re supposed to pay, and that no payouts are issued for losing outcomes. With a CP-based paytable, one could define one set of CPs in the paytable and the CP engine would use them in all stages of development and play - calculating the payback percentage, determining what sounds and graphics to play during the game, and evaluating the wins at the end of the game. By CPs at every stage, consistency is 100% guaranteed.
Previous Post:
Next Post:





Comments