NumPy·Capstone·22 min·0/4 exercises

Capstone: Conway’s Game of Life, without a single loop

A whole simulation in two lines of array arithmetic.

np.rollboolean masksastypeshifted sumswrap-aroundsimulation

Watch it happen

Play it through, or step back and forth yourself.

grid34 x 22 · generation 0 · 136 alive

Three rules, applied to every cell at once. A live cell with 2 or 3 live neighbours survives; a dead cell with exactly 3 comes alive; everything else dies. That's the whole of Conway's Game of Life.

The idea

Conway's Game of Life has three rules and produces gliders, oscillators and — famously — universal computation. It's also the perfect final exercise for this track, because the obvious implementation is a nest of loops and the NumPy one is two lines.

The rules

  1. A live cell with 2 or 3 live neighbours stays alive.
  2. A dead cell with exactly 3 live neighbours becomes alive.
  3. Everything else dies or stays dead.

Crucially, every cell is decided simultaneously from the current state. You can't update in place as you sweep — cells later in the sweep would see the new values, and the simulation would be wrong. That constraint is exactly what array thinking gives you for free.

Counting neighbours without looping

The insight: instead of asking each cell about its eight neighbours, shift the entire grid in each of the eight directions and add the copies up. Every cell then holds its own neighbour count.

n = sum(
    np.roll(np.roll(grid, dy, axis=0), dx, axis=1)
    for dy in (-1, 0, 1)
    for dx in (-1, 0, 1)
    if (dy, dx) != (0, 0)
)

That loop runs eight times regardless of grid size — it's a loop over directions, not over cells. On a million-cell grid it's still eight operations.

np.roll wraps around the edges, which makes the board a torus and means there are no boundary cases to special-case. A glider that leaves the right edge comes back on the left.

Applying the rules

With the counts in hand, the three rules collapse into one boolean expression:

grid = ((n == 3) | ((grid == 1) & (n == 2))).astype(np.uint8)

Read it out loud: "exactly three neighbours, or already alive with exactly two". That's rules 1 and 2; rule 3 is everything the expression doesn't select. No branches, no loops, and every cell decided from the same snapshot — the simultaneity is automatic, because the right-hand side is fully evaluated before the assignment happens.

What this capstone uses

Nearly the whole track. Boolean masks and combining them with & and | (lesson 7), astype and uint8 (lesson 3), shifting views (lessons 4 and 5), broadcasting when you compare an array to a scalar (lesson 13), and the vectorisation habit that made you look for the loop-free version at all (lesson 14).

Work through the exercises in order — they build the simulation one piece at a time, and the last one runs it.

Going further

Once it works, try: hard edges instead of wrapping (pad with zeros rather than rolling); other rulesets like HighLife, which adds "a dead cell with 6 neighbours also revives"; counting neighbours with a convolution instead; or tracking how many generations until the board settles.

Practice

Write it yourself. The answer is there when you want it.

Putting the kettle on…

Starting up…

Write it yourself

not graded

Write life_step(g): count each cell's eight neighbours by rolling the grid one step in every direction and summing, then return the cells with exactly three neighbours, plus the live cells with two. Run it four times from grid.copy(), print the board with show, and finish with the number of cells still alive.

Write something and press Run — the output appears here.

Your turn

4 exercises. Write the code yourself, then press Check — a nudge and the answer are there if you want them.

Shift grid one row upwards, wrapping around, using np.roll.

your answer

Build the neighbour count array for grid — the sum of all eight shifted copies. Same shape as grid.

your answer

Given that count, apply the rules and return the next generation as a uint8 array: alive where the count is exactly 3, or where the cell is already alive and the count is exactly 2.

your answer

Run the simulation for 10 generations from grid and return how many cells are alive at the end.

your answer