How to Write Conway’s “Game of Life” in Python
By: Martin Andersson Aaberge
Tags: Software Engineering, Programming, Python, Data Science, Python3

Writing the Grid

The constructor takes care of creating all the variables we need. It also creates the grid list based on how many rows the user wants. Notice that the constructor also calls the generate method to generate the board right away.

generate_board(self)

This method gives each cell a 33% chance to spawn as a living cell.

The lucky cells will get a living status by using the cell method .set_alive() .

draw_board(self)

First, it prints a number of rows to make sure we print a clean terminal. The number will vary depending on your setup.

Then we use a nested loop to check what kind of character we have to print for that square. We do that by using the cell method .get_print_character() .

check_neighbour(self,check_row,check_column)

Before we actually solve this one, we need to take a look at how this is built and how we can check the neighbours. These are vital for the game and we must nail them!

I’m going to write a test class called Tablecheck in table_check.py and a main.py to test the code with. This is so we can look at the theory together. The actual grid is made with list comprehension because it can be any size the user wants.

building this grid in python

If we break down the grid, we can see that there are rows and columns. If we convert this to Python you can place lists within lists to mimic this behaviour. A,B,C is the first list (self._grid[0]), D,E,F is the second list (self._grid[1]) and G,H,I is the third list (self._grid[2]).

To access the letters inside these lists, we need to get into them. The syntax would be:

self._grid[X][Y]

A lives in self._grid[0][0]

E lives in self._grid[1][1]

G lives in self._grid[2][0]

and so on.

Blue represent rows , Orange represents columns

We can write the grid layout like this in Python:

self._grid is a list with three lists in it, creating a grid-like pattern. To draw a board like this I add this to my main.py :

This prints the following:

Photo by Ali Yahya on Unsplash

check_neighbours(self,check_row,check_column)

OK, now on to what I believe is the trickiest part about the whole code: checking all the neighbours.

With check_neighbours() we have to pass in the coordinates we want to check and then we have to travel within our lists to check all the surrounding neighbours.

This means we have to jump one row back (-1), check our own row (0) and the next row (1). In Python terms that means going through a range from -1 to 2 , range(-1,2). We have to do the same with the columns.

If I want to stay true to my piece, Document Your Python Code Without Writing Documentation, I have to set start and end range as variables and make it clear how they can be affected.

# how deep I will search around the current square.

search_min = -1

search_max = 2

Cycling through a range is easy, but you’re probably already wondering how we deal with going outside the board.

When we check “A”, we don’t want to waste time checking what is outside of the board. The same goes for the other corner elements.

What we have to do here is to check if we’re working in any of the corners or any of the edges.

For the check itself, we need to cycle through the lists based on what the program puts into check_row and check_column (the parameters for check_neighbour ).

We will loop through the range we have set, search_min —search_max . Since the range starts at -1 normally we add 1, so we check the correct rows. We want to jump 1 unit back, but we also want to work on the correct index. Lists in Python start at 0

The conditions in the loops are to ignore squares outside of the board and itself.

Here’s what is going on in this function:

search depth : I want to hop one row back, check my own row and hop one forward- as we’ve already discussed.

: I want to hop one row back, check my own row and hop one forward- as we’ve already discussed. I’ll create an empty list to place all the neighbours in. It’s important that we update all cells at once, so we can’t change the status on any cell before we’ve checked everything. This will be a temporary container of cells.

We enter the row loop and loop through the columns in the row.

neighbour_row and neighbour_column are variables that make sure we’re checking the correct rows and columns in the conditions.

and are variables that make sure we’re checking the correct rows and columns in the conditions. valid_neighbour is true by default. If a neighbour is inside the board and not itself, it is a valid neighbour. That is what all the if statements are for.

is true by default. If a neighbour is inside the board and not itself, it is a valid neighbour. That is what all the if statements are for. When we are done checking, we return the list of valid neighbours.

update_board(self)

Another hard function is the update board function. This cycles through all the cells, check the neighbours and, based on how many alive cells it finds, it stores the cell it’s currently checking in either a death list or a life list.

Then, after all cells are checked, they change status.