In this short series of three blog posts we show how easy it can be to take an everyday activity and analyse it using Python, gaining insights that might illuminate or in some cases even surprise...
Anyone who has ever played games against young children knows that they absolutely must go first, and my daughter Eva is no exception. However, as someone with a PhD applied to poker and being a self-confessed chess addict I know only too well that the order of play in games is a vital factor in one's probability of success.
During the recent half-term school holidays I got a chance to practice my die-rolling skills with many games of snakes and ladders. The board we used is shown below:
If there is anyone reading this who doesn't know the rules of Snakes and Ladders (what planet are you from?) then they are as follows: two players take turns in rolling a standard six-sided die and, starting from zero (i.e. off the board) they race to reach 100 or more. If a player lands on a square occupied by the base of a ladder they climb up to the top of it, whilst if they land on the head of a snake they descend to the base of its tail.
This game is very simple to simulate and analyse, and in this short series of blog posts we will use Python to do this. Before we get to some code though, let's describe in words what we have to do.
Each player will be represented by an integer variable initialised to zero as their starting "square". They then roll a die, for which we need to generate a number from one to six inclusive with equal probability. We will then add this to their current square value as their move along the board. Before completing their turn we must finally check to see whether they have landed on the base of a ladder or head of a snake, and if they have move them accordingly.
Play continues sequentially in this way until one of the players reaches 100 or higher. That person is designated the winner and the game ends.
Now that we have set out all the steps required it is relatively easy to create a program to model the game:
import numpy as np
import sys
def snakes_and_ladders(x):
dict_sal = {21:3, 24:7, 35:9, 50:11, 53:15, 60:23,
75:44, 89:48, 93:25, 97:65, 99:58,
4:16, 12:33, 18:22, 26:37, 42:61,
49:51, 55:74, 82:98, 85:95, 88:92}
return dict_sal.get(x, x)
def roll_die(x):
x += np.random.randint(1, 7)
x = snakes_and_ladders(x)
return x
p1 = p2 = 0
while True:
p1 = roll_die(p1)
if p1 >= 100:
print "Player 1 wins!"
sys.exit(0)
p2 = roll_die(p2)
if p2 >= 100:
print "Player 2 wins!"
sys.exit(0)
The above code is available here.
As you can see we implement the game as an infinte loop and exit when one of the two counter variables p1 or p2 hits 100 or more.
We use numpy's random integer implementation in the roll_die
function, noting that randint(a, b)
generates a random integer from a to b-1 inclusive.
The snakes and ladders effects are captured in the snakes_and_ladders
function, and here we use a Python dictionary to map from starting to finishing squares. The return value of dict_sal.get(x, x)
means that should the starting square x not be contained within the dictionary we simply pass x back unchanged.
Now we have the bare machinery in place we can move towards some more interesting results. In the next article we will simulate many millions of games and find out just how much of an advantage my daughter gets by insisting on being first to act.