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

