Game of Life
by James Hall: L6 Age ~17
Introduction
John Conway’s Game of Life simulates how small structures with simple rules of interaction can lead to complex behaviour. The user places cells in a grid, and these cells obey the following rules (copied from an on-line version of the game).
For a space that is 'populated':
- each cell with one or no neighbours dies, as if by loneliness;
- each cell with four or more neighbours dies, as if by overpopulation;
- each cell with two or three neighbours survives.
For a space that is 'empty' or 'unpopulated':
- each cell with three neighbours becomes populated.
The following screenshot shows the program in action.

Program in action
Click repeatedly in the grid to create a pattern of cells. The buttons described here from the top down allow you to (1) start afresh, (2) run the algorithm a step at a time, (3) run rapidly through each stage, (4) pause, (5) save the complete grid to a file, (6) open an existing file of a complete grid and (7) save a selected rectangular area of the grid (a "pattern"). When you press the last button you see new buttons to save a pattern or cancel. Use the mouse in the grid to drag a green rectangle to mark the area to save. We show a pattern being saved in the following screenshot.

Saving a pattern
All the patterns are saved to a single text file. The first line contains the number of the pattern saved and then the dimensions of each pattern are followed by a bitmap of the pattern. An example is:

Pattern file
You can select a pattern by clicking on its name in the white box at the bottom right of the screen then display it by clicking on the grid with the right mouse button.
Download here a zip file containing the source code (gameoflife.txt, saveall.txt, loadall.txt and savepatt.txt) a folder savedata of text files and a folder named Icons. You should unzip the files so that Icons and savedata are in the program folder. You need to compile the source files to produce the executables saveall.exe, loadall.exe and savepatt.exe that program GameOfLife uses. (You may already have saveall.exe and loadall.exe if you have used program Crazy Paint).
In order to run program GameOfLife, you will need to have downloaded Stefan Berinde`s wingraph.zip file as described in our Graphics tutorial. You should copy the unzipped wincrt.pas, winmouse,pas and wingraph.pas (from the src folder) into your program folder. (The compiled units are included in the zip file but you might as well have the source code available for reference). You should find these files useful for your own graphics programs.
Follow the link at the bottom of this page to see the source code of savepatt, which is very similar to that of saveall (included in our section on program CrazyPaint).
The Program
program GameOfLife; { Copyright (c) 2011 James Hall Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License, as described at http://www.apache.org/licenses/ and http://www.pp4s.co.uk/licenses/ } {$mode objfpc}{$H+} {$APPTYPE GUI} uses Classes, SysUtils, wingraph, wincrt, winmouse, DOS, math; var gd, gm : smallint; grid : array[0 .. 101, 0 .. 101] of boolean; neighbours : array[0 .. 101, 0 .. 101] of integer; screen : array[1 .. 900, 1 .. 900] of integer; pattadd : array[1 .. 100, 1 .. 2] of integer; //New pattern to add to patt patt : array[1 .. 100, 1 .. 100, 1 .. 100] of integer; //All the patterns pattname : array[1 .. 100] of string[20]; i, j, m, n, max, temp, numpatt, currpat, boxline, startx, starty : integer; big : int64; click, clickr, gogogo : boolean; currname : string; //Name of current pattern bitmap : pointer; mybitmap : file; datafile : textfile; procedure neicheck; //Calculates and stores number of neighbours for each cell begin for i := 1 to max do for j := 1 to max do begin neighbours[i, j] := 0; for m := 1 to 3 do for n := 1 to 3 do if not((m = 2) and (n = 2)) then if grid[i + m - 2, j + n - 2] = true then inc(neighbours[i, j]); end; end; procedure setgrid; //Determine which cells survive based on the number of neighbours begin for i := 1 to max do for j := 1 to max do begin if (neighbours[i, j] < 2) or (neighbours[i, j] > 3) then grid[i, j] := false; if (neighbours[i, j] = 3) then grid[i, j] := true; end; end; procedure drawgrid; begin for i := 1 to max do for j := 1 to max do begin setfillstyle(solidfill, black); if grid[i, j] = true then //Draw white square to represent cell setfillstyle(solidfill, white); bar(95 + i * 5, 95 + j * 5, 100 + i * 5, 100 + j * 5); end; setcolor(gray); //Draw vertical and horizontal lines for i := 1 to max + 1 do line(95 + i * 5, 100, 95 + i * 5, 100 + max * 5); for i := 1 to max + 1 do line(100, 95 + i * 5, 100 + max * 5, 95 + i * 5); end; procedure newgrid; //Clear all cells begin gogogo := false; for i := 0 to max + 1 do for j := 0 to max + 1 do begin grid[i, j] := false; neighbours[i, j] := 0; end; end; procedure putbut(x, y, num : integer; name : string); //Puts the button with icon "name" on to the screen var width, height, memorysize : integer; begin assignfile(mybitmap, name); reset(mybitmap, 1); blockread(mybitmap, temp, 2); for i := 1 to 4 do blockRead(mybitmap, temp, 4); blockread(mybitmap, width, 4); blockread(mybitmap, height, 4); closefile(mybitmap); for i := 1 to width do for j := 1 to height do screen[x - 1 + i, y - 1 + j] := num; memorysize := imageSize(1, 1, width, height); getMem(bitmap, memorysize); assignfile(mybitmap, name); reset(mybitmap, 1); blockread(mybitmap, bitmap^, memorysize); closefile(mybitmap); putimage(x, y, bitmap^, copyput); freeMem(bitmap, memorysize); end; procedure setscreen; {Puts all icons on the screen using the putbut procedure and writes button codes 2-8 to the screen array} begin for i := 1 to 900 do for j := 1 to 700 do screen[i, j] := 0; for i := 1 to 5 * max do for j := 1 to 5 * max do screen[99 + i, 99 + j] := 1; putbut(650, 100, 2, 'Icons/new.bmp'); putbut(650, 160, 3, 'Icons/step.bmp'); putbut(650, 220, 4, 'Icons/play.bmp'); putbut(650, 280, 5, 'Icons/stop.bmp'); putbut(650, 340, 6, 'Icons/save.bmp'); putbut(650, 400, 7, 'Icons/load.bmp'); putbut(650, 440, 8, 'Icons/save.bmp'); for i := 700 to 850 do for j := 600 to 690 do screen[i, j] := 9; end; procedure checkname; //Reads the currently selected name, which is the second line in allnames.txt begin assignfile(datafile, 'savedata/allnames.txt'); reset(datafile); readln(datafile, currname); readln(datafile, currname); closefile(datafile); end; procedure checkalt; //Check procedure to make sure that savedata folder is in place begin assignfile(datafile, 'savedata/saveint.txt'); reset(datafile); readln(datafile, temp); rewrite(datafile); writeln(datafile, '0'); closefile(datafile); end; procedure saveall; //Writes the current grid as an int64 per line of the text file selected by saveall.exe //Uses 50 bits of two int64 integers to represent each horizontal line of the grid begin exec('saveall.exe', ''); checkalt; if temp = 1 then //savedata folder is in place begin checkname; assignfile(datafile, 'savedata/' + currname + '.txt'); rewrite(datafile); m := 0; n := 1; for j := 1 to 200 do begin big := 0; for i := 1 to 50 do begin inc(m); if m > 100 then begin m := 1; inc(n); end; if grid[m, n] = true then big := big + round(power(2 , i - 1)); end; writeln(datafile, big); end; closefile(datafile); end; end; procedure loadall; //Loads a grid from the file selected by loadall.exe begin exec('loadall.exe', ''); checkalt; if temp = 1 then begin for i := 1 to 100 do for j := 1 to 100 do grid[i, j] := false; checkname; assignfile(datafile, 'savedata/' + currname + '.txt'); reset(datafile); m := 0; n := 1; readln(datafile, big); for i := 1 to 100 do for j := 1 to 100 do begin inc(m); if m > 50 then begin m := 1; inc(n); readln(datafile, big); end; //Examine the bits in big if big mod 2 = 1 then grid[j, i] := true; big := big div 2; end; closefile(datafile); end; end; procedure loadpatterns; {Loads all the patterns in patterns.txt to the array patt then loads all the pattern names in patternnames.txt to the array pattname} var tempstr : string; begin assignfile(datafile, 'savedata\patterns.txt'); reset(datafile); readln(datafile, numpatt); for i := 1 to numpatt do begin readln(datafile, pattadd[i, 1]); readln(datafile, pattadd[i, 2]); for j := 1 to pattadd[i, 2] do begin readln(datafile, tempstr); for n := 1 to pattadd[i, 1] do patt[i, n, j] := strtoint(tempstr[n]); end; end; closefile(datafile); assignfile(datafile, 'savedata\patternnames.txt'); reset(datafile); readln(datafile, numpatt); readln(datafile, currpat); for i := 1 to numpatt do readln(datafile, pattname[i]); closefile(datafile); end; procedure savepatterns; {Saves all the patterns in the array patt to patterns.txt then saves all the pattern names in the array pattname to patternnames.txt} var tempstr : string; begin m := 0; assignfile(datafile, 'savedata\patterns.txt'); rewrite(datafile); writeln(datafile, numpatt); for i := 1 to numpatt do begin writeln(datafile, pattadd[i, 1]); writeln(datafile, pattadd[i, 2]); for j := 1 to pattadd[i, 2] do begin tempstr := ''; for n := 1 to pattadd[i, 1] do tempstr := tempstr + inttostr(patt[i, n, j]); writeln(datafile, tempstr); end; end; closefile(datafile); assignfile(datafile, 'savedata\patternnames.txt'); rewrite(datafile); writeln(datafile, numpatt); writeln(datafile, currpat); for i := 1 to numpatt do writeln(datafile, pattname[i]); closefile(datafile); end; procedure checkbox; //User selects the pattern from those displayed in the white box. //This pattern is added to the screen using a right click. begin //600 680 if (getmousey - 595) div 10 + boxline - 1 <= numpatt then begin currpat := (getmousey - 595) div 10 + boxline - 1; setfillstyle(solidfill, black); setcolor(white); bar(700, 580, 850, 595); outtextxy(700, 580, pattname[currpat]); end; end; procedure updatebox(way : integer); //Write the names of the patterns to the white box begin if way = 1 then if boxline > 1 then dec(boxline); if way = 2 then if boxline < numpatt - 6 then inc(boxline); setfillstyle(solidfill, white); bar(700, 600, 800, 680); setcolor(black); for i:= 1 to 7 do if i <= numpatt then outtextxy(700, 590 + 10 * i, pattname[boxline + i - 1]); end; procedure snap; {Creates bitmap of area of grid selected by user and creates buttons to save and cancel} var down, endit : boolean; topx, topy, botx, boty, len, wid : integer; begin for i := 1 to 800 do for j := 1 to 800 do screen[i, j] := 0; //Set code for grid to 1 for i := 1 to 5 * max do for j := 1 to 5 * max do screen[99 + i, 99 + j] := 1; //Code for Cancel for i := 550 to 620 do for j := 650 to 665 do screen[i, j] := 3; //Code for Save (pattern) for i := 480 to 530 do for j := 650 to 665 do screen[i, j] := 4; //Create Cancel and Save buttons setfillstyle(solidfill, gray); setcolor(black); bar(550, 650, 620, 665); outtextxy(560, 650, 'Cancel'); bar(480, 650, 530, 665); outtextxy(490, 650, 'Save'); down := false; endit := false; topx := 0; topy := 0; botx := 0; boty := 0; repeat case getmousebuttons of 0 : down := false; mouseleftbutton : begin case screen[getmousex, getmousey] of 1 : begin //Click on grid if down = true then begin setcolor(gray); rectangle(topx * 5 + 95, topy * 5 + 95, botx * 5 + 100, boty * 5 + 100); botx := (getmousex - 95) div 5; boty := (getmousey - 95) div 5; if botx > 100 then botx := 100; if boty > 100 then boty := 100; //Draw green rectangel to mark area for saving setcolor(green); rectangle(topx * 5 + 95, topy * 5 + 95, botx * 5 + 100, boty * 5 + 100); end; if down = false then begin setcolor(gray); rectangle(topx * 5 + 95, topy * 5 + 95, botx * 5 + 100, boty * 5 + 100); topx := (getmousex - 95) div 5; topy := (getmousey - 95) div 5; botx := topx; boty := boty; down := true; end; end; 3 : begin //Cancel down := false; endit := true; end; 4 : begin //save pattern to file selected using savepatt.exe len := botx - topx + 1; wid := boty - topy + 1; if (len > 0) and (wid > 0) then begin exec('savepatt.exe', ''); checkalt; if temp = 1 then begin assignfile(datafile, 'savedata\patternnames.txt'); reset(datafile); readln(datafile, numpatt); readln(datafile, currpat); //Read existing pattern names for i := 1 to numpatt do readln(datafile, pattname[i]); closefile(datafile); pattadd[currpat, 1] := len; pattadd[currpat, 2] := wid; for i := 1 to wid do for j := 1 to len do patt[currpat, j, i] := 0; //Add the bitmap to the array for i := 1 to wid do for j := 1 to len do if grid[topx + j - 1, topy + i - 1] = true then patt[currpat, j, i] := 1; //Write the bitmap one line per bit to the console window for i := 1 to wid do for j := 1 to len do writeln(patt[currpat, j, i]); endit := true; end; end; end; end; end; end; updategraph(updatenow); until endit = true; setfillstyle(solidfill, black); bar(480, 650, 620, 665); drawgrid; setscreen; end; begin currpat := 0; boxline := 1; currname := 'Untitled'; click := false; clickr := false; setwindowsize(900, 700); gd := 9; gm := 13; loadpatterns; initgraph(gd, gm, 'Game of Life'); updategraph(updateoff); newgrid; max := 100; drawgrid; setscreen; updatebox(0); repeat case getmousebuttons of mouseleftbutton : click := true; mouserightbutton : clickr := true; 0 : begin if click = true then begin case screen[getmousex, getmousey] of 1: begin //Click in grid if (getmousex < 600) and (getmousex >= 100) and (getmousey < 600) and (getmousey >= 100) then begin if grid[(getmousex - 95) div 5, (getmousey - 95) div 5] = true then grid[(getmousex - 95) div 5, (getmousey - 95) div 5] := false else grid[(getmousex - 95) div 5,(getmousey - 95) div 5] := true; end; end; 2: begin //Reset currname := 'Untitled'; newgrid; drawgrid; end; 3: begin //Step through neicheck; setgrid; delay(1); drawgrid; end; 4: gogogo := true; //Start rapid stepping 5: gogogo := false; //Pause rapid stepping 6: saveall; //Save complete grid to file 7 : loadall; //Load complete grid from file 8 : snap; //Save area of grid 9 : checkbox; //Select a pattern to be added to the screen with //a click of the right mouse button end; delay(5); drawgrid; click := false; end; if clickr = true then //Right button clicked to add pattern to grid begin case screen[getmousex, getmousey] of 1 : begin if currpat <> 0 then begin if (getmousex < 600) and (getmousex >= 100) and (getmousey < 600) and (getmousey >= 100) then begin startx := (getmousex - 95) div 5; starty := (getmousey - 95) div 5; for i := 1 to pattadd[currpat, 1] do for j := 1 to pattadd[currpat, 2] do begin if (i + startx < 102) and (j + starty < 102) then begin if patt[currpat, i, j] = 1 then grid[startx + i - 1, starty + j - 1] := true; if patt[currpat, i, j] = 0 then grid[startx + i - 1, starty + j - 1] := false; end; end; end; end; end; end; delay(5); drawgrid; clickr := false; end; end; end; case getmousewheel of 120 : updatebox(1); -120 : updatebox(2); end; if gogogo = true then begin neicheck; setgrid; delay(1); drawgrid; end; updategraph(updatenow); until closegraphrequest = true; savepatterns; closegraph; end.
Remarks
Could you write your own game of life program?