Game of Life (with randomization)

This is Conway's Game of life with a little bit of randomization added. We then render the result with matplitlib's animation tools.


In [ ]:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
%matplotlib inline

Randomization Parameters

k_1: $ \frac{1}{k_1} $ probability that a square with only one neighbour comes alive.

k_2: $ \frac{1}{k_2} $ probability that a square with two neighbours dies.

k_3: $ \frac{1}{k_3} $ probability that a square with three neighbour dies.

k_4: $ \frac{1}{k_4} $ probability that a square with four neighbours comes alive.


In [ ]:
k_1 = 1000
k_2 = 1000
k_3 = 1000
k_4 = 1000

Initial layout parameters

width: The width of the board in pixels (squares).

height: The height of the board in pixels (squares).

q: $ \frac{1}{q} $ probability that a square is initially alive.


In [ ]:
width = 1024
height = 512
q = 10

The initial state of the board


In [ ]:
Z = (np.random.randint(0,q,(height,width)) == 0).astype(int)

Plotting parameters

dpi: The dpi to use when rendering the plots

history: Display an average color for a square over the last history steps. A setting of one will display the usual game of life output, and higher values will show grey trails as things move around the board.

frames: The number of iteratiions (and hence frames) to create the animation of.

output_file: The place to save the file to. Note that this will be relative to the working directory of the ipython notebook instance that you are running.


In [ ]:
dpi = 72.0

history = 50

frames = 1000

output_file = "im.mp4"

The iteration step

Z: Should be a matrix of ones and zeros which represents the state of the board. The board will then be iterated and returned as a matrix of ones and zeros.

This function will add in the randomness according to the parameters listed above.

In [ ]:
def iterate(Z):
    # Count neighbours
    N = (Z[0:-2,0:-2] + Z[0:-2,1:-1] + Z[0:-2,2:] +
         Z[1:-1,0:-2]                + Z[1:-1,2:] +
         Z[2:  ,0:-2] + Z[2:  ,1:-1] + Z[2:  ,2:])

    # Apply rules
    birth = (N==3) & (Z[1:-1,1:-1]==0)
    survive = ((N==2) | (N==3)) & (Z[1:-1,1:-1]==1)
    # Add randomess
    R_1 = (N==1) * (np.random.randint(0,k_1, np.array(Z.shape) - np.array([2,2])) == 0)
    R_2 = (N==2) * (np.random.randint(0,k_2, np.array(Z.shape) - np.array([2,2])) == 0)
    R_3 = (N==3) * (np.random.randint(0,k_3, np.array(Z.shape) - np.array([2,2])) == 0)
    R_4 = (N==4) * (np.random.randint(0,k_4, np.array(Z.shape) - np.array([2,2])) == 0)   
    Z[...] = 0
    Z[1:-1,1:-1][birth | survive] = 1
    Z[1:-1,1:-1][R_1] = 1
    Z[1:-1,1:-1][R_2] = 0
    Z[1:-1,1:-1][R_3] = 0
    Z[1:-1,1:-1][R_2] = 1
    return Z

Plot iteration

This will iterate the plot. This function is passed to animation.FuncAnimation.


In [ ]:
ars = []
def iterate_plot(*args):
    global Z
    global ars
    global history
    Z = iterate(Z)
    ars.append(Z.copy())
    if len(ars) > history:
        ars.pop(0)
    im.set_array(sum(ars) / 50.)
    return im

Produce the plot


In [ ]:
size = np.array(Z.shape)
figsize= size[1]/float(dpi),size[0]/float(dpi)
fig = plt.figure(figsize=figsize, dpi=dpi, facecolor="white")
fig.add_axes([0.0, 0.0, 1.0, 1.0], frameon=False)
plt.xticks([]), plt.yticks([])

im = plt.imshow(Z,interpolation='nearest', cmap=plt.cm.gray_r)
ani = animation.FuncAnimation(fig, iterate_plot, interval=50, frames=frames)
ani.save(output_file, codec="libx264")
plt.show()

In [ ]: