liveBook · Manning

Game of Life

As explained in Fast Python: High performance techniques for large datasets

Conway's Game of Life is a cellular automaton whose grid cells change state across discrete generations according to local neighborhood rules.

Game of Life is a zero-player cellular automaton: a grid evolves through generations, with each cell becoming alive or dead according to its neighborhood. A live cell survives with two or three neighbors; a dead cell becomes live with three; all other cells die or remain dead. The grid wraps at every edge, so opposite borders provide neighboring cells. section 5.6

An implementation-oriented treatment emphasizes:

  • Inspecting neighboring array positions rather than only the current element, which makes a universal-function approach insufficient for some algorithms. section 5.5.1
  • Keeping Python orchestration simple while placing the computationally expensive update in Cython with NumPy arrays. section 5.6
  • Using low-level multithreaded parallelism to bypass the GIL's single-Python-thread limitation. section 5.6
  • Extending the two-state model into QuadLife, where live cells are red, green, blue, or yellow and majority or all-different neighbor colors determine the next color. section 5.6

Scaling a spaceship simulation

A 400-generation simulation on a 400 × 250 map takes under one second with the optimized implementation. The same workload takes about 11 seconds at 1920 × 1080 and 48 seconds at 3840 × 2160. Runtime grows linearly with generations and quadratically with the side length of a square world; memory is also quadratic in world size. A pure-Python version would take roughly 54 days for the comparable 4K video. section 5.6.3

import sys
 
import numpy as np
import pyximport
pyximport.install(                                     #1
    language_level=3,
    setup_args={
        'include_dirs': np.get_include()})
 
import cquadlife as quadlife
 
SIZE_X = int(sys.argv[1])                              #2
SIZE_Y = int(sys.argv[2])
GENERATIONS = int(sys.argv[3])
 
world = quadlife.create_random_world(SIZE_Y, SIZE_X)   #3
for i in range(GENERATIONS):                           #4
    world = quadlife.live(world)

Inspect the runnable Python driver that creates a random NumPy world, accepts dimensions and generation count, and repeatedly calls the Cython update function. section 5.6

How Fast Python: High performance techniques for large datasets covers it

How deep it goes
Descends from transition rules to Cython array access, multithreading, timing, and scaling, but stops short of formal complexity theory.
Examples
Uses oscillating dashes, stable boxes, dying patterns, QuadLife colors, and spaceship videos across several resolutions.
What it assumes
Assumes familiarity with Python while reinforcing Cython and NumPy interactions through a complete array-processing example.
What you can do after
Build it from scratch as a Python-driven Cython and NumPy simulation, then optimize neighborhood access with low-level parallelism.
How it connects
Connects cellular automata with NumPy, Cython, multithreading, the GIL, and computational complexity.

Also explained in

Compare how 2 books explain Game of Life →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Fast Python: High performance techniques for large datasets. The section links point to the parts of the book it draws on.