John Conway's Game of Life

The cellular automata game Life, invented by the mathematician John Conway, makes a fun programming exercise. Let's review the rules:

The world of the Game of Life is an infinite two-dimensional orthogonal grid of cells, each of which is in one of two possible states, live or empty. Each cell has eight neighbors, the cells that are horizontally, vertically, or diagonally adjacent. At each step in time, the following rules are applied to create the next generation:

  • Any live cell with two or three live neighbors lives on to the next generation.
  • Any empty cell with exactly three live neighbors becomes a live cell in the next generation.
  • All other cells are empty in the next generation.

For example, in the diagram below, grey cells are live. In the transition from Generation 0 to 1, the cell marked "E" becomes empty (dies off) because it has zero live neighbors. In the next transition, the cell marked "L" becomes live, because it has 3 live neighbors. All other cells stay the same.

   
                        
                E      
                    
                
                    

Generation 0
   
                    
                        
         L         
                
                    

Generation 1
   
                    
                        
                    
                
                    

Generation 2

The world continues to evolve by these rules for as long as you care to observe.

Developing a Life Program

To create a program to play Life, start with the vocabulary of concepts:

  • World
  • Cell
  • Live/Empty
  • Neighbors
  • Next Generation
  • Display
  • Live Neighbor Counts

and consider how to implement them:

  • World: The state of the world must represent which cells are empty and which are live. The tricky part is that the number of cells is infinite, and we can't store an infinite array in a finite computer. I can think of three ways to deal with this problem:

    1. Change the rules; make the world finite instead of infinite. (Cells at the edge of the world have fewer neighbors, or perhaps they wrap around to the other side of the world.)
    2. Use a finite rectangular window that covers all the live cells in the infinite grid.
      Example: world = [[0, 0, 0, 0, 0], [0, 0, 0, 1, 0], [0, 1, 0, 0, 0], [0, 1, 1, 0, 0], [0, 0, 0, 0, 0]]

    3. Represent a world as a set of live cells. This set will grow and shrink in size from one generation to the next, but we don't have to worry about overflowing the edges of an array.
      Example: world = {(3, 1), (1, 2), (1, 3), (2, 3)}
      I will go with this choice.

  • Cell: Each cell will be represented as an (x, y) pair of integer coordinates.
    Example: cell = (1, 2).
  • Live/Empty: A cell is live if it is a member of the set of live cells.
    Example: "cell in world" is True, given the definition of cell and world above.
  • Neighbors: The cell (x, y) has eight neighbors, formed by adding or subtracting 1 from x or y or both. We can define a function neighbors(cell) to return this set.
    Example: neighbors((8, 8)) == [(7, 7), (8, 7), (9, 7), (7, 8), (9, 8), (7, 9), (8, 9), (9, 9)]

    (x-1, y-1) (x, y-1) (x+1, y-1)
    (x-1, y) (x, y) (x+1, y)
    (x-1, y+1) (x, y+1) (x+1, y+1)
  • Display: We will need some way to display the state of the world. Let's defer that for now.

  • Next Generation: We can define a function, next_generation(world), that takes a world as input and returns a new world with the new set of live cells according to the rules. Example: next_generation({(3, 1), (1, 2), (1, 3), (2, 3)}) == {(1, 2), (1, 3), (2, 3)}

  • Live Neighbor Counts: I need to know how many live neighbors each cell has. A good way to represent this is a dict of {(x, y): count}. But which cells need to be the keys of this dict? We can start with the live cells, and also add any cells neighboring the live cells. An efficient way to generate this dict is to create a Counter and pass it every neighbor of every live cell. This may feel like we're doing the counting "backwards." Instead of asking "for each cell, how many live neighbors does it have?" we are saying "for each live cell, increment the count of each of its neighbors." The two amount to the same thing because neighbor is symmetric—if P is a neighbor of Q, then Q is a neighbor of P. Below we see the neighbor counts for each of the three generations:

          1    1    1 
  1    1    2         1 
  2    2    4    2    1 
  2    2    2    1     
  1    2    2    1     

Generation 0
                    
  1    1    1          
  2    2    3    1     
  2    2    2    1     
  1    2    2    1     

Generation 1
                    
  1    2    2    1      
  2    3    3    2     
  2    3    3    2     
  1    2    2    1     

Generation 2

Here is the implementation.Note that in next_generation the neighbor_counts is used two ways: possible_cells is used to iterate over all cells that might be live, and counts is used to check if a cell has the right number of neighbors.

In [1]:
from collections import Counter

def next_generation(world):
    "The set of live cells in the next generation."
    possible_cells = counts = neighbor_counts(world)
    return {cell for cell in possible_cells
            if (counts[cell] == 3) 
            or (counts[cell] == 2 and cell in world)}

def neighbor_counts(world):
    "A {cell: int} counter of the number of live neighbors for each cell that has neighbors."
    return Counter(nb for cell in world 
                      for nb in neighbors(cell))

def neighbors(cell):
    "All 8 adjacent neighbors of cell."
    (x, y) = cell
    return [(x-1, y-1), (x, y-1), (x+1, y-1), 
            (x-1, y),             (x+1, y), 
            (x-1, y+1), (x, y+1), (x+1, y+1)]

We can see how this works:

In [2]:
world = {(3, 1), (1, 2), (1, 3), (2, 3)}
next_generation(world)
Out[2]:
{(1, 2), (1, 3), (2, 3)}
In [3]:
next_generation(next_generation(world))
Out[3]:
{(1, 2), (1, 3), (2, 2), (2, 3)}
In [4]:
neighbors((2, 4))
Out[4]:
[(1, 3), (2, 3), (3, 3), (1, 4), (3, 4), (1, 5), (2, 5), (3, 5)]
In [5]:
neighbor_counts(world)
Out[5]:
Counter({(0, 1): 1,
         (0, 2): 2,
         (0, 3): 2,
         (0, 4): 1,
         (1, 1): 1,
         (1, 2): 2,
         (1, 3): 2,
         (1, 4): 2,
         (2, 0): 1,
         (2, 1): 2,
         (2, 2): 4,
         (2, 3): 2,
         (2, 4): 2,
         (3, 0): 1,
         (3, 2): 2,
         (3, 3): 1,
         (3, 4): 1,
         (4, 0): 1,
         (4, 1): 1,
         (4, 2): 1})

run is a function to play n generations of Life:

In [6]:
def run(world, n):
    "Run the world for n generations. No display; just return the nth generation."
    for g in range(n):
        world = next_generation(world)
    return world
In [7]:
run(world, 100)
Out[7]:
{(1, 2), (1, 3), (2, 2), (2, 3)}

Display

Now let's see how to display each generation. We'll consider a window on the infinite plane, specified as a range of Xs and Ys coordinates. The function picture turns a world into a string showing what the world looks like, and display_run runs the world, displaying the picture at each step.

In [8]:
import time
from IPython.display import clear_output, display_html

LIVE  = '@'
EMPTY = '.'
PAD   = ' '

def display_run(world, n=10, Xs=range(10), Ys=range(10), pause=0.1):
    "Step and display the world for the given number of generations."
    for g in range(n + 1):
        clear_output()
        display_html('Generation {}, Population {}\n{}'
                     .format(g, len(world), pre(picture(world, Xs, Ys))), 
                     raw=True)
        time.sleep(pause)
        world = next_generation(world)
        
def pre(text): return '<pre>' + text + '</pre>'
        
def picture(world, Xs, Ys):
    "Return a picture: a grid of characters representing the cells in this window."
    def row(y): return PAD.join(LIVE if (x, y) in world else EMPTY for x in Xs)
    return '\n'.join(row(y) for y in Ys)
In [9]:
print(picture(world, range(5), range(5)))
. . . . .
. . . @ .
. @ . . .
. @ @ . .
. . . . .
In [10]:
display_run(world, 10, range(5), range(5))
Generation 10, Population 4
. . . . .
. . . . .
. @ @ . .
. @ @ . .
. . . . .

Interesting Worlds

Now let's take a look at some initial worlds that Life enthusiasts have discovered. It would be tedious to define these with an explicit set of (x, y) coordinates, so we will define the function shape that takes a picture as input and returns a world; shape and picture are more-or-less inverses.

In [11]:
def shape(picture, offset=(3, 3)):
    "Convert a graphical picture (e.g. '@ @ .\n. @@') into a world (set of cells)."
    cells = {(x, y) 
             for (y, row) in enumerate(picture.splitlines())
             for (x, c) in enumerate(row.replace(PAD, ''))
             if c == LIVE}
    return move(cells, offset)

def move(cells, offset):
    "Move/Translate/slide a set of cells by a (dx, dy) displacement/offset."
    (dx, dy) = offset
    return {(x+dx, y+dy) for (x, y) in cells}

blinker     = shape("@@@")
block       = shape("@@\n@@")
beacon      = block | move(block, (2, 2))
toad        = shape(".@@@\n@@@.")
glider      = shape(".@.\n..@\n@@@")
rpentomino  = shape(".@@\n@@.\n.@.", (36, 20))
line        = shape(".@@@@@@@@.@@@@@...@@@......@@@@@@@.@@@@@", (10, 10))
growth      = shape("@@@.@\n@\n...@@\n.@@.@\n@.@.@", (10, 10))

Here is how shape and move work:

In [12]:
shape("""@ @ .
         . @ @""")
Out[12]:
{(3, 3), (4, 3), (4, 4), (5, 4)}
In [13]:
block
Out[13]:
{(3, 3), (3, 4), (4, 3), (4, 4)}
In [14]:
move(block, (100, 200))
Out[14]:
{(103, 203), (103, 204), (104, 203), (104, 204)}

Let's run some examples. If you are viewing a static notebook, you will only see the last generation; rerun each cell to see all the generations.

In [15]:
display_run(blinker)
Generation 10, Population 3
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . @ @ @ . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
In [16]:
display_run(beacon)
Generation 10, Population 8
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . @ @ . . . . .
. . . @ @ . . . . .
. . . . . @ @ . . .
. . . . . @ @ . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
In [17]:
display_run(toad)
Generation 10, Population 6
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . @ @ @ . . .
. . . @ @ @ . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
In [18]:
display_run(glider, 15)
Generation 15, Population 5
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . @ . .
. . . . . . . . @ @
. . . . . . . @ @ .
In [19]:
display_run(rpentomino, 130, range(55), range(40))
Generation 130, Population 178
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . @ . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . @ @ . . . . . . . . . . . . . @ . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . @ . @ . . . . . . . . . . . . . @ . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . @ . . . . . . .
. . . . . . . . . . . @ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . @ @ @ @ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . @ @ . . . . . . .
. . . . . @ . @ @ @ @ . @ @ . . . . . . . . . . . . . . . . . . . . . . @ @ . . . . . . . @ . @ @ . . . . . .
. . . @ @ . . @ @ . . @ @ @ . . . . . . . . . . . . . . . . . . . . . . @ @ . . . . . . . @ . @ . . . @ @ . .
. . . . . @ @ @ . . . @ @ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . @ . . . . . . . .
. . . . . . . . . . . @ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . @ . @ . . . . . . . . . . . . . . . . . . . . . . @ @ . . . . . . . . . . . . . . . . . . . .
. . . . . . . . @ @ . . . . . . . . . . . . . . . . . @ . @ . . . @ @ . . . . . . . . . . . . . . . . . . . .
. . . . @ @ @ . . @ @ . . . . . . . . . . . . . . . . @ . @ . . . . . . . . . . . . . . . . @ @ . . . . . . .
. . . . @ @ . . . @ @ . . . . . . . . . . . . . . . . @ . . . . . . . . . . . . . . . . . @ @ @ . . @ @ . . .
. . . . @ . . . . . . . . . . . . . . . . . . . . . . . @ . . . . . . . . @ . . . . . . . @ . . @ . @ @ @ . .
. . . . @ @ . @ . . . . . . . . . . . . . . . . . . . . . . . . . . . @ @ . @ . . . . . . . @ . . . @ . @ . .
. . . . . @ @ @ . . . . . . . . . . . . . . . . . . . . . . . . @ . . . . . . @ . . . . . . . . . . . @ . . .
. . . . @ . . @ . . . . . . . . . . . . . . . . . . . . . . . @ . . . . . . @ . . . @ @ . . . . . . . . . . .
. . . . . @ . . . . . . . . . . . . . . . . . . . . . . . . @ . . . . . @ @ . . . @ . . @ . . . . . . . . . .
@ @ . @ @ @ . . . . . . . . . . . . . . . . . . . . . . . . @ . . @ . . . . . . . @ . . @ . . . . . . . . . .
. . @ @ . . . . . . . . . . . . . . . . . . . . . . . . . . @ @ . @ @ @ . . . . . . @ @ . . . . . . . . . . .
. . . @ @ . . . . . . . . . . . . . . . . . . . . . . . . . . . @ . . . @ . . . . . . . . . . . . . . . . . .
@ @ . @ @ . . . . . . . . . . @ @ . . . . . . . . . . . . . . . @ . . . . . . . . . . . . . . . . . . @ . . .
@ . . @ . . . . . . . . . . . @ @ . . . . . . . . . . . . . . . . . . @ . . . . . . . . . . . . . . . . @ @ .
@ @ @ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . @ @ . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . @ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . @ @ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . @ . @ . @ @ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . @ . @ . @ @ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . @ . @ @ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . @ . @ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
In [20]:
zoo = (move(blinker, (5, 25)) | move(glider, (8, 13)) | move(blinker, (20, 25))  |
       move(beacon, (24, 25)) | move(toad, (30, 25))  | move(block, (13, 25)) | move(block, (17, 33)))

display_run(zoo, 160, range(50), range(40))
Generation 160, Population 111
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . @ . . . . @ . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . @ . . . @ . @ . . @ . . @
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . @ @ . @ . . @ . @ @ . @ . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . @ @ . @ @ . . @ @ . . . @ . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . @ . . . @ . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . @ @ @ @ . @ @ @ @ @ . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . @ @ @ . . . @ . . . @ @ . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . @ @ . . @ . @ . @ . . . . . . . . . . @
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . @ . . . . . @ . . . . . . . . @ .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . @ @ . @ @ . . @ . . . . . . . @ @ .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . @ @ . . . . @ . . . . . . @ @ . @ .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . @ . . @ . . . . . . . . @ @ . @ @
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . @ @ @ . . . . . . . . @ @ . @ @
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . @ . . . . . . . . . . . @ @ .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . @ @ . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . @ @ . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . @ @ @ . . . . . @ @ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . @ . . @ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . @ @ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . @ @ . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . @ @ . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
In [21]:
display_run(growth, 100, range(40), range(40))
Generation 100, Population 72
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . @ . . . @ @ . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . @ . @ . . @ . @ . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . @ . . . @ . . @ . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . @ @ . . . . @ . @ . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . @ @ @ @ . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . @ @ @ . @ @ . @ @ . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . @ @ . . . @ . @ @ @ . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . @ @ @ . . . . . @ . . @ . . . . . . . . . . . . . . . . . . .
. . . . . . . @ @ @ @ @ . @ @ @ @ @ @ @ . . . . . . . . . . . . . . . . . . . .
. . . . . . . @ @ @ . @ @ @ @ @ @ @ @ . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . @ @ . @ @ @ @ @ . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

Outside of IPython

If you want to run this code in your terminal, outside of an Ipython/Jupyter notebook, you can remove the line:

from IPython.display import clear_output, display_html

and add these lines:

def clear_output(): print("\033[;H\033[2J") # ANSI terminal home and clear

def display_html(text, raw=False): print(text)

def pre(text): return text

Coding Kata

I once attended a code kata in which one of the exercises was to write the Game of Life without using any conditional (e.g. if) statements. I did it by using roughly the program shown here, but changing the lone if to a filter in next_generation:

In [22]:
def next_generation(world):
    "The set of live cells in the next generation."
    possible_cells = counts = neighbor_counts(world)
    def good(cell): return counts[cell] == 3 or (counts[cell] == 2 and cell in world)
    return set(filter(good, possible_cells))