Conway's Game of Life

From Rosetta Code
Jump to: navigation, search
Task
Conway's Game of Life
You are encouraged to solve this task according to the task description, using any language you may know.

The Game of Life is a cellular automaton devised by the British mathematician John Horton Conway in 1970. It is the best-known example of a cellular automaton.

Conway's game of life is described here:

A cell C is represented by a 1 when alive or 0 when dead, in an m-by-m square array of cells. We calculate N - the sum of live cells in C's eight-location neighbourhood, then cell C is alive or dead in the next generation based on the following table:

   C   N                 new C
   1   0,1             ->  0  # Lonely
   1   4,5,6,7,8       ->  0  # Overcrowded
   1   2,3             ->  1  # Lives
   0   3               ->  1  # It takes three to give birth!
   0   0,1,2,4,5,6,7,8 ->  0  # Barren

Assume cells beyond the boundary are always dead.

The "game" is actually a zero-player game, meaning that its evolution is determined by its initial state, needing no input from human players. One interacts with the Game of Life by creating an initial configuration and observing how it evolves.

Although you should test your implementation on more complex examples such as the glider in a larger universe, show the action of the blinker (three adjoining cells in a row all alive), over three generations, in a 3 by 3 grid.

References

Contents

[edit] 6502 Assembly

Works with: [6502asm.com] version 1.2
randfill:   stx $01          ;$200 for indirect
ldx #$02  ;addressing
stx $02
randloop: lda $fe  ;generate random
and #$01  ;pixels on the
sta ($01),Y  ;screen
jsr inc0103
cmp #$00
bne randloop
lda $02
cmp #$06
bne randloop
 
 
clearmem: lda #$df  ;set $07df-$0a20
sta $01  ;to $#00
lda #$07
sta $02
clearbyte: lda #$00
sta ($01),Y
jsr inc0103
cmp #$20
bne clearbyte
lda $02
cmp #$0a
bne clearbyte
 
 
starttick:
copyscreen: lda #$00  ;set up source
sta $01  ;pointer at
sta $03  ;$01/$02 and
lda #$02  ;dest pointer
sta $02  ;at $03/$04
lda #$08
sta $04
ldy #$00
copybyte: lda ($01),Y  ;copy pixel to
sta ($03),Y  ;back buffer
jsr inc0103  ;increment pointers
cmp #$00  ;check to see
bne copybyte  ;if we're at $600
lda $02  ;if so, we've
cmp #$06  ;copied the
bne copybyte  ;entire screen
 
 
conway: lda #$df  ;apply conway rules
sta $01  ;reset the pointer
sta $03  ;to $#01df/$#07df
lda #$01  ;($200 - $21)
sta $02  ;($800 - $21)
lda #$07
sta $04
onecell: lda #$00  ;process one cell
ldy #$01  ;upper cell
clc
adc ($03),Y
ldy #$41  ;lower cell
clc
adc ($03),Y
chkleft: tax  ;check to see
lda $01  ;if we're at the
and #$1f  ;left edge
tay
txa
cpy #$1f
beq rightcells
leftcells: ldy #$00  ;upper-left cell
clc
adc ($03),Y
ldy #$20  ;left cell
clc
adc ($03),Y
ldy #$40  ;lower-left cell
clc
adc ($03),Y
chkright: tax  ;check to see
lda $01  ;if we're at the
and #$1f  ;right edge
tay
txa
cpy #$1e
beq evaluate
rightcells: ldy #$02  ;upper-right cell
clc
adc ($03),Y
ldy #$22  ;right cell
clc
adc ($03),Y
ldy #$42  ;lower-right cell
clc
adc ($03),Y
evaluate: ldx #$01  ;evaluate total
ldy #$21  ;for current cell
cmp #$03  ;3 = alive
beq storex
ldx #$00
cmp #$02  ;2 = alive if
bne storex  ;c = alive
lda ($03),Y
and #$01
tax
storex: txa  ;store to screen
sta ($01),Y
jsr inc0103  ;move to next cell
conwayloop: cmp #$e0  ;if not last cell,
bne onecell  ;process next cell
lda $02
cmp #$05
bne onecell
jmp starttick  ;run next tick
 
 
inc0103: lda $01  ;increment $01
cmp #$ff  ;and $03 as 16-bit
bne onlyinc01  ;pointers
inc $02
inc $04
onlyinc01: inc $01
lda $01
sta $03
rts

[edit] ACL2

(defun print-row (row)
(if (endp row)
nil
(prog2$ (if (first row)
(cw "[]")
(cw " "))
(print-row (rest row)))))
 
(defun print-grid-r (grid)
(if (endp grid)
nil
(progn$ (cw "|")
(print-row (first grid))
(cw "|~%")
(print-grid-r (rest grid)))))
 
(defun print-line (l)
(if (zp l)
nil
(prog2$ (cw "-")
(print-line (1- l)))))
 
(defun print-grid (grid)
(progn$ (cw "+")
(print-line (* 2 (len (first grid))))
(cw "+~%")
(print-grid-r grid)
(cw "+")
(print-line (* 2 (len (first grid))))
(cw "+~%")))
 
(defun neighbors-row-r (row)
(if (endp (rest (rest row)))
(list (if (first row) 1 0))
(cons (+ (if (first row) 1 0)
(if (third row) 1 0))
(neighbors-row-r (rest row)))))
 
(defun neighbors-row (row)
(cons (if (second row) 1 0)
(neighbors-row-r row)))
 
(defun zip+ (xs ys)
(if (or (endp xs) (endp ys))
(append xs ys)
(cons (+ (first xs) (first ys))
(zip+ (rest xs) (rest ys)))))
 
(defun counts-row (row)
(if (endp row)
nil
(cons (if (first row) 1 0)
(counts-row (rest row)))))
 
(defun neighbors-r (grid prev-counts curr-counts next-counts
prev-neighbors curr-neighbors
next-neighbors)
(if (endp (rest grid))
(list (zip+ (zip+ prev-counts
prev-neighbors)
(neighbors-row (first grid))))
(cons (zip+ (zip+ (zip+ prev-counts next-counts)
(zip+ prev-neighbors next-neighbors))
curr-neighbors)
(neighbors-r (rest grid)
curr-counts
next-counts
(counts-row (third grid))
curr-neighbors
next-neighbors
(neighbors-row (third grid))))))
 
(defun neighbors (grid)
(neighbors-r grid
nil
(counts-row (first grid))
(counts-row (second grid))
nil
(neighbors-row (first grid))
(neighbors-row (second grid))))
 
(defun life-rules-row (life neighbors)
(if (or (endp life) (endp neighbors))
nil
(cons (or (and (first life)
(or (= (first neighbors) 2)
(= (first neighbors) 3)))
(and (not (first life))
(= (first neighbors) 3)))
(life-rules-row (rest life) (rest neighbors)))))
 
(defun life-rules-r (grid neighbors)
(if (or (endp grid) (endp neighbors))
nil
(cons (life-rules-row (first grid) (first neighbors))
(life-rules-r (rest grid) (rest neighbors)))))
 
(defun conway-step (grid)
(life-rules-r grid (neighbors grid)))
 
(defun conway (grid steps)
(if (zp steps)
nil
(progn$ (print-grid grid)
(conway (conway-step grid) (1- steps)))))

Output:

+------+
|  []  |
|  []  |
|  []  |
+------+
+------+
|      |
|[][][]|
|      |
+------+
+------+
|  []  |
|  []  |
|  []  |
+------+

[edit] Ada

with Ada.Text_IO;  use Ada.Text_IO;
 
procedure Life is
type Cell is (O, X); -- Two states of a cell
-- Computation of neighborhood
function "+" (L, R : Cell) return Integer is
begin
case L is
when O =>
case R is
when O => return 0;
when X => return 1;
end case;
when X =>
case R is
when O => return 1;
when X => return 2;
end case;
end case;
end "+";
function "+" (L : Integer; R : Cell) return Integer is
begin
case R is
when O => return L;
when X => return L + 1;
end case;
end "+";
-- A colony of cells. The borders are dire and unhabited
type Petri_Dish is array (Positive range <>, Positive range <>) of Cell;
 
procedure Step (Culture : in out Petri_Dish) is
Above : array (Culture'Range (2)) of Cell := (others => O);
Left  : Cell;
This  : Cell;
begin
for I in Culture'First (1) + 1 .. Culture'Last (1) - 1 loop
Left := O;
for J in Culture'First (2) + 1 .. Culture'Last (2) - 1 loop
case Above (J-1) + Above (J) + Above (J+1) +
Left + Culture (I, J+1) +
Culture (I+1, J-1) + Culture (I+1, J) + Culture (I+1, J+1) is
when 2 => -- Survives if alive
This := Culture (I, J);
when 3 => -- Survives or else multiplies
This := X;
when others => -- Dies
This := O;
end case;
Above (J-1) := Left;
Left  := Culture (I, J);
Culture (I, J) := This;
end loop;
Above (Above'Last - 1) := Left;
end loop;
end Step;
 
procedure Put (Culture : Petri_Dish) is
begin
for I in Culture'Range loop
for J in Culture'Range loop
case Culture (I, J) is
when O => Put (' ');
when X => Put ('#');
end case;
end loop;
New_Line;
end loop;
end Put;
 
Blinker : Petri_Dish := (2..4 =>(O,O,X,O,O), 1|5 =>(O,O,O,O,O));
Glider  : Petri_Dish :=
( (O,O,O,O,O,O,O,O,O,O,O),
(O,O,X,O,O,O,O,O,O,O,O),
(O,O,O,X,O,O,O,O,O,O,O),
(O,X,X,X,O,O,O,O,O,O,O),
(O,O,O,O,O,O,O,O,O,O,O),
(O,O,O,O,O,O,O,O,O,O,O)
);
begin
for Generation in 1..3 loop
Put_Line ("Blinker" & Integer'Image (Generation));
Put (Blinker);
Step (Blinker);
end loop;
for Generation in 1..5 loop
Put_Line ("Glider" & Integer'Image (Generation));
Put (Glider);
Step (Glider);
end loop;
end Life;

The solution uses one cell thick border around square Petri dish as uninhabited dire land. This simplifies computations of neighborhood. Sample output contains 3 generations of the blinker and 5 of the glider:

[edit] Sample output:

Blinker 1

  #
  #
  #

Blinker 2


 ###


Blinker 3

  #
  #
  #

Glider 1

  #
   #
 ###


Glider 2


 # #
  ##
  #

Glider 3


   #
 # #
  ##

Glider 4


  #
   ##
  ##

Glider 5


   #
    #
  ###

[edit] ALGOL 68

See Conway's Game of Life/ALGOL 68

[edit] APL

[1]

[edit] AutoHotkey

ahk discussion

rows := cols := 10                               ; set grid dimensions
i = -1,0,1, -1,1, -1,0,1 ; neighbors' x-offsets
j = -1,-1,-1, 0,0, 1,1,1 ; neighbors' y-offsets
StringSplit i, i, `, ; make arrays
StringSplit j, j, `,
 
Loop % rows { ; setup grid of checkboxes
r := A_Index, y := r*17-8 ; looks good in VISTA
Loop % cols {
c := A_Index, x := c*17-5
Gui Add, CheckBox, x%x% y%y% w17 h17 vv%c%_%r% gCheck
}
}
Gui Add, Button, % "x12 w" x+2, step ; button to step to next generation
Gui Show
Return
 
Check:
GuiControlGet %A_GuiControl% ; manual set of cells
Return
 
ButtonStep: ; move to next generation
Loop % rows {
r := A_Index
Loop % cols {
c := A_Index, n := 0
Loop 8 ; w[x,y] <- new states
x := c+i%A_Index%, y := r+j%A_Index%, n += 1=v%x%_%y%
GuiControl,,v%c%_%r%,% w%c%_%r% := v%c%_%r% ? n=2 || n=3 : n=3
}
}
Loop % rows { ; update v[x,y] = states
r := A_Index
Loop % cols
v%A_Index%_%r% := w%A_Index%_%r%
}
Return
 
GuiClose: ; exit when GUI is closed
ExitApp

[edit] AWK

 
BEGIN { c=220; d=619; i=10000; printf("\033[2J");
while(i--) m[i]=0;
while(d--) m[int(rand()*1000)]=1;
while(c--){
for(i=52; i<=949; i++){
d=m[i-1]+m[i+1]+m[i-51]+m[i-50]+m[i-49]+m[i+49]+m[i+50]+m[i+51];
n[i]=m[i];
if(m[i]==0 && d==3) n[i]=1;
else if(m[i]==1 && d<2) n[i]=0;
else if(m[i]==1 && d>3) n[i]=0;
}
printf("\033[1;1H");
for(i=1;i<=1000;i++)
{
if(n[i]) printf("O"); else printf(".");
m[i]=n[i];
if(!(i%50)) printf("\n");
}
printf("%3d\n",c); x=30000; while(x--) ;
}
}
 

[edit] BASIC256

Saving to PNG files function is omited. You can find it in the Galton box animation example.

"Thunderbird" methuselah evolution in the Game of Life (created with BASIC-256)
X = 59 : Y = 35 : H = 4
 
fastgraphics
graphsize X*H,Y*H
 
dim c(X,Y) : dim cn(X,Y) : dim cl(X,Y)
c[X/2-1,Y/3+1] = 1 : c[X/2,Y/3+1] = 1 : c[X/2+1,Y/3+1] = 1 # Thunderbird methuselah
c[X/2,Y/3+3] = 1 : c[X/2,Y/3+4] = 1 : c[X/2,Y/3+5] = 1
 
s = 0
do
color black
rect 0,0,graphwidth,graphheight
alive = 0 : stable = 1
s = s + 1
for y = 0 to Y-1
for x = 0 to X-1
xm1 = (x-1+X)%X : xp1 = (x+1+X)%X
ym1 = (y-1+Y)%Y : yp1 = (y+1+Y)%Y
cn[x,y] = c[xm1,y] + c[xp1,y]
cn[x,y] = c[xm1,ym1] + c[x,ym1] + c[xp1,ym1] + cn[x,y]
cn[x,y] = c[xm1,yp1] + c[x,yp1] + c[xp1,yp1] + cn[x,y]
if c[x,y] = 1 then
if cn[x,y] < 2 or cn[x,y] > 3 then
cn[x,y] = 0
else
cn[x,y] = 1
alive = alive + 1
end if
else
if cn[x,y] = 3 then
cn[x,y] = 1
alive = alive + 1
else
cn[x,y] = 0
end if
end if
if c[x,y] then
if cn[x,y] then
if cl[x,y] then color purple # adult
if not cl[x,y] then color green # newborn
else
if cl[x,y] then color red # old
if not cl[x,y] then color yellow # shortlived
end if
rect x*H,y*H,H,H
end if
next x
next y
refresh
pause 0.06
# Copy arrays
for i = 0 to X-1
for j = 0 to Y-1
if cl[i,j]<>cn[i,j] then stable = 0
cl[i,j] = c[i,j]
c[i,j] = cn[i,j]
next j
next i
until not alive or stable
 
if not alive then
print "Died in "+s+" iterations"
color black
rect 0,0,graphwidth,graphheight
refresh
else
print "Stabilized in "+(s-2)+" iterations"
end if

Text output:

Stabilized in 243 iterations

[edit] BBC BASIC

      dx% = 64
dy% = 64
DIM old&(dx%+1,dy%+1), new&(dx%+1,dy%+1)
VDU 23,22,dx%*4;dy%*4;16,16,16,0
OFF
 
REM Set blinker:
old&(50,50) = 1 : old&(50,51) = 1 : old&(50,52) = 1
REM Set glider:
old&(5,7) = 1 : old&(6,7) = 1 : old&(7,7) = 1 : old&(7,6) = 1 : old&(6,5) = 1
 
REM Draw initial grid:
FOR X% = 1 TO dx%
FOR Y% = 1 TO dy%
IF old&(X%,Y%) GCOL 11 ELSE GCOL 4
PLOT 69, X%*8-6, Y%*8-4
NEXT
NEXT X%
 
REM Run:
GCOL 4,0
REPEAT
FOR X% = 1 TO dx%
FOR Y% = 1 TO dy%
S% = old&(X%-1,Y%) + old&(X%,Y%-1) + old&(X%-1,Y%-1) + old&(X%+1,Y%-1) + \
\ old&(X%+1,Y%) + old&(X%,Y%+1) + old&(X%-1,Y%+1) + old&(X%+1,Y%+1)
O% = old&(X%,Y%)
N% = -(S%=3 OR (O%=1 AND S%=2))
new&(X%,Y%) = N%
IF N%<>O% PLOT X%*8-6, Y%*8-4
NEXT
NEXT X%
SWAP old&(), new&()
WAIT 30
UNTIL FALSE

Output:
Lifebbc.gif

[edit] C

Play game of life on your console: gcc -std=c99 -Wall game.c; ./a.out [width] [height]

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
 
#define for_x for (int x = 0; x < w; x++)
#define for_y for (int y = 0; y < h; y++)
#define for_xy for_x for_y
void show(void *u, int w, int h)
{
int (*univ)[w] = u;
printf("\033[H");
for_y {
for_x printf(univ[y][x] ? "\033[07m \033[m" : " ");
printf("\033[E");
}
fflush(stdout);
}
 
void evolve(void *u, int w, int h)
{
unsigned (*univ)[w] = u;
unsigned new[h][w];
 
for_y for_x {
int n = 0;
for (int y1 = y - 1; y1 <= y + 1; y1++)
for (int x1 = x - 1; x1 <= x + 1; x1++)
if (univ[(y1 + h) % h][(x1 + w) % w])
n++;
 
if (univ[y][x]) n--;
new[y][x] = (n == 3 || (n == 2 && univ[y][x]));
}
for_y for_x univ[y][x] = new[y][x];
}
 
void game(int w, int h)
{
unsigned univ[h][w];
for_xy univ[y][x] = rand() < RAND_MAX / 10 ? 1 : 0;
while (1) {
show(univ, w, h);
evolve(univ, w, h);
usleep(200000);
}
}
 
int main(int c, char **v)
{
int w = 0, h = 0;
if (c > 1) w = atoi(v[1]);
if (c > 2) h = atoi(v[2]);
if (w <= 0) w = 30;
if (h <= 0) h = 30;
game(w, h);
}

Also see Conway's Game of Life/C

[edit] C++

Considering that the simplest implementation in C++ would lack any use of the object-oriented paradigm, this code was specifically written to demonstrate the various object-oriented features of C++. Thus, while it is somewhat verbose, it fully simulates Conway's Game of Life and is relatively simple to expand to feature different starting shapes.

#include <iostream>
#define HEIGHT 4
#define WIDTH 4
 
struct Shape {
public:
char xCoord;
char yCoord;
char height;
char width;
char **figure;
};
 
struct Glider : public Shape {
static const char GLIDER_SIZE = 3;
Glider( char x , char y );
~Glider();
};
 
struct Blinker : public Shape {
static const char BLINKER_HEIGHT = 3;
static const char BLINKER_WIDTH = 1;
Blinker( char x , char y );
~Blinker();
};
 
class GameOfLife {
public:
GameOfLife( Shape sh );
void print();
void update();
char getState( char state , char xCoord , char yCoord , bool toggle);
void iterate(unsigned int iterations);
private:
char world[HEIGHT][WIDTH];
char otherWorld[HEIGHT][WIDTH];
bool toggle;
Shape shape;
};
 
GameOfLife::GameOfLife( Shape sh ) :
shape(sh) ,
toggle(true)
{
for ( char i = 0; i < HEIGHT; i++ ) {
for ( char j = 0; j < WIDTH; j++ ) {
world[i][j] = '.';
}
}
for ( char i = shape.yCoord; i - shape.yCoord < shape.height; i++ ) {
for ( char j = shape.xCoord; j - shape.xCoord < shape.width; j++ ) {
if ( i < HEIGHT && j < WIDTH ) {
world[i][j] =
shape.figure[ i - shape.yCoord ][j - shape.xCoord ];
}
}
}
}
 
void GameOfLife::print() {
if ( toggle ) {
for ( char i = 0; i < HEIGHT; i++ ) {
for ( char j = 0; j < WIDTH; j++ ) {
std::cout << world[i][j];
}
std::cout << std::endl;
}
} else {
for ( char i = 0; i < HEIGHT; i++ ) {
for ( char j = 0; j < WIDTH; j++ ) {
std::cout << otherWorld[i][j];
}
std::cout << std::endl;
}
}
for ( char i = 0; i < WIDTH; i++ ) {
std::cout << '=';
}
std::cout << std::endl;
}
 
void GameOfLife::update() {
if (toggle) {
for ( char i = 0; i < HEIGHT; i++ ) {
for ( char j = 0; j < WIDTH; j++ ) {
otherWorld[i][j] =
GameOfLife::getState(world[i][j] , i , j , toggle);
}
}
toggle = !toggle;
} else {
for ( char i = 0; i < HEIGHT; i++ ) {
for ( char j = 0; j < WIDTH; j++ ) {
world[i][j] =
GameOfLife::getState(otherWorld[i][j] , i , j , toggle);
}
}
toggle = !toggle;
}
}
 
char GameOfLife::getState( char state, char yCoord, char xCoord, bool toggle ) {
char neighbors = 0;
if ( toggle ) {
for ( char i = yCoord - 1; i <= yCoord + 1; i++ ) {
for ( char j = xCoord - 1; j <= xCoord + 1; j++ ) {
if ( i == yCoord && j == xCoord ) {
continue;
}
if ( i > -1 && i < HEIGHT && j > -1 && j < WIDTH ) {
if ( world[i][j] == 'X' ) {
neighbors++;
}
}
}
}
} else {
for ( char i = yCoord - 1; i <= yCoord + 1; i++ ) {
for ( char j = xCoord - 1; j <= xCoord + 1; j++ ) {
if ( i == yCoord && j == xCoord ) {
continue;
}
if ( i > -1 && i < HEIGHT && j > -1 && j < WIDTH ) {
if ( otherWorld[i][j] == 'X' ) {
neighbors++;
}
}
}
}
}
if (state == 'X') {
return ( neighbors > 1 && neighbors < 4 ) ? 'X' : '.';
}
else {
return ( neighbors == 3 ) ? 'X' : '.';
}
}
 
void GameOfLife::iterate( unsigned int iterations ) {
for ( int i = 0; i < iterations; i++ ) {
print();
update();
}
}
 
Glider::Glider( char x , char y ) {
xCoord = x;
yCoord = y;
height = GLIDER_SIZE;
width = GLIDER_SIZE;
figure = new char*[GLIDER_SIZE];
for ( char i = 0; i < GLIDER_SIZE; i++ ) {
figure[i] = new char[GLIDER_SIZE];
}
for ( char i = 0; i < GLIDER_SIZE; i++ ) {
for ( char j = 0; j < GLIDER_SIZE; j++ ) {
figure[i][j] = '.';
}
}
figure[0][1] = 'X';
figure[1][2] = 'X';
figure[2][0] = 'X';
figure[2][1] = 'X';
figure[2][2] = 'X';
}
 
Glider::~Glider() {
for ( char i = 0; i < GLIDER_SIZE; i++ ) {
delete[] figure[i];
}
delete[] figure;
}
 
Blinker::Blinker( char x , char y ) {
xCoord = x;
yCoord = y;
height = BLINKER_HEIGHT;
width = BLINKER_WIDTH;
figure = new char*[BLINKER_HEIGHT];
for ( char i = 0; i < BLINKER_HEIGHT; i++ ) {
figure[i] = new char[BLINKER_WIDTH];
}
for ( char i = 0; i < BLINKER_HEIGHT; i++ ) {
for ( char j = 0; j < BLINKER_WIDTH; j++ ) {
figure[i][j] = 'X';
}
}
}
 
Blinker::~Blinker() {
for ( char i = 0; i < BLINKER_HEIGHT; i++ ) {
delete[] figure[i];
}
delete[] figure;
}
 
int main() {
Glider glider(0,0);
GameOfLife gol(glider);
gol.iterate(5);
Blinker blinker(1,0);
GameOfLife gol2(blinker);
gol2.iterate(4);
}
 

Which outputs first a glider, then a blinker, over a few iterations. The following output is reformatted for convenience.

.X.. .... .... .... ....
..X. X.X. ..X. .X.. ..X.
XXX. .XX. X.X. ..XX ...X
.... .X.. .XX. .XX. .XXX
==== ==== ==== ==== ====

.X.. .... .X.. 
.X.. XXX. .X..
.X.. .... .X..
.... .... ....
==== ==== ====

[edit] C#

using System;
 
// made by Colby Newman 6/22/14
 
namespace ConwaysGameofLife
{
class Program
{
static void Main(string[] args)
{
int[,] board = {
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, //
{ 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, //
{ 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 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 }, // This section is where the 'dead' (0) and 'alive' (1) tiles
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // are created. To add tiles, change the corresponding 0 to a 1,
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // and to remove, vice versa
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, //
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Given:
{ 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 1x Glider
{ 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 1x Flip-Flop-Whatever
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } // 1x Still Life
};
drawBoard(board);
while (true)
{
board = updateBoard(board);
drawBoard(board);
}
}
 
static void drawBoard(int[,] board) // draw board
{
Console.Clear();
for (int i = 0; i <= 11; i++)
{
for (int j = 0; j <= 11; j++)
{
//Console.SetCursorPosition(j, i);
Console.Write((board[i, j] == 1) ? "██" : "||");
}
Console.WriteLine();
}
}
 
static int[,] updateBoard(int[,] board) // update board
{
int[,] newboard = new int[12,12];
for (int i = 0; i <= 11; i++)
{
for (int j = 0; j <= 11; j++)
{
int neighbors = 0;
try { neighbors += board[i, j + 1]; }
catch { }
try { neighbors += board[i, j - 1]; }
catch { }
try { neighbors += board[i + 1, j]; }
catch { }
try { neighbors += board[i - 1, j]; } // we try and catch because some indexes are off
catch { } // the board (like if it was on a border) and are outside
try { neighbors += board[i + 1, j + 1]; } // the bounds of the array
catch { }
try { neighbors += board[i - 1, j - 1]; }
catch { }
try { neighbors += board[i + 1, j - 1]; }
catch { }
try { neighbors += board[i - 1, j + 1]; }
catch { }
if (neighbors < 2) newboard[i, j] = 0; // underpopulation, dies
if (neighbors > 3) newboard[i, j] = 0; // overpopulation, dies
if (board[i, j] == 0 && neighbors == 3) newboard[i, j] = 1; // a cell is born!
if (neighbors >= 2 && neighbors <= 3 && board[i, j] == 1) newboard[i, j] = 1; // else same as given
}
}
return newboard;
}
}
}

Sample Output
First frame. Keep in mind that the program runs until stopped by the user, calculating the board and generating new frames.

||||||||||||||||||||||||
||||██||||||||||||||||||
||||||██||||||||██████||
||██████||||||||||||||||
||||||||||||||||||||||||
||||||||||||||||||||||||
||||||||||||||||||||||||
||||||||||||||||||||||||
||||||||||||||||||||||||
||████||||||||||||||||||
||████||||||||||||||||||
||||||||||||||||||||||||

[edit] Clojure

Based on the implementation by Christophe Grand here: http://clj-me.cgrand.net/2011/08/19/conways-game-of-life/ This implementation models the live cells as a set of coordinates.

(defn moore-neighborhood [[x y]]
(for [dx [-1 0 1]
dy [-1 0 1]
:when (not (= [dx dy] [0 0]))]
[(+ x dx) (+ y dy)]))
 
(defn step [set-of-cells]
(set (for [[cell count] (frequencies (mapcat moore-neighborhood set-of-cells))
:when (or (= 3 count)
(and (= 2 count) (contains? set-of-cells cell)))]
cell)))
 
(defn print-world
([set-of-cells] (print-world set-of-cells 10))
([set-of-cells world-size]
(let [r (range 0 (+ 1 world-size))]
(pprint (for [y r] (apply str (for [x r] (if (set-of-cells [x y]) \# \.))))))))
 
(defn run-life [world-size num-steps set-of-cells]
(loop [s num-steps
cells set-of-cells]
(print-world cells world-size)
(when (< 0 s)
(recur (- s 1) (step cells)))))
 
(def *blinker* #{[1 2] [2 2] [3 2]})
(def *glider* #{[1 0] [2 1] [0 2] [1 2] [2 2]})
 

[edit] Common Lisp

(defun next-life (array &optional results)
(let* ((dimensions (array-dimensions array))
(results (or results (make-array dimensions :element-type 'bit))))
(destructuring-bind (rows columns) dimensions
(labels ((entry (row col)
"Return array(row,col) for valid (row,col) else 0."
(if (or (not (< -1 row rows))
(not (< -1 col columns)))
0
(aref array row col)))
(neighbor-count (row col &aux (count 0))
"Return the sum of the neighbors of (row,col)."
(dolist (r (list (1- row) row (1+ row)) count)
(dolist (c (list (1- col) col (1+ col)))
(unless (and (eql r row) (eql c col))
(incf count (entry r c))))))
(live-or-die? (current-state neighbor-count)
(if (or (and (eql current-state 1)
(<= 2 neighbor-count 3))
(and (eql current-state 0)
(eql neighbor-count 3)))
1
0)))
(dotimes (row rows results)
(dotimes (column columns)
(setf (aref results row column)
(live-or-die? (aref array row column)
(neighbor-count row column)))))))))
 
(defun print-grid (grid &optional (out *standard-output*))
(destructuring-bind (rows columns) (array-dimensions grid)
(dotimes (r rows grid)
(dotimes (c columns (terpri out))
(write-char (if (zerop (aref grid r c)) #\+ #\#) out)))))
 
(defun run-life (&optional world (iterations 10) (out *standard-output*))
(let* ((world (or world (make-array '(10 10) :element-type 'bit)))
(result (make-array (array-dimensions world) :element-type 'bit)))
(do ((i 0 (1+ i))) ((eql i iterations) world)
(terpri out) (print-grid world out)
(psetq world (next-life world result)
result world))))
(run-life (make-array '(3 3) 
:element-type 'bit
:initial-contents '((0 0 0)
(1 1 1)
(0 0 0)))
3)

produces

+++
###
+++

+#+
+#+
+#+

+++
###
+++

A version using a sparse list of living cells rather than an explicit board.

(defun moore-neighborhood (cell)
(let ((r '(-1 0 1)))
(mapcan
(lambda (delta-x)
(loop for delta-y in r
unless (and (= delta-x 0) (= delta-y 0))
collect (cons (+ (car cell) delta-x) (+ (cdr cell) delta-y))))
r)))
 
(defun frequencies (cells)
(let ((h (make-hash-table :test #'equal)))
(loop for c in cells
if (gethash c h)
do (incf (gethash c h))
else
do (setf (gethash c h) 1))
h))
 
(defun life-step (cells)
(let ((f (frequencies (mapcan #'moore-neighborhood cells))))
(loop for k being the hash-keys in f
when (or
(= (gethash k f) 3)
(and (= (gethash k f) 2) (member k cells :test #'equal)))
collect k)))
 
(defun print-world (live-cells &optional (world-size 10))
(dotimes (y world-size)
(dotimes (x world-size)
(if (member (cons x y) live-cells :test #'equal)
(format t "X")
(format t ".")))
(format t "~%")))
 
(defun run-life (world-size steps cells)
(print-world cells world-size)
(format t "~%")
(when (< 0 steps)
(run-life world-size (- steps 1) (life-step cells))))
 
(defparameter *blinker* '((1 . 2) (2 . 2) (3 . 2)))
(defparameter *glider* '((1 . 0) (2 . 1) (0 . 2) (1 . 2) (2 . 2)))

[edit] D

import std.stdio, std.string, std.algorithm, std.array, std.conv;
 
struct GameOfLife {
enum Cell : char { dead = ' ', alive = '#' }
Cell[][] grid, newGrid;
 
this(in int x, in int y) pure nothrow @safe {
grid = new typeof(grid)(y + 2, x + 2);
newGrid = new typeof(grid)(y + 2, x + 2);
}
 
void opIndexAssign(in string[] v, in size_t y, in size_t x)
pure /*nothrow*/ @safe /*@nogc*/ {
foreach (immutable nr, row; v)
foreach (immutable nc, state; row)
grid[y + nr][x + nc] = state.to!Cell;
}
 
void iteration() pure nothrow @safe @nogc {
newGrid[0][] = Cell.dead;
newGrid[$ - 1][] = Cell.dead;
foreach (row; newGrid)
row[0] = row[$ - 1] = Cell.dead;
 
foreach (immutable r; 1 .. grid.length - 1)
foreach (immutable c; 1 .. grid[0].length - 1) {
uint count = 0;
foreach (immutable i; -1 .. 2)
foreach (immutable j; -1 .. 2)
if (i != 0 || j != 0)
count += grid[r + i][c + j] == Cell.alive;
immutable a = count == 3 ||
(count == 2 && grid[r][c] == Cell.alive);
newGrid[r][c] = a ? Cell.alive : Cell.dead;
}
 
grid.swap(newGrid);
}
 
string toString() const pure /*nothrow @safe*/ {
auto ret = "-".replicate(grid[0].length - 1) ~ "\n";
foreach (const row; grid[1 .. $ - 1])
ret ~= "|%(%c%)|\n".format(row[1 .. $ - 1]);
return ret ~ "-".replicate(grid[0].length - 1);
}
}
 
void main() /*@safe*/ {
immutable glider1 = [" #", "# #", " ##"];
immutable glider2 = ["# ", "# #", "## "];
 
auto uni = GameOfLife(60, 20);
uni[3, 2] = glider1;
uni[3, 15] = glider2;
uni[3, 19] = glider1;
uni[3, 32] = glider2;
uni[5, 50] = [" # #", "# ", "# #", "#### "];
uni.writeln;
 
foreach (immutable _; 0 .. 20) {
uni.iteration;
uni.writeln;
}
}
Output, first iteration:
-------------------------------------------------------------
|                                                            |
|                                                            |
|   #          #     #          #                            |
| # #          # # # #          # #                          |
|  ##          ##   ##          ##                 #  #      |
|                                                 #          |
|                                                 #   #      |
|                                                 ####       |
|                                                            |
|                                                            |
|                                                            |
|                                                            |
|                                                            |
|                                                            |
|                                                            |
|                                                            |
|                                                            |
|                                                            |
|                                                            |
|                                                            |
-------------------------------------------------------------

[edit] Faster Version

Same output.

import std.stdio, std.string, std.algorithm, std.typetuple,
std.array, std.conv;
 
template Iota(int start, int stop) {
static if (stop <= start)
alias Iota = TypeTuple!();
else
alias Iota = TypeTuple!(Iota!(start, stop - 1), stop - 1);
}
 
struct GameOfLife {
enum Cell : char { dead = ' ', alive = '#' }
Cell[] grid, newGrid;
immutable size_t nCols;
 
this(in int nx, in int ny) pure nothrow @safe {
nCols = nx + 2;
grid = new typeof(grid)(nCols * (ny + 2));
newGrid = new typeof(grid)(grid.length);
}
 
void opIndexAssign(in string[] v, in size_t y, in size_t x)
pure /*nothrow*/ @safe /*@nogc*/ {
foreach (immutable nr, const row; v)
foreach (immutable nc, immutable state; row)
grid[(y + nr) * nCols + x + nc] = state.to!Cell;
}
 
void iteration() pure nothrow @safe @nogc {
newGrid[0 .. nCols] = Cell.dead;
newGrid[$ - nCols .. $] = Cell.dead;
foreach (immutable nr; 1 .. (newGrid.length / nCols) - 1) {
newGrid[nr * nCols + 0] = Cell.dead;
newGrid[nr * nCols + nCols - 1] = Cell.dead;
}
 
foreach (immutable nr; 1 .. (grid.length / nCols) - 1) {
size_t nr_nCols = nr * nCols;
foreach (immutable nc; 1 .. nCols - 1) {
uint count = 0;
/*static*/ foreach (immutable i; Iota!(-1, 2))
/*static*/ foreach (immutable j; Iota!(-1, 2))
static if (i != 0 || j != 0)
count += (grid[nr_nCols + i * nCols + nc + j] == Cell.alive);
immutable a = count == 3 ||
(count == 2 && grid[nr_nCols + nc] == Cell.alive);
newGrid[nr_nCols + nc] = a ? Cell.alive : Cell.dead;
}
}
 
swap(grid, newGrid);
}
 
string toString() const pure /*nothrow @safe*/ {
string ret = "-".replicate(nCols - 1) ~ "\n";
foreach (immutable nr; 1 .. (grid.length / nCols) - 1)
ret ~= "|%(%c%)|\n".format(grid[nr * nCols + 1 .. nr * nCols + nCols - 1]);
return ret ~ "-".replicate(nCols - 1);
}
}
 
void main() {
immutable glider1 = [" #", "# #", " ##"];
immutable glider2 = ["# ", "# #", "## "];
 
auto uni = GameOfLife(60, 20);
uni[3, 2] = glider1;
uni[3, 15] = glider2;
uni[3, 19] = glider1;
uni[3, 32] = glider2;
uni[5, 50] = [" # #", "# ", "# #", "#### "];
uni.writeln;
 
foreach (immutable _; 0 .. 20) {
uni.iteration;
uni.writeln;
}
}

[edit] Dart

/**
* States of a cell. A cell is either [ALIVE] or [DEAD].
* The state contains its [symbol] for printing.
*/
class State {
const State(this.symbol);
 
static final ALIVE = const State('#');
static final DEAD = const State(' ');
 
final String symbol;
}
 
/**
* The "business rule" of the game. Depending on the count of neighbours,
* the [cellState] changes.
*/
class Rule {
Rule(this.cellState);
 
reactToNeighbours(int neighbours) {
if (neighbours == 3) {
cellState = State.ALIVE;
} else if (neighbours != 2) {
cellState = State.DEAD;
}
}
 
var cellState;
}
 
/**
* A coordinate on the [Grid].
*/
class Point {
const Point(this.x, this.y);
 
operator +(other) => new Point(x + other.x, y + other.y);
 
final int x;
final int y;
}
 
/**
* List of the relative indices of the 8 cells around a cell.
*/
class Neighbourhood {
List<Point> points() {
return [
new Point(LEFT, UP), new Point(MIDDLE, UP), new Point(RIGHT, UP),
new Point(LEFT, SAME), new Point(RIGHT, SAME),
new Point(LEFT, DOWN), new Point(MIDDLE, DOWN), new Point(RIGHT, DOWN)
];
}
 
static final LEFT = -1;
static final MIDDLE = 0;
static final RIGHT = 1;
static final UP = -1;
static final SAME = 0;
static final DOWN = 1;
}
 
/**
* The grid is an endless, two-dimensional [field] of cell [State]s.
*/
class Grid {
Grid(this.xCount, this.yCount) {
_field = new Map();
_neighbours = new Neighbourhood().points();
}
 
set(point, state) {
_field[_pos(point)] = state;
}
 
State get(point) {
var state = _field[_pos(point)];
return state != null ? state : State.DEAD;
}
 
int countLiveNeighbours(point) =>
_neighbours.filter((offset) => get(point + offset) == State.ALIVE).length;
 
_pos(point) => '${(point.x + xCount) % xCount}:${(point.y + yCount) % yCount}';
 
print() {
var sb = new StringBuffer();
iterate((point) { sb.add(get(point).symbol); }, (x) { sb.add("\n"); });
return sb.toString();
}
 
iterate(eachCell, [finishedRow]) {
for (var x = 0; x < xCount; x++) {
for (var y = 0; y < yCount; y++) {
eachCell(new Point(x, y));
}
if(finishedRow != null) {
finishedRow(x);
}
}
}
 
final xCount, yCount;
List<Point> _neighbours;
Map<String, State> _field;
}
 
/**
* The game updates the [grid] in each step using the [Rule].
*/
class Game {
Game(this.grid);
 
tick() {
var newGrid = createNewGrid();
 
grid.iterate((point) {
var rule = new Rule(grid.get(point));
rule.reactToNeighbours(grid.countLiveNeighbours(point));
newGrid.set(point, rule.cellState);
});
 
grid = newGrid;
}
 
createNewGrid() => new Grid(grid.xCount, grid.yCount);
 
printGrid() => print(grid.print());
 
Grid grid;
}
 
main() {
// Run the GoL with a blinker.
runBlinker();
}
 
runBlinker() {
var game = new Game(createBlinkerGrid());
 
for(int i = 0; i < 3; i++) {
game.printGrid();
game.tick();
}
game.printGrid();
}
 
createBlinkerGrid() {
var grid = new Grid(4, 4);
loadBlinker(grid);
return grid;
}
 
loadBlinker(grid) => blinkerPoints().forEach((point) => grid.set(point, State.ALIVE));
 
blinkerPoints() => [new Point(0, 1), new Point(1, 1), new Point(2, 1)];

Test cases driving the design of this code:

#import('<path to sdk>/lib/unittest/unittest.dart');
 
main() {
group('rules', () {
test('should let living but lonely cell die', () {
var rule = new Rule(State.ALIVE);
rule.reactToNeighbours(1);
expect(rule.cellState, State.DEAD);
});
test('should let proper cell live on', () {
var rule = new Rule(State.ALIVE);
rule.reactToNeighbours(2);
expect(rule.cellState, State.ALIVE);
});
test('should let dead cell with three neighbours be reborn', () {
var rule = new Rule(State.DEAD);
rule.reactToNeighbours(3);
expect(rule.cellState, State.ALIVE);
});
test('should let living cell with too many neighbours die', () {
var rule = new Rule(State.ALIVE);
rule.reactToNeighbours(4);
expect(rule.cellState, State.DEAD);
});
});
 
group('grid', () {
var origin = new Point(0, 0);
test('should have state', () {
var grid = new Grid(1, 1);
expect(grid.get(origin), State.DEAD);
grid.set(origin, State.ALIVE);
expect(grid.get(origin), State.ALIVE);
});
test('should have dimension', () {
var grid = new Grid(2, 3);
expect(grid.get(origin), State.DEAD);
grid.set(origin, State.ALIVE);
expect(grid.get(origin), State.ALIVE);
expect(grid.get(new Point(1, 2)), State.DEAD);
grid.set(new Point(1, 2), State.ALIVE);
expect(grid.get(new Point(1, 2)), State.ALIVE);
});
test('should be endless', () {
var grid = new Grid(2, 4);
grid.set(new Point(2, 4), State.ALIVE);
expect(grid.get(origin), State.ALIVE);
grid.set(new Point(-1, -1), State.ALIVE);
expect(grid.get(new Point(1, 3)), State.ALIVE);
});
test('should print itself', () {
var grid = new Grid(1, 2);
grid.set(new Point(0, 1), State.ALIVE);
expect(grid.print(), " #\n");
});
});
 
group('game', () {
test('should exists', () {
var game = new Game(null);
expect(game, isNotNull);
});
test('should create a new grid when ticked', () {
var grid = new Grid(1, 1);
var game = new Game(grid);
game.tick();
expect(game.grid !== grid);
});
test('should have a grid with the same dimension after tick', (){
var game = new Game(new Grid(2, 3));
game.tick();
expect(game.grid.xCount, 2);
expect(game.grid.yCount, 3);
});
test('should apply rules to middle cell', (){
var grid = new Grid(3, 3);
grid.set(new Point(1, 1), State.ALIVE);
var game = new Game(grid);
game.tick();
expect(game.grid.get(new Point(1, 1)), State.DEAD);
 
grid.set(new Point(0, 0), State.ALIVE);
grid.set(new Point(1, 0), State.ALIVE);
game = new Game(grid);
game.tick();
expect(game.grid.get(new Point(1, 1)), State.ALIVE);
});
test('should apply rules to all cells', (){
var grid = new Grid(3, 3);
grid.set(new Point(0, 1), State.ALIVE);
grid.set(new Point(1, 0), State.ALIVE);
grid.set(new Point(1, 1), State.ALIVE);
var game = new Game(grid);
game.tick();
expect(game.grid.get(new Point(0, 0)), State.ALIVE);
});
});
}

Output:

 #
 #
 #



###



 #
 #
 #



###


[edit] E

Just does three generations of a blinker in a dead-boundary grid, as specified. (User:Kevin Reid has graphical and wrapping versions.)

def gridWidth := 3
def gridHeight := 3
def X := 0..!gridWidth
def Y := 0..!gridHeight
 
def makeFlexList := <elib:tables.makeFlexList>
def makeGrid() {
def storage := makeFlexList.fromType(<type:java.lang.Boolean>, gridWidth * gridHeight)
storage.setSize(gridWidth * gridHeight)
 
def grid {
to __printOn(out) {
for y in Y {
out.print("[")
for x in X {
out.print(grid[x, y].pick("#", " "))
}
out.println("]")
}
}
to get(xb :int, yb :int) {
return if (xb =~ x :X && yb =~ y :Y) {
storage[y * gridWidth + x]
} else {
false
}
}
to put(x :X, y :Y, c :boolean) {
storage[y * gridWidth + x] := c
}
}
return grid
}
 
def mooreNeighborhood := [[-1,-1],[0,-1],[1,-1],[-1,0],[1,0],[-1,1],[0,1],[1,1]]
def computeNextLife(prevGrid, nextGrid) {
for y in Y {
for x in X {
var neighbors := 0
for [nx, ny] ? (prevGrid[x+nx, y+ny]) in mooreNeighborhood {
neighbors += 1
}
def self := prevGrid[x, y]
nextGrid[x, y] := (self && neighbors == 2 || neighbors == 3)
}
}
}
 
var currentFrame := makeGrid()
var nextFrame := makeGrid()
currentFrame[1, 0] := true
currentFrame[1, 1] := true
currentFrame[1, 2] := true
 
for _ in 1..3 {
def frame := nextFrame
computeNextLife(currentFrame, frame)
nextFrame := currentFrame
currentFrame := frame
println(currentFrame)
}



[edit] Erlang

 
 
-module(life).
 
-export([bang/1]).
 
 
-define(CHAR_DEAD, 32). % " "
-define(CHAR_ALIVE, 111). % "o"
-define(CHAR_BAR, 45). % "-"
 
-define(GEN_INTERVAL, 100).
 
 
-record(state, {x  :: non_neg_integer()
,y  :: non_neg_integer()
,n  :: pos_integer()
,bar  :: nonempty_string()
,board  :: array()
,gen_count  :: pos_integer()
,gen_duration :: non_neg_integer()
,print_time  :: non_neg_integer()
}).
 
 
%% ============================================================================
%% API
%% ============================================================================
 
bang(Args) ->
[X, Y] = [atom_to_integer(A) || A <- Args],
{Time, Board} = timer:tc(fun() -> init_board(X, Y) end),
State = #state{x = X
,y = Y
,n = X * Y
,bar = [?CHAR_BAR || _ <- lists:seq(1, X)]
,board = Board
,gen_count = 1 % Consider inital state to be generation 1
,gen_duration = Time
,print_time = 0 % There was no print time yet
},
life_loop(State).
 
 
%% ============================================================================
%% Internal
%% ============================================================================
 
life_loop(
#state{x = X
,y = Y
,n = N
,bar = Bar
,board = Board
,gen_count = GenCount
,gen_duration = Time
,print_time = LastPrintTime
}=State) ->
 
{PrintTime, ok} = timer:tc(
fun() ->
do_print_screen(Board, Bar, X, Y, N, GenCount, Time, LastPrintTime)
end
),
 
{NewTime, NewBoard} = timer:tc(
fun() ->
next_generation(X, Y, Board)
end
),
 
NewState = State#state{board = NewBoard
,gen_count = GenCount + 1
,gen_duration = NewTime
,print_time = PrintTime
},
 
NewTimeMil = NewTime / 1000,
NextGenDelay = at_least_zero(round(?GEN_INTERVAL - NewTimeMil)),
timer:sleep(NextGenDelay),
 
life_loop(NewState).
 
 
at_least_zero(Integer) when Integer >= 0 -> Integer;
at_least_zero(_) -> 0.
 
 
do_print_screen(Board, Bar, X, Y, N, GenCount, Time, PrintTime) ->
ok = do_print_status(Bar, X, Y, N, GenCount, Time, PrintTime),
ok = do_print_board(Board).
 
 
do_print_status(Bar, X, Y, N, GenCount, TimeMic, PrintTimeMic) ->
TimeSec = TimeMic / 1000000,
PrintTimeSec = PrintTimeMic / 1000000,
ok = io:format("~s~n", [Bar]),
ok = io:format(
"X: ~b Y: ~b CELLS: ~b GENERATION: ~b DURATION: ~f PRINT TIME: ~f~n",
[X, Y, N, GenCount, TimeSec, PrintTimeSec]
),
ok = io:format("~s~n", [Bar]).
 
 
do_print_board(Board) ->
% It seems that just doing a fold should be faster than map + to_list
% combo, but, after measuring several times, map + to_list has been
% consistently (nearly twice) faster than either foldl or foldr.
RowStrings = array:to_list(
array:map(
fun(_, Row) ->
array:to_list(
array:map(
fun(_, State) ->
state_to_char(State)
end,
Row
)
)
end,
Board
)
),
 
ok = lists:foreach(
fun(RowString) ->
ok = io:format("~s~n", [RowString])
end,
RowStrings
).
 
 
state_to_char(0) -> ?CHAR_DEAD;
state_to_char(1) -> ?CHAR_ALIVE.
 
 
next_generation(W, H, Board) ->
array:map(
fun(Y, Row) ->
array:map(
fun(X, State) ->
Neighbors = filter_offsides(H, W, neighbors(X, Y)),
States = neighbor_states(Board, Neighbors),
LiveNeighbors = lists:sum(States),
new_state(State, LiveNeighbors)
end,
Row
)
end,
Board
).
 
 
new_state(1, LiveNeighbors) when LiveNeighbors < 2 -> 0;
new_state(1, LiveNeighbors) when LiveNeighbors < 4 -> 1;
new_state(1, LiveNeighbors) when LiveNeighbors > 3 -> 0;
new_state(0, LiveNeighbors) when LiveNeighbors =:= 3 -> 1;
new_state(State, _LiveNeighbors) -> State.
 
 
neighbor_states(Board, Neighbors) ->
[array:get(X, array:get(Y, Board)) || {X, Y} <- Neighbors].
 
 
filter_offsides(H, W, Coordinates) ->
[{X, Y} || {X, Y} <- Coordinates, is_onside(X, Y, H, W)].
 
 
is_onside(X, Y, H, W) when (X >= 0) and (Y >= 0) and (X < W) and (Y < H) -> true;
is_onside(_, _, _, _) -> false.
 
 
neighbors(X, Y) ->
[{X + OffX, Y + OffY} || {OffX, OffY} <- offsets()].
 
 
offsets() ->
[offset(D) || D <- directions()].
 
 
offset('N') -> { 0, -1};
offset('NE') -> { 1, -1};
offset('E') -> { 1, 0};
offset('SE') -> { 1, 1};
offset('S') -> { 0, 1};
offset('SW') -> {-1, 1};
offset('W') -> {-1, 0};
offset('NW') -> {-1, -1}.
 
 
directions() ->
['N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW'].
 
 
init_board(X, Y) ->
array:map(fun(_, _) -> init_row(X) end, array:new(Y)).
 
 
init_row(X) ->
array:map(fun(_, _) -> init_cell_state() end, array:new(X)).
 
 
init_cell_state() ->
crypto:rand_uniform(0, 2).
 
 
atom_to_integer(Atom) ->
list_to_integer(atom_to_list(Atom)).
 
 



[edit] F#

The following F# implementation uses for visualization and is easily compiled into a standalone executable:
let count (a: _ [,]) x y =
let m, n = a.GetLength 0, a.GetLength 1
let mutable c = 0
for x in x-1..x+1 do
for y in y-1..y+1 do
if x>=0 && x<m && y>=0 && y<n && a.[x, y] then
c <- c + 1
if a.[x, y] then c-1 else c
 
let rule (a: _ [,]) x y =
match a.[x, y], count a x y with
| true, (2 | 3) | false, 3 -> true
| _ -> false
 
open System.Windows
open System.Windows.Media.Imaging
 
[<System.STAThread>]
do
let rand = System.Random()
let n = 256
let game = Array2D.init n n (fun _ _ -> rand.Next 2 = 0) |> ref
let image = Controls.Image(Stretch=Media.Stretch.Uniform)
let format = Media.PixelFormats.Gray8
let pixel = Array.create (n*n) 0uy
let update _ =
game := rule !game |> Array2D.init n n
for x in 0..n-1 do
for y in 0..n-1 do
pixel.[x+y*n] <- if (!game).[x, y] then 255uy else 0uy
image.Source <-
BitmapSource.Create(n, n, 1.0, 1.0, format, null, pixel, n)
Media.CompositionTarget.Rendering.Add update
Window(Content=image, Title="Game of Life")
|> (Application()).Run |> ignore

[edit] Forth

gencell uses an optimization for the core Game of Life rules: new state = (old state | neighbors == 3).

 \ The fast wrapping requires dimensions that are powers of 2.
1 6 lshift constant w \ 64
1 4 lshift constant h \ 16
 
 : rows w * 2* ;
1 rows constant row
h rows constant size
 
create world size allot
world value old
old w + value new
 
variable gens
 : clear world size erase 0 gens ! ;
 : age new old to new to old 1 gens +! ;
 
 : col+ 1+ ;
 : col- 1- dup w and + ; \ avoid borrow into row
 : row+ row + ;
 : row- row - ;
 : wrap ( i -- i ) [ size w - 1- ] literal and ;
 : w@ ( i -- 0/1 ) wrap old + c@ ;
 : w! ( 0/1 i -- ) wrap old + c! ;
 
 : foreachrow ( xt -- )
size 0 do I over execute row +loop drop ;
 
 : showrow ( i -- ) cr
old + w over + swap do I c@ if [char] * else bl then emit loop ;
 : show ['] showrow foreachrow cr ." Generation " gens @ . ;
 
 : sum-neighbors ( i -- i n )
dup col- row- w@
over row- w@ +
over col+ row- w@ +
over col- w@ +
over col+ w@ +
over col- row+ w@ +
over row+ w@ +
over col+ row+ w@ + ;
 : gencell ( i -- )
sum-neighbors over old + c@
or 3 = 1 and swap new + c! ;
 : genrow ( i -- )
w over + swap do I gencell loop ;
 : gen ['] genrow foreachrow age ;
 
 : life begin gen 0 0 at-xy show key? until ;
 
\ patterns
char | constant '|'
 : pat ( i addr len -- )
rot dup 2swap over + swap do
I c@ '|' = if drop row+ dup else
I c@ bl = 1+ over w! col+ then
loop 2drop ;
 
 : blinker s" ***" pat ;
 : toad s" ***| ***" pat ;
 : pentomino s" **| **| *" pat ;
 : pi s" **| **|**" pat ;
 : glider s" *| *|***" pat ;
 : pulsar s" *****|* *" pat ;
 : ship s" ****|* *| *| *" pat ;
 : pentadecathalon s" **********" pat ;
 : clock s" *| **|**| *" pat ;
 
clear 0 glider show
*
*
***
 
Generation 0 ok
gen show
 
* *
**
*
Generation 1 ok

[edit] Fortran

Works with: Fortran version 90 and later
 PROGRAM LIFE_2D
IMPLICIT NONE
 
INTEGER, PARAMETER :: gridsize = 10
LOGICAL :: cells(0:gridsize+1,0:gridsize+1) = .FALSE.
INTEGER :: i, j, generation=0
REAL :: rnums(gridsize,gridsize)
 
! Start patterns
! **************
! cells(2,1:3) = .TRUE.  ! Blinker
! cells(3,4:6) = .TRUE. ; cells(4,3:5) = .TRUE.  ! Toad
! cells(1,2) = .TRUE. ; cells(2,3) = .TRUE. ; cells(3,1:3) = .TRUE.  ! Glider
cells(3:5,3:5) = .TRUE. ; cells(6:8,6:8) = .TRUE. ! Figure of Eight
! CALL RANDOM_SEED
! CALL RANDOM_NUMBER(rnums)
! WHERE (rnums>0.6) cells(1:gridsize,1:gridsize) = .TRUE.  ! Random universe
 
CALL Drawgen(cells(1:gridsize, 1:gridsize), generation)
DO generation = 1, 8
CALL Nextgen(cells)
CALL Drawgen(cells(1:gridsize, 1:gridsize), generation)
END DO
 
CONTAINS
 
SUBROUTINE Drawgen(cells, gen)
LOGICAL, INTENT(IN OUT) :: cells(:,:)
INTEGER, INTENT(IN) :: gen
 
WRITE(*, "(A,I0)") "Generation ", gen
DO i = 1, SIZE(cells,1)
DO j = 1, SIZE(cells,2)
IF (cells(i,j)) THEN
WRITE(*, "(A)", ADVANCE = "NO") "#"
ELSE
WRITE(*, "(A)", ADVANCE = "NO") " "
END IF
END DO
WRITE(*,*)
END DO
WRITE(*,*)
END SUBROUTINE Drawgen
 
SUBROUTINE Nextgen(cells)
LOGICAL, INTENT(IN OUT) :: cells(0:,0:)
LOGICAL :: buffer(0:SIZE(cells, 1)-1, 0:SIZE(cells, 2)-1)
INTEGER :: neighbours, i, j
 
buffer = cells ! Store current status
DO i = 1, SIZE(cells, 1)-2
DO j = 1, SIZE(cells, 2)-2
if(buffer(i, j)) then
neighbours = sum(count(buffer(i-1:i+1, j-1:j+1), 1)) - 1
else
neighbours = sum(count(buffer(i-1:i+1, j-1:j+1), 1))
end if
 
SELECT CASE(neighbours)
CASE (0:1, 4:8)
cells(i,j) = .FALSE.
 
CASE (2)
! No change
 
CASE (3)
cells(i,j) = .TRUE.
END SELECT
 
END DO
END DO
END SUBROUTINE Nextgen
 
END PROGRAM LIFE_2D

[edit] Sample output:

Blinker
 Generation 0
    
  ### 
    
 
 Generation 1
  #  
  #  
  #  
 
 Generation 2
    
 ###
Figure of Eight (a period eight oscillator)
 Generation 0
           
           
   ###      
   ###      
   ###      
      ###   
      ###   
      ###   
           
           
 
 Generation 1
           
    #       
   # #      
  #   #     
   #   #    
    #   #   
     #   #  
      # #   
       #    
           
 
 Generation 2
           
    #       
   ###      
  ### #     
   #   #    
    #   #   
     # ###  
      ###   
       #    
           
 
 Generation 3
           
   ###      
  #         
  #   #     
  #  # #    
    # #  #  
     #   #  
         #  
      ###   
           
 
 Generation 4
    #       
   ##       
  # ##      
 ###  #     
   # # #    
    # # #   
     #  ### 
      ## #  
       ##   
       #    
 
 Generation 5
   ##       
           
 #   #      
 #    #     
   # # #    
    # # #   
     #    # 
      #   # 
           
       ##   
 
 Generation 6
           
    #       
           
  # ###     
    ## #    
    # ##    
     ### #  
           
       #    
           
 
 Generation 7
           
           
   ##       
   ## #     
       #    
    #       
     # ##   
       ##   
           
         
 
 Generation 8
           
           
   ###      
   ###      
   ###      
      ###   
      ###   
      ###

[edit] FunL

import lists.zipWithIndex
import util.Regex
 
data Rule( birth, survival )
 
val Mirek = Regex( '([0-8]+)/([0-8]+)' )
val Golly = Regex( 'B([0-8]+)/S([0-8]+)' )
 
def decode( rule ) =
def makerule( b, s ) = Rule( [int(d) | d <- b], [int(d) | d <- s] )
 
case rule
Mirek( s, b ) -> makerule( b, s )
Golly( b, s ) -> makerule( b, s )
_ -> error( "unrecognized rule: $rule" )
 
def fate( state, crowding, rule ) = crowding in rule( int(state) )
 
def crowd( buffer, x, y ) =
res = 0
 
def neighbour( x, y ) =
if x >= 0 and x < N and y >= 0 and y < N
res += int( buffer(x, y) )
 
for i <- x-1..x+1
neighbour( i, y - 1 )
neighbour( i, y + 1 )
 
neighbour( x - 1, y )
neighbour( x + 1, y )
res
 
def display( buffer ) =
for j <- 0:N
for i <- 0:N
print( if buffer(i, j) then '*' else '\u00b7' )
 
println()
 
def generation( b1, b2, rule ) =
for i <- 0:N, j <- 0:N
b2(i, j) = fate( b1(i, j), crowd(b1, i, j), rule )
 
def pattern( p, b, x, y ) =
for (r, j) <- zipWithIndex( list(WrappedString(p).stripMargin().split('\n')).drop(1).dropRight(1) )
for i <- 0:r.length()
b(x + i, y + j) = r(i) == '*'
 
var current = 0
val LIFE = decode( '23/3' )
val N = 4
val buffers = (array( N, N, (_, _) -> false ), array( N, N ))
 
def reset =
for i <- 0:N, j <- 0:N
buffers(0)(i, j) = false
 
current = 0
 
def iteration =
display( buffers(current) )
generation( buffers(current), buffers(current = (current + 1)%2), LIFE )
println( 5'-' )
 
// two patterns to be tested
blinker = '''
|
|***
'''
 
glider = '''
| *
| *
|***
'''
 
// load "blinker" pattern and run for three generations
pattern( blinker, buffers(0), 0, 0 )
 
repeat 3
iteration()
 
// clear grid, load "glider" pattern and run for five generations
reset()
pattern( glider, buffers(0), 0, 0 )
 
repeat 5
iteration()
Output:
····
***·
····
····
-----
·*··
·*··
·*··
····
-----
····
***·
····
····
-----
·*··
··*·
***·
····
-----
····
*·*·
·**·
·*··
-----
····
··*·
*·*·
·**·
-----
····
·*··
··**
·**·
-----
····
··*·
···*
·***
-----

[edit] Go

package main
 
import (
"bytes"
"fmt"
"math/rand"
"time"
)
 
type Field struct {
s [][]bool
w, h int
}
 
func NewField(w, h int) Field {
s := make([][]bool, h)
for i := range s {
s[i] = make([]bool, w)
}
return Field{s: s, w: w, h: h}
}
 
func (f Field) Set(x, y int, b bool) {
f.s[y][x] = b
}
 
func (f Field) Next(x, y int) bool {
on := 0
for i := -1; i <= 1; i++ {
for j := -1; j <= 1; j++ {
if f.State(x+i, y+j) && !(j == 0 && i == 0) {
on++
}
}
}
return on == 3 || on == 2 && f.State(x, y)
}
 
func (f Field) State(x, y int) bool {
for y < 0 {
y += f.h
}
for x < 0 {
x += f.w
}
return f.s[y%f.h][x%f.w]
}
 
type Life struct {
w, h int
a, b Field
}
 
func NewLife(w, h int) *Life {
a := NewField(w, h)
for i := 0; i < (w * h / 2); i++ {
a.Set(rand.Intn(w), rand.Intn(h), true)
}
return &Life{
a: a,
b: NewField(w, h),
w: w, h: h,
}
}
 
func (l *Life) Step() {
for y := 0; y < l.h; y++ {
for x := 0; x < l.w; x++ {
l.b.Set(x, y, l.a.Next(x, y))
}
}
l.a, l.b = l.b, l.a
}
 
func (l *Life) String() string {
var buf bytes.Buffer
for y := 0; y < l.h; y++ {
for x := 0; x < l.w; x++ {
b := byte(' ')
if l.a.State(x, y) {
b = '*'
}
buf.WriteByte(b)
}
buf.WriteByte('\n')
}
return buf.String()
}
 
func main() {
l := NewLife(80, 15)
for i := 0; i < 300; i++ {
l.Step()
fmt.Print("\x0c")
fmt.Println(l)
time.Sleep(time.Second / 30)
}
}

Running this program will compute and draw the first 300 "frames". The final frame looks like this:

        ** ****        *                                                        
         * **                                                                   
          *                                         **                          
*                                                  *  *                         
 *                   **        ****                 **                         *
  *                  **       *  **                                             
  *                      **        *                                            
                         **      *                                              
  ****              *        *                                     **         * 
 ***   **           *         ** ****                             *  *        **
* **     *  **      *              **                              **           
  *    *** ***                                                                  
            * **                                                                
         **   **       **                                                       
         ** *  *      * *                                                       

[edit] Haskell

import Data.Array.Unboxed
 
type Grid = UArray (Int,Int) Bool
-- The grid is indexed by (y, x).
 
life :: Int -> Int -> Grid -> Grid
{- Returns the given Grid advanced by one generation. -}
life w h old =
listArray b (map f (range b))
where b@((y1,x1),(y2,x2)) = bounds old
f (y, x) = ( c && (n == 2 || n == 3) ) || ( not c && n == 3 )
where c = get x y
n = count [get (x + x') (y + y') |
x' <- [-1, 0, 1], y' <- [-1, 0, 1],
not (x' == 0 && y' == 0)]
 
get x y | x < x1 || x > x2 = False
| y < y1 || y > y2 = False
| otherwise = old ! (y, x)
 
count :: [Bool] -> Int
count = length . filter id

Example of use:

import Data.List (unfoldr)
 
grid :: [String] -> (Int, Int, Grid)
grid l = (width, height, a)
where (width, height) = (length $ head l, length l)
a = listArray ((1, 1), (height, width)) $ concatMap f l
f = map g
g '.' = False
g _ = True
 
printGrid :: Int -> Grid -> IO ()
printGrid width = mapM_ f . split width . elems
where f = putStrLn . map g
g False = '.'
g _ = '#'
 
split :: Int -> [a] -> [[a]]
split n = takeWhile (not . null) . unfoldr (Just . splitAt n)
 
blinker = grid
[".#.",
".#.",
".#."]
 
glider = grid
["............",
"............",
"............",
".......###..",
".......#....",
"........#...",
"............"]
 
printLife :: Int -> (Int, Int, Grid) -> IO ()
printLife n (w, h, g) = mapM_ f $ take n $ iterate (life w h) g
where f g = do
putStrLn "------------------------------"
printGrid w g
 
main = printLife 10 glider

Here's the gridless version. It could probably be improved with some light use of Data.Set, but I leave that as an exercise for the reader. Note that the function lifeStep is the solution in its entirety. The rest of this code deals with printing and test data for the particular model of the world we're using.

module Main where
import Data.List
 
lifeStep :: [(Int, Int)] -> [(Int, Int)]
lifeStep cells = [head g | g <- grouped cells, viable g]
where grouped = group . sort . concatMap neighbors
neighbors (x, y) = [(x+dx, y+dy) | dx <- [-1..1], dy <- [-1..1], (dx,dy) /= (0,0)]
viable [_,_,_] = True
viable [c,_] = c `elem` cells
viable _ = False
 
 
showWorld :: [(Int, Int)] -> IO ()
showWorld cells = mapM_ putStrLn $ worldToGrid cells
where worldToGrid cells = [[cellChar (x, y) | x <- [least..greatest]] | y <- [least..greatest]]
cellChar cell = if cell `elem` cells then '#' else '.'
(least, greatest) = worldBounds cells
 
worldBounds cells = (least, greatest)
where least = min x y
greatest = max x' y'
(x, y) = head cells
(x', y') = last cells
 
runLife :: Int -> [(Int, Int)] -> IO ()
runLife steps cells = rec (steps - 1) cells
where rec 0 cells = showWorld cells
rec s cells = do showWorld cells
putStrLn ""
rec (s - 1) $ lifeStep cells
 
glider = [(1, 0), (2, 1), (0, 2), (1, 2), (2, 2)]
blinker = [(1, 0), (1, 1), (1, 2)]
 
main :: IO ()
main = do
putStrLn "Glider >> 10"
putStrLn "------------"
runLife 10 glider
putStrLn ""
putStrLn "Blinker >> 3"
putStrLn "------------"
runLife 3 blinker

[edit] Icon and Unicon

global limit
 
procedure main(args)
n := args[1] | 50 # default is a 50x50 grid
limit := args[2] | &null # optional limit to number of generations
write("Enter the starting pattern, end with EOF")
grid := getInitialGrid(n)
play(grid)
end
 
# This procedure reads in the initial pattern, inserting it
# into an nXn grid of cells. The nXn grid also gets a
# new border of empty cells, which just makes the test simpler
# for determining what do with a cell on each generation.
# It would be better to let the user move the cursor and click
# on cells to create/delete living cells, but this version
# assumes a simple ASCII terminal.
procedure getInitialGrid(n)
static notBlank, allStars
initial {
notBlank := ~' '
allStars := repl("*",*notBlank)
}
 
g := [] # store as an array of strings
 
put(g,repl(" ",n))
while r := read() do { # read in rows of grid
r := left(r,n) # force each to length n
put(g," "||map(r,notBlank,allStars)||" ") # and making any life a '*'
}
while *g ~= (n+2) do
put(g,repl(" ",n))
 
return g
end
 
# Simple-minded procedure to 'play' Life from a starting grid.
procedure play(grid)
while not allDone(grid) do {
display(grid)
grid := onePlay(grid)
}
end
 
# Display the grid
procedure display(g)
write(repl("-",*g[1]))
every write(!g)
write(repl("-",*g[1]))
end
 
# Compute one generation of Life from the current one.
procedure onePlay(g)
ng := []
every put(ng, !g) # new generation starts as copy of old
every ng[r := 2 to *g-1][c := 2 to *g-1] := case sum(g,r,c) of {
3: "*" # cell lives (or is born)
2: g[r][c] # cell unchanged
default: " " # cell dead
}
return ng
end
 
# Return the number of living cells surrounding the current cell.
procedure sum(g,r,c)
cnt := 0
every (i := -1 to 1, j := -1 to 1) do
if ((i ~= 0) | (j ~= 0)) & (g[r+i][c+j] == "*") then cnt +:= 1
return cnt
end
 
# Check to see if all the cells have died or we've exceeded the
# number of allowed generations.
procedure allDone(g)
static count
initial count := 0
return ((count +:= 1) > \limit) | (trim(!g) == " ")
end

A sample run:

->life 3 3
Enter the starting pattern, end with EOF

***
---
   
     
 *** 
   
   
---
---
   
  *  
  *  
  *
   
---
---
   
     
 *** 
   
   
---
->

[edit] J

Solution:

pad=: 0,0,~0,.0,.~]
life=: (_3 _3 (+/ e. 3+0,4&{)@,;._3 ])@pad
NB. the above could also be a one-line solution:
life=: (_3 _3 (+/ e. 3+0,4&{)@,;._3 ])@(0,0,~0,.0,.~])
 

In other words, given a life instance, the next generation can be found by:

  1. . adding extra empty cells, surrounding the life instance,
  2. . tessellating the result, finding every overlapping 3 by 3 subinstance,
  3. . totaling the number of live cells in each subinstance,
  4. . treating a subinstance as a live cell iff that total is a member of the sequence 3,x where x is 3 if the center cell was previously dead, and 4 if the center cell was previously alive (that said, note that 4 is also the index of the center cell, with the sub instance arranged as a flat list).

Example (showing generations 0, 1 and 2 of a blinker):

   life^:0 1 2 #:0 7 0
0 0 0
1 1 1
0 0 0
 
0 1 0
0 1 0
0 1 0
 
0 0 0
1 1 1
0 0 0

[edit] JAMES II/Rule-based Cellular Automata

Library: JAMES II
@caversion 1;
 
dimensions 2;
 
//using Moore neighborhood
neighborhood moore;
 
//available states
state DEAD, ALIVE;
 
/*
if current state is ALIVE and the
neighborhood does not contain 2 or
3 ALIVE states the cell changes to
DEAD
*/
rule{ALIVE}:!ALIVE{2,3}->DEAD;
 
/*
if current state is DEAD and there
are exactly 3 ALIVE cells in the
neighborhood the cell changes to
ALIVE
*/
rule{DEAD}:ALIVE{3}->ALIVE;

Animated output for the blinker example:

JAMES II Game of Life Blinker.gif

[edit] Java

public class GameOfLife{
public static void main(String[] args){
String[] dish= {
"_#_",
"_#_",
"_#_",};
int gens= 3;
for(int i= 0;i < gens;i++){
System.out.println("Generation " + i + ":");
print(dish);
dish= life(dish);
}
}
 
public static String[] life(String[] dish){
String[] newGen= new String[dish.length];
for(int row= 0;row < dish.length;row++){//each row
newGen[row]= "";
for(int i= 0;i < dish[row].length();i++){//each char in the row
String above= "";//neighbors above
String same= "";//neighbors in the same row
String below= "";//neighbors below
if(i == 0){//all the way on the left
//no one above if on the top row
//otherwise grab the neighbors from above
above= (row == 0) ? null : dish[row - 1].substring(i,
i + 2);
same= dish[row].substring(i + 1, i + 2);
//no one below if on the bottom row
//otherwise grab the neighbors from below
below= (row == dish.length - 1) ? null : dish[row + 1]
.substring(i, i + 2);
}else if(i == dish[row].length() - 1){//right
//no one above if on the top row
//otherwise grab the neighbors from above
above= (row == 0) ? null : dish[row - 1].substring(i - 1,
i + 1);
same= dish[row].substring(i - 1, i);
//no one below if on the bottom row
//otherwise grab the neighbors from below
below= (row == dish.length - 1) ? null : dish[row + 1]
.substring(i - 1, i + 1);
}else{//anywhere else
//no one above if on the top row
//otherwise grab the neighbors from above
above= (row == 0) ? null : dish[row - 1].substring(i - 1,
i + 2);
same= dish[row].substring(i - 1, i)
+ dish[row].substring(i + 1, i + 2);
//no one below if on the bottom row
//otherwise grab the neighbors from below
below= (row == dish.length - 1) ? null : dish[row + 1]
.substring(i - 1, i + 2);
}
int neighbors= getNeighbors(above, same, below);
if(neighbors < 2 || neighbors > 3){
newGen[row]+= "_";//<2 or >3 neighbors -> die
}else if(neighbors == 3){
newGen[row]+= "#";//3 neighbors -> spawn/live
}else{
newGen[row]+= dish[row].charAt(i);//2 neighbors -> stay
}
}
}
return newGen;
}
 
public static int getNeighbors(String above, String same, String below){
int ans= 0;
if(above != null){//no one above
for(char x: above.toCharArray()){//each neighbor from above
if(x == '#') ans++;//count it if someone is here
}
}
for(char x: same.toCharArray()){//two on either side
if(x == '#') ans++;//count it if someone is here
}
if(below != null){//no one below
for(char x: below.toCharArray()){//each neighbor below
if(x == '#') ans++;//count it if someone is here
}
}
return ans;
}
 
public static void print(String[] dish){
for(String s: dish){
System.out.println(s);
}
}
}

Output:

Generation 0:
_#_
_#_
_#_
Generation 1:
___
###
___
Generation 2:
_#_
_#_
_#_

[edit] Stretch

This fills in a random 10% of the grid, then activates the Game on it. Uncomment the call to the setCustomConfig function to use your own input. Just mind the grid limits. Use the input file given below to create a cool screensaver on your terminal.

 
//package conway;
 
import java.util.*;
import java.io.*;
 
public class GameOfLife
{
//Set grid size
int l=20,b=60;
public static void main(String[] args)
{
 
GameOfLife now=new GameOfLife();
now.setGame();
}
void setGame()
{
char[][] config=new char[l][b];
startGame(config,l,b);
}
void startGame(char[][] mat,int l, int b)
{
Scanner s=new Scanner(System.in);
String ch="";
float per=0;
while(!ch.equals("y"))
{
per=setConfig(mat);
//setCustomConfig(mat,"GOLglidergun.txt");
display2D(mat);
System.out.println((per*100)+"% of grid filled.");
System.out.println("Begin? y/n");
ch=s.nextLine();
}
while(!ch.equals("x"))
{
mat=transform(mat,l,b);
display2D(mat);
 
System.out.println("Ctrl+Z to stop.");
 
try
{
Thread.sleep(100);
}
catch(Exception e)
{
System.out.println("Something went horribly wrong.");
}
 
//ch=s.nextLine();
}
s.close();
System.out.println("Game Over");
}
 
char[][] transform(char[][] mat,int l, int b)
{
 
char[][] newmat=new char[l][b];
for(int i=0;i<l;i++)
for(int j=0;j<b;j++)
newmat[i][j]=flip(mat,i,j);
return newmat;
}
char flip(char[][] mat,int i, int j)
{
int count=around(mat,i,j);
if(mat[i][j]=='*')
{
if(count<2||count>3)
return '_';
return '*';
}
else
{
if(count==3)
return '*';
return '_';
}
}
int around(char[][] mat, int i, int j)
{
int count=0;
for(int x=i-1;x<=i+1;x++)
for(int y=j-1;y<=j+1;y++)
{
if(x==i&&y==j)
continue;
count+=eval(mat,x,y);
}
return count;
}
int eval(char[][] mat, int i, int j)
{
if(i<0||j<0||i==l||j==b)
return 0;
if(mat[i][j]=='*')
return 1;
return 0;
}
 
float setCustomConfig(char[][] arr,String infile)
{
try
{
BufferedReader br=new BufferedReader(new FileReader(infile));
String line;
for(int i=0;i<arr.length;i++)
{
line=br.readLine();
for(int j=0;j<arr[0].length;j++)
arr[i][j]=line.charAt(j);
}
br.close();
}
catch(Exception e)
{
System.out.println(e.getMessage());
}
return 0;
}
 
float setConfig(char[][] arr)
{
//Enter percentage of grid to be filled.
float per=0.10f;//(float)Math.random();
for(int i=0;i<arr.length;i++)
setConfig1D(arr[i],per);
return per;
}
void setConfig1D(char[] arr,float per)
{
for(int i=0;i<arr.length;i++)
{
if(Math.random()<per)
arr[i]='*';
else
arr[i]='_';
}
}
void display2D(char[][] arr)
{
for(int i=0;i<arr.length;i++)
display1D(arr[i]);
System.out.println();
}
void display1D(char[] arr)
{
for(int i=0;i<arr.length;i++)
System.out.print(arr[i]);
System.out.println();
}
}
 

Glider Gun design. Save it in GOLglidergun.txt and uncomment the setCustomConfig function.

____________________________________________________________
_________________________*__________________________________
_______________________*_*__________________________________
_____________**______**____________**_______________________
____________*___*____**____________**_______________________
_**________*_____*___**_____________________________________
_**________*___*_**____*_*__________________________________
___________*_____*_______*__________________________________
____________*___*___________________________________________
_____________**_____________________________________________
____________________________________________________________
____________________________________________________________
____________________________________________________________
____________________________________________________________
____________________________________________________________
____________________________________________________________
____________________________________________________________
____________________________________________________________
____________________________________________________________
____________________________________________________________

[edit] Swing

See Conway's Game of Life/Java/Swing

[edit] JavaScript

Works with: SpiderMonkey
Works with: V8
function GameOfLife () {
 
this.init = function (turns,width,height) {
this.board = new Array(height);
for (var x = 0; x < height; x++) {
this.board[x] = new Array(width);
for (var y = 0; y < width; y++) {
this.board[x][y] = Math.round(Math.random());
}
}
this.turns = turns;
}
 
this.nextGen = function() {
this.boardNext = new Array(this.board.length);
for (var i = 0; i < this.board.length; i++) {
this.boardNext[i] = new Array(this.board[i].length);
}
for (var x = 0; x < this.board.length; x++) {
for (var y = 0; y < this.board[x].length; y++) {
var n = 0;
for (var dx = -1; dx <= 1; dx++) {
for (var dy = -1; dy <= 1; dy++) {
if ( dx == 0 && dy == 0){}
else if (typeof this.board[x+dx] !== 'undefined'
&& typeof this.board[x+dx][y+dy] !== 'undefined'
&& this.board[x+dx][y+dy]) {
n++;
}
}
}
var c = this.board[x][y];
switch (n) {
case 0:
case 1:
c = 0;
break;
case 2:
break;
case 3:
c = 1;
break;
default:
c = 0;
}
this.boardNext[x][y] = c;
}
}
this.board = this.boardNext.slice();
}
 
this.print = function() {
for (var x = 0; x < this.board.length; x++) {
var l = "";
for (var y = 0; y < this.board[x].length; y++) {
if (this.board[x][y])
l += "X";
else
l += " ";
}
print(l);
}
}
 
this.start = function() {
for (var t = 0; t < this.turns; t++) {
print("---\nTurn "+(t+1));
this.print();
this.nextGen()
}
}
 
}
 
 
var game = new GameOfLife();
 
print("---\n3x3 Blinker over three turns.");
game.init(3);
game.board = [
[0,0,0],
[1,1,1],
[0,0,0]];
game.start();
 
print("---\n10x6 Glider over five turns.");
game.init(5);
game.board = [
[0,0,0,0,0,0,0,0,0,0],
[0,0,1,0,0,0,0,0,0,0],
[0,0,0,1,0,0,0,0,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]];
game.start();
 
print("---\nRandom 5x10");
game.init(5,5,10);
game.start();

Output:

---
3x3 Blinker over three turns.
---
Turn 1
   
XXX
   
---
Turn 2
 X 
 X 
 X 
---
Turn 3
   
XXX
   
---
10x6 Glider over five turns.
---
Turn 1
          
  X       
   X      
 XXX      
          
          
---
Turn 2
          
          
 X X      
  XX      
  X       
          
---
Turn 3
          
          
   X      
 X X      
  XX      
          
---
Turn 4
          
          
  X       
   XX     
  XX      
          
---
Turn 5
          
          
   X      
    X     
  XXX     
          
---
Random 5x10
---
Turn 1
XXXX 
   XX
X    
 XX X
  XX 
X   X
X    
X   X
 X   
X  XX
---
Turn 2
 XXXX
X  XX
 XX X
 XX  
  X X
 X X 
XX   
XX   
XX XX
     
---
Turn 3
 XX X
X    
X   X
     
     
XX X 
     
     
XXX  
     
---
Turn 4
 X   
X  X 
     
     
     
     
     
 X   
 X   
 X   
---
Turn 5
     
     
     
     
     
     
     
     
XXX  
     
Library: HTML5

Essentially the same as the above straight JavaScript but displayed in an HTML5 Canvas.

 
<html>
<head>
<title></title>
<script type="text/javascript">
 
function GameOfLife () {
 
this.init = function (turns,width,height) {
this.board = new Array(height);
for (var x = 0; x < height; x++) {
this.board[x] = new Array(width);
for (var y = 0; y < width; y++) {
this.board[x][y] = Math.round(Math.random());
}
}
this.turns = turns;
}
 
this.nextGen = function() {
this.boardNext = new Array(this.board.length);
for (var i = 0; i < this.board.length; i++) {
this.boardNext[i] = new Array(this.board[i].length);
}
for (var x = 0; x < this.board.length; x++) {
for (var y = 0; y < this.board[x].length; y++) {
var n = 0;
for (var dx = -1; dx <= 1; dx++) {
for (var dy = -1; dy <= 1; dy++) {
if ( dx == 0 && dy == 0){}
else if (typeof this.board[x+dx] !== 'undefined'
&& typeof this.board[x+dx][y+dy] !== 'undefined'
&& this.board[x+dx][y+dy]) {
n++;
}
}
}
var c = this.board[x][y];
switch (n) {
case 0:
case 1:
c = 0;
break;
case 2:
break;
case 3:
c = 1;
break;
default:
c = 0;
}
this.boardNext[x][y] = c;
}
}
this.board = this.boardNext.slice();
}
 
this.print = function(ctx,w,h) {
if (!w)
w = 8;
if (!h)
h = 8;
for (var x = 0; x < this.board.length; x++) {
var l = "";
for (var y = 0; y < this.board[x].length; y++) {
if (this.board[x][y])
// x and y reversed to draw matrix like it looks in source
// rather than the "actual" positions
ctx.fillStyle = "orange";
else
ctx.fillStyle = "black";
ctx.fillRect(y*h,x*w,h,w);
}
}
}
 
this.start = function(ctx,w,h) {
for (var t = 0; t < this.turns; t++) {
this.print(ctx,w,h);
this.nextGen()
}
}
 
}
 
function init() {
// Change document title and text under canvas
document.title = "Conway's Game of Life";
 
// Setup game boards for Conway's Game of Life
var blinker = new GameOfLife();
blinker.board = [
[0,1,0],
[0,1,0],
[0,1,0]];
 
var glider = new GameOfLife();
glider.board = [
[0,0,0,0,0,0],
[0,0,1,0,0,0],
[0,0,0,1,0,0],
[0,1,1,1,0,0],
[0,0,0,0,0,0],
[0,0,0,0,0,0]];
 
var random = new GameOfLife();
random.init(null,8,8);
 
// Get canvas contexts or return 1
blinker.canvas = document.getElementById('blinker');
glider.canvas = document.getElementById('glider');
random.canvas = document.getElementById('random');
if (blinker.canvas.getContext && glider.canvas.getContext && random.canvas.getContext) {
blinker.ctx = blinker.canvas.getContext('2d');
glider.ctx = glider.canvas.getContext('2d');
random.ctx = random.canvas.getContext('2d');
} else {
return 1;
}
 
 
// Run main() at set interval
setInterval(function(){run(glider,glider.ctx,25,25)},250);
setInterval(function(){run(blinker,blinker.ctx,25,25)},250);
setInterval(function(){run(random,random.ctx,25,25)},250);
return 0;
}
 
function run(game,ctx,w,h) {
game.print(ctx,w,h);
game.nextGen()
 
return 0;
}
 
</script>
</head>
<body onLoad="init();">
3x3 Blinker<br>
<canvas id="blinker" width="75" height="75">
No canvas support found!
</canvas><br><br>
6x6 Glider<br>
<canvas id="glider" width="150" height="150">
No canvas support found!
</canvas><br><br>
8x8 Random<br>
<canvas id="random" width="200" height="200">
No canvas support found!
</canvas><br>
</body>
</html>

Output for 3x3 Blinker:

Blinker.gif

[edit] Liberty BASIC

It will run slow for grids above say 25!

 
nomainwin
 
gridX = 20
gridY = gridX
 
mult =500 /gridX
pointSize =360 /gridX
 
dim old( gridX +1, gridY +1), new( gridX +1, gridY +1)
 
'Set blinker:
old( 16, 16) =1: old( 16, 17) =1 : old( 16, 18) =1
 
'Set glider:
old( 5, 7) =1: old( 6, 7) =1: old( 7, 7) =1
old( 7, 6) =1: old( 6, 5) =1
 
WindowWidth =570
WindowHeight =600
 
open "Conway's 'Game of Life'." for graphics_nsb_nf as #w
 
#w "trapclose [quit]"
#w "down ; size "; pointSize
#w "fill black"
 
'Draw initial grid
for x = 1 to gridX
for y = 1 to gridY
'#w "color "; int( old( x, y) *256); " 0 255"
if old( x, y) <>0 then #w "color red" else #w "color darkgray"
#w "set "; x *mult +20; " "; y *mult +20
next y
next x
' ______________________________________________________________________________
'Run
do
for x =1 to gridX
for y =1 to gridY
'find number of live Moore neighbours
neighbours =old( x -1, y -1) +old( x, y -1) +old( x +1, y -1)+_
old( x -1, y) +old( x +1, y )+_
old( x -1, y +1) +old( x, y +1) +old( x +1, y +1)
was =old( x, y)
if was =0 then
if neighbours =3 then N =1 else N =0
else
if neighbours =3 or neighbours =2 then N =1 else N =0
end if
new( x, y) = N
'#w "color "; int( N /8 *256); " 0 255"
if N <>0 then #w "color red" else #w "color darkgray"
#w "set "; x *mult +20; " "; y *mult +20
next y
next x
scan
'swap
for x =1 to gridX
for y =1 to gridY
old( x, y) =new( x, y)
next y
next x
'Re-run until interrupted...
loop until FALSE
'User shutdown received
[quit]
close #w
end
 


[edit] Lua

function Evolve( cell )
local m = #cell
local cell2 = {}
for i = 1, m do
cell2[i] = {}
for j = 1, m do
cell2[i][j] = cell[i][j]
end
end
 
for i = 1, m do
for j = 1, m do
local count
if cell2[i][j] == 0 then count = 0 else count = -1 end
for x = -1, 1 do
for y = -1, 1 do
if i+x >= 1 and i+x <= m and j+y >= 1 and j+y <= m and cell2[i+x][j+y] == 1 then count = count + 1 end
end
end
if count < 2 or count > 3 then cell[i][j] = 0 end
if count == 3 then cell[i][j] = 1 end
end
end
 
return cell
end
 
 
m = 3 -- number rows / colums
num_iterations = 10
 
cell = {}
for i = 1, m do
cell[i] = {}
for j = 1, m do
cell[i][j] = 0
end
end
 
cell[2][2], cell[2][1], cell[2][3] = 1, 1, 1
 
for l = 1, num_iterations do
for i = 1, m do
for j = 1, m do
if cell[i][j] == 1 then io.write( "#" ) else io.write( " " ) end
end
io.write( "\n" )
end
 
cell = Evolve( cell )
end
 

[edit] MATLAB

MATLAB has a builtin Game of Life GUI. Type
life
to run it. To view the code, type
open(fullfile(matlabroot, 'toolbox', 'matlab', 'demos', 'life.m'))

Here is an example code, more simple (runs the game of life for N generations in a square of side S) :

function GoL(S, N) %
colormap copper; whitebg('black');
G= round(rand(S));
A = [S 1:S-1]; B = [2:S 1];
for k=1:N
Sum = G(A,:)+G(B,:)+G(:,B)+G(:,A)+G(A,B)+G(A,A)+G(B,B)+G(B,A);
G = double((G & (Sum == 2)) | (Sum == 3));
surf(G); view([0 90]); pause(0.001)
end
end

[edit] Mathematica

Mathematica has cellular automaton functionality built in, so implementing Conway's Game of Life is a one-liner:

CellularAutomaton[{224,{2,{{2,2,2},{2,1,2},{2,2,2}}},{1,1}}, startconfiguration, steps];

Example of a glyder progressing 8 steps and showing the 9 frames afterwards as grids of hashes and dots:

results=CellularAutomaton[{224,{2,{{2,2,2},{2,1,2},{2,2,2}}},{1,1}},{{{0,1,0},{0,0,1},{1,1,1}},0},8];
Do[Print[i-1];Print[Grid[results[[i]]/.{1->"#",0->"."}]];,{i,1,Length[results]}]

gives back:

0
.#...
..#..
###..
.....
.....

1
.....
#.#..
.##..
.#...
.....

2
.....
..#..
#.#..
.##..
.....

3
.....
.#...
..##.
.##..
.....

4
.....
..#..
...#.
.###.
.....

5
.....
.....
.#.#.
..##.
..#..

6
.....
.....
...#.
.#.#.
..##.

7
.....
.....
..#..
...##
..##.

8
.....
.....
...#.
....#
..###

[edit] Maxima

life(A) := block(
[p, q, B: zerofor(A), s],
[p, q]: matrix_size(A),
for i thru p do (
for j thru q do (
s: 0,
if j > 1 then s: s + A[i, j - 1],
if j < q then s: s + A[i, j + 1],
if i > 1 then (
s: s + A[i - 1, j],
if j > 1 then s: s + A[i - 1, j - 1],
if j < q then s: s + A[i - 1, j + 1]
),
if i < p then (
s: s + A[i + 1, j],
if j > 1 then s: s + A[i + 1, j - 1],
if j < q then s: s + A[i + 1, j + 1]
),
B[i, j]: charfun(s = 3 or (s = 2 and A[i, j] = 1))
)
),
B
)$
 
 
/* a glider */
 
L: matrix([0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 1, 0, 0, 0, 0, 0, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])$
 
gen(A, n) := block(thru n do A: life(A), A)$
 
gen(L, 4);
matrix([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 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, 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, 0, 0, 0, 0, 0, 0, 0])

[edit] Nimrod

Translation of: C
import os, strutils, math
 
randomize()
var w, h: int
if paramCount() >= 2:
w = parseInt(paramStr(1))
h = parseInt(paramStr(2))
if w <= 0: w = 30
if h <= 0: h = 30
 
# Initialize
var univ, utmp = newSeq[seq[bool]] h
for y in 0 .. <h:
univ[y].newSeq w
utmp[y].newSeq w
for x in 0 .. <w:
if random(10) < 1:
univ[y][x] = true
 
while true:
# Show
stdout.write "\e[H"
for y in 0 .. <h:
for x in 0 .. <w:
stdout.write if univ[y][x]: "\e[07m \e[m" else: " "
stdout.write "\e[E"
stdout.flushFile
 
# Evolve
for y in 0 .. <h:
for x in 0 .. <w:
var n = 0
for y1 in y-1 .. y+1:
for x1 in x-1 .. x+1:
if univ[(y1+h) mod h][(x1 + w) mod w]:
inc n
 
if univ[y][x]: dec n
utmp[y][x] = n == 3 or (n == 2 and univ[y][x])
swap(univ,utmp)
 
sleep 200

[edit] OCaml

let get g x y =
try g.(x).(y)
with _ -> 0
 
let neighbourhood g x y =
(get g (x-1) (y-1)) +
(get g (x-1) (y )) +
(get g (x-1) (y+1)) +
(get g (x ) (y-1)) +
(get g (x ) (y+1)) +
(get g (x+1) (y-1)) +
(get g (x+1) (y )) +
(get g (x+1) (y+1))
 
let next_cell g x y =
let n = neighbourhood g x y in
match g.(x).(y), n with
| 1, 0 | 1, 1 -> 0 (* lonely *)
| 1, 4 | 1, 5 | 1, 6 | 1, 7 | 1, 8 -> 0 (* overcrowded *)
| 1, 2 | 1, 3 -> 1 (* lives *)
| 0, 3 -> 1 (* get birth *)
| _ (* 0, (0|1|2|4|5|6|7|8) *) -> 0 (* barren *)
 
let copy g = Array.map Array.copy g
 
let next g =
let width = Array.length g
and height = Array.length g.(0)
and new_g = copy g in
for x = 0 to pred width do
for y = 0 to pred height do
new_g.(x).(y) <- (next_cell g x y)
done
done;
(new_g)
 
let print g =
let width = Array.length g
and height = Array.length g.(0) in
for x = 0 to pred width do
for y = 0 to pred height do
if g.(x).(y) = 0
then print_char '.'
else print_char 'o'
done;
print_newline()
done

put the code above in a file named "life.ml", and then use it in the ocaml toplevel like this:

# #use "life.ml";;
val get : int array array -> int -> int -> int = <fun>
val neighbourhood : int array array -> int -> int -> int = <fun>
val next_cell : int array array -> int -> int -> int = <fun>
val copy : 'a array array -> 'a array array = <fun>
val next : int array array -> int array array = <fun>
val print : int array array -> unit = <fun>

# let g = [|
  [| 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; 0; 0; |];
  [| 0; 0; 0; 0; 1; 1; 1; 0; 0; 0; |];
  [| 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; 0; 0; 0; 0; 0; 0; |];
|] ;;
val g : int array array =
  [|[|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; 0; 0|];
    [|0; 0; 0; 0; 1; 1; 1; 0; 0; 0|];
    [|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; 0; 0; 0; 0; 0; 0|]|]
# print g;;
..........
..........
..........
..........
....ooo...
...ooo....
..........
..........
..........
..........
- : unit = ()
# print (next g) ;;
..........
..........
..........
.....o....
...o..o...
...o..o...
....o.....
..........
..........
..........
- : unit = ()

[edit] A graphical version

This implementation has 6 starting patterns (most get quite large) and a random option, and you can set the grid size.

let alive = 0
let dead = 0xFFFFFF
 
let iteration ib ob m n =
let rule = function 3,_ | 2,true -> alive | _ -> dead in
let f x y =
if x >= 0 && x < m && y >= 0 && y < n && ib.(x).(y) = alive
then 1 else 0 in
let count b q =
let a, c, p, r = b-1, b+1, q-1, q+1 in
f a p + f a q + f a r + f b p + f b r + f c p + f c q + f c r in
for i = 0 to m-1 do
for j = 0 to n-1 do
ob.(i).(j) <- rule (count i j, ib.(i).(j) = alive)
done
done
 
let make_random w h bd =
Random.self_init ();
for i = 0 to w-1 do
for j = 0 to h-1 do
bd.(i).(j) <- if Random.bool () then alive else dead
done
done
 
let set_cells a b cells w h bd =
let w', h' = w/2 - a, h/2 - b in
List.iter (fun (i,j) -> bd.(i+w').(j+h') <- alive) cells
 
let make_blinker = set_cells 1 1 [(1,0); (1,1); (1,2)]
 
let make_acorn =
set_cells 1 3 [(0,1); (1,3); (2,0); (2,1); (2,4); (2,5); (2,6)]
 
let make_growth =
set_cells 2 3
[(0,6); (1,4); (1,6); (1,7); (2,4); (2,6); (3,4); (4,2); (5,0); (5,2)]
 
let make_rabbits =
set_cells 1 3
[(0,0); (0,4); (0,5); (0,6); (1,0); (1,1); (1,2); (1,5); (2,1)]
 
let make_engine =
set_cells (-100) (-100)
[(0,1); (0,3); (1,0); (2,1); (2,4); (3,3); (3,4); (3,5); (4,26); (4,27); (5,26); (5,27)]
 
let make_line w h bd =
let w', h', l = w/2, h/2, w/3 in
for i = -l to l do bd.(i+w').(h') <- alive done
 
let () =
let argc = Array.length Sys.argv in
let init =
let default () = (print_endline "Using random start"; make_random) in
if argc < 2 then default () else
match Sys.argv.(1) with
| "acorn" -> make_acorn
| "blinker" -> make_blinker
| "growth" -> make_growth
| "engine" -> make_engine
| "line" -> make_line
| "rabbits" -> make_rabbits
| "random" -> make_random
| "-h" -> Printf.printf
"Usage: %s [acorn|growth|blinker|engine|line|rabbits|random] width height\n" Sys.argv.(0);
exit 0
| _ -> default () in
let width = if argc > 2 then int_of_string Sys.argv.(2) else 300 in
let height = if argc > 3 then int_of_string Sys.argv.(3) else 300 in
let bd1 = Array.make_matrix width height dead in
let bd2 = Array.make_matrix width height dead in
let border = 5 in
let disp m = Graphics.draw_image (Graphics.make_image m) border border in
init width height bd1;
Graphics.open_graph (Printf.sprintf " %dx%d" (height+2*border) (width+2*border));
while true do
disp bd1;
iteration bd1 bd2 width height;
disp bd2;
iteration bd2 bd1 width height
done
Compile with:
ocamlopt -o life graphics.cmxa life.ml
and run with
./life acorn 250 250
If you run the blinker it will probably blink too fast to see unless you choose a large grid size.

[edit] ooRexx

/* REXX ---------------------------------------------------------------
* 07.08.2014 Walter Pachl Conway's Game of life graphical
* Input is a file containing the initial pattern.
* The compute area is extended when needed
* (i.e., when cells are born outside the current compute area)
* When computing the pattern sequence is complete, the graphical
* output starts and continues until Cancel is pressed.
* 10.08.2014 WP fixed the output of what.txt
*--------------------------------------------------------------------*/

Parse Arg what speed
If what='?' Then Do
Say 'Create a file containing the pattern to be processed'
Say 'named somename.in (octagon.in such as this for the octagon):'
Say ' ** '
Say ' * * '
Say ' * * '
Say '* *'
Say '* *'
Say ' * * '
Say ' * * '
Say ' ** '
Say 'Run the program by entering "rexx conlife somename [pause]"',
'on the command line.'
Say '(pause is the amount of milliseconds between 2 pictures.',
'default is 1000)'
Say 'A file somename.lst will be created.'
Say 'Hereafter you will see the pattern''s development',
'in a new window.'
Say 'Press the Cancel button to end the presentation.'
Exit
End
Parse Version interpreter '_' level '('
If interpreter<>'REXX-ooRexx' Then Do
Say interpreter level
Say 'This program must be run with object Rexx.'
Exit
End
If what='' Then what='octagon'
If right(what,3)='.in' then
what=left(what,length(what)-3)
infile=what'.in'
If lines(infile)=0 Then Do
Say 'Input file' infile 'not found.'
Say 'Enter conlife ? for help.'
Exit
End
If speed='' Then
speed=1000
.local~myspeed=speed
 
Call tl what
--'type' what'.lst'
 
.local~title=what
array=.local~myarrayData
d = .drawDlg~new
if d~initCode <> 0 then do
say 'The Draw dialog was not created correctly. Aborting.'
return d~initCode
end
d~execute("SHOWTOP")
return 0
 
::requires "ooDialog.cls"
 
::class 'drawDlg' subclass UserDialog
 
::attribute interrupted unguarded
 
::method init
expose walterFont
 
forward class (super) continue
-- colornames:
-- 1 dark red 7 light grey 13 red
-- 2 dark green 8 pale green 14 light green
-- 3 dark yellow 9 light blue 15 yellow
-- 4 dark blue 10 white 16 blue
-- 5 purple 11 grey 17 pink
-- 6 blue grey 12 dark grey 18 turquoise
 
self~interrupted = .true
 
-- Create a font to write the nice big letters and digits
opts = .directory~new
opts~weight = 700
walterFont = self~createFontEx("Arial",14,opts)
walterFont = self~createFontEx("Courier",18,opts)
 
if \self~createcenter(200, 235,"Walter's Clock", , ,"System",14) then
self~initCode = 1
 
::method defineDialog
 
self~createPushButton(/*IDC_PB_DRAW*/100, 0, 0,240,200,"DISABLED NOTAB") -- The drawing surface.
 
self~createPushButton(IDCANCEL,160,220, 35, 12,,"&Cancel")
 
::method initDialog unguarded
expose x y dc myPen change al. fid nn what array
change = 0
x = self~factorx
y = self~factory
dc = self~getButtonDC(100)
myPen = self~createPen(1,'solid',0)
t = .TimeSpan~fromMicroSeconds(500000) -- .5 seconds
msg = .Message~new(self,'life')
alrm = .Alarm~new(t, msg)
array=.local~myArrayData
Do s=1 to array~items
al.s=array[s]
Parse Var al.s ' == ' al.s
End
nn=s-2
--say 'nn'nn
Call lineout fid
 
::method interrupt unguarded
 
self~interrupted = .true
 
::method cancel unguarded -- Stop the drawing program and quit.
expose x y
self~hide
self~interrupted = .true
return self~cancel:super
 
::method leaving unguarded -- Best place to clean up resources
expose dc myPen walterFont
 
self~deleteObject(myPen)
self~freeButtonDC(/*IDC_PB_DRAW*/100,dc)
self~deleteFont(walterFont)
 
::method life unguarded /* draw individual pixels */
expose x y dc myPen change walterFont al. nn what
mx = trunc(20*x); my = trunc(20*y); size = 400
 
curPen = self~objectToDC(dc, myPen)
 
-- Select the nice big letters and digits into the device context to use to
-- to write with:
curFont = self~fontToDC(dc, walterFont)
 
-- Create a white brush and select it into the device to paint with.
whiteBrush = self~createBrush(10)
curBrush = self~objectToDC(dc, whiteBrush)
 
-- Paint the drawing area surface with the white brush
self~rectangle(dc, 1, 1, 500, 600, 'FILL')
self~writeDirect(dc, 10, 20,'Conway''s Game of Life')
self~writeDirect(dc, 10, 40,.local~title)
self~writeDirect(dc, 10,460,'Walter Pachl, 8 Aug 2014')
dx=.local~dxval
dy=.local~dyval
do s=1 By 1 until self~interrupted
self~transparentText(dc)
self~interrupted = .false
sm=s//nn+1
If s>1 Then Do
ali=al.sb
Do While ali<>''
Parse Var ali x ',' y ali
zxa=(x+dx)*10
zya=(y+dy)*10
self~draw_square(dc,zxa,zya,3,10)
End
End
self~draw_square(dc, 380, 10,100,10)
self~writeDirect(dc, 340, 20,time())
self~writeDirect(dc, 340, 40,right(sm,2) 'of' right(nn,2))
ali=al.sm
Do While ali<>''
Parse Var ali x ',' y ali
zxa=(x+dx)*10
zya=(y+dy)*10
self~draw_square(dc,zxa,zya,3,5)
End
-- self~interrupted = .true
sb=sm
self~objectToDC(dc, curPen)
self~objectToDC(dc, curBrush)
call msSleep .local~myspeed
--self~pause
End
 
::method pause
j = msSleep(10)
 
::method draw_square
Use Arg dc, x, y, d, c
Do zx=x-d to x+d
Do zy=y-d to y+d
self~drawPixel(dc, zx, zy, c)
End
End
 
::method quot
Parse Arg x,y
If y=0 Then Return '???'
Else Return x/y
 
::routine tl
/* REXX ---------------------------------------------------------------
* 02.08.2014 Walter Pachl
* Input is a file containing the initial pattern
* The compute area is extended when needed
* (cells are born outside the current compute area)
* The program stops when the picture shown is the same as the first
* or equal to the previous one
*--------------------------------------------------------------------*/

Parse Arg f
If f='' Then f='bipole'
fid=f'.in'
oid=f'.txt'; 'erase' oid
oil=f'.lst'; 'erase' oil
debug=0
If debug Then Do
dbg=f'.xxx'; 'erase' dbg
End
ml=0
l.=''
ol.=''
Parse Value '10 10' With xb yb
xc=copies(' ',xb)
Do ri=yb+1 By 1 While lines(fid)>0
l.ri=xc||linein(fid)
ml=max(ml,length(strip(l.ri,'T')))
End
ri=ri-1
ml=ml+xb
ri=ri+yb
yy=ri
a.=' '
b.=' '
m.=''
x.=''
list.=''
Parse Value 1 ml 1 yy With xmi xma ymi yma
Parse Value '-10 30 -10 30' With xmi xma ymi yma
Parse Value '999 -999 999 -999 999 -999 999 -999',
With xmin xmax ymin ymax xlo xhi ylo yhi
Do y=1 To yy
z=yy-y+1
l=l.z
Do x=1 By 1 While l<>''
Parse Var l c +1 l
If c='*' Then
a.x.z='*'
End
End
Call show
Do step=1 To 60
Call store
If step>1 & is_equal(step,1) Then Leave
If step>1 & is_equal(step,step-1) Then Leave
Call show_neighbors
Do y=yma To ymi By -1
ol=format(x,3)' '
Do x=xmi To xma
neighbors=neighbors(x,y)
If a.x.y=' ' Then Do /* dead cell */
If neighbors=3 Then Do
b.x.y='*' /* gets life */
mmo=xmi xma ymi yma
xmi=min(xmi,x-1)
xma=max(xma,x+1)
ymi=min(ymi,y-1)
yma=max(yma,y+1)
mm=xmi xma ymi yma
If mm<>mmo Then
Call debug mmo '1->' mm
End
Else /* life cell */
b.x.y=' ' /* remains dead */
End
Else Do /* life cell */
If neighbors=2 |,
neighbors=3 Then Do
b.x.y='*' /* remains life */
mmo=xmi xma ymi yma
xmi=min(xmi,x-1)
xma=max(xma,x+1)
ymi=min(ymi,y-1)
yma=max(yma,y+1)
mm=xmi xma ymi yma
If mm<>mmo Then
Call debug mmo '2->' mm
End
Else
b.x.y=' ' /* dies */
End
End
End
/* b. is the new state and is now copied to a. */
Do y=yma To ymi By -1
Do x=xmi To xma
a.x.y=b.x.y
End
End
End
/* Output name and all states */
Call lineout oid,' 'f
st=' +' /* top and bottom border */
sb=' +' /* top and bottom border */
Do s=1 To step
st=st||'-'right(s,2,'-')||copies('-',xmax-xmin-2)'+'
sb=sb||copies('-',xmax-xmin+1)'+'
End
array=.array~new
Do y=ymin To ymax
Do s=1 To step
Do x=xmin To xmax
If substr(m.s.y,x,1)='*' Then Do
xlo=min(xlo,x)
xhi=max(xhi,x)
ylo=min(ylo,y)
yhi=max(yhi,y)
End
End
End
End
Do y=ymin To ymax
ol=''
Do s=1 To step
Do x=xmin To xmax
If substr(m.s.y,x,1)='*' Then Do
list.s=list.s (x-xlo+1)','||(y-ylo+1)
End
End
array[s]=s '-' words(list.s) '==' list.s
End
--Call lineout oid,ol '|'
.local~myArrayData=array
End
height=yhi-ylo+1
width=xhi-xlo+1
.local~dxval=(48-width)%2
.local~dyval=(48-height)%2
Call o st /* top border */
xl.='|'
Do y=ymax To ymin By -1
Do s=1 To step
xl.y=xl.y||substr(ol.s.y,xmin,xmax-xmin+1)'|'
End
End
Do y=ymax To ymin By -1
Call o ' 'xl.y
End
Call o sb /* bottom border */
Call lineout oid
Say 'frames are shown in' oid
If debug Then Do
Say 'original area' 1 ml '/' 1 yy
Say 'compute area ' xmi xma '/' ymi yma
Say 'used area ' xlo xhi '/' ylo yhi
End
Do s=1 To step
call lineout oil,s '==>' words(list.s) '==' list.s
End
Return
 
o: Parse Arg lili
Call lineout oid,lili
Return
 
set: Parse Arg x,y
a.x.y='*'
Return
 
neighbors: Procedure Expose a. debug
Parse Arg x,y
neighbors=0
do xa=x-1 to x+1
do ya=y-1 to y+1
If xa<>x | ya<>y then
If a.xa.ya='*' Then
neighbors=neighbors+1
End
End
Return neighbors
 
store:
/* store current state (a.) in lines m.step.* */
Do y=yma To ymi By -1
ol=''
Do x=xmi To xma
z=a.x.y
ol=ol||z
End
x.step.y=ol
If ol<>'' then Do
ymin=min(ymin,y)
ymax=max(ymax,y)
p=pos('*',ol)
q=length(strip(ol,'T'))
If p>0 Then
xmin=min(xmin,p)
xmax=max(xmax,q)
End
m.step.y=ol
ol.step.y=ol
--If pos('*',ol)>0 Then Do
-- Say '====>' right(step,2) right(y,3) '>'ol'<' xmin xmax
-- Say ' 'copies('1234567890',3)
-- End
End
Return
 
is_equal:
/* test ist state a.b is equal to state a.a */
Parse Arg a,b
Do y=yy To 1 By -1
If x.b.y<>x.a.y Then
Return 0
End
Return 1
 
show: Procedure Expose dbg a. yy ml debug
Do y=-5 To 13
ol='>'
Do x=-5 To 13
ol=ol||a.x.y
End
Call debug ol
End
Return
 
show_neighbors: Procedure Expose a. xmi xma ymi yma dbg debug
Do y=yma To ymi By -1
ol=format(y,3)' '
Do x=xmi To xma
ol=ol||neighbors(x,y)
End
Call debug ol
End
Return
 
debug:
If debug Then
Return lineout(dbg,arg(1))
Else
Return
 
Output:
blinker.txt
 blinker
 +--1+--2+--3+
 |   | * |   |
 |***| * |***|
 |   | * |   |
 +---+---+---+  
blinker.lst
1 ==> 3 ==  1,2 2,2 3,2
2 ==> 3 ==  2,1 2,2 2,3
3 ==> 3 ==  1,2 2,2 3,2    

[edit] Oz

declare
Rules = [rule(c:1 n:[0 1] new:0) %% Lonely
rule(c:1 n:[4 5 6 7 8] new:0) %% Overcrowded
rule(c:1 n:[2 3] new:1) %% Lives
rule(c:0 n:[3] new:1) %% It takes three to give birth!
rule(c:0 n:[0 1 2 4 5 6 7 8] new:0) %% Barren
]
 
Blinker = ["..."
"###"
"..."]
 
Toad = ["...."
".###"
"###."
"...."]
 
Glider = [".#.........."
"..#........."
"###........."
"............"
"............"
"............"
"............"
"............"
"............"
"............"
"............"]
 
Init = Blinker
MaxGen = 2
 
%% G(i) -> G(i+1)
fun {Evolve Gi}
fun {Get X#Y}
Row = {CondSelect Gi Y unit}
in
{CondSelect Row X 0} %% cells beyond boundaries are dead (0)
end
fun {GetNeighbors X Y}
{Map [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]
Get}
end
in
{Record.mapInd Gi
fun {$ Y Row}
{Record.mapInd Row
fun {$ X C}
N = {Sum {GetNeighbors X Y}}
in
for Rule in Rules return:Return do
if C == Rule.c andthen {Member N Rule.n} then
{Return Rule.new}
end
end
end}
end}
end
 
%% For example: [".#"
%% "#."] -> grid(1:row(1:0 2:1) 2:row(1:1 2:0))
fun {ReadG LinesList}
{List.toTuple grid
{Map LinesList
fun {$ Line}
{List.toTuple row
{Map Line
fun {$ C}
if C == &. then 0
elseif C == &# then 1
end
end}}
end}}
end
 
%% Inverse to ReadG
fun {ShowG G}
{Map {Record.toList G}
fun {$ Row}
{Map {Record.toList Row}
fun {$ C}
if C == 0 then &.
elseif C == 1 then &#
end
end}
end}
end
 
%% Helpers
fun {Sum Xs} {FoldL Xs Number.'+' 0} end
fun lazy {Iterate F V} V|{Iterate F {F V}} end
 
G0 = {ReadG Init}
Gn = {Iterate Evolve G0}
in
for
Gi in Gn
I in 0..MaxGen
do
{System.showInfo "\nGen. "#I}
{ForAll {ShowG Gi} System.showInfo}
end

[edit] PARI/GP

Basic implementation; prints a matrix representing the state of the game directly. Supports large games but this example uses only the required 3 X 3 blinker.

step(M)={
my(N=M,W=matsize(M)[1],L=#M,t);
for(l=1,W,for(w=1,L,
t=sum(i=l-1,l+1,sum(j=w-1,w+1,if(i<1||j<1||i>W||j>L,0,M[i,j])));
N[l,w]=(t==3||(M[l,w]&&t==4))
));
N
};
M=[0,1,0;0,1,0;0,1,0];
for(i=1,3,print(M);M=step(M))

[edit] Perl

This a perl example the simulates Conway's life starting with a random grid of the given size for the given number of steps. Example:

life.pl numrows numcols numiterations 
life.pl 5 10 15 

would do 15 iterations over 5 rows and 10 columns.

my ($width, $height, $generations) = @ARGV;
 
my $printed;
 
sub generate {
(map
{[ (map { rand () < 0.5 } 1 .. $width), 0 ]}
1 .. $height),
[(0) x ($width + 1)];
}
 
sub nexgen {
my @prev = map {[@$_]} @_;
my @new = map {[ (0) x ($width + 1) ]} 0 .. $height;
foreach my $row ( 0 .. $height - 1 ) {
foreach my $col ( 0 .. $width - 1 ) {
my $val =
$prev[ $row - 1 ][ $col - 1 ] +
$prev[ $row - 1 ][ $col ] +
$prev[ $row - 1 ][ $col + 1 ] +
$prev[ $row ][ $col - 1 ] +
$prev[ $row ][ $col + 1 ] +
$prev[ $row + 1 ][ $col - 1 ] +
$prev[ $row + 1 ][ $col ] +
$prev[ $row + 1 ][ $col + 1 ];
$new[$row][$col] =
( $prev[$row][$col] && $val == 2 || $val == 3 );
}
}
return @new;
}
 
sub printlife {
my @life = @_;
if ($printed) {
# Move the cursor up to print over prior generation.
print "\e[1A" x $height;
}
$printed = 1;
foreach my $row ( 0 .. $height - 1 ) {
foreach my $col ( 0 .. $width - 1 ) {
print($life[$row][$col]
? "\e[33;45;1m \e[0m"
: "\e[1;34;1m \e[0m");
}
print "\n";
}
}
 
my @life = generate;
print "Start\n";
printlife @life;
foreach my $stage ( 1 .. $generations ) {
sleep 1;
print "Generation $stage\n\e[1A";
@life = nexgen @life;
printlife @life;
}
print "\n";
Another version, takes up the whole area of your terminal. Using warping edges.
my $w = `tput cols` - 1;
my $h = `tput lines` - 1;
my $r = "\033[H";
 
my @universe = map([ map(rand(1) < .1, 1 .. $w) ], 1 .. $h);
sub iterate {
my @new = map([ map(0, 1 .. $w) ], 1 .. $h);
for my $i (0 .. $h - 1) {
for my $j (0 .. $w - 1) {
my $neighbor = 0;
for ( [-1, -1], [-1, 0], [-1, 1],
[ 0, -1], [ 0, 1],
[ 1, -1], [ 1, 0], [ 1, 1] )
{
my $y = $_->[0] + $i;
my $x = $_->[1] + $j;
$neighbor += $universe[$y % $h][$x % $w];
last if $neighbor > 3;
}
 
$new[$i][$j] = $universe[$i][$j]
? ($neighbor == 2 or $neighbor == 3)
: $neighbor == 3;
}}
@universe = @new;
}
 
while(1) {
print $r;
print map((map($_ ? "#" : " ", @$_), "\n"), @universe);
iterate;
}

[edit] Perl 6

Works with: Rakudo version 2014.02-38-g1726c7d built on MoarVM version 2014.02-18-g3c82a79
class Automaton {
subset World of Str where {
.lines>>.chars.uniq == 1 and m/^^<[.#\n]>+$$/
}
has Int ($.width, $.height);
has @.a;
 
multi method new (World $s) {
self.new:
:width(.pick.chars), :height(.elems),
:a( .map: { [ .comb ] } )
given $s.lines;
}
 
method gist { join "\n", map { [~] @$_ }, @!a }
 
method C (Int $r, Int $c --> Bool) {
@!a[$r % $!height][$c % $!width] eq '#';
}
method N (Int $r, Int $c --> Int) {
+grep ?*, map { self.C: |@$_ },
[ $r - 1, $c - 1], [ $r - 1, $c ], [ $r - 1, $c + 1],
[ $r , $c - 1], [ $r , $c + 1],
[ $r + 1, $c - 1], [ $r + 1, $c ], [ $r + 1, $c + 1];
}
 
method succ {
self.new: :$!width, :$!height,
:a(
gather for ^$.height -> $r {
take [
gather for ^$.width -> $c {
take
(self.C($r, $c) == 1 && self.N($r, $c) == 2|3 )
|| (self.C($r, $c) == 0 && self.N($r, $c) == 3)
?? '#' !! '.'
}
]
}
)
}
}
 
my Automaton $glider .= new: '............
............
............
.......###..
.......#....
........#...
............'
;
 
 
for ^10 {
say $glider++;
say '--';
}

[edit] PicoLisp

This example uses 'grid' and 'disp' from "lib/simul.l". These functions maintain an array of multiply linked objects, and are also used in the chess program and other games in the distribution.

(load "@lib/simul.l")
 
(de life (DX DY . Init)
(let Grid (grid DX DY)
(for This Init
(=: life T) )
(loop
(disp Grid NIL
'((This) (if (: life) "X " " ")) )
(wait 1000)
(for Col Grid
(for This Col
(let N # Count neighbors
(cnt
'((Dir) (get (Dir This) 'life))
(quote
west east south north
((X) (south (west X)))
((X) (north (west X)))
((X) (south (east X)))
((X) (north (east X))) ) )
(=: next # Next generation
(if (: life)
(>= 3 N 2)
(= N 3) ) ) ) ) )
(for Col Grid # Update
(for This Col
(=: life (: next)) ) ) ) ) )
 
(life 5 5 b3 c3 d3)

Output:

 5
 4
 3   X X X
 2
 1
   a b c d e
 5
 4     X
 3     X
 2     X
 1
   a b c d e
 5
 4
 3   X X X
 2
 1
   a b c d e

[edit] PL/I

(subscriptrange):
Conway: procedure options (main); /* 20 November 2013 */
/* A grid of (1:100, 1:100) is desired; the array GRID is defined as (0:101, 0:101), */
/* to satisfy the requirement that elements off-grid are zero. */
declare n fixed binary; /* grid size) */
 
put ('What grid size do you want?');
get (n);
put skip list ('Generating a grid of size ' || trim(n) );
 
begin;
declare grid (0:n+1,0:n+1) bit(1) initial ((*) '0'b);
declare new (0:n+1,0:n+1) bit(1);
declare cell(3,3) defined grid(1sub-2+i, 2sub-2+j) bit (1);
declare (i, j, k) fixed binary;
 
/* Initialize some cells. */
grid(2,2) = '1'b; grid(2,3) = '1'b; grid(2,4) = '1'b;
 
/* Print the initial state. */
put list ('Initial pattern:');
do i = 1 to n;
put skip;
do j = 1 to n;
put edit (grid(i,j)) (b(1));
end;
end;
 
do k = 1 to 4;
/* Do one generation of life */
new = '0'b;
/* For each C, the center of a 3 x 3 cell matrix. */
do i = 1 to n;
do j = 1 to n;
if grid(i,j) then
select (sum(cell)-1);
when (0,1) new(i,j) = '0'b;
when (4,5,6,7,8) new(i,j) = '0'b;
when (2,3) new(i,j) = '1'b;
end;
else
select (sum(cell));
when (3) new(i,j) = '1'b;
otherwise new(i,j) = '0'b;
end;
end;
end;
grid = new; /* Update GRID with the new generation. */
 
/* Print the generation. */
put skip(2) list ('Generation ' || trim(k));
do i = 1 to n;
put skip;
do j = 1 to n;
put edit (grid(i,j)) (b(1));
end;
end;
end;
end;
end Conway;

Results:

What grid size do you want? 

Generating a grid of size 5 

Initial conditions: 
00000
01110
00000
00000
00000

Generation 1 
00100
00100
00100
00000
00000

Generation 2 
00000
01110
00000
00000
00000

Generation 3 
00100
00100
00100
00000
00000

Generation 4 
00000
01110
00000
00000
00000

[edit] PostScript

%!PS-Adobe-3.0
%%BoundingBox: 0 0 400 400
 
/size 400 def
 
realtime srand
/rand1 { rand 2147483647 div } def
 
/m { moveto } bind def
/l { rlineto} bind def
/drawboard {
0 1 n 1 sub { /y exch def
0 1 n 1 sub { /x exch def
board x get y get 1 eq {
x c mul y c mul m
c 0 l 0 c l c neg 0 l
closepath fill
} if
} for } for
} def
 
/r1n { dup 0 lt { n add } if dup n ge { n sub } if } def
/neighbors { /y exch def /x exch def 0
y 1 sub 1 y 1 add { r1n /y1 exch def
x 1 sub 1 x 1 add { r1n /x1 exch def
board x1 get y1 get add
} for } for
board x get y get sub
} def
 
/iter {
/board
[0 1 n 1 sub { /x exch def
[0 1 n 1 sub { /y exch def
x y neighbors
board x get y get
0 eq { 3 eq {1}{0} ifelse }
{ dup 2 eq exch 3 eq or {1}{0} ifelse } ifelse
} for ]
} for ] def
} def
 
/n 200 def
/initprob .15 def
/c size n div def
/board [ n {[ n { rand1 initprob le {1}{0} ifelse } repeat]} repeat ] def
 
1000 { drawboard showpage iter } repeat
%%EOF

[edit] Prolog

 
%----------------------------------------------------------------------%
% GAME OF LIFE  %
%  %
% Adapt the prediacte grid_size according to the grid size of the  %
% start pic.  %
% Modify the number of generations.  %
% Run PROLOG and type '[gol].' to compile the source file.  %
% Create a subfolder <subfolder> where your gol.pl resides and place  %
% your initial PBM '<filename>0.0000.pbm' inside <subfolder>.  %
% You need to adjust the number of zeros after <filename>. The  %
% sequence of zeros after '0.' must be as long as the number of  %
% generations. This is important to obtain a propper animation.  %
% (Maybe someone knows a better solution for this)  %
% Start PROLOG and run  %
%  %
% cellular('./<subloder>/<filename>').  %
%  %
% Inside <subfolder> run the following shell command  %
%  %
% convert -delay 25 -loop 0 <filename>* <filename>.gif  %
%  %
%----------------------------------------------------------------------%
 
%----------------------------------------------------------------------%
% Special thanks to René Thiemann improving the runtime performance.  %
%----------------------------------------------------------------------%
 
% Size of the 2D grid
grid_size(300).
% Number of generations
generations(1000).
 
%----------------------------------------------------------------------%
% Main procedure: generate n generations of life and store each file.  %
% cellular( +File path )  %
%----------------------------------------------------------------------%
cellular(I) :-
grid_size(GS),
string_concat(I,'0.0000.pbm',I1),
read_pbm(I1,GS,M),
cellular_(I,M,GS,1), !.
 
cellular_(I,M,GS,N) :-
N1 is N+1,
format(atom(N0),'~4d',N),
string_concat(I,N0,I1),
string_concat(I1,'.pbm',I2),
step(M,M1),
write_pbm(M1,GS,I2), !,
cellular_(I,M1,GS,N1).
cellular_(_,_,_,GE) :-
generations(GE),!.
 
%----------------------------------------------------------------------%
% Apply the Game Of Life rule set to every cell.  %
% step( +OldMatrix, +NewMatrix )  %
%  %
% ss | s | ... | s ss ... step_ss  %
% ----+---+-----+--- s ... step_s  %
% ii | i | ... | i ii ... step_ii  %
% ----+---+-----+--- i ... step_i  %
%  : | : |  : | : ee ... step_ee  %
% ----+---+-----+--- e ... step_e  %
% ii | i | ... | i  %
% ----+---+-----+---  %
% ee | e | ... | e  %
%  %
%----------------------------------------------------------------------%
step([R1,R2|M],[H|T]) :-
step_ss(R1,R2,H), !,
step_([R1,R2|M],T).
 
step_([R1,R2,R3|M],[H|T]) :-
step_ii(R1,R2,R3,H),
step_([R2,R3|M],T), !.
step_([R1,R2],[H]) :-
step_ee(R1,R2,H).
 
% Start case
step_ss([A1,A2|R1],[B1,B2|R2],[H|T]) :-
rule([0,0,0],[0,A1,A2],[0,B1,B2],H),
step_s([A1,A2|R1],[B1,B2|R2],T).
step_s([A1,A2,A3|R1],[B1,B2,B3|R2],[H|T]) :-
rule([0,0,0],[A1,A2,A3],[B1,B2,B3],H),
step_s([A2,A3|R1],[B2,B3|R2],T).
step_s([A1,A2],[B1,B2],[H]) :-
rule([0,0,0],[A1,A2,0],[B1,B2,0],H).
 
% Immediate case
step_ii([A1,A2|R1],[B1,B2|R2],[C1,C2|R3],[H|T]) :-
rule([0,A1,A2],[0,B1,B2],[0,C1,C2],H),
step_i([A1,A2|R1],[B1,B2|R2],[C1,C2|R3],T).
step_i([A1,A2,A3|R1],[B1,B2,B3|R2],[C1,C2,C3|R3],[H|T]) :-
rule([A1,A2,A3],[B1,B2,B3],[C1,C2,C3],H),
step_i([A2,A3|R1],[B2,B3|R2],[C2,C3|R3],T).
step_i([A1,A2],[B1,B2],[C1,C2],[H]) :-
rule([A1,A2,0],[B1,B2,0],[C1,C2,0],H).
 
% End case
step_ee([A1,A2|R1],[B1,B2|R2],[H|T]) :-
rule([0,A1,A2],[0,B1,B2],[0,0,0],H),
step_e([A1,A2|R1],[B1,B2|R2],T).
step_e([A1,A2,A3|R1],[B1,B2,B3|R2],[H|T]) :-
rule([A1,A2,A3],[B1,B2,B3],[0,0,0],H),
step_e([A2,A3|R1],[B2,B3|R2],T).
step_e([A1,A2],[B1,B2],[H]) :-
rule([A1,A2,0],[B1,B2,0],[0,0,0],H).
 
%----------------------------------------------------------------------%
% o Any dead cell with exactly three live neighbours becomes a live  %
% cell, as if by reproduction.  %
% o Any other dead cell remains dead.  %
% o Any live cell with fewer than two live neighbours dies, as if  %
% caused by under-population.  %
% o Any live cell with two or three live neighbours lives on to the  %
% next generation.  %
% o Any live cell with more than three live neighbours dies, as if by  %
% overcrowding.  %
%  %
% [Source: Wikipedia]  %
%----------------------------------------------------------------------%
rule([A,B,C],[D,0,F],[G,H,I],1) :- A+B+C+D+F+G+H+I =:= 3.
rule([_,_,_],[_,0,_],[_,_,_],0).
rule([A,B,C],[D,1,F],[G,H,I],0) :- A+B+C+D+F+G+H+I < 2.
rule([A,B,C],[D,1,F],[G,H,I],1) :- A+B+C+D+F+G+H+I =:= 2.
rule([A,B,C],[D,1,F],[G,H,I],1) :- A+B+C+D+F+G+H+I =:= 3.
rule([A,B,C],[D,1,F],[G,H,I],0) :- A+B+C+D+F+G+H+I > 3.
 
%----------------------------------------------------------------------%
% Read a 2bit Protable Bitmap into a GS x GS 2-dimensional list.  %
% read_pbm( +File path, +Grid size, -List 2D )  %
%----------------------------------------------------------------------%
read_pbm(F,GS,M) :-
open(F,read,S),
skip(S,10),
skip(S,10),
get(S,C),
read_file(S,C,L),
nest(L,GS,M),
close(S).
 
read_file(S,C,[CHR|T]) :-
CHR is C-48,
get(S,NC),
read_file(S,NC,T).
read_file(_,-1,[]) :- !.
 
%----------------------------------------------------------------------%
% Morph simple list into a 2-dimensional one with size GS x GS  %
% nest( ?List simple, ?Grid size, ?2D list )  %
%----------------------------------------------------------------------%
nest(L,GS,[H|T]) :-
length(H,GS),
append(H,S,L),
nest(S,GS,T).
nest(L,GS,[L]) :-
length(L,S),
S =< GS, !.
 
%----------------------------------------------------------------------%
% Write a GS x GS 2-dimensional list into a 2bit Protable Bitmap.  %
% write_pbm( +List 2D, +Grid size, +File path )  %
%----------------------------------------------------------------------%
write_pbm(L,GS,F) :-
open(F,write,S),
write(S,'P1'), nl(S),
write(S,GS), put(S,' '), write(S,GS), nl(S),
write_file(S,L),
close(S).
 
write_file(S,[H|T]) :-
write_line(S,H), nl(S),
write_file(S,T).
write_file(_,[]) :- !.
 
write_line(S,[H|T]) :-
write(S,H), put(S,' '),
write_line(S,T).
write_line(_,[]) :- !.
 

Sample output:
100x100.gif

[edit] PureBasic

EnableExplicit
Define.i x, y ,Xmax ,Ymax ,N
Xmax = 13 : Ymax = 20
Dim world.i(Xmax+1,Ymax+1)
Dim Nextworld.i(Xmax+1,Ymax+1)
 
; Glider test
;------------------------------------------
world(1,1)=1 : world(1,2)=0 : world(1,3)=0
world(2,1)=0 : world(2,2)=1 : world(2,3)=1
world(3,1)=1 : world(3,2)=1 : world(3,3)=0
;------------------------------------------
 
OpenConsole()
EnableGraphicalConsole(1)
ClearConsole()
Print("Press any key to interrupt")
Repeat
ConsoleLocate(0,2)
PrintN(LSet("", Xmax+2, "-"))
;---------- endless world ---------
For y = 1 To Ymax
world(0,y)=world(Xmax,y)
world(Xmax+1,y)=world(1,y)
Next
For x = 1 To Xmax
world(x,0)=world(x,Ymax)
world(x,Ymax+1)=world(x,1)
Next
world(0 ,0 )=world(Xmax,Ymax)
world(Xmax+1,Ymax+1)=world(1 ,1 )
world(Xmax+1,0 )=world(1 ,Ymax)
world( 0,Ymax+1)=world(Xmax,1 )
;---------- endless world ---------
For y = 1 To Ymax
Print("|")
For x = 1 To Xmax
Print(Chr(32+world(x,y)*3))
N = world(x-1,y-1)+world(x-1,y)+world(x-1,y+1)+world(x,y-1)
N + world(x,y+1)+world(x+1,y-1)+world(x+1,y)+world(x+1,y+1)
If (world(x,y) And (N = 2 Or N = 3))Or (world(x,y)=0 And N = 3)
Nextworld(x,y)=1
Else
Nextworld(x,y)=0
EndIf
Next
PrintN("|")
Next
PrintN(LSet("", Xmax+2, "-"))
Delay(100)
;Swap world() , Nextworld()  ;PB <4.50
CopyArray(Nextworld(), world());PB =>4.50
Dim Nextworld.i(Xmax+1,Ymax+1)
Until Inkey() <> ""
 
PrintN("Press any key to exit"): Repeat: Until Inkey() <> ""

Sample output:
Game-of-life-PureBasic.gif

[edit] Python

This implementation uses defaultdict(int) to create dictionaries that return the result of calling int(), i.e. zero for any key not in the dictionary. This 'trick allows celltable to be initialized to just those keys with a value of 1.

Python allows many types other than strings and ints to be keys in a dictionary. The example uses a dictionary with keys that are a two entry tuple to represent the universe, which also returns a default value of zero. This simplifies the calculation N as out-of-bounds indexing of universe returns zero.

import random
from collections import defaultdict
 
printdead, printlive = '-#'
maxgenerations = 3
cellcount = 3,3
celltable = defaultdict(int, {
(1, 2): 1,
(1, 3): 1,
(0, 3): 1,
} ) # Only need to populate with the keys leading to life
 
##
## Start States
##
# blinker
u = universe = defaultdict(int)
u[(1,0)], u[(1,1)], u[(1,2)] = 1,1,1
 
## toad
#u = universe = defaultdict(int)
#u[(5,5)], u[(5,6)], u[(5,7)] = 1,1,1
#u[(6,6)], u[(6,7)], u[(6,8)] = 1,1,1
 
## glider
#u = universe = defaultdict(int)
#maxgenerations = 16
#u[(5,5)], u[(5,6)], u[(5,7)] = 1,1,1
#u[(6,5)] = 1
#u[(7,6)] = 1
 
## random start
#universe = defaultdict(int,
# # array of random start values
# ( ((row, col), random.choice((0,1)))
# for col in range(cellcount[0])
# for row in range(cellcount[1])
# ) ) # returns 0 for out of bounds
 
for i in range(maxgenerations):
print "\nGeneration %3i:" % ( i, )
for row in range(cellcount[1]):
print " ", ''.join(str(universe[(row,col)])
for col in range(cellcount[0])).replace(
'0', printdead).replace('1', printlive)
nextgeneration = defaultdict(int)
for row in range(cellcount[1]):
for col in range(cellcount[0]):
nextgeneration[(row,col)] = celltable[
( universe[(row,col)],
-universe[(row,col)] + sum(universe[(r,c)]
for r in range(row-1,row+2)
for c in range(col-1, col+2) )
) ]
universe = nextgeneration
Output:
(sample)
Generation   0:
   ---
   ###
   ---

Generation   1:
   -#-
   -#-
   -#-

Generation   2:
   ---
   ###
   ---

[edit] Boardless approach

A version using the boardless approach.

from collections import Counter
 
def life(world, N):
"Play Conway's game of life for N generations from initial world."
for g in range(N+1):
display(world, g)
counts = Counter(n for c in world for n in offset(neighboring_cells, c))
world = set(c for c in counts if counts[c] == 3
or counts[c] == 2 and c in world)
return world
 
 
neighboring_cells = [(-1, -1), (-1, 0), (-1, 1),
( 0, -1), ( 0, 1),
( 1, -1), ( 1, 0), ( 1, 1)]
 
def offset(world, delta):
"Slide/offset all the cells in world by delta, a (dx, dy) vector."
(dx, dy) = delta
return set((x+dx, y+dy) for (x, y) in world)
 
def display(world, g):
"Display the world as a grid of characters."
print ' GENERATION {}:'.format(g)
Xs, Ys = zip(*world)
Xrange = range(min(Xs), max(Xs)+1)
for y in range(min(Ys), max(Ys)+1):
print ''.join('#' if (x, y) in world else '.'
for x in Xrange)
 
blinker = {(1, 0), (1, 1), (1, 2)}
block = {(0, 0), (1, 1), (0, 1), (1, 0)}
toad = {(1, 2), (0, 1), (0, 0), (0, 2), (1, 3), (1, 1)}
glider = {(0, 1), (1, 0), (0, 0), (0, 2), (2, 1)}
world = (block | offset(blinker, (5, 2)) | offset(glider, (15, 5)) | offset(toad, (25, 5))
| {(18, 2), (19, 2), (20, 2), (21, 2)} | offset(block, (35, 7)))
 
 
life(world, 5)
Output:
          GENERATION 0:
##...................................
##...................................
......#...........####...............
......#..............................
......#..............................
...............##........#...........
...............#.#.......##..........
...............#.........##........##
..........................#........##
          GENERATION 1:
##...................................
##.................##................
...................##................
.....###...........##................
.....................................
...............##........##..........
..............##........#............
................#..........#.......##
.........................##........##
          GENERATION 2:
##...................................
##.................##................
......#...........#..#...............
......#............##................
......#..............................
..............###........#...........
..............#..........##..........
...............#.........##........##
..........................#........##
          GENERATION 3:
##...................................
##.................##................
..................#..#...............
.....###...........##................
...............#.....................
..............##.........##..........
..............#.#.......#............
...........................#.......##
.........................##........##
          GENERATION 4:
##...................................
##.................##................
......#...........#..#...............
......#............##................
......#.......##.....................
..............#.#........#...........
..............#..........##..........
.........................##........##
..........................#........##
          GENERATION 5:
##...................................
##.................##................
..................#..#...............
.....###...........##................
..............##.....................
.............##..........##..........
...............#........#............
...........................#.......##
.........................##........##

[edit] R

# Generates a new board - either a random one, sample blinker or gliders, or user specified.
gen.board <- function(type="random", nrow=3, ncol=3, seeds=NULL)
{
if(type=="random")
{
return(matrix(runif(nrow*ncol) > 0.5, nrow=nrow, ncol=ncol))
} else if(type=="blinker")
{
seeds <- list(c(2,1),c(2,2),c(2,3))
} else if(type=="glider")
{
seeds <- list(c(1,2),c(2,3),c(3,1), c(3,2), c(3,3))
}
board <- matrix(FALSE, nrow=nrow, ncol=ncol)
for(k in seq_along(seeds))
{
board[seeds[[k]][1],seeds[[k]][2]] <- TRUE
}
board
}
 
# Returns the number of living neighbours to a location
count.neighbours <- function(x,i,j)
{
sum(x[max(1,i-1):min(nrow(x),i+1),max(1,j-1):min(ncol(x),j+1)]) - x[i,j]
}
 
# Implements the rulebase
determine.new.state <- function(board, i, j)
{
N <- count.neighbours(board,i,j)
(N == 3 || (N ==2 && board[i,j]))
}
 
# Generates the next interation of the board from the existing one
evolve <- function(board)
{
newboard <- board
for(i in seq_len(nrow(board)))
{
for(j in seq_len(ncol(board)))
{
newboard[i,j] <- determine.new.state(board,i,j)
}
}
newboard
}
 
# Plays the game. By default, the board is shown in a plot window, though output to the console if possible.
game.of.life <- function(board, nsteps=50, timebetweensteps=0.25, graphicaloutput=TRUE)
{
if(!require(lattice)) stop("lattice package could not be loaded")
nr <- nrow(board)
 
for(i in seq_len(nsteps))
{
if(graphicaloutput)
{
print(levelplot(t(board[nr:1,]), colorkey=FALSE))
} else print(board)
 
Sys.sleep(timebetweensteps)
 
newboard <- evolve(board)
 
if(all(newboard==board))
{
message("board is static")
break
} else if(sum(newboard) < 1)
{
message("everything is dead")
break
} else board <- newboard
}
invisible(board)
}
 
# Example usage
game.of.life(gen.board("blinker"))
game.of.life(gen.board("glider", 18, 20))
game.of.life(gen.board(, 50, 50))

[edit] Racket

 
#lang racket
(require 2htdp/image 2htdp/universe)
 
;;;
;;; Grid object
;;;
 
(define (make-empty-grid m n)
(build-vector m (lambda (y) (make-vector n 0))))
 
(define rows vector-length)
 
(define (cols grid)
(vector-length (vector-ref grid 0)))
 
(define (make-grid m n living-cells)
(let loop ([grid (make-empty-grid m n)]
[cells living-cells])
(if (empty? cells)
grid
(loop (2d-set! grid (caar cells) (cadar cells) 1) (cdr cells)))))
 
(define (2d-ref grid i j)
(cond [(< i 0) 0]
[(< j 0) 0]
[(>= i (rows grid)) 0]
[(>= j (cols grid)) 0]
[else (vector-ref (vector-ref grid i) j)]))
 
(define (2d-refs grid indices)
(map (lambda (ind) (2d-ref grid (car ind) (cadr ind))) indices))
 
(define (2d-set! grid i j val)
(vector-set! (vector-ref grid i) j val)
grid)
 
;;; cartesian product of 2 lists
(define (cart l1 l2)
(if (empty? l1)
'()
(append (let loop ([n (car l1)] [l l2])
(if (empty? l) '() (cons (list n (car l)) (loop n (cdr l)))))
(cart (cdr l1) l2))))
 
;;; Count living cells in the neighbourhood
(define (n-count grid i j)
(- (apply + (2d-refs grid (cart (list (- i 1) i (+ i 1))
(list (- j 1) j (+ j 1)))))
(2d-ref grid i j)))
 
;;;
;;; Rules and updates of the grid
;;;
 
;;; rules are stored in a 2d array: r_i,j = new state of a cell
;;; in state i with j neighboors
(define conway-rules
(list->vector (list (list->vector '(0 0 0 1 0 0 0 0 0))
(list->vector '(0 0 1 1 0 0 0 0 0)))))
 
(define (next-state rules grid i j)
(let ([current (2d-ref grid i j)]
[N (n-count grid i j)])
(2d-ref rules current N)))
 
(define (next-grid rules grid)
(let ([new-grid (make-empty-grid (rows grid) (cols grid))])
(let loop ([i 0] [j 0])
(if (>= i (rows grid))
new-grid
(if (>= j (cols grid))
(loop (+ i 1) 0)
(begin (2d-set! new-grid i j (next-state rules grid i j))
(loop i (+ j 1))))))))
 
(define (next-grid! rules grid)
(let ([new-grid (next-grid rules grid)])
(let loop ((i 0))
(if (< i (rows grid))
(begin (vector-set! grid i (vector-ref new-grid i))
(loop (+ i 1)))
grid))))
 
;;;
;;; Image / Animation
;;;
 
(define (grid->image grid)
(let ([m (rows grid)] [n (cols grid)] [size 5])
(let loop ([img (rectangle (* m size) (* n size) "solid" "white")]
[i 0] [j 0])
(if (>= i (rows grid))
img
(if (>= j (cols grid))
(loop img (+ i 1) 0)
(if (= (2d-ref grid i j) 1)
(loop (underlay/xy img (* i (+ 1 size)) (* j (+ 1 size))
(square (- size 2) "solid" "black"))
i (+ j 1))
(loop img i (+ j 1))))))))
 
 
 
(define (game-of-life grid refresh_time)
(animate (lambda (n)
(if (= (modulo n refresh_time) 0)
(grid->image (next-grid! conway-rules grid))
(grid->image grid)))))
 
;;;
;;; Examples
;;;
 
(define (blinker)
(make-grid 3 3 '((0 1) (1 1) (2 1))))
 
(define (thunder)
(make-grid 70 50 '((30 19) (30 20) (30 21) (29 17) (30 17) (31 17))))
 
(define (cross)
(let loop ([i 0] [l '()])
(if (>= i 80)
(make-grid 80 80 l)
(loop (+ i 1) (cons (list i i) (cons (list (- 79 i) i) l))))))
 
;;; To run examples:
;;; (game-of-life (blinker) 30)
;;; (game-of-life (thunder) 2)
;;; (game-of-life (cross) 2)
 

Output for an 80x80 cross: Game of life cross.gif

[edit] Retro

 
create world
20 20 * allot
 
create next
20 20 * allot
 
create initial
( 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 )
( 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 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , 0 , 0 , 1 , 1 , 0 ,
( 2 ) 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , 0 , 0 , 1 , 1 , 0 ,
( 3 ) 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , 0 , 0 , 0 , 0 , 0 ,
( 4 ) 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
( 5 ) 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
( 6 ) 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
( 7 ) 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
( 8 ) 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
( 9 ) 0 , 0 , 0 , 0 , 0 , 0 , 1 , 0 , 1 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
( 10 ) 0 , 0 , 0 , 0 , 0 , 0 , 1 , 0 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
( 11 ) 0 , 0 , 0 , 0 , 0 , 0 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
( 12 ) 0 , 0 , 0 , 0 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
( 13 ) 0 , 0 , 1 , 0 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
( 14 ) 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , 1 , 0 , 0 , 0 , 0 ,
( 15 ) 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , 1 , 0 , 0 , 0 , 0 ,
( 16 ) 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , 1 , 0 , 0 ,
( 17 ) 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , 1 , 0 , 0 ,
( 18 ) 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
( 19 ) 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
 
( Assumes anything outside the bounds is "dead" )
{{
variable surrounding
 : get ( rc- )
2over [ 0 19 within ] bi@ and
[ world + [ 20 * ] dip + @ ] [ 2drop 0 ] if ;
 : neighbor? ( rc- ) get +surrounding ;
 : NW ( rc-rc ) 2over [ 1- ] bi@ neighbor? ;
 : NN ( rc-rc ) 2over [ 1- ] dip neighbor? ;
 : NE ( rc-rc ) 2over [ 1- ] dip 1+ neighbor? ;
 : WW ( rc-rc ) 2over 1- neighbor? ;
 : EE ( rc-rc ) 2over 1+ neighbor? ;
 : SW ( rc-rc ) 2over [ 1+ ] dip 1- neighbor? ;
 : SS ( rc-rc ) 2over [ 1+ ] dip neighbor? ;
 : SE ( rc-rc ) 2over [ 1+ ] bi@ neighbor? ;
 : count ( rc-rcn )
0 !surrounding
NW NN NE
WW EE
SW SS SE @surrounding ;
 : alive ( rc-n )
count
[ 0 1 within ] [ drop 0 ] when
[ 4 8 within ] [ drop 0 ] when
[ 2 3 within ] [ drop 1 ] when ;
 : dead ( rc-n )
count
[ 3 = ] [ drop 1 ] when
[ 0 2 within ] [ drop 0 ] when
[ 4 8 within ] [ drop 0 ] when ;
 : newState ( rc-n )
2over get 1 = [ alive ] [ dead ] if ;
 : set ( nrc- ) next + [ 20 * ] dip + ! ;
 : cols ( r- )
20 [ over swap newState 2rot set ] iter drop ;
 : output ( n- ) [ 'o ] [ '. ] if putc space ;
---reveal---
 : display ( - )
cr world 20 [ 20 [ @+ output ] times cr ] times drop ;
 : start ( - )
initial world 20 20 * copy display ;
 : gen ( - )
20 [ cols ] iter next world 20 20 * copy ;
 : delay ( - ) time 1+ [ time over <= ] while drop ;
 : run ( n- )
[ delay clear gen display ] times ;
}}
 
start 20 run

[edit] REXX

[edit] version 1

This version has been trimmed down from the original REXX program, otherwise the size of the program (with all its options and optional formatting) would probably be on the big side for general viewing, and maybe a wee bit complex to demonstrate how to program for this task.

/*REXX program displays Conway's game of life, it stops after N repeats.*/
signal on halt /*handle cell growth interruptus.*/
parse arg peeps '(' generations rows cols bare! life! clearScreen repeats
blank = 'BLANK' /*the "name" for blank*/
generations = p(generations 100) /*#generations allowed*/
rows = p(rows 3) /*number of cell rows.*/
cols = p(cols 3) /* " " " cols.*/
bare! = pickChar(bare! blank) /*an empty cell thingy*/
clearScreen = p(clearScreen 0) /*1 = clear the screen*/
life! = pickChar(life! '☼') /*looks like an ameba.*/
repeats = p(repeats 2) /*stop if 2 repeats.*/
fents=max(linesize()-1,cols) /*fence width shown after display*/
#repeats=0; $.=bare! /*the universe is new, and barren*/
gens=abs(generations) /*use this for convenience. */
x=space(peeps) /*remove superfluous spaces. */
if x=='' then x='2,1 2,2 2,3' /* [↓] process the cells given.*/
do while x\==''; parse var x _ x; parse var _ r ',' c .
$.r.c=life!; rows=max(rows,r); cols=max(cols,c)
end /*while*/
life=0;  !.=0; call showCells /*show initial state of the cells*/
/*─────────────────────────────────────watch cell colony grow/live/die. */
do life=1 for gens; @.=bare!
do r=1 for rows
do c=1 for cols;  ??=$.r.c; n=neighbors()
if ??==bare! then do; if n==3 then ??=life!; end
else if n<2 | n>3 then ??=bare!
@.r.c=??
end /*c*/
end /*r*/
call assign$ /*assign alternate cells ──► real*/
if generations>0 | life==gens then call showCells
end /*life*/
/*─────────────────────────────────────stop watching the universe (life)*/
halt: cycles=life-1; if cycles\==gens then say 'REXX program interrupted.'
exit /*stick a fork in it, we're done.*/
/*───────────────────────────────SHOWCELLS subroutine───────────────────*/
showCells: if clearScreen then 'CLS' /* ◄─── change this for your OS.*/
call showRows /*show the rows in proper order. */
say right(copies('═',fents)life,fents) /*show&tell for a bunch of cells.*/
if _=='' then exit /*if no life, then stop the run. */
if !._ then #repeats=#repeats+1 /*we detected a repeated pattern.*/
!._=1 /*existence state & compare later*/
if repeats\==0 & #repeats<=repeats then return /*so far, so good.*/
say '"Life" repeated itself' repeats "times, program is stopping."
exit /*stick a fork in it, we're done.*/
/*───────────────────────────────1─liner subroutines───────────────────────────────────────────────────────────────────────*/
$: parse arg _row,_col; return $._row._col==life!
assign$: do r=1 for rows; do c=1 for cols; $.r.c=@.r.c; end; end; return
err: say;say;say center(' error! ',max(40,linesize()%2),"*");say;do j=1 for arg();say arg(j);say;end;say;exit 13
neighbors: return $(r-1,c-1)+$(r-1,c)+$(r-1,c+1)+$(r,c-1)+$(r,c+1)+$(r+1,c-1)+$(r+1,c)+$(r+1,c+1)
p: return word(arg(1),1)
pickChar: _=p(arg(1));if translate(_)==blank then _=' ';if length(_)==3 then _=d2c(_);if length(_)==2 then _=x2c(_);return _
showRows: _=; do r=rows by -1 for rows; z=; do c=1 for cols; z=z||$.r.c; end; z=strip(z,'T'); say z; _=_||z; end; return

Programming note:   the   neighbors   subroutine (above) could be optimized for speed by setting some short-circuit values   (r-1, c-1, r+1, and c+1)   and using those values in the subsequent expressions.


This REXX program makes use of   LINESIZE   REXX program (or BIF) which is used to determine the screen width (or linesize) of the terminal (console).
The   LINESIZE.REX   REXX program is included here ──► LINESIZE.REX.

output when using the default input:

☼☼☼

══════════════════════════════════════════════════════════════════════════════0
 ☼
 ☼
 ☼
══════════════════════════════════════════════════════════════════════════════1

☼☼☼

══════════════════════════════════════════════════════════════════════════════2
 ☼
 ☼
 ☼
══════════════════════════════════════════════════════════════════════════════3

☼☼☼

══════════════════════════════════════════════════════════════════════════════4
"Life" repeated itself 2 times,  program is stopping.

[edit] version 2

/* REXX ---------------------------------------------------------------
* 02.08.2014 Walter Pachl
* Input is a file containing the initial pattern
* The compute area is extended when needed
* (cells are born outside the current compute area)
* The program stops when the picture shown is the same as the first
* or equal to the previous one
*--------------------------------------------------------------------*/

Parse Arg f
If f='' Then f='bipole'
fid=f'.in'
oid=f'.txt'; 'erase' oid
debug=0
If debug Then Do
dbg=f'.xxx'; 'erase' dbg
End
ml=0
l.=''
Do ri=3 By 1 While lines(fid)>0
l.ri=' 'linein(fid)
ml=max(ml,length(strip(l.ri,'T')))
End
ml=ml+2
ri=ri+1
yy=ri
If debug Then
say 'ml='ml 'yy='yy
yb=1
a.=' '
b.=' '
m.=''
x.=''
Parse Value 1 ml 1 yy With xmi xma ymi yma
Parse Value '999 0' With xmin xmax
Parse Value '999 0' With ymin ymax
 
Do y=1 To yy
z=yy-y-1
l=l.z
Do x=1 By 1 While l<>''
Parse Var l c +1 l
If c='*' Then Do
a.x.z='*'
End
End
End
Call show
Do step=1 To 60
Call store
If step>1 & is_equal(step,1) Then Leave
If step>1 & is_equal(step,step-1) Then Leave
Call show_neighbors
Do y=yma To ymi By -1
ol=format(x,2)' '
Do x=xmi To xma
neighbors=neighbors(x,y)
If a.x.y=' ' Then Do /* dead cell */
If neighbors=3 Then Do
b.x.y='*' /* gets life */
mmo=xmi xma ymi yma
xmi=min(xmi,x-1)
xma=max(xma,x+1)
ymi=min(ymi,y-1)
yma=max(yma,y+1)
mm=xmi xma ymi yma
If mm<>mmo Then
Call debug mmo '->' mm
End
Else /* life cell */
b.x.y=' ' /* remains dead */
 
End
Else Do /* life cell */
If neighbors=2 |,
neighbors=3 Then b.x.y='*' /* remains life */
Else b.x.y=' ' /* dies */
End
End
End
/* b. is the new state and is now copied to a. */
Do y=yma To ymi By -1
Do x=xmi To xma
a.x.y=b.x.y
End
End
End
/* Output name and all states */
Call lineout oid,' 'f
st=' +' /* top and bottom border */
sb=' +' /* top and bottom border */
Do s=1 To step
st=st||'-'right(s,2,'-')||copies('-',xmax-xmin)'+'
sb=sb||copies('-',xmax-xmin+3)'+'
End
Call lineout oid,st /* top border */
Do y=ymin To ymax
ol=''
Do s=1 To step
ol=ol '|' substr(m.s.y,xmin,xmax-xmin+1)
End
Call lineout oid,ol '|'
End
Call lineout oid,sb /* bottom border */
Call lineout oid
'type' oid
If debug Then Do
Say 'original area' 1 ml '/' 1 yy
Say 'compute area ' xmi xma '/' ymi yma
End
Exit
 
set: Parse Arg x,y
a.x.y='*'
Return
 
neighbors: Procedure Expose a. debug
Parse Arg x,y
neighbors=0
do xa=x-1 to x+1
do ya=y-1 to y+1
If xa<>x | ya<>y then
If a.xa.ya='*' Then
neighbors=neighbors+1
End
End
Return neighbors
 
store:
/* store current state (a.) in lines m.step.* */
Do y=yy To 1 By -1
ol=''
Do x=1 To ml
z=a.x.y
ol=ol||z
End
x.step.y=ol
If ol<>'' then Do
ymin=min(ymin,y)
ymax=max(ymax,y)
p=pos('*',ol)
q=length(strip(ol,'T'))
If p>0 Then
xmin=min(xmin,p)
xmax=max(xmax,q)
End
m.step.y=ol
Call debug '====>' right(step,2) y ol xmin xmax
End
Return
 
is_equal:
/* test ist state a.b is equal to state a.a */
Parse Arg a,b
Do y=yy To 1 By -1
If x.b.y<>x.a.y Then
Return 0
End
Return 1
 
show: Procedure Expose dbg a. yy ml debug
Do y=1 To yy
ol='>'
Do x=1 To ml
ol=ol||a.x.y
End
Call debug ol
End
Return
 
show_neighbors: Procedure Expose a. xmi xma ymi yma dbg debug
Do y=yma To ymi By -1
ol=format(y,2)' '
 
Do x=xmi To xma
ol=ol||neighbors(x,y)
End
Call debug ol
End
Return
 
debug:
If debug Then
Return lineout(dbg,arg(1))
Else
Return
Output:
 blinker
+--1--+--2--+--3--+
|     |  *  |     |
| *** |  *  | *** |
|     |  *  |     |
+-----+-----+-----+ 
oktagon
*--1-------*--2-------*--3-------*--4-------*--5-------*--6-------*
|    **    |    **    |   *  *   |          |          |    **    |
|   *  *   |   ****   |   *  *   |   *  *   |   ****   |   *  *   |
|  *    *  |  *    *  | ** ** ** |  * ** *  |  * ** *  |  *    *  |
| *      * | **    ** |   *  *   |   *  *   |  **  **  | *      * |
| *      * | **    ** |   *  *   |   *  *   |  **  **  | *      * |
|  *    *  |  *    *  | ** ** ** |  * ** *  |  * ** *  |  *    *  |
|   *  *   |   ****   |   *  *   |   *  *   |   ****   |   *  *   |
|    **    |    **    |   *  *   |          |          |    **    |
*----------*----------*----------*----------*----------*----------*

[edit] Ruby

def game_of_life(name, size, generations, initial_life=nil)
board = new_board size
seed board, size, initial_life
print_board board, 0, name
reason = generations.times do |gen|
new = evolve board, size
print_board new, gen+1, name
break :all_dead if barren? new, size
break :static if board == new
board = new
end
if reason == :all_dead then puts "no more life."
elsif reason == :static then puts "no movement"
else puts "specified lifetime ended"
end
end
 
def new_board(n)
Array.new(n) {Array.new(n, 0)}
end
 
def seed(board, n, points=nil)
if points.nil?
# randomly seed board
srand
indices = []
n.times {|x| n.times {|y| indices << [x,y] }}
indices.shuffle[0,10].each {|x,y| board[y][x] = 1}
else
points.each {|x, y| board[y][x] = 1}
end
end
 
def evolve(board, n)
new = new_board n
n.times {|i| n.times {|j| new[i][j] = fate board, i, j, n}}
new
end
 
def fate(board, i, j, n)
i1 = [0, i-1].max; i2 = [i+1, n-1].min
j1 = [0, j-1].max; j2 = [j+1, n-1].min
sum = 0
for ii in (i1..i2)
for jj in (j1..j2)
sum += board[ii][jj] if not (ii == i and jj == j)
end
end
(sum == 3 or (sum == 2 and board[i][j] == 1)) ? 1 : 0
end
 
def barren?(board, n)
n.times {|i| n.times {|j| return false if board[i][j] == 1}}
true
end
 
def print_board(m, generation, name)
puts "#{name}: generation #{generation}"
m.each {|row| row.each {|val| print "#{val == 1 ? '#' : '.'} "}; puts}
puts
end
 
 
game_of_life "blinker", 3, 2, [[1,0],[1,1],[1,2]]
#game_of_life "glider", 4, 4, [[1,0],[2,1],[0,2],[1,2],[2,2]]
#game_of_life "random", 5, 10

The above implementation uses only methods. Below is one that is object-oriented and feels perhaps a bit more Ruby-ish.

class Game
def initialize(name, size, generations, initial_life=nil)
@board = GameBoard.new size, initial_life
@board.display 0, name
 
reason = generations.times do |gen|
new_board = evolve
new_board.display gen+1, name
break :all_dead if new_board.barren?
break :static if @board == new_board
@board = new_board
end
 
if reason == :all_dead then puts "No more life."
elsif reason == :static then puts "No movement."
else puts "Specified lifetime ended."
end
end
 
def evolve
new_board = GameBoard.new @board.size, @board.life
@board.size.times do |i|
@board.size.times do |j|
if cell_fate i, j
new_board[i,j].live
else
new_board[i,j].die
end
end
end
new_board
end
 
def cell_fate(i, j)
left = [0, i-1].max; right = [i+1, @board.size-1].min
top = [0, j-1].max; bottom = [j+1, @board.size-1].min
sum = 0
for x in (left..right)
for y in (top..bottom)
sum += @board[x,y].value if (x != i or y != j)
end
end
(sum == 3 or (sum == 2 and @board[i,j].alive?))
end
end
 
class GameBoard
attr_reader :size
 
def initialize(size, initial_life=nil)
@size = size
@board = Array.new(size) {Array.new(size) {Cell.new false}}
self.seed_board initial_life
end
 
def seed_board(life)
if life.nil?
# randomly seed board
srand # seed the random number generator
indices = []
@size.times {|x| @size.times {|y| indices << [x,y] }}
indices.shuffle[0,10].each {|x,y| @board[y][x].live}
else
life.each {|x, y| @board[y][x].live}
end
end
 
def [](x, y)
@board[y][x]
end
 
def ==(board)
self.life == board.life
end
 
def barren?
@size.times do |i|
@size.times do |j|
return false if @board[i][j].alive?
end
end
true
end
 
def life
indices = []
@size.times do |x|
@size.times do |y|
if @board[y][x].alive?
indices << [x,y]
end
end
end
indices
end
 
def display(generation, name)
puts "#{name}: generation #{generation}"
@board.each do |row|
row.each do |cell|
print "#{cell.alive? ? '#' : '.'} "
end
puts
end
puts
end
 
def apocalypse
# utility function to entirely clear the game board
@board.each do |row|
row.each do |cell|
if cell.alive?
cell.die
end
end
end
end
end
 
class Cell
def initialize is_alive
@is_alive = is_alive
end
 
def alive?
@is_alive
end
 
def value
if self.alive?
return 1
else
return 0
end
end
 
def live
@is_alive = true
end
 
def die
@is_alive = false
end
end
 
game_of_life = Game.new "blinker", 3, 2, [[1,0],[1,1],[1,2]]
game_of_life = Game.new "glider", 4, 4, [[1,0],[2,1],[0,2],[1,2],[2,2]]
game_of_life = Game.new "random", 5, 10

[edit] Scala

See Conway's Game of Life/Scala

[edit] Scheme

Works with: Scheme version implementing R6RS (tested with PLT Scheme, Petite Chez Scheme)
 
;;An R6RS Scheme implementation of Conway's Game of Life --- assumes
;;all cells outside the defined grid are dead
 
;if n is outside bounds of list, return 0 else value at n
(define (nth n lst)
(cond ((> n (length lst)) 0)
((< n 1) 0)
((= n 1) (car lst))
(else (nth (- n 1) (cdr lst)))))
 
;return the next state of the supplied universe
(define (next-universe universe)
;value at (x, y)
(define (cell x y)
(if (list? (nth y universe))
(nth x (nth y universe))
0))
;sum of the values of the cells surrounding (x, y)
(define (neighbor-sum x y)
(+ (cell (- x 1) (- y 1))
(cell (- x 1) y)
(cell (- x 1) (+ y 1))
(cell x (- y 1))
(cell x (+ y 1))
(cell (+ x 1) (- y 1))
(cell (+ x 1) y)
(cell (+ x 1) (+ y 1))))
;next state of the cell at (x, y)
(define (next-cell x y)
(let ((cur (cell x y))
(ns (neighbor-sum x y)))
(cond ((and (= cur 1)
(or (< ns 2) (> ns 3)))
0)
((and (= cur 0) (= ns 3))
1)
(else cur))))
;next state of row n
(define (row n out)
(let ((w (length (car universe))))
(if (= (length out) w)
out
(row n
(cons (next-cell (- w (length out)) n)
out)))))
;a range of ints from bot to top
(define (int-range bot top)
(if (> bot top) '()
(cons bot (int-range (+ bot 1) top))))
(map (lambda (n)
(row n '()))
(int-range 1 (length universe))))
 
;represent the universe as a string
(define (universe->string universe)
(define (prettify row)
(apply string-append
(map (lambda (b)
(if (= b 1) "#" "-"))
row)))
(if (null? universe)
""
(string-append (prettify (car universe))
"\n"
(universe->string (cdr universe)))))
 
;starting with seed, show reps states of the universe
(define (conway seed reps)
(when (> reps 0)
(display (universe->string seed))
(newline)
(conway (next-universe seed) (- reps 1))))
 
;; --- Example Universes --- ;;
 
;blinker in a 3x3 universe
(conway '((0 1 0)
(0 1 0)
(0 1 0)) 5)
 
;glider in an 8x8 universe
(conway '((0 0 1 0 0 0 0 0)
(0 0 0 1 0 0 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 0 0 0 0 0 0)) 30)

[edit] Sample output:

-#-
-#-
-#-

---
###
---

-#-
-#-
-#-

---
###
---

-#-
-#-
-#-

--#-----
---#----
-###----
--------
--------
--------
--------
--------

--------
-#-#----
--##----
--#-----
--------
--------
--------
--------

--------
---#----
-#-#----
--##----
--------
--------
--------
--------

--------
--#-----
---##---
--##----
--------
--------
--------
--------

--------
---#----
----#---
--###---
--------
--------
--------
--------

--------
--------
--#-#---
---##---
---#----
--------
--------
--------

--------
--------
----#---
--#-#---
---##---
--------
--------
--------

--------
--------
---#----
----##--
---##---
--------
--------
--------

--------
--------
----#---
-----#--
---###--
--------
--------
--------

--------
--------
--------
---#-#--
----##--
----#---
--------
--------

--------
--------
--------
-----#--
---#-#--
----##--
--------
--------

--------
--------
--------
----#---
-----##-
----##--
--------
--------

--------
--------
--------
-----#--
------#-
----###-
--------
--------

--------
--------
--------
--------
----#-#-
-----##-
-----#--
--------

--------
--------
--------
--------
------#-
----#-#-
-----##-
--------

--------
--------
--------
--------
-----#--
------##
-----##-
--------

--------
--------
--------
--------
------#-
-------#
-----###
--------

--------
--------
--------
--------
--------
-----#-#
------##
------#-

--------
--------
--------
--------
--------
-------#
-----#-#
------##

--------
--------
--------
--------
--------
------#-
-------#
------##

--------
--------
--------
--------
--------
--------
-------#
------##

--------
--------
--------
--------
--------
--------
------##
------##

--------
--------
--------
--------
--------
--------
------##
------##

--------
--------
--------
--------
--------
--------
------##
------##

--------
--------
--------
--------
--------
--------
------##
------##

--------
--------
--------
--------
--------
--------
------##
------##

--------
--------
--------
--------
--------
--------
------##
------##

--------
--------
--------
--------
--------
--------
------##
------##

--------
--------
--------
--------
--------
--------
------##
------##

--------
--------
--------
--------
--------
--------
------##
------##

[edit] SETL

Compiler: GNU SETL

This version uses a live cell set representation (set of coordinate pairs.) This example first appeared here.

program life;
 
const
initialMatrix =
[".....",
"..#..",
"...#.",
".###.",
"....."];
 
loop
init
s := initialLiveSet();
do
output(s);
nm := {[[x+dx, y+dy], [x, y]]: [x, y] in s, dx in {-1..1}, dy in {-1..1}};
s := {c: t = nm{c} | 3 in {#t, #(t less c)}};
end;
 
proc output(s);
system("clear");
(for y in [0..24])
(for x in [0..78])
nprint(if [x, y] in s then "#" else " " end);
end;
print();
end;
select([], 250);
end proc;
 
proc initialLiveSet();
return {[x,y]: row = initialMatrix(y), c = row(x) | c = '#'};
end proc;
 
end program;

[edit] Shen

Somewhat verbose but functional implementation (tested with chibi-scheme). Running this shows ten iterations of a toad.

(define conway-nth
\\ returns value of x from row if it exists, else 0
_ [] -> 0
N _ -> 0 where (< N 0)
0 [A|B] -> A
N [A|B] -> (conway-nth (- N 1) B))
 
(define row-retrieve
_ [] -> []
0 [] -> []
0 [A|B] -> A
N [A|B] -> (row-retrieve (- N 1) B))
 
(define cell-retrieve
X Y Universe -> (conway-nth X (row-retrieve Y Universe)))
 
(define neighbors
\\ takes an X and Y, retrieves the number of neighbors
X Y Universe -> (let ++ (+ 1)
-- (/. X (- X 1))
(+ (cell-retrieve (++ X) Y Universe)
(cell-retrieve (++ X) (++ Y) Universe)
(cell-retrieve (++ X) (-- Y) Universe)
(cell-retrieve (-- X) Y Universe)
(cell-retrieve (-- X) (++ Y) Universe)
(cell-retrieve (-- X) (-- Y) Universe)
(cell-retrieve X (++ Y) Universe)
(cell-retrieve X (-- Y) Universe))))
 
(define handle-alive
X Y Universe -> (if (or (= (neighbors X Y Universe) 2)
(= (neighbors X Y Universe) 3))
1 0))
 
(define handle-dead
X Y Universe -> (if (= (neighbors X Y Universe) 3)
1 0))
 
(define next-row
\\ first argument must be a previous row, second must be 0 when
\\ first called, third must be a Y value and the final must be the
\\ current unierse
[] _ _ _ -> []
[1|B] X Y Universe -> (cons (handle-alive X Y Universe)
(next-row B (+ X 1) Y Universe))
[_|B] X Y Universe -> (cons (handle-dead X Y Universe)
(next-row B (+ X 1) Y Universe)))
 
(define next-universe
\\ both the first and second arguments must be the same universe,
\\ the third must be 0 upon first call
[] _ _ -> []
[Row|Rest] Y Universe -> (cons (next-row Row 0 Y Universe)
(next-universe Rest (+ Y 1) Universe)))
 
(define display-row
[] -> (nl)
[1|Rest] -> (do (output "* ")
(display-row Rest))
[_|Rest] -> (do (output " ")
(display-row Rest)))
 
(define display-universe
[] -> (nl 2)
[Row|Rest] -> (do (display-row Row)
(display-universe Rest)))
 
(define iterate-universe
0 _ -> (nl)
N Universe -> (do (display-universe Universe)
(iterate-universe (- N 1)
(next-universe Universe 0 Universe))))
 
(iterate-universe
10
[[0 0 0 0 0 0]
[0 0 0 0 0 0]
[0 0 1 1 1 0]
[0 1 1 1 0 0]
[0 0 0 0 0 0]
[0 0 0 0 0 0]])

[edit] SystemVerilog

Note using non-blocking assignments, so that the code behaves as if every cell is updated in parallel on each clock edge. (I didn't need to use a clock here, but doing so looks more like standard verilog coding that is familiar to hardware designers).

module gol;
 
parameter NUM_ROWS = 20;
parameter NUM_COLS = 32;
 
bit [NUM_COLS:1] cell[1:NUM_ROWS];
bit clk;
 
initial begin
cell[10][10:8] = 3'b111;
cell[11][10:8] = 3'b100;
cell[12][10:8] = 3'b010;
repeat(8) #5 clk = ~clk;
end
 
always @(posedge clk) begin
foreach (cell[y,x]) begin
automatic int count = $countones({ cell[y-1][x-1+:3], cell[y][x-1], cell[y][x+1], cell[y+1][x-1+:3] });
if (count == 3) cell[y][x] <= 1'b1;
else if (count != 2) cell[y][x] <= 1'b0;
end
end
 
always @(negedge clk) begin
$display("--");
foreach (cell[y]) $display( "  %b", cell[y] );
end
 
endmodule

[edit] Tcl

Works with: Tcl version 8.5
package require Tcl 8.5 
 
proc main {} {
evolve 3 blinker [initialize_tableau {3 3} {{0 1} {1 1} {2 1}}]
evolve 5 glider [initialize_tableau {4 4} {{0 1} {1 2} {2 0} {2 1} {2 2}}]
}
 
proc evolve {generations name tableau} {
for {set gen 1} {$gen <= $generations} {incr gen} {
puts "$name generation $gen:"
print $tableau
set tableau [next_generation $tableau]
}
puts ""
}
 
proc initialize_tableau {size initial_life} {
lassign $size ::max_x ::max_y
set tableau [blank_tableau]
foreach point $initial_life {
lset tableau {*}$point 1
}
return $tableau
}
 
proc blank_tableau {} {
return [lrepeat $::max_x [lrepeat $::max_y 0]]
}
 
proc print {tableau} {
foreach row $tableau {puts [string map {0 . 1 #} [join $row]]}
}
 
proc next_generation {tableau} {
set new [blank_tableau]
for {set x 0} {$x < $::max_x} {incr x} {
for {set y 0} {$y < $::max_y} {incr y} {
lset new $x $y [fate [list $x $y] $tableau]
}
}
return $new
}
 
proc fate {point tableau} {
set current [value $point $tableau]
set neighbours [sum_neighbours $point $tableau]
return [expr {($neighbours == 3) || ($neighbours == 2 && $current == 1)}]
}
 
proc value {point tableau} {
return [lindex $tableau {*}$point]
}
 
proc sum_neighbours {point tableau} {
set sum 0
foreach neighbour [get_neighbours $point] {
incr sum [value $neighbour $tableau]
}
return $sum
}
 
proc get_neighbours {point} {
lassign $point x y
set results [list]
foreach x_off {-1 0 1} {
foreach y_off {-1 0 1} {
if { ! ($x_off == 0 && $y_off == 0)} {
set i [expr {$x + $x_off}]
set j [expr {$y + $y_off}]
if {(0 <= $i && $i < $::max_x) && (0 <= $j && $j < $::max_y)} {
lappend results [list $i $j]
}
}
}
}
return $results
}
 
main
blinker generation 1:
. # .
. # .
. # .
blinker generation 2:
. . .
# # #
. . .
blinker generation 3:
. # .
. # .
. # .

glider generation 1:
. # . .
. . # .
# # # .
. . . .
glider generation 2:
. . . .
# . # .
. # # .
. # . .
glider generation 3:
. . . .
. . # .
# . # .
. # # .
glider generation 4:
. . . .
. # . .
. . # #
. # # .
glider generation 5:
. . . .
. . # .
. . . #
. # # #

[edit] TI-83 BASIC

This implementation is loosely based on the Processing Version. It uses the home screen and draws cells as "X"s. It is extremely slow, and limited to a bounding box of 16 by 8. In order for it to work, you need to initialize arrays [A] and [B] to be 18x10.

 PROGRAM:CONWAY
:While 1
:For(X,2,9,1)
:For(Y,2,17,1)
:If [A](Y,X)
:Then
:Output(X-1,Y-1,"X")
:Else
:Output(X-1,Y-1," ")
:End
:[A](Y-1,X-1)+[A](Y,X-1)+[A](Y+1,X-1)+[A](Y-1,X)+[A](Y+1,X)+[A](Y-1,X+1)+[A](Y,X+1)+[A](Y+1,X+1)→N
:If ([A](Y,X) and (N=2 or N=3)) or (not([A](Y,X)) and N=3)
:Then
:1→[B](Y,X)
:Else
:0→[B](Y,X)
:End
:End
:End
:[B]→[A]
:End
 

Here is an additional, very simple program to input the top corner of the GRAPH screen into the starting array. Make sure to draw on pixels in the rectangle (1,1) to (8,16).

PROGRAM:PIC2LIFE
:For(I,0,17,1)
:For(J,0,9,1)
:pxl-Test(J,I)→[A](I+1,J+1)
:End
:End
 

[edit] TI-89 BASIC

This program draws its cells as 2x2 blocks on the graph screen. In order to avoid needing external storage for the previous generation, it uses the upper-left corner of each block to mark the next generation's state in all cells, then updates each cell to match its corner pixel.

A further improvement would be to have an option to start with the existing picture rather than clearing, and stop at a point where the picture has clean 2x2 blocks.

Define life(pattern) = Prgm
Local x,y,nt,count,save,xl,yl,xh,yh
Define nt(y,x) = when(pxlTest(y,x), 1, 0)
 
{}→save
setGraph("Axes", "Off")→save[1]
setGraph("Grid", "Off")→save[2]
setGraph("Labels", "Off")→save[3]
FnOff
PlotOff
ClrDraw
 
If pattern = "blinker" Then
36→yl
40→yh
78→xl
82→xh
PxlOn 36,80
PxlOn 38,80
PxlOn 40,80
ElseIf pattern = "glider" Then
30→yl
40→yh
76→xl
88→xh
PxlOn 38,76
PxlOn 36,78
PxlOn 36,80
PxlOn 38,80
PxlOn 40,80
ElseIf pattern = "r" Then
38-5*2→yl
38+5*2→yh
80-5*2→xl
80+5*2→xh
PxlOn 38,78
PxlOn 36,82
PxlOn 36,80
PxlOn 38,80
PxlOn 40,80
EndIf
 
While getKey() = 0
© Expand upper-left corner to whole cell
For y,yl,yh,2
For x,xl,xh,2
If pxlTest(y,x) Then
PxlOn y+1,x
PxlOn y+1,x+1
PxlOn y, x+1
Else
PxlOff y+1,x
PxlOff y+1,x+1
PxlOff y, x+1
EndIf
EndFor
EndFor
 
© Compute next generation
For y,yl,yh,2
For x,xl,xh,2
nt(y-1,x-1) + nt(y-1,x) + nt(y-1,x+2) + nt(y,x-1) + nt(y+1,x+2) + nt(y+2,x-1) + nt(y+2,x+1) + nt(y+2,x+2) → count
If count = 3 Then
PxlOn y,x
ElseIf count ≠ 2 Then
PxlOff y,x
EndIf
EndFor
EndFor
EndWhile
 
© Restore changed options
setGraph("Axes", save[1])
setGraph("Grid", save[2])
setGraph("Labels", save[3])
EndPrgm

[edit] Ursala

Three functions are defined: rule takes a pair (c,<n..>) representing a cell and its list of neighboring cells to the new cell, neighborhoods takes board of cells <<c..>..> to a structure <<(c,<n..>)..>..> explicitly pairing each cell with its neighborhood, and evolve(n) takes a board <<c..>..> to a sequence of n boards evolving from it.

#import std
#import nat
 
rule = -: ^(~&,~&l?(~&r-={2,3},~&r-={3})^|/~& length@F)* pad0 iota512
 
neighborhoods = ~&thth3hthhttPCPthPTPTX**K7S+ swin3**+ swin3@hNSPiCihNCT+ --<0>*+ 0-*
 
evolve "n" = next"n" rule**+ neighborhoods

test program:

blinker =
 
(==`O)**t -[
+++
OOO
+++]-
 
glider =
 
(==`O)**t -[
+O++++
++O+++
OOO+++
++++++
++++++]-
 
#show+
 
examples = mat0 ~&?(`O!,`+!)*** evolve3(blinker)-- evolve5(glider)

output:

+++
OOO
+++

+O+
+O+
+O+

+++
OOO
+++

+O++++
++O+++
OOO+++
++++++
++++++

++++++
O+O+++
+OO+++
+O++++
++++++

++++++
++O+++
O+O+++
+OO+++
++++++

++++++
+O++++
++OO++
+OO+++
++++++

++++++
++O+++
+++O++
+OOO++
++++++

[edit] Vedit macro language

This implementation uses an edit buffer for data storage and to show results. For purpose of this task, the macro writes the initial pattern in the buffer. However, easier way to enter patterns would be by editing them directly in the edit buffer before starting the macro (in which case the Ins_Text commands would be omitted).

The macro calculates one generation and then waits for a key press before calculating the next generation.

The algorithm used is kind of reverse to the one normally used in Life implementations. Instead of counting cells around each location, this implementation finds each living cell and then increments the values of the 8 surrounding cells. After going through all the living cells, each location of the grid contains an unique ascii value depending on the original value (dead or alive) and the number of living cells in surrounding positions. Two Replace commands are then used to change characters into '.' or 'O' to represent dead and living cells in the new generation.

IT("Generation 0    ") IN
IT(".O.") IN
IT(".O.") IN
IT(".O.")
 
#9 = 2 // number of generations to calculate
#10 = Cur_Line
#11 = Cur_Col-1
for (#2 = 1; #2 <= #9; #2++) {
Update()
Get_Key("Next gen...", STATLINE)
Call("calculate")
itoa(#2, 20, LEFT)
GL(1) GC(12) Reg_Ins(20, OVERWRITE)
}
EOF
Return
 
// Calculate one generation
:calculate:
Goto_Line(2)
While (At_EOF == 0) {
Search("|A",ERRBREAK) // find next living cell
#3 = Cur_Line
#4 = #7 = #8 = Cur_Col
if (#4 > 1) { // increment cell at left
#7 = #4-1
Goto_Col(#7)
Ins_Char(Cur_Char+1,OVERWRITE)
}
if (#4 < #11) { // increment cell at right
#8 = #4+1
Goto_Col(#8)
Ins_Char(Cur_Char+1,OVERWRITE)
}
if (#3 > 2) { // increment 3 cells above
Goto_Line(#3-1)
Call("inc_3")
}
if (#3 < #10) { // increment 3 cells below
Goto_Line(#3+1)
Call("inc_3")
}
Goto_Line(#3)
Goto_Col(#4+1)
}
 
Replace("[1QR]", "O", REGEXP+BEGIN+ALL) // these cells alive
Replace("[/-7P-X]", ".", REGEXP+BEGIN+ALL) // these cells dead
Return
 
// increment values of 3 characters in a row
:inc_3:
for (#1 = #7; #1 <= #8; #1++) {
Goto_Col(#1)
Ins_Char(Cur_Char+1,OVERWRITE)
}
Return

Output:

Generation 0    
.O.
.O.
.O.
 
Generation 1
...
OOO
...
 
Generation 2
.O.
.O.
.O.

[edit] Wortel

Mapping over a matrix.

@let {
life &m ~!* m &[a y] ~!* a &[v x] @let {
neigh @sum [
@`-x 1 @`-y 1 m @`x @`-y 1 m @`+x 1 @`-y 1 m
@`-x 1 @`y m @`+x 1 @`y m
@`-x 1 @`+y 1 m @`x @`+y 1 m @`+x 1 @`+y 1 m
]
@+ || = neigh 3 && v = neigh 2
}
 
blinker [
[0 0 0 0 0]
[0 0 0 0 0]
[0 1 1 1 0]
[0 0 0 0 0]
[0 0 0 0 0]
]
 
[[
 !^life 0 blinker
 !^life 1 blinker
 !^life 2 blinker
]]
}

Returns:

[
 [[0 0 0 0 0]
  [0 0 0 0 0]
  [0 1 1 1 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 0]
  [0 0 1 0 0]
  [0 0 0 0 0]]
 [[0 0 0 0 0]
  [0 0 0 0 0]
  [0 1 1 1 0]
  [0 0 0 0 0]
  [0 0 0 0 0]]
]

Different solution by using functions that operate on matrices.

@let {
 ; Translation of the APL game of life (http://catpad.net/michael/apl/).
life &m @let {
 ; create functions that work on two matrices
makemf &f @[\@mapm @[\@mapm f ^@,] ^@,]
addm  !makemf ^+
orm  !makemf ^||
andm  !makemf ^&&
eqm  !makemf ^=
 
 ; bool matrix to number matrix
tonum *^*^@+
 
 ; create a matrix of value v in the shape of matrix m
repm &[v m] @rep #m &,@rep #m.0 v
 
 ; move a matrix in directions by padding zeroes
movel \!*~t0j
mover \!*~i0SO
moveu &m ~, &,@rep #m.0 0 !~t m
moved &m , &,@rep #m.0 0 !~i m
 
 ; cache up and down
mu  !moveu m
md  !moved m
 
 ; calculate the neighbours
neigh  !/addm [
 !movel mu mu  !mover mu
 !movel m  !mover m
 !movel md md  !mover md
]
 
 ; ((neigh = 2) AND m) OR (neigh = 3)
 ; (2 neighbours AND alive) OR (3 neighbours)
 !tonum !!orm !!andm m !!eqm neigh !!repm 2 m
 !!eqm neigh !!repm 3 m
}
 
blinker [
[0 0 0 0 0]
[0 0 0 0 0]
[0 1 1 1 0]
[0 0 0 0 0]
[0 0 0 0 0]
]
 
[[
 !^life 0 blinker
 !^life 1 blinker
 !^life 2 blinker
]]
}

Returns:

[
 [[0 0 0 0 0]
  [0 0 0 0 0]
  [0 1 1 1 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 0]
  [0 0 1 0 0]
  [0 0 0 0 0]]
 [[0 0 0 0 0]
  [0 0 0 0 0]
  [0 1 1 1 0]
  [0 0 0 0 0]
  [0 0 0 0 0]]
]

[edit] XPL0

def     M=3;                    \array size
char NowGen(M+2, M+2), \size with surrounding borders
NewGen(M+2, M+2);
int X, Y, I, J, N, Gen;
code ChOut=8, CrLf=9;
 
[for Y:= 0 to M+1 do \set up initial state
for X:= 0 to M+1 do
[NowGen(X,Y):= ^ ; NewGen(X,Y):= ^ ];
NowGen(1,2):= ^#; NowGen(2,2):= ^#; NowGen(3,2):= ^#;
 
for Gen:= 1 to 3 do
[for Y:= 1 to M do \show current generation
[for X:= 1 to M do [ChOut(0, NowGen(X,Y)); ChOut(0,^ )];
CrLf(0);
];
CrLf(0);
 
for Y:= 1 to M do \determine next generation
for X:= 1 to M do
[N:= 0; \count adjacent live (#) cells
for J:= Y-1 to Y+1 do
for I:= X-1 to X+1 do
if NowGen(I,J) = ^# then N:= N+1;
if NowGen(X,Y) = ^# then N:= N-1; \don't count center
NewGen(X,Y):= ^ ; \assume death
if N=2 then NewGen(X,Y):= NowGen(X,Y) \actually no change
else if N=3 then NewGen(X,Y):= ^#; \actually birth
];
I:= NowGen; NowGen:= NewGen; NewGen:= I; \swap arrays
];
]

Output:

      
# # # 
      

  #   
  #   
  #   

      
# # #

[edit] XSLT

So when the following templates

<xsl:template match="/table">
<table>
<xsl:apply-templates />
</table>
</xsl:template>
 
<xsl:template match="tr">
<tr><xsl:apply-templates /></tr>
</xsl:template>
 
<xsl:template match="td">
<xsl:variable name="liveNeighbours">
<xsl:apply-templates select="current()" mode="countLiveNeighbours" />
</xsl:variable>
 
<xsl:choose>
<xsl:when test="(current() = 'X' and $liveNeighbours = 2) or $liveNeighbours = 3">
<xsl:call-template name="live" />
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="die" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
 
<xsl:template match="td" mode="countLiveNeighbours">
<xsl:variable name="currentX" select="count(preceding-sibling::td) + 1" />
<xsl:variable name="precedingRow" select="parent::tr/preceding-sibling::tr[1]" />
<xsl:variable name="followingRow" select="parent::tr/following-sibling::tr[1]" />
 
<xsl:variable name="neighbours" select="$precedingRow/td[$currentX - 1] |
$precedingRow/td[$currentX] |
$precedingRow/td[$currentX + 1] |
preceding-sibling::td[1] |
following-sibling::td[1] |
$followingRow/td[$currentX - 1] |
$followingRow/td[$currentX] |
$followingRow/td[$currentX + 1]" />

 
<xsl:value-of select="count($neighbours[text() = 'X'])" />
</xsl:template>
 
<xsl:template name="die">
<td>_</td>
</xsl:template>
 
<xsl:template name="live">
<td>X</td>
</xsl:template>

are applied against the document

<table>
<tr><td>_</td><td>X</td><td>_</td></tr>
<tr><td>_</td><td>X</td><td>_</td></tr>
<tr><td>_</td><td>X</td><td>_</td></tr>
</table>

then the transformed XML document contains the new universe evolved by one tick:

<table>
<tr><td>_</td><td>_</td><td>_</td></tr>
<tr><td>X</td><td>X</td><td>X</td></tr>
<tr><td>_</td><td>_</td><td>_</td></tr>
</table>

[edit] zkl

class Life{
fcn init(n, r1,c1, r2,c2, etc){
var N=n, cells=Data(n*n), tmp=Data(n*n),
ds=T(T(-1,-1),T(-1,0),T(-1,1), T(0,-1),T(0,1), T(1,-1),T(1,0),T(1,1));
icells:=vm.arglist[1,*];
(N*N).pump(Void,cells.append.fpM("1-",0)); // clear board
icells.pump(Void,Void.Read,fcn(row,col){ cells[row*N+col]=1 });
}
fcn get(row,col){
if((0<=row<N) and (0<=col<N)) return(cells[row*N+col]);
return(0);
}
fcn postToastie(row,col){
n:=ds.reduce('wrap(n,[(r,c)]){n+get(r+row,c+col)},0);
c:=get(row,col);
((n==2 and c==1) or n==3).toInt()
}
fcn cycle{
tmp.clear();
foreach row in (N){ foreach col in (N) {
tmp.append(postToastie(row,col)) } }
t:=cells; cells=tmp; tmp=t;
}
fcn toString{
cells.pump(0,String,fcn(c,rn){
(if(c)"*" else "-") + (if(rn.inc()%N) "" else "\n")
}.fp1(Ref(1)));
}
fcn toAnsi{
cells.pump(0,"\e[H",fcn(c,rn){
(if(c)"\e[07m \e[m" else " ") + (if(rn.inc()%N) "" else "\e[E")
}.fp1(Ref(1)));
}
fcn dance(n=300){ do(n){ toAnsi().print(); Atomic.sleep(0.2); cycle(); } }
}

The data structure is a Data, which is a linear block of bytes.

cells:=Life(4, 0,1, 1,1, 2,1);		  // blinker
do(3){ cells.println("="*4); cells.cycle(); }
 
cells:=Life(30, 0,1, 1,2, 2,0, 2,1, 2,2); // glider
cells.dance(100);
Output:

Just the glider (reformatted), if you have an ANSI terminal (eg xterm), you'll see the glider moving down the screen.

-*--    ----   -*--
-*--    ***-   -*--
-*--    ----   -*--
----    ----   ----
====    ====   ====

[edit] ZPL

program Life;
 
config var
n : integer = 100;
 
region
BigR = [0 .. n+1, 0 .. n+1];
R = [1 .. n, 1 .. n ];
 
direction
nw = [-1, -1]; north = [-1, 0]; ne = [-1, 1];
west = [ 0, -1]; east = [ 0, 1];
sw = [ 1, -1]; south = [ 1, 0]; se = [ 1, 1];
 
var
TW  : [BigR] boolean; -- The World
NN  : [R] integer; -- Number of Neighbours
 
procedure Life();
begin
-- Initialize world
[R] repeat
NN := TW@nw + TW@north + TW@ne +
TW@west + TW@east +
TW@sw + TW@south + TW@se;
TW := (TW & NN = 2) | ( NN = 3);
until !(|<< TW);
end;
Personal tools
Namespaces

Variants
Actions
Community
Explore
Misc
Toolbox