Conway's Game of Life is a common problem. I had to write a version of it to get my current C# job, so I thought it would be a nice way to get into Python (especially as the example in the book are a little too simple for my liking)
I've slightly abused the unit tests to print out the grids, normally I would not use a unit test as a functional test!
I'm not sure if I have grasped the "pythonic" principles so please let rip.
from random import randint
class Engine(object):
_grid = []
def __init__(self, rows, cols):
self._rows = rows
self._cols = cols
def seed(self, grid):
""" Add a 'seed' grid; this allows adding known patterns
:param grid: a 2 dimensional list containing only 1 or 0
:return: null
"""
self._grid = grid
return
def generate(self):
""" Using an existing 'seed' grid, follow the rules to change the
values in the list
:return: a 2 dimensional list containing only 1 or 0
"""
# NB: if the system was not set up with a 'seed' pattern, generate one.
if len(self._grid) == 0:
self._grid = [[randint(0, 1) for row in range(self._rows)]
for col in range(self._cols)]
generated = [[0 for row in range(self._rows)]
for col in range(self._cols)]
for y in range(self._cols):
for x in range(self._rows):
neighbours = self.__neighbourCount(x, y)
# Handles
# Rule 1 - Any live cell with fewer than two live neighbours
# dies, as if caused by under-population (by defaulting to 0)
# Rule 2 - Any live cell with two or three live neighbours
# lives on to the next generation.
# Rule 3 - Any live cell with more than three live neighbours
# dies, as if by overcrowding (by defaulting to 0)
if ((self._grid[y][x] == 1) and
(neighbours == 2 or neighbours == 3)):
generated[y][x] = 1
# Rule 4 - Any dead cell with exactly three live neighbours
# becomes a live cell
if self._grid[y][x] == 0 and neighbours == 3:
generated[y][x] = 1
self._grid = generated
return generated
def __neighbourCount(self, x, y):
""" Return the sum of the value of neighbours of a cell in the grid
:param x: the X coordinate index of the cell
:param y: the Y coordinate index of the cell
:return:
"""
count = 0
for row in [-1, 0, 1]:
for col in (-1, 0, 1):
if (not row == col == 0 and
(0 <= x + row < self._rows and
0 <= y + col < self._cols)):
count += self._grid[(y + col) % self._cols][(x + row) % self._rows]
return count
Unit tests:
from nose.tools import *
from GameOfLife.engine import Engine
def test_GenerateWithOscillatorSeed():
rows = 10
cols = 10
input = [[0 for row in range(rows)] for col in range(cols)]
input[2][1] = 1
input[2][2] = 1
input[2][3] = 1
expected = [[0 for row in range(rows)] for col in range(cols)]
expected[1][2] = 1
expected[2][2] = 1
expected[3][2] = 1
engine = Engine(rows, cols)
engine.seed(input)
actual = engine.generate()
print 'INPUT ----------------------------------------------'
print(formatPrintOut(input))
print ''
print 'EXPECTED ----------------------------------------------'
print(formatPrintOut(expected))
print ''
print 'ACTUAL ----------------------------------------------'
print(formatPrintOut(actual))
assert actual == expected
# engine.seed(actual)
regenerated = engine.generate()
print ''
print 'REGENERATED ----------------------------------------------'
print(formatPrintOut(regenerated))
assert regenerated == input
def test_GenerateWithPulsarSeed():
rows = 17
cols = 17
input = [[0 for row in range(rows)] for col in range(cols)]
input = [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0],
[0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0],
[0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0],
[0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0],
[0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0],
[0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0],
[0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
]
engine = Engine(rows, cols)
engine.seed(input)
iteration1 = engine.generate()
iteration2 = engine.generate()
iteration3 = engine.generate()
print 'INPUT ----------------------------------------------'
print(formatPrintOut(input))
print ''
print 'ITERATION 1 ----------------------------------------------'
print(formatPrintOut(iteration1))
print ''
print 'ITERATION 2 ----------------------------------------------'
print(formatPrintOut(iteration2))
print ''
print 'ITERATION 3 ----------------------------------------------'
print(formatPrintOut(iteration3))
print ''
assert input == iteration3
def formatPrintOut(grid):
return '\n'.join([''.join(['{:4}'.format('.' if item == 0 else 'X')
for item in row])
for row in grid])
Finally, output proving it is working:
PS C:> nosetests -a INPUT ---------------------------------------------- . . . . . . . . . . . . . . . . . . . . . X X X . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EXPECTED ---------------------------------------------- . . . . . . . . . . . . X . . . . . . . . . X . . . . . . . . . X . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ACTUAL ---------------------------------------------- . . . . . . . . . . . . X . . . . . . . . . X . . . . . . . . . X . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . REGENERATED ---------------------------------------------- . . . . . . . . . . . . . . . . . . . . . X X X . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . INPUT ---------------------------------------------- . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . X X X . . . X X X . . . . . . . . . . . . . . . . . . . . . . . X . . . . X . X . . . . X . . . . X . . . . X . X . . . . X . . . . X . . . . X . X . . . . X . . . . . . X X X . . . X X X . . . . . . . . . . . . . . . . . . . . . . . . . X X X . . . X X X . . . . . . X . . . . X . X . . . . X . . . . X . . . . X . X . . . . X . . . . X . . . . X . X . . . . X . . . . . . . . . . . . . . . . . . . . . . . X X X . . . X X X . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ITERATION 1 ---------------------------------------------- . . . . . . . . . . . . . . . . . . . . . . X . . . . . X . . . . . . . . . . X . . . . . X . . . . . . . . . . X X . . . X X . . . . . . . . . . . . . . . . . . . . . . . X X X . . X X . X X . . X X X . . . . X . X . X . X . X . X . . . . . . . . X X . . . X X . . . . . . . . . . . . . . . . . . . . . . . . . . . X X . . . X X . . . . . . . . X . X . X . X . X . X . . . . X X X . . X X . X X . . X X X . . . . . . . . . . . . . . . . . . . . . . . X X . . . X X . . . . . . . . . . X . . . . . X . . . . . . . . . . X . . . . . X . . . . . . . . . . . . . . . . . . . . . . ITERATION 2 ---------------------------------------------- . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . X X . . . . . X X . . . . . . . . . X X . . . X X . . . . . . . X . . X . X . X . X . . X . . . . X X X . X X . X X . X X X . . . . . X . X . X . X . X . X . . . . . . . X X X . . . X X X . . . . . . . . . . . . . . . . . . . . . . . . . X X X . . . X X X . . . . . . . X . X . X . X . X . X . . . . . X X X . X X . X X . X X X . . . . X . . X . X . X . X . . X . . . . . . . X X . . . X X . . . . . . . . . X X . . . . . X X . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ITERATION 3 ---------------------------------------------- . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . X X X . . . X X X . . . . . . . . . . . . . . . . . . . . . . . X . . . . X . X . . . . X . . . . X . . . . X . X . . . . X . . . . X . . . . X . X . . . . X . . . . . . X X X . . . X X X . . . . . . . . . . . . . . . . . . . . . . . . . X X X . . . X X X . . . . . . X . . . . X . X . . . . X . . . . X . . . . X . X . . . . X . . . . X . . . . X . X . . . . X . . . . . . . . . . . . . . . . . . . . . . . X X X . . . X X X . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ---------------------------------------------------------------------- Ran 2 tests in 0.082s OK
class
needs indents after it) (hint: Control-k allows for easy indenting) \$\endgroup\$