Checkout the live demo on my Home Page!
I was sitting in a lecture the other day when the lecturers screen saver appeared on the projector. It immediately drew all of our attention as it was a live simulation of John Conway’s Game of Life. I had read and watched videos about this topic before and it is quite an interesting simulation. It acts as a model for population evolves over time and given the right inputs (or not!) it can produce some fascinating results for such a simple concept. I first heard about it from the same place I hear most of the interesting facts about the world, from Mr Michael Stevens aka Vsauce. If you haven’t heard of Vsauce you should definitely check out his YouTube channel. He makes extremely interesting videos on a huge variety of topics ranging from science and math to philosophy and psychology and his content is pretty widely accepted as some of the best on YouTube in that space.
Here is a quick snippet from the video where he mentions it (~90 seconds and it should start from the right point!)
So just to reiterate what Michael said (or for those who didn’t feel like watching), the simulation contains a grid of cells which are either alive (lit up) or dead (dark) at any point in time. The interesting part comes from the rules which are applied to this grid of cells which govern how the cells survive, are killed and are reborn. The most common ruleset is the following:
- Any live cell with fewer than two live neighbours dies, as if caused by underpopulation.
- Any live cell with more than three live neighbours dies, as if by overpopulation.
- Any live cell with two or three live neighbours lives on to the next generation.
- Any dead cell with exactly three live neighbours becomes a live cell, as if by reproduction.
With those four simple rules, some really interesting patterns and phenomena occur such as oscilators and spaceships which can traverse the grid! This article is just to show a simple implementation of The Game of Life in Processing that I wrote on a
hungover lazy Sunday afternoon and won’t be exploring any of the outcomes in any great detail, but you can find a bunch more info from people who know a whole lot more about it than I do right here!
The first thing I did was decide on the data structure I would use to represent the grid. The name pretty much gives the game away as a grid is basically a synonym for a matrix or 2D array. Next I declared some constans and global variables which would be useful later on. The only one of note here for now is
SQUARE_SIZE which will be the number of pixels each cell will be, the rest are self-explanatory or will be explained later (you decide!).
Next the good old
void setup(). Here we define the size of our canvas, do some initialization and figure out the number of rows and columns of cells we can have. I was lazy and purposely set my
SQUARE_SIZE = 10 as I knew the size of my canvas would be a multiple of 10 meaning I wouldn’t have any remainder pixels.
initGrid() function just creates instances of a
Cell class (which will be created later) and slots them into a 2D array to act as our
The other function here is
generateRandomGrid(). This just iterates over our global
grid and randomly populates some cells with living cells. This is handy for just creating a random grid to run in order to observe the simulation.
Next up is the other good old -
void draw(). This is split in two by an if statement which tests the
settingUp boolean we defined earlier. In setup mode, you can bring cells to life by left clicking and kill off cells by right clicking by making calls on the
Cell objects themselves (these will be shown later). This requires a method to convert the mouse click coordinates into row and column indices into the
grid and this is done using the
getIndex(mouseX, mouseY) function. Finally in setup mode, we check for some key strokes
'g' and respond to them by starting the simulation and regenerating a new random board respectively.
The other half (in running mode), simply applys Conway’s rules to the current board by calling
applyRules() and checks if the
'r' key has been preset, which initiates a reset of the board through
Finally for both
!settingUp (simulating), we make a call to
drawGrid() which simply draws grid full of living cells.
You may have have noticed the calls to
frameRate(15). These (obviously) adjust the frame rate and are are used to slow the simulation down a bit so that we can better see whats going on with how the cells are interacting.
The bodies of the methods mentioned above are given below.
This returns valid indices into the
grid. This actually caused some problems as clicking and dragging the mouse off the canvas actually caused processing to pass me coordinates that were out of range of the canvas (ie coordinates < 0 or coordinates > canvas height etc). A simple bit of logic can be used to combat this however. Taking
x as an example coordinate we can:
int largerThanZero = max(x, 0)- This ensures that
min(largerThanZero, width-1)- This ensures that
xis not larger than the size of the canvas.
Now that we have cleaned x and y coordinates, we can use integer division to get the nearest cell in our grid and return those as the indices.
This is where the interesting logic of the simulation comes in. The rules were given above but here is a quick refresher in some weird pseudo-peseudoish-code:
- (<2 neighbours) = DIE
- (>3 neighbours) = DIE.
- (2 <= neighbours <= 3) && currentlyAlive = LIVE
- (neighbours == 3) && currentlyDead = REBIRTH
A key concept here is that rules apply to all of the cells in the grid for a given generation simultaenously. That is, you can’t update the board as you iterate over it checking the cells of the current generation. We need a snapshot of the board in its current state to compare each cell against. Or more simply, we create a new board on each generation, fill that board based on the previous generation rather than updating the old board.
This function then is rather simple. We create our new grid using
initGrid() and iterate over the board. For each cell, we check how many neighbours it has using the (yet to be defined)
getNumberOfNeigbours(rowIndex, columnIndex) function, apply Conway’s rules to that cell and figure out if it should be alive or dead in the next grid and update our new grid accordingly! Again we use some
Cell instance methods
kill(), create()(bring back to life) and
age() that cell. Ageing has no effect on the simulation but is just used to set the colour of the cell in order to get a visual representation of how long a given cell has been around for.
And of course the
getNumberOfNeighbours(row, column) function will return the number of neighbours the cell at that position has. For the general case each cell has 8 possible neighbours - 3 above it, 2 on either side of it and 3 below it. However (literal) edge cases exist for cells that are on the border.
To handle these we can define the max and min allowed values for the row an column, and iterate from
rowMax and inside that iterate from
colMax. In the general case where
c are the cell in questions row and column:
However we apply the same trick of maximizing any potentially negative rows/cols with 0 and minimizing any rows/cols that can be greater than the
grid dimensions with the
Finally we can iterate over the 9 adjacent cells, and perform one last check that we aren’t at the
column that corresponds to ourselves (as we don’t want to count ourselves!).
Finally after all that work it’s time to draw something to the screen. This part is easy, just iterate over the grid and call
cell.drawCell(row, column) on each cell in the grid and they will handle drawing themsevles. The only other things in this method are clearing the prevously drawn cells by redrawing a black backgroun with
background(0) and (conditionally) drawing an overlay containing some info about the simulation (how many are currently alive / generation number etc).
The last function we need is one to reset the board and it is pretty self explanatory - it puts us back in setup mode, recreates a fresh grid and reinitializes the generation and number of alive cells to 0. In Java, we can rest assured that garbage collection will handle freeing up our now out of scope old grid.
Almost there, just a super basic class to represent our
Cells. The only interesting thing about this Cell class is the concept of “colour-aging”. As discussed, I thought it would be nice to have some visual feedback on how long a certain cell had been around for. So the cells start out with a
blue value of 255 (fully blue) and a
red value of 0 (no red). On each tick a cell survives, we remove
COLOUR_STEP from it’s
blue value and add
COLOUR_STEP to it’s red value. This allows the colour of the cells to change over time based on how many generations they have been alive for.
That’s it! Now we have our own Conway’s Game of Life. I’m sure this is nothing new and that there is a tonne of them already written in Processing, but I just decided to write my own for fun! Only after I built it in Processing did I realise I could use processing.js to run it in a browser!
Feel free to play around with the simulator on my homepage.
Thanks for reading!