Your goal in this assignment is to implement a famous mathematical “game” (or cellular automata to use the correct term) the Game of Life, also known as Life. It was devised by the British mathematician John Conway in 1970.
Your solution to this homework will also be marked on code quality (1%), which is distinct from code functionality (2%). To gain full marks, your submission must be correct with high code quality.
Practical information#
The assignment is due Sunday, 21st April 2024 at 11:55pm. To submit your solution, you will upload a single python file via wattle. Here is the assignment submission link.
NOTE: No excuses about technical problems like internet connection or computer issues are accepted. You can actually submit the homework as many times as you like, as the last submitted file will be marked. Do not wait until the last minute!
The homework is individual. You must write your own solution, and you are expected to be able to explain every aspect of it. You are not allowed to share your solution (code) with other students; this includes posting it (or parts of it) to the discussion forum, or to any other on-line forum. You may be contacted for an additional oral assessment, which may result in a change of mark.
If you have questions?
- Ask the tutor in your lab,
- Look at the Wattle discussion forum for similar question, and only post your question if not found,
- Go to drop-in sessions.
There are two tasks which will be assessed. For each task, you will have to write a function.
Understanding the rules of Life#
The Game of Life is “played” on a board, which is a 2D (two-dimensional) grid of cells.
Cells are either dead (represented as an integer 0
on the board) or alive (an integer 1
on the board).
Life runs over several steps (generations). At each step we start from a given configuration of the board (i.e., distribution of live and dead cells), and we generate the next board according to the following rules:
- A live cell with zero or one live neighbours dies (underpopulation),
- A live cell with two or three live neighbours survives (i.e., remains alive in the next generation),
- A live cell with more than three live neighbours dies (overpopulation),
- A dead cell with exactly three live neighbours becomes a live cell (reproduction).
Fig 1. The blue cell has eight neighbours
(C-Centre, N-North, S-South, E-East, W-West etc).
Cells at the edge of the board will have less than 8 neighbours.
How to represent the board in Python#
There are several possible choices to represent the 2D grid of numbers. From the data structures we have seen so far we may either use a list of lists (Lectures 11 and 12) or a 2D NumPy array (Lecture 13). You may choose which one of the two data structures you prefer.
We have provided you with two skeleton code files:
if you choose to use a list of lists data structure then download game_of_life.py, or
if you choose to use a NumPy array data structure then download game_of_life_numpy.py.
Write your functions in your downloaded skeleton code file.
Task 1#
Write a function count_live_neighbours(board, row, column)
which returns the number of live neighbours of a cell, given a board
and the coordinates of the cell on the board given by row
and column
, i.e., the cell at board[row][column]
.
Assumptions#
You can assume that all values on the board are either an integer 0
or an integer 1
.
The board is rectangular, but you cannot assume that the board is square.
You can assume that row
and column
are integers greater or equal to zero and within the boundaries of the board.
In the case of a list of lists, you can also assume that all lists in the list of lists have the same length.
Restrictions#
You must not import any modules except copy
, numpy
and matplotlib
.
Write your implementation of count_live_neighbours
in your downloaded skeleton code file.
The template function has only a pass
statement which you should replace with your code.
Once you have written the function you can test it by calling the provided test_count_live_neighbours
function.
Task 2#
Write a function generate_next_board(board)
that, given the current board, generates the next board. Both board
and the returned value are Life boards.
You must NOT modify board
. Instead, you should work with a copy of board
which can be modified and returned. (Think why!)
The same assumptions and restrictions apply as in task 1.
Write your implementation of generate_next_board
in your downloaded skeleton code file.
Once you have written the function you can test it by calling the provided test_generate_next_board
function.
Testing#
Like in previous homeworks, the skeleton code file has testing functions that you must NOT modify.
Note that these testing functions only test a limited number of test cases. Passing all the tests does not necessarily mean that your code is correct, but failing just one test means that your code is wrong.
Evolution#
We have provided other functions in the skeleton code file for your pleasure:
draw_board(board)
will be useful when you start designing your own boards, and
evolve_life(initial_generation, number_steps)
which will run your generate_next_board
for number_steps
displaying each generation in real time!
To display an evolution, assign the return value of an evolve_life board to a variable. For example
animation1 = evolve_life_beacon(10)
will display a board with an oscillating pattern of periodicity 2 called “Beacon”:
In the skeleton code file you will also find display functions for the well known “Glider Gun”, and “line” and “box” which display simpler boards that you can easily experiment with.
Ask your tutor if you’re not sure how to use these functions.
And there are many other interesting boards in the Wikipedia Game of Life reference. Check them out. Sorry no bonus marks, but lots of fun if you can get them going!
Marking#
In this homework we will be marking your submission for code quality including:
- Using good function, parameter and variable names.
The names of some functions in the homework are fixed, but if you define additional functions then they should be given good descriptive names. - Appropriate use of docstrings and comments.
A docstring should be the first statement in every function definition. You must not use strings as comments anywhere else inside a function.
Comments should be accurate, relevant and readable, but should not distract the reader. This means not too many comments, not too few, but just right! - Good code organisation.
This includes appropriate use of functions to decompose a problem and avoid code repetition.
What to submit#
You should upload your game_of_life.py
or game_of_life_numpy.py
file using Wattle.
The file you submit must meet the following requirements:
- It must be syntactically correct Python code.
- It must be named exactly either
game_of_life.py
orgame_of_life_numpy.py
. - It must contain your uID and name to sign the agreement of academic integrity.
- Like the file you downloaded, it should contain only function definitions, comments, docstrings and import statements.
In marking this assignment we will consider the following:
- Does your submitted file satisfy the requirements specified above?
- Does your implementation compute the correct value for all valid arguments?
- The quality of your submitted Python code, including its organisation, naming and documentation (with docstrings and comments).