In this page we describe a number of slot games and show how they are implemented inside SLOT-IDE.
You should check this page every now and then because we will keep adding games. For each game comes a general description of the most relevant rules and a detailed explanation of the code and the corresponding game script. Finally we also present the PAR SHEET and simulation report generated by SLOT-IDE.

Some of the games can be very technical and require little background in math, some require writing code, what's important is that by understanding the content of this page you will learn how to represent even the hardest rules of a slot game within SLOT-IDE. Furthermore the material presented can be a precious source of ideas for designing new games, and the relevant code from the game scripts can simply be reused by SLOT-IDE users via cut & paste.

Games so far:

Generating the mathematics of a 5x3 slot with a freespins bonus and a pick a prize bonus

This section shows how to generate the mathematics of a

5x3 slot with expanding wild that sticks on the reels during the bonus

This is the first example game showing how it is possible to implement complex bonuses within SLOT-IDE. The game features a wild symbol that expands on the three central reels only and a scatter that triggers the free spins bonus. While in the bonus the wild symbol after expanding will stick on the reel, covering all symbols in view for the subsequent spins.
Therefore the bonus starts with no reel covered by the wild and then evolves accross eight possible states corresponding to the wild being or not being stuck to any of the three central reels.
Before you go on reading, be aware that to fully understand how the game is implemented some background in math and probability is needed, furthermore the implementation requires writing and understaing a bit of javascript code. Besides being very technical, what's good about this example game is that it proves how SLOT-IDE can also by helpful to more experienced game designers, as it can act as a powerful framework to base the game design on. If you are unfamiliar with code, probability and math, you'll probably find the other example games more interesting and accessible than this one.

The game script shown here below was implemented purposely to pay too much for a real slot game, since this is a demonstration game only.
The bonus can be implemented by defining three properties: enterState to set the initial state when the bonus is triggered, stateProvider to define the eight possible bonus states and stateTransitions to control the evolution from state to state after each spin.

What follows is a brief explanation of the relevant code.

The first part should be straightforward: we simply define symbols, reel strips and paytable. The only thing to notice here is that we use the expand property to specify that the wild expands on the three central reels.
Next part is the interesting one. Each of the eight bonus states is labeled with a name, the initial state is called NoWilds (since there is no wild stuck on any reel when the bonus is triggered) and it is set as the value for the enterState property.
Now we need to provide a function, as the value of stateProvider property, that is in charge of creating the needed states, starting from NoWilds state. In the function body, we first create an object called wildReels that contains for each state the reels where the wild symbol is stuck. Then we create the state object for the needed state by first cloning the game and then modifying the reels to represent the sticky wilds: from a theoretical point of view, the wild symbol expanded and stuck on a reel is equivalent to replacing the reel with one of the same length where all symbols are wilds. Finally, in the state object we are creating, we also need to remove each reel where the wild has already expanded from the value of the expand property of the wild symbols. For instance, when the object for state WildOn23 is needed, stateProvider will be called and the state property of the context object set as WildOn23. The function will then return a state where the second and third reels contain wild symbols only.

Besides defining the initial state and providing a function that is in charge of creating the needed state objects we need to tell the engine where each state can go after playing a spin. We do so by defining a function for the stateTransitions property that returns the list of possible destination states and transition probabilities for the current state (for instance when playing a spin in WildOn34 the state can only evolve to WildOn34 itself and to WildOn234 depending on whether or not a wild is obtained on the second reel).
For each possible destination state we need to return the transition probability, that is the probability of evolving from the current state to the destination state. The probability can be obtained from the chance of getting or not getting a wild on the needed reels, but what complicates the situation is that for this game a state transition can occur while the bonus is getting (re)triggered or not (it can be possible to get a wild on some reels while having three or more scatters in view and thus triggering the bonus).
Therefore we need to compute the conditional probability of the state transition given that the bonus is being triggered or not as this influences the chances of getting wilds. We can rely on the triggering property of the context to know if at the time our function is called the bonus is being triggered. Also, rather than computing the conditional probability of the state transition given the bonus trigger, we can compute the conditional probability of triggering the bonus given the state transition by using Bayes' theorem which connects the conditional probabilities of two dependent events: P(A|B) = P(B|A)(P(A)/P(B)), where in our case event A is the state transition and event B is the triggering of the bonus. Therefore our transition probability can be expressed in terms of:
  • probability of triggering the bonus given the state transition (P(B|A) in the relation above and probTrigGivenTx in the code)
  • probability of the state transition regardless of the bonus triggering (P(A) in the relation above and probTx in the code)
  • probability of the bonus triggering regardless the state transition (P(B) in the relation above and probTrig in the code)
Now to the code: we read the name of the current state from the context and then loop through all possible eight states. We create an array that will contain the destination state/probability pairs for the current state, and we store it in a hashmap (this.bonus.txMap) that will contain the transitions for all eight states to all eight states. This way if the list of transitions for the current state has already been computed then it will be found in the hashmap and will be returned by the function without computing it again, this is crucial for performance. Of course we need to distinguish for each state the list of transitions when triggering the bonus from the transitions when the bonus isn't being triggered, we easily do so by using a key (txName) that depends on both the name of the current state and the triggering flag.
The rest of the code just calculates the three aforementioned probabilities by taking advantage of SLOT-IDE APIs: the first probability is obtained by invoking getConditionalScatterProb. This is an internal function of the engine that returns the conditional probability of hitting scatter combinations (in this case we need three or more scatters) given that the wild symbol is stuck on one or more reels (either because new wilds were obtained during the spin or because they were already stuck on the reels): the last two arguments passed to the function are in fact the three central reels where wild symbols can stick and the reels with the expanding wild stuck after the spin (obtained from the destination state). The second probability can simply be computed from the chance of getting or not getting a wild (number of wild on the reel divided by reel length) on the three central reels, depending on the state transition. Finally the probability of triggering the bonus is obtained by reading the computed scatter hits from the object of the current state and dividing them by the game cycle.
What was explained covers the case where it is assumed that the bonus is being triggered (triggering flag set to true), however the case when the bonus is not being triggered is simply obtained by considering the omplement of each of the two probabilities that involve the bonus trigger (this is done in the code by the ternary operator whose condition is the value of the triggering flag).


Now the results.
  • Running theoretical calculations took six seconds and produced the game PAR-SHEET (click to download)
  • Running a simulation of one million games with five selected paylines produced the simulation report (click to download)
new Game({
    symbols: [
        {name: 'Wild', isWild:true, expand:[1,2,3]}, 
        'Picture1', 'Picture2', 'Picture3', 'Nine', 'Ten',
        'Jack', 'Queen', 'King', 'Ace', 
        {name: 'Scatter', wildReplace: false} 
    ],    
    reelFreqs: {                                
        'Wild':     [1,1,1,1,1],
        'Picture1': [2,3,2,2,3],
        'Picture2': [2,3,3,3,4],
        'Picture3': [3,3,3,2,3],
        'Nine':     [3,3,3,4,4],
        'Ten':      [4,2,3,3,3],
        'Jack':     [4,3,4,3,4],
        'Queen':    [3,4,4,4,3],
        'King':     [4,4,4,5,3],
        'Ace':      [4,5,4,4,3],
        'Scatter':  [2,1,1,1,1]               
    },
    paytable: [        
        {on: {occurs:[2,3,4,5], of:'Wild',   mode:'line'}, pay: [5,50,500,5000]},
        {on: {occurs:[2,3,4,5], of:'Picture1',   mode:'line'}, pay: [3,40,200,1000]},
        {on: {occurs:[2,3,4,5], of:'Picture2',   mode:'line'}, pay: [2,30,150,500]},
        {on: {occurs:[2,3,4,5], of:'Picture3',   mode:'line'}, pay: [2,25,100,300]},
        {on: {occurs:[3,4,5], of:'Nine',   mode:'line'}, pay: [20,75,200]},
        {on: {occurs:[3,4,5], of:'Ten',   mode:'line'}, pay: [20,75,200]},
        {on: {occurs:[3,4,5], of:'Jack',   mode:'line'}, pay: [15,50,100]},
        {on: {occurs:[3,4,5], of:'Queen',   mode:'line'}, pay: [15,50,100]},
        {on: {occurs:[3,4,5], of:'King',   mode:'line'}, pay: [10,25,50]},
        {on: {occurs:[3,4,5], of:'Ace',   mode:'line'}, pay: [10,25,50]},
        {on: {occurs:[3,4,5], of:'Scatter',   mode:'scatter'}, pay: [5,25,100], trigger: 'freespins'}
    ],
    bonus: {
      freespins: {
        spins: 5,
        enterState: 'NoWilds',            
        stateProvider: function()
        {   
          this.bonus.wildReels = {
		   'NoWilds':[],     'WildsOn2':[1],   'WildOn3':[2],    'WildOn4':[3], 
		   'WildOn23':[1,2], 'WildOn24':[1,3], 'WildOn34':[2,3], 'WildOn234':[1,2,3]
          };

          var state = this.game.clone();   
          for (var i=0; i < this.bonus.wildReels[this.state].length; i++)
          {
            var reel = this.bonus.wildReels[this.state][i];
            for (var k in state.reelFreqs)
            {
                if (k != 'Wild'){
                    state.reelFreqs['Wild'][reel] += state.reelFreqs[k][reel];
                    state.reelFreqs[k][reel] = 0;
                }            
            }            
            state.symbols[0].expand.splice(state.symbols[0].expand.indexOf(reel), 1);
          }
          return state;                
        },
        stateTransitions: function()
        {

          if (!this.bonus.txMap) {
            this.bonus.txMap = new Object();                    
          }

          var txName = this.state + (this.triggering ? "trig" : "!trig");
			
          if (!this.bonus.txMap[txName])
          {
            this.bonus.txMap[txName] = new Array();
				
            var state = this.states[this.state];
				  
				  
            for (var dstState in this.bonus.wildReels)
            {
              var probTrigGivenTx = 
			    state.getConditionalScatterProb('Scatter', [3,4,5], 'Wild', [1,2,3], this.bonus.wildReels[dstState]);
              
              var probTrig = (state.hits['scatter']['Scatter'][3] + 
                              state.hits['scatter']['Scatter'][4] + 
                              state.hits['scatter']['Scatter'][5])/state.cycle;                                            
										
              //ok so now we need the tx prob...
              var probTx = 1.0;
				
              for (var reel=1; reel<=3; reel++)
              {
                if (this.bonus.wildReels[this.state].contains(reel) && !this.bonus.wildReels[dstState].contains(reel))
                {
                  probTx = 0.0; break;
                } else if (!this.bonus.wildReels[this.state].contains(reel) && !this.bonus.wildReels[dstState].contains(reel))
                {
                  //we don't get a wild..
                  probTx *= (state.reels[reel].length - state.reelFreqs['Wild'][reel]) / state.reels[reel].length;   
                } else if (!this.bonus.wildReels[this.state].contains(reel) && this.bonus.wildReels[dstState].contains(reel))
                {
                  //we do get a wild..
                  probTx *= state.reelFreqs['Wild'][reel] / state.reels[reel].length;   
                }    
              }                                                
				
              var prob = this.triggering ? probTrigGivenTx * (probTx/probTrig) : (1.0-probTrigGivenTx) * (probTx/(1.0-probTrig));
              if (prob > 0.0) this.bonus.txMap[txName].push({prob: prob, state: dstState});
            }
          }
			
          return this.bonus.txMap[txName];
        }
      }
  }
});

5x3 slot with a spin-as-long-as-you-win bonus

This game features a freespin bonus where you keep playing as long as you have a win: when entering the bonus the player spins one time and wins another spin if he/she has obtained any win and so on, indefinitely.
To make the game more fun, when no win is obtained in the free spin and the bonus terminates, an extra prize is awared to the player.
Because of wins on the paylines, the chance of obtaining any win depends on the number of paylines selected and so does the corresponding bonus contribution to the theoretical payback percentage of the game. It is instead preferable to design a game whose theoretical payback does not depend on the selected paylines, also we would like to increase the chance of obtaining any win so that the game is fun even when few paylines are selected. We can satisfy both requirements by changing the way symbols pay during the bonus: in the bonus all symbols pay when they are scattered on consecutive reels starting from the left-most reel.

Now that the relevant rules have been explained it's time to look at the game script. We want SLOT-IDE to generate the game and we want the target RTP to be 87.22%. We also set an additional costraint to control the generation of reel strips: the scatter contribution to the payback must be at least 5%. By setting this constraint scatter wins cannot be too infrequent, and as a consequence we will have a good hit frequency for the bonus.

To define the bonus, we set spins property to one, because anytime the bonus is entered or anytime a win is obtained during the bonus an extra spin is won. Next we need to define the game state for the bonus. While in the bonus a modified version of the game is played, with each symbol paying as a scatter on consecutive reels. We name the state bonusReels and create it in the stateProvider function which will be invoked after the bonus is entered. In the function body after cloning the base game we loop through the paytable and set the pay mode of each symbol to consecutive (scattered symbols on consecutive reels). Finally we remove the trigger from the paytable, that's because during the bonus the bonus itself is not retriggered by scatter wins.
Now we need to define a custom trigger to represent the situation when the bonus is retriggered after a winning spin. The custom trigger doesn't have to affect the initial triggering of the bonus and so if the bonus is being entered (context flag playing set to false) we return a zero probability. At the bottom of the game script, after defining our game, we have created a function called getAnyWinProbability that returns the probability of obtaining any win during the bonus. In our custom trigger we invoke this function and return the corresponding probability (because of performance we cache the value of the probability so that it is computed only once).
The bonus is now defined and the only reamaining rule that needs to be covered is the additional prize that is awarded when no win is obtained and the bonus terminates. This is done by connecting a function to the additionalPrize property. This function will be invoked after each spin is played in the bonus and we can use it to return our prizes. We have chosen to award 10x the total bet 10% of the time, 5x the total bet 30% of the time and twice the total bet the remaining 60% of the time. We need to be careful to award the prize only when the bonus isn't being retriggered (no win was obtained). This is easily done with a ternary operator that reads the value of the triggering context property.

Regarding the getAnyWinProbability function, it should not be hard to understand its implementation. It reads the paytable and reel strips of the bonus state to compute the probability of having any win in the bonus. The probability is computed by looking at the first three reels only, in fact when the game window contains the needed number of scatters to achieve a win (two or three depending on the symbol) on the three left-most reels then it contributes for L4xL5 winning windows (where L4 and L5 are the length of the fourth and fifth reels respectively), without needing to look at the remaining reels. To analize the 3x3 window and determine if it does contain a win, three cases need to be considered: either the first two reels contain a wild or one of the picture symbols, or the first three reels all contain at least one time the symbol Scatter or the first three reels contain at least one time the remaining symbols or wild.
Finally to compute the probability the number of 3x3 winning windows is multiplied by L4xL5 and divided by the cycle of the bonus state.


Now the results.
  • Running theoretical calculations produced the game PAR-SHEET (click to download)
  • Running a simulation of one million games with ten selected paylines produced the simulation report (click to download)
new Game({
    symbols: [
        {name: 'Wild', isWild:true}, 
        'Picture1', 'Picture2', 'Picture3', 'Nine', 'Ten',
        'Jack', 'Queen', 'King', 'Ace', 
        {name: 'Scatter', wildReplace: false}
    ],
    rtp: {total: 0.8722, scatterPaysMin: 0.05},    
    paytable: [        
        {on: {occurs:[2,3,4,5], of:'Wild',   mode:'line'}, pay: [5,50,500,5000]},
        {on: {occurs:[2,3,4,5], of:'Picture1',   mode:'line'}, pay: [3,40,200,1000]},
        {on: {occurs:[2,3,4,5], of:'Picture2',   mode:'line'}, pay: [2,30,150,500]},
        {on: {occurs:[2,3,4,5], of:'Picture3',   mode:'line'}, pay: [2,25,100,300]},
        {on: {occurs:[3,4,5], of:'Nine',   mode:'line'}, pay: [20,75,200]},
        {on: {occurs:[3,4,5], of:'Ten',   mode:'line'}, pay: [20,75,200]},
        {on: {occurs:[3,4,5], of:'Jack',   mode:'line'}, pay: [15,50,100]},
        {on: {occurs:[3,4,5], of:'Queen',   mode:'line'}, pay: [15,50,100]},
        {on: {occurs:[3,4,5], of:'King',   mode:'line'}, pay: [10,25,50]},
        {on: {occurs:[3,4,5], of:'Ace',   mode:'line'}, pay: [10,25,50]},
        {on: {occurs:[3,4,5], of:'Scatter',   mode:'scatter'}, pay: [5,25,100], trigger: 'freespins'}
    ],
    bonus: {
        freespins: {
            spins: 1, 
            enterState: 'bonusReels',
            stateProvider: function()
            {
                var s = this.game.clone();
                for (var i=0; i < s.paytable.length; i++)
                {
                    s.paytable[i].on.mode = 'consecutive';     
                    delete s.paytable[i].trigger;
                }
                
                return s;
            },
            trigger: function()
            {       
                if (!this.playing) return 0.0;                
                if (!this.bonus.pAnyWin) this.bonus.pAnyWin = getAnyWinProbability(this.states[this.state]);   
                return this.bonus.pAnyWin;                    
            },
            additionalPrize: function()
            {
                return this.triggering ? 0.0 : [
                    {prize: 10, prob: 0.1},
                    {prize: 5, prob: 0.3},
                    {prize: 2, prob: 0.6}
                ];  
            }
        }  
    }
});

function getAnyWinProbability(state)
{
    var winningWindows = 0;
    
    for (var i=0; i < state.reels[0].length; i++)
    {
        for (var j=0;j < state.reels[1].length; j++)
        {
            for (var k=0; k < state.reels[2].length; k++) 
            {
                var syms = [{},{},{}];
                for (var row=0; row<3; row++) 
                {
                     var sym0 = state.reels[0][(i+row) % state.reels[0].length]; 
                     var sym1 = state.reels[1][(j+row) % state.reels[1].length]; 
                     var sym2 = state.reels[2][(k+row) % state.reels[2].length]; 
                     if (!syms[0][sym0]) syms[0][sym0] = 0; syms[0][sym0]++;
                     if (!syms[1][sym1]) syms[1][sym1] = 0; syms[1][sym1]++;
                     if (!syms[2][sym2]) syms[2][sym2] = 0; syms[2][sym2]++;
                }
                
                var anyWin = false;
                
                for (var p=0; p < state.paytable.length; p++)
                {                                    
                    var s = state.paytable[p].on.of;
                    
                    if (s=='Wild'||s=='Picture1'||s=='Picture2'||s=='Picture3')
                    {
                        //require 2 reels
                        if ((syms[0]['Wild']||syms[0][s])&&(syms[1]['Wild']||syms[1][s]))  
                        {
                            anyWin = true; break;
                        }
                    } else if (s!='Scatter')
                    {
                        //require 3 reels   
                        if ((syms[0]['Wild']||syms[0][s])&&(syms[1]['Wild']||syms[1][s])&&(syms[2]['Wild']||syms[2][s]))     
                        {
                            anyWin = true; break;    
                        }
                    } else {
                        //require scatters 3 reels
                        if (syms[0][s]&&syms[1][s]&&syms[2][s])     
                        {
                            anyWin = true; break;    
                        }   
                    }
                }
                if (anyWin) winningWindows++;
            }
        }
    }
    
    return (winningWindows*state.reels[3].length*state.reels[4].length) / state.cycle;        
}