Project: Conway’s Game Of Life#

Overview#

Conway’s Game of Life is a classic example of a cellular automaton. It consists of a grid of cells, each of which can be alive or dead. The grid evolves in discrete steps according to these simple rules, applied to each cell based on its eight neighbors:

  1. Any live cell with fewer than two live neighbors dies (underpopulation).

  2. Any live cell with two or three live neighbors remains alive (survival).

  3. Any live cell with more than three live neighbors dies (overpopulation).

  4. Any dead cell with exactly three live neighbors becomes a live cell (reproduction).

A number of animated gifs and visuals can be seen on the Wiki page.

Learning Objectives#

  • Practice building up a project incrementally, starting with a straightforward solution and progressively introducing optimisations.

  • Practice understanding the impact of code changes on performance in regards to execution time and memory footprint.

  • Practice designing and running targeted tests to verify correctness at each development stage.

  • Practice measuring and analysing performance using benchmarking and profiling tools.

Your Path Forward#

Choose the path (or combination of paths) that best matches your interests, and document your experiments as you go.

  1. Build the Naïve Solution
    Implement the simple Game of Life from from scratch by following the four substeps (initialisation, neighbor counting, state update, and visualisation). If you get stuck or want a reference, check out the “Naïve Implementation: Four Substeps” code below.

  2. Profile the Baseline
    Take your solution to the naïve implementation solution below and use tools like @time, @btime (BenchmarkTools), and Profile.@profile to identify time consuming computation. You can also profile a ready made naive solution avaiable within the course notes.

  3. Understanding Code Changes on Performance
    Starting from the baseline naive solution, experiment with different code changes, measure the impact of each change, and compare results.

  4. Follow Your Own Curiosity
    If neither profiling nor low-level tweaking appeals, explore something else that interests you:

    • Build richer or interactive visualisations (e.g., with Makie or Plotly)

    • Extend to custom rule sets or sparse/infinite grids

    • Integrate a GUI

    • Anything else that fits your learning goals!

Naïve Implementation: Four Substeps#

Below is a simple version of Game of Life.

Substep 1: Initialise the Grid#

Goal: Initialise a 100×100 boolean grid with random true/false values to represent the starting state of the Game of Life.


Substep 2: Neighbor Counting#

Goal: Count the number of live neighbors surrounding cell (i, j) in a finite Matrix{Bool} grid.

How do you think this could be made more efficient?


Substep 3: Compute Next State#

Goal: Compute and return the next generation of the Game of Life grid by applying Conway’s rules, using a fresh Matrix{Bool} for the updated state.

Are there any opportunities for a boost in performance?


Substep 4: Run Simulation#

Goal: Simulate the Game of Life for a given number of steps.

How do you think this could be made more performant?


Substep 5: Visualize#

Goal: Animate the evolution of the Game of Life grid and save the result as a GIF using Plots.


To view the live implementation along with an animated GIF, please proceed to the next page of this course!

Hint Sheet#

Keep in mind that while we haven’t discussed these topics in class, they’re offered here as guidance to help you with this project research.


```{admonition} Hint 2 
:class: dropdown
### Animations with `Plots.jl`

**Module**: `using Plots`

**Macro**: `@animate` collects plot frames in a loop, which can then be exported with `gif(anim, filepath; fps=10)`.

````julia 
anim = @animate for step in 1:steps
    heatmap(grid; color=:grays, axis=false, legend=false, framestyle=:none)
end