Conway's Game of Life

Conway’s Game of Life has simple rules, yet can breed unpredictable behaviour. The game proceeds on a regular grid as follows: you update all cells at once, cells survive if there are more than one, but less than four, cells in neighbouring sites. Dead cells are (re)born if they have exactly three neighbours. It was originally played with counters on a Go board, but rose to fame with the increasing availability of computers, which can play it faster. I won’t go into more detail because many excellent descriptions already exist:

Nevertheless, here are a couple of my implementations which may be of interest.

Life in one (long) Line of Matlab

It is possible to play the Game of Life in a single line of Matlab. It weighs in with twice the characters of the single line of APL, but at least you can find them all on a normal keyboard. If you can see a way of condensing it from these 53 characters, claim your prize in the comment form below. . .

l=9;G=int8(rand(l));u=[2:l 1];d=[l 1:l-1];for i=u,n=G(u,:)+G(d,:)+G(:,u)+G(:,d)+G(u,u)+G(u,d)+G(d,u)+G(d,d);G=n==3|G&n==2,end

A Sedate Matlab Life

An exercise in conciseness, with the update rule complete in two statements and although it would be hard to beat a version in one line of APL, this Matlab code wins in the readability stakes! You'll need Matlab; it runs with Octave too, but creates a new window for the grid at each timestep, so move the display outside the loop (and try imshow instead).

Update - someone emailed to tell me that Matlab actually plays life as a built-in function - just type life. But having looked at the code, while the method is obviously identical to the one below, I don't mind claiming that the code here is a little more elegant.

Life grid, showing various interesting patterns

A stage of Life, as lived by Matlab

% life.m - Conway's Game of Life
%
% A grid of dead and living cells is made.
% Cells are born to three adjacent parents,
% and die of overcrowding or loneliness.
%        Iain Haslam, December 2005
 
len=50; GRID=int8(rand(len,len));
up=[2:len 1]; down=[len 1:len-1]; %the world is round
colormap(gray(2));
for i=1:100
    neighbours=GRID(up,:)+GRID(down,:)+GRID(:,up)+GRID(:,down)+...
        GRID(up,up)+GRID(up,down)+GRID(down,up)+GRID(down,down);
    GRID = neighbours==3 | GRID & neighbours==2;
    image(GRID*2); pause(0.02);
end

The code as presented here starts with a random grid of living and dead cells. If you want to see a particular pattern,
start with a blank grid GRID=zero(len,len), and add one of the following:

Blinker GRID(5,4:6)=1;
Toad GRID(5,4:6)=1;GRID(6,3:5)=1;
Glider GRID(3,1:3)=1;GRID(2,3)=1;GRID(1,2)=1;
Lightweight
spaceship
GRID(5,5:8)=1;GRID(4,9)=1;GRID(3:4,5)=1; GRID(2,6)=1;GRID(2,9)=1;
Diehard GRID(10,10:11)=1;GRID(11,11)=1; GRID(11,15:17)=1;GRID(9,16)=1;

Python

The Matlab version only allows you to define the initial grid by altering the code. This Python version allows you to toggle each cell's state of health with a mouse-click.

It also serves as a concise example of how to create a useful Tk Dialog with Python. Naturally, you'll need Python and
TkInter installed to run it, but they’re both free and top notch, so go for it.

#!/usr/bin/python 
"""A minimal implementation of Conway's Game of Life.
 
Each cell's survival depends on the number of occupied nearest and
next-nearest neighbours (calculated in Grid::step). A living cell dies
of overcrowding or loneliness if it has more than three or fewer than
two neighbours; a dead cell is brought to life if it has exactly three
neighbours (determined in Cell::setNextState).
    Iain Haslam, June 2005.
""" 
from Tkinter import *
root = Tk()
 
class Cell(Label):
    DEAD = 0
    LIVE = 1
    def __init__(self,parent):
        Label.__init__(self,parent,relief="raised",width=2,borderwidth=1)
        self.bind("<Button-1>", self.toggle)
        self.displayState(Cell.DEAD)
 
    def toggle(self,event):
        self.displayState(1-self.state)
 
    def setNextState(self,numNeighbours):
        """Work out whether this cell will be alive at the next iteration.""" 
        if self.state==Cell.LIVE and \
            (numNeighbours>3 or numNeighbours<2):
            self.nextState = Cell.DEAD
        elif self.state==Cell.DEAD and numNeighbours==3:
            self.nextState = Cell.LIVE
        else:
            self.nextState = self.state
 
    def stepToNextState(self):
        self.displayState(self.nextState)
 
    def displayState(self,newstate):
        self.state = newstate
        if self.state==Cell.LIVE:
            self["bg"] = "black" 
        else:
            self["bg"] = "white" 
 
class Grid:
    def __init__(self,parent,sizex,sizey):
        self.sizex = sizex
        self.sizey = sizey
        #numpy.zeros(sizex,sizey) is a better choice,
        #but an additional dependency might be rude...
        self.cells = []
        for a in range(0,self.sizex):
            rowcells = []
            for b in range(0,self.sizey):
                c = Cell(parent)
                c.grid(row=b, column=a)
                rowcells.append(c)
            self.cells.append(rowcells)
        if sizex>10 and sizey>10:
            #Start with a glider
            self.cells[10][9].displayState(Cell.LIVE)
            self.cells[11][10].displayState(Cell.LIVE)
            self.cells[9][11].displayState(Cell.LIVE)
            self.cells[10][11].displayState(Cell.LIVE)
            self.cells[11][11].displayState(Cell.LIVE)
 
    def step(self):
        """Calculate then display the next iteration of the game of life.
 
        This function uses wraparound boundary conditions.
        """ 
        cells = self.cells
        for x in range(0,self.sizex):
            if x==0: x_down = self.sizex-1
            else: x_down = x-1
            if x==self.sizex-1: x_up = 0
            else: x_up = x+1
            for y in range(0,self.sizey):
                if y==0: y_down = self.sizey-1
                else: y_down = y-1
                if y==self.sizey-1: y_up = 0
                else: y_up = y+1
                sum = cells[x_down][y].state + cells[x_up][y].state + \
                    cells[x][y_down].state + cells[x][y_up].state + \
                    cells[x_down][y_down].state + cells[x_up][y_up].state + \
                    cells[x_down][y_up].state + cells[x_up][y_down].state
                cells[x][y].setNextState(sum)
        for row in cells:
            for cell in row:
                cell.stepToNextState()
 
    def clear(self):
        for row in self.cells:
            for cell in row:
                cell.displayState(Cell.DEAD)
 
if __name__ == "__main__":
    frame = Frame(root)
    frame.pack()
    grid = Grid(frame,25,25)
    bottomFrame = Frame(root)
    bottomFrame.pack(side=BOTTOM)
    buttonStep = Button(bottomFrame,text="Step",command=grid.step)
    buttonStep.pack(side=LEFT)
    buttonClear = Button(bottomFrame,text="Clear",command=grid.clear)
    buttonClear.pack(side=LEFT,after=buttonStep)
    root.mainloop()

Put that into a text file, run it through Python, and you'll get:

Screenshot of pylife running under Gnome. It's a bit bland.

Pylife running under Gnome

Martin Gardner's Colossal Book of Mathematics is based on the column where the Game of Life originally appeared, and it's plenty of fun. Here's a review (it's a pdf) - if you're impatient, just read the final two sentences.

Comments

Compact MATLAB Life

Some of the best tricks in writing MATLAB code is knowing when to use built-in functions. Maybe my compact version of Life ought to be in a category all its own for its use of conv2().

Following the same constraints as your version, here's one that weighs in at 89 characters:

g=rand(9)>.5;p=ones(3);p(5)=0;for(i=[1:9])n=conv2(double(g),p,'same');g=(n==3|g&n==2),end

If left to run indefinitely, it can be reduced to 85:

g=rand(9)>.5;p=ones(3);p(5)=0;while(1)n=conv2(double(g),p,'same');g=(n==3|g&n==2),end

One of the niceties of this approach is that if you take out the first command assigning g, and just let g be some variable in the workspace, the rest of the code will happily run on that grid. None of the rest of the code concerns itself very much with size. So do I get a prize?

Game of Life in one line of Python

I can't condense your Matlab Game of Life, but I have written a Python version in one line:

while True: a=[[([[sum([b[y1][x1] for b in [[[((-1<x2+dx<len(a[0])) and (-1<y2+dy<len(a))) and a[y2+dy][x2+dx] or 0 for x2 in range(len(a[0]))] for y2 in range(len(a))] for (dx,dy) in [(dx,dy) for dx in [-1,0,1] for dy in [-1,0,1] if (dy!=0 or dx!=0)]]]) for x1 in range(len(a[0]))] for y1 in range(len(a))][y][x]==3 or ([[sum([c[y3][x3] for c in [[[((-1<x4+dx<len(a[0])) and (-1<y4+dy<len(a))) and a[y4+dy][x4+dx] or 0 for x4 in range(len(a[0]))] for y4 in range(len(a))] for (dx,dy) in [(dx,dy) for dx in [-1,0,1] for dy in [-1,0,1] if (dy!=0 or dx!=0)]]]) for x3 in range(len(a[0]))] for y3 in range(len(a))][y][x]==2 and a[y][x]==1)) and 1 or 0 for x in range(len(a[0]))] for y in range(len(a))]

It's not as compact as the APL version and not as readable as the Matlab version, but it is possible.

For an explanation:
http://www.petercollingridge.co.uk/blog/python-game-of-life-in-one-line

Matlab version

I believe the one-line Matlab version given above is missing the code to display the results. In addition, it only shows calculates the first 9 generations. Here is the 'fixed' version. I've placed it on several lines because the syntax highlighter didn't like the one long line, to make it more readable, and be able to more easily see the similarities and differences.

l=9;G=int8(rand(l));
u=[2:l,1];d=[l,1:l-1];
while 1,
    n=G(u,:)+G(d,:)+G(:,u)+G(:,d)+G(u,u)+G(u,d)+G(d,u)+G(d,d);
    G=n==3|G&n==2;
    image(G);drawnow,
end

Here is a different approach that is shorter

s=9;G=rand(s)>.5;
o=-1:1;
while 1,
    n=G-G; for r=o,for c=o,n=n+circshift(G,[r,c]),end,end;
    G=G&n==4|n==3;
    image(G);drawnow;
end

both should be prefixed with colormap(gray(2)) to get a nicer display, and both must be terminated with ctrl-C