PyGameofLife is a simple implementation of Conway’s Game of Life in python, with a command-line interface that can generate pretty gifs. The source code emphasises simplicity and intuitive code rather than efficiency. A quick demo is shown here:
python life.py -seed r_pentomino -n 500 -interval 50
Because this is just a small educational script, I have chosen not to distribute it on PyPI. The only dependencies are numpy
and matplotlib
, which can be installed easily via pip
:
pip install numpy matplotlib
To get started with PyGameofLife, navigate to a directory where you want to use the project, then clone it with:
git clone https://github.com/robertmartin8/PyGameofLife
Move into the directory that was just created (either through your filesystem or with cd PyGameofLife
), and you are ready to go!
If you don't want to use git clone
for whatever reason, you can manually download it, unzip it, and move the folder somewhere convenient (preferably either the desktop or your home directory). Then, open up your command line, and cd
to the correct directory. For example:
cd Users/your_username/Desktop/PyGameofLife-master
That's all. You can interface with PyGameofLife via the command line. As an example, try running the following:
python life.py
After a brief moment, you should see infinite.gif
appear. Open the file with any gif viewer (your browser should work):
There are many many things you can customise.
If at any time you need a quick reference, just run the help
command:
python life.py --help
Which will give you the following
usage: life.py [-h] [--universe-size UNIVERSE_SIZE] [-seed SEED] [-n N]
[-quality QUALITY] [-cmap CMAP] [-interval INTERVAL]
[--seed-position SEED_POSITION]
PyGameofLife. By default, produces 50 generations of the 'infinite' seed
optional arguments:
-h, --help show this help message and exit
--universe-size UNIVERSE_SIZE
comma-separated dimensions of universe (x by y)
-seed SEED seed for Life, see readme for list
-n N number of universe iterations
-quality QUALITY image quality in DPI
-cmap CMAP colour scheme
-interval INTERVAL interval (in milliseconds) between iterations
--seed-position SEED_POSITION
comma-separated coordinates of seed
However, I wll present some of the different options along with animations below.
This is arguably where all of the magic of Life lies: simple seeds can produce exceedingly complex behaviour (it can be used to build a Turing machine), which is near-impossible to predict just by looking at the seed.
A seed is just a starting pattern that is placed somewhere in the universe. There are a number of seeds built in, and they should be referred to by the names below.
{
"diehard": [[0, 0, 0, 0, 0, 0, 1, 0],
[1, 1, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 0, 1, 1, 1]],
"boat": [[1, 1, 0],
[1, 0, 1],
[0, 1, 0]],
"r_pentomino": [[0, 1, 1],
[1, 1, 0],
[0, 1, 0]],
"pentadecathlon": [[1, 1, 1, 1, 1, 1, 1, 1],
[1, 0, 1, 1, 1, 1, 0, 1],
[1, 1, 1, 1, 1, 1, 1, 1]],
"beacon": [[1, 1, 0, 0],
[1, 1, 0, 0],
[0, 0, 1, 1],
[0, 0, 1, 1]],
"acorn": [[0, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 1, 0, 0, 0],
[1, 1, 0, 0, 1, 1, 1]],
"spaceship": [[0, 0, 1, 1, 0],
[1, 1, 0, 1, 1],
[1, 1, 1, 1, 0],
[0, 1, 1, 0, 0]],
"block_switch_engine": [[0, 0, 0, 0, 0, 0, 1, 0],
[0, 0, 0, 0, 1, 0, 1, 1],
[0, 0, 0, 0, 1, 0, 1, 0],
[0, 0, 0, 0, 1, 0, 0, 0],
[0, 0, 1, 0, 0, 0, 0, 0],
[1, 0, 1, 0, 0, 0, 0, 0]],
"infinite": [[1, 1, 1, 0, 1],
[1, 0, 0, 0, 0],
[0, 0, 0, 1, 1],
[0, 1, 1, 0, 1],
[1, 0, 1, 0, 1]],
}
So you can do something like:
python life.py -seed diehard
If you'd like to add your own seed, the best way is to edit the source code and add it to the seeds
dictionary at the top of life.py
. By the way, if you try boat
and it doesn't seem to be working, there's a good reason.
If you'd like to change the starting position of the seed, you can specify the coordinates (comma separated x
and y
) where the top-left of the seed will be placed.
python life.py -seed diehard --seed-position 20,20
By default, the size of the universe is 100x100. This is quite big relative to the size of most of the seeds, but is useful because many seeds in Life produce quite expansive results. But say you want a nice picture of the beacon, which we know is roughly the shape of a 4x4 square. We therefore probably want a 6x6 universe, with the top-left of the seed pegged at coordinates (1,1), because indexing starts from 0 in python.
python life.py -seed beacon --universe-size 6,6 --seed-position 1,1
As seen above, if you want to change the size of size of the grid, you must also specify the coordinates of the seed, otherwise you will probably get a nasty error.
Colours are amended with the -cmap
flag. A list of all possible colourmaps can be found in the
matplotlib documentation. Note,
however, that you will only get the colours at each end of the spectrum, because the cells in
Life are either dead (0) or alive (1). Colourmaps can be reversed by appending _r
to the name of the original colourmap. Please note that the cmap names are case sensitive.
python life.py -seed beacon -cmap plasma_r --universe-size 6,6 --seed-position 1,1
- The
-n
flag controls the number of iterations: the time taken to produce the animation grows linearly with the number of iterations. -interval
is quite useful, and changes the rate at which Life iterates. In the simple example of the beacon, you could increase the flashing frequency by reducing the interval.- Lastly,
-quality
dictates the image quality of the resulting animation. Be warned that higher DPI values greatly increase both the time taken to generate animations and the resulting fileszie.
This is just a quick project to demonstrate the versatility of matplotlib's animation functions. PyGameofLife definitely doesn't present the most efficient solution, but the code is readable and intuitive (and arguably produces pretty output).
For a write-up explaining a little bit more about how the code works, check out the related article on my website.