Let’s Play Game of Life with Python
Is it fascinating to see how simple rules can lead to something so complex? For example, like the flock of birds or school of fish. The rule for them is pretty simple, they will survive if they are able to avoid predators. One of the explanations of how birds work as a flock was stated by a Zoologist Wayne Potts in journal Nature in 1984. His work showed that birds in flocks don’t just follow a leader or their neighbours. Instead, they anticipate sudden changes in the flock’s direction of motion. So, if there is a bird at the edge of the flock changes its direction, the rest will follow like a wave.
The rules in flocking boiled down to three main rules:
- Separation — avoid crowding neighbours (short range repulsion)
- Alignment — steer towards average heading of neighbours
- Cohesion — steer towards average position of neighbours (long range attraction)
Because of these three rules, we can see an astounding unison movement of birds; its wonderful, graceful, and synchronized display. But all the birds do is just trying to stay alive.
Now, we will see how simple rules can lead to a complex yet interesting system. But we won’t see it in nature, we will see it on our screen through programming simulation. It’s time to talk about Conway’s Game of Life.
A bit of background before we jump into the coding session.
Quoted from Wikipedia:
The Game of Life, also known simply as Life, is a cellular automaton devised by the British mathematician John Horton Conway in 1970. It is a zero-player game, meaning that its evolution is determined by its initial state, requiring no further input. One interacts with the Game of Life by creating an initial configuration and observing how it evolves. It is Turing complete and can simulate a universal constructor or any other Turing machine.
Conway’s Game of Life universe is an infinite two-dimensional grid, each of which has two states, alive or dead, or populated or underpopulated. Each cell is surrounded by eight neighbours which will determine whether the centre cell will become alive, dead, or stay alive in the next period of time.
These are the transition rules that governed Conway’s Game of Life universe:
- Any live cell with fewer than two live neighbours dies, as if by underpopulation.
- Any live cell with two or three live neighbours lives on to the next generation.
- Any live cell with more than three live neighbours dies, as if by overpopulation.
- Any dead cell with exactly three live neighbours becomes a live cell, as if by reproduction.
With only four rules, Game of Life able to create many forms, some are static (still lifes), others are oscillating statically in-place (oscillators), and also there are moving objects (spaceships). The result will be such an amazing display of grid.
The simulation of Conway’s Game of Life will be made using pygame in Python. We will go through the basic of pygame, then start to fully implement it. But you can skip the basic if you want to go right away to the implementation.
Let’s start with the foundation of pygame that will let us create the simulation. For our minimalistic simulation, we will only need to learn a small number of tools which pygame provides. So if you want to learn more about pygame, I will list the resources you need at the end of the article.
Before we start to code, you have to make sure that you have pygame installed in your environment. After that, we can start to write the code.
First, we import pygame module in line 1, initialize the pygame module in line 3, and create a screen as the canvas for our drawings in line 8. Also, we defined the screen width and screen height in line 5 and 6, then we put them as
set_mode method arguments. If you run this code, what will happen? Wait, it opens a new window but the window is immediately closed.
This is where an infinite loop will come to be handy. Because if we want to keep the window open, we will need an infinite loop so the program has no time to reach the end of the process. But, an infinite loop would be a bad thing if we don’t provide it with halting conditions, otherwise, it will just run forever. Now, let’s make some change to our code.
We see start from line 11 to the end of the code, we’ve added an infinite while loop with an event-listener as the halting condition.
pygame.event.get() will provide us with event objects, in this case, the event it listens is when the window close button is pressed. If the window close button is pressed, then the infinite loop will terminate. Note that, I’ve seen several other ways to write the infinite loop such as using flag
pygame.quit. But, the above code is working fine for me.
The default background colour of the screen is black, but we can change it. For example, we want to change it to white.
In line 17, we provided an RGB value (255, 255, 255) which means white into
screen.fill, so we can have a white-coloured background. Note that, we also added line 18, because if you forget to add this line, the background colour won’t change into white. We always need this line, if we want to update our screen.
In the last part of this subsection, we will draw a rectangular shape on our screen.
In line 19, the drawing method is called. Here, we draw a rectangular shape with midnight-blue colour. The first argument we have to provide to the draw-rectangle method is the surface (or canvas) we want to draw on. The second argument is the colour we want to use, and the last argument is a list composed by the coordinate of the top left corner (x and y) and followed by the size of the rectangle (width and height).
That’s pretty much pygame’s basic we need to create our simulation. Now, we will implement it one by one.
Now, let’s create the main file (
main.py) where the running program is placed.
The above code is not so different from the last code in learning basic section right? The main differences are in line 13 and 28. We will create
GameOfLife class in a different file and try to understand how it works. Note that if you are confused about what line 6 does, this line is to provide a running window with a good title.
Let’s start by creating the class and its constructor.
Here, we defined the constructor and all its arguments (mostly keyword arguments, since I don’t want to specify a lot of arguments when trying to call it). The surface is the screen we use to draw, width and height are the screen’s properties, scale will determine the level of zoom of our grid (the larger it gets, the larger the grid will be), offset is to specify the space between cell, active colour and inactive colour are self-explanatory right? columns, rows and grid are probably the most important components. So at the time the object is created, the grid will be filled randomly by True and False (provided by NumPy module) and the size of the grid is equal to the values of columns and rows.
We still can’t see how our code will show us the result. Now, let’s put another piece of code inside our class definition.
run is the one we call in the main file. Method
draw_grid will draw the current state of our grid. Here, we defined two for-loop that will go through each cell and draw them on the screen. In line 9, we check whether the value in position
col is True or False. If it’s True, then we draw with active colour and if it’s False we draw with inactive colour. Then, Let’s run the code by typing
python main.py. Probably the result you’ll see will be like this.
Good good, we have our grid. But we still missing the evolution part.
The job of these two methods is to update our current grid values. In
update_grid, first, we copy the current grid because we don’t want to update it one by one as the loop goes through each cell, the grid will be updated after all cells get updated. In line 6, the value in position
updated_grid will be changed by
This is where the rules for Conway’s Game of LIfe is implemented. From line 16 to 25, we check how many alive neighbours around our current cell. In line 28 to 37, we determine what the next value of the current cell will become.
- If the current cell is alive and the number of alive neighbours is less than 2, the current cell will die as if by underpopulation.
- If the current cell is alive and the number of alive neighbours is 2 or 3, the current cell will stay alive.
- If the current cell is alive and the number of alive neighbours is greater than 3, the current cell will die as if by overpopulation.
- Last, if the current cell is dead and has 3 alive neighbours around it, it will become alive as if by reproduction.
Now let's change
run method so we can see our grid updating.
python main.py to see how it looks like.
Pretty cool right? Well, I think this is cool.
There are several reasons why I build this programming simulation. First, I knew Conway’s Game of Life pretty long time ago, even before I knew how to code and I was amazed by how it looks. Second, this is a great and simple coding challenge for me, it’s quite good to get my hands dirty and have some entertainment. Probably the next project similar to this is to simulate how a flock of birds work or how the virus spread through people. Until then, see you next time and thank you for reading.
If you have some questions or feedback, please let me know.
You can find the full code here: https://github.com/agusrichard/python-workbook/tree/master/game-of-life