One Day Challenge: Life
Conway’s Game of Life is a classic Turing-Complete simulation of Cellular Automata. Which is a very fancy way of saying it’s a simulation composed of simple rules governing cells on a grid. However using these simple rules the cells form a system complex enough to simulate any other system.
Cellular Automata are a great example of Emergent Systems, a system that is capable of more complexity than it would obviously seem. Conway’s Game of Life is the most famous of these systems. It is composed of a grid of cells, each of it can be either alive or dead. From there the Game of Life can be reduced down to only 3 rules:
- If an alive cell has 2 alive neighbors it says alive
- If a dead cell has 3 alive neighbors it becomes alive
- In all other cases the cell dies of either starvation or overpopulation.
My implementation of the Game of Life was written in Clojure since I have recently been learning and become enamored with the language. Clojure is a modern LISP dialect that offers a number of additions and cleanups over classic LISPs while still keeping the clean syntax and powerful abstractions. Clojure also leans more heavily into Functional Programming concepts such as immutability and atomic concurrency.
My code can be found here. As per the challenge I wrote it on one day, and with the REPL-driven-development style that Clojure and other LISPs afford it was actually a wonderful experience since I was able to write and test each function in isolation and then join them together to create the larger program.
The implementation itself is actually very simple. It creates a 2D array of
booleans originally set to
false, randomly sets a certain number of them to
true then goes through every cell to determine if it will live or die based on
the number of alive neighbors it has based on the rules above. The Game of Life
is itself a Pure Function. Each
board state is only affected by the previous board state and nothing else,
meaning the intermediate forms also have no affect.
I did run into a few interesting challenges along the way. I had some instances
where I flipped the X and Y values of the 2D array, which is a constant danger
when using 2D arrays and why they are often abstracted away. I also had to come
up with a way to get the nth value of an array or
nil instead of an error.
And now for the grand finale… a demonstration! (The simulation is slowed down considerably to keep it from going faster than you can watch).