Sunteți pe pagina 1din 7

Matthew Burger

Professor Buckingham
MATH 3096 – Math of Games and Puzzles
30 November 2017

Project 3: Treatment of Snakes and Ladders as a Markov Chain

Snakes and Ladders is a game in which one or more players attempt to navigate an N-by-
N grid of squares, starting a location off the board (i.e. position 0). Player movement is dictated
by the roll of a six-sided die, resulting in the movement of the player between one and six spaces
ahead. However, there are special spaces that will advance or hinder the progress of the player.
Landing on the lower portion of a ladder will advance the player to the top of the ladder, whereas
landing on the top portion of a snake will force the player to recede to the bottom portion of the
snake. The game continues in this manner until one of the players reaches the N2 location.

The probability of moving from one location to another is constant. Furthermore, the
current state of a player is independent of previous states. Due to these properties, an N-by-N
Snakes and Ladders board may be modeled as a Markov chain with N2 + 1 states and an N2 + 1-
by-N2 + 1 transition matrix. Furthermore, the game may be simulated using a simple Monte
Carlo algorithm, allowing one to determine, experimentally, the average length of a game of
snakes and ladders. Also, one may determine the number of turns by which an arbitrarily large
percentage of games will complete; for the sake of this endeavor, this threshold will be set at
96%.

Due to the popularity of the general Snakes and Ladders game, many companies have
commercialized it over the centuries since its invention. One particularly popular example is the
Milton Bradley variant known as Chutes and Ladders. This variant consists of a 10-by-10
gridded playing board with ten chutes (snakes) and nine ladders. This standardized variant of
Snakes and Ladders will be modeled as a Markov chain and simulated in Python via Monte
Carlo analysis. For the sake of simplicity, the game will be considered complete if the player
lands on or surpasses the final square; in other words, rolling a value greater than that needed to
reach square 100 will not result in staying in one’s current position.

In the standard Chutes and Ladders layout, the chutes and ladders are located as detailed
in Table 1.

Type Start End


Chute 98 78
Chute 95 75
Chute 93 73
Chute 87 24
Chute 64 60
Chute 62 19
Chute 56 53
Chute 49 11
Chute 47 26
Chute 16 6
Ladder 1 38
Ladder 4 14
Ladder 9 31
Ladder 21 42
Ladder 28 84
Ladder 36 44
Ladder 51 67
Ladder 71 91
Ladder 80 100
Table 1: Chutes and Ladders Locations

Let P be the current arbitrary position of a player. If there were no chutes or ladders
1
present on the board, the probability of moving from P to P + 1 through P + 6.is 6 for each
transition; the probability for any other transition is zero. An excerpt of the transition matrix for
this configuration is created in LaTeX and given in Figure 1.

Figure 1: Transition Matrix Excerpt without Chutes or Ladders


However, the presence of chutes and ladders dramatically alters the landscape of the transition
matrix. Using the standard layout specified in Table 1, the transition matrix excerpt is transformed from
that in Figure 1 to that given in Figure 2.

Figure 2: Transition Matrix Excerpt with Chutes and Ladders

As shown in Figure 2, the transition probabilities for movement to the square 4 are zero.
This is due to the presence of the bottom of a ladder within that square, forcing the player to
move to square 14, instead. Furthermore, movement to square 1 and square 9 is not possible
either; these squares force players to move to square 38 and square 31, respectively. Continuing
in this fashion, a full 101-by-101 matrix may be created. Due to the sheer scale of this matrix, the
entirety of the matrix will not be constructed. However, the final portion of the matrix is given in
Figure 3.

Figure 3: Transition Matrix Excerpt at Endgame

Due to the introduction of chutes and ladders, several states (such as squares 4 and 9)
become redundant. Therefore, the transition matrix may be reduced from 101-by-101 to 82-by-
82. Even in this reduced form, the transition matrix is still too large to be constructed in its
entirety with the confines of this paper.
With the game modeled as a Markov chain, the experimental examination may begin.
The simulations are performed using a combination of built-in Python functions and functions
provided by the SciPy and NumPy packages; plotting and statistical analysis is provided by the
Matplotlib package. After simulating a game of chutes and ladders one million times, the average
number of moves to complete the game was experimentally determined to be 35.82 with a
standard deviation of 23.32. A plot of the theoretical probability density function (with
experimentally determined µ and σ) is overlaid with a histogram of the experimental results; this
plot is given in Figure 4.

Figure 4: PDF of Chutes and Ladders with One Million Trials

As shown in Figure 4, the experimental results roughly follow a right-skewed normal


distribution. A normal distribution is expected due to the central limit theorem.
A plot of the theoretical cumulative distribution function for µ = 35.82 and σ = 23.32 is
overlaid with its experimental counterpart; this plot is given in Figure 5.

Figure 5: CDF of Chutes and Ladders with One Million Trials

As indicated by the cursor in Figure 5, the experimental threshold for 96% of games to
complete is 86.00 moves. While the maximum number of terms is unbounded, none of the
experimental runs resulted in more than approximately 360 moves. Furthermore, if the endgame
condition were altered such that players needed an exact die roll to reach the end (i.e. for P = end
– k, only die roll k allows the game to end), the average number of games would increase.
Furthermore, the 96% threshold would also increase.
In short, the Chutes and Ladders variant of Snakes and Ladders is a perfect example of a
Markovian game. Through Monte Carlo-style simulation, the average number of turns per game
is 35.82 and 96% of all games will be completed in 86 moves or fewer.
Appendix
The source code for the Python simulation is as follows:
######################################################################################
# Program: chutes_and_ladders.py
# Purpose: Simulates Chutes and Ladders variant of Snakes and Ladders and plots
# relevant statistical analysis
# Written by Matthew Burger
# Last Modified: 27 November 2017
######################################################################################

import random
import numpy as np
from scipy.stats import norm
import matplotlib.pyplot as plt
from matplotlib import mlab

# Roll a die
def dieRoll():

return random.randint(1,6)

#Move to new location based upon die roll and chutes/ladders


def move(size,location,chutes,ladders):

newLocation = location + dieRoll()

if newLocation >= size:


location = size
elif newLocation in chutes.keys():
location = chutes[newLocation]
elif newLocation in ladders.keys():
location = ladders[newLocation]
else:
location = newLocation

return location

# Simulate one game and return number of moves required


def gameLength():
size = 100
chutes = {98:78, 95:75, 93:73, 87:24, 64:60, 62:19, 56:53, 49:11, 47:26, 16:6}
ladders = {1:38, 4:14, 9:31, 21:42, 28:84, 36:44, 51:67, 71:91, 80:100}
location = 0
moves = 0
limit = 750

while moves <= limit and location < size:


moves += 1
location = move(size,location,chutes,ladders)

return moves

class SnaptoCursor(object):
def __init__(self, ax, x, y):
self.ax = ax
self.ly = ax.axvline(color='k', alpha=0.2) # the vert line
self.marker, = ax.plot([0],[0], marker="o", color="crimson", zorder=3)
self.x = x
self.y = y
self.txt = ax.text(0.7, 0.9, '')
def mouse_move(self, event):
if not event.inaxes: return
x, y = event.xdata, event.ydata
indx = np.searchsorted(self.x, [x])[0]
x = self.x[indx]
y = self.y[indx]
self.ly.set_xdata(x)
self.marker.set_data([x],[y])
self.txt.set_text('x=%1.2f, y=%1.2f' % (x, y))
self.txt.set_position((x,y))
self.ax.figure.canvas.draw_idle()

simLength = int(1e6)
tally = np.zeros(simLength)

if (__name__ == '__main__'):

for k in range(simLength):
tally[k] = gameLength()

mu, std = norm.fit(tally)

# Plot the PDF


plt.hist(tally, bins='auto', normed=True, alpha=0.6, color='g',
label='Experimental')
xmin, xmax = plt.xlim()
x = np.linspace(xmin, xmax, 100)
p = norm.pdf(x, mu, std)
plt.plot(x, p, 'k', linewidth=2, label='Theoretical')
plt.grid(True)
plt.legend(loc='right')
plt.xlabel("Number of Moves")
plt.ylabel("Probability Density")
plt.title("PDF for mu = %.2f, std. deviation = %.2f" %(mu, std))
plt.show()

# Plot the CDF


fig, ax = plt.subplots()

n, bins, patches = ax.hist(tally, bins='auto', normed=1, histtype='step',


cumulative=True, label='Experimental')
y = mlab.normpdf(bins, mu, std).cumsum()
y /= y[-1]

cursor = SnaptoCursor(ax, bins, n)


cid = plt.connect('motion_notify_event', cursor.mouse_move)

ax.plot(bins, y, 'k--', linewidth=1.5, label='Theoretical')


plt.grid(True)
plt.legend(loc='right')
plt.xlabel("Number of Moves")
plt.ylabel("Cumulative Probability")
plt.title("CDF for mu = %.2f, std. deviation = %.2f" %(mu, std))
plt.show()

S-ar putea să vă placă și