Conway's Game of Life: Difference between revisions
m →{{header|Mathematica}}: Scrolled output. |
|||
Line 1,104: | Line 1,104: | ||
</lang> |
</lang> |
||
gives back: |
gives back: |
||
<pre style="height:30ex;overflow:scroll"> |
|||
<pre> |
|||
0 |
0 |
||
.#... |
.#... |
Revision as of 04:56, 23 June 2009
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.
Ada
<lang 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; </lang> 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:
Sample output:
Blinker 1 # # # Blinker 2 ### Blinker 3 # # # Glider 1 # # ### Glider 2 # # ## # Glider 3 # # # ## Glider 4 # ## ## Glider 5 # # ###
ALGOL 68
The first Life program was written for the PDP-7 by M. J. T. Guy and S. R. Bourne in 1970. It was written in ALGOL 68. c.f. Scientific American 223 (October 1970): 120-123 <lang cpp>MODE UNIVERSE = [upb OF class universe, upb OF class universe]BOOL;
STRUCT(
INT upb, BOOL lifeless, alive, PROC(REF UNIVERSE)VOID init, PROC(REF UNIVERSE)STRING repr, PROC(REF UNIVERSE, INT, INT)VOID insert glider, PROC(REF UNIVERSE)VOID next
) class universe = (
- upb = # 50,
- lifeless = # FALSE,
- alive = # TRUE,
- PROC init = # (REF UNIVERSE self)VOID:
FOR row index FROM LWB self TO UPB self DO init row(self[row index, ]) OD,
- PROC repr = # (REF UNIVERSE self)STRING:(
FORMAT cell = $b("[]", " ")$, horizon = $"+"n(UPB self)("--")"+"l$;
FILE outf; STRING out; associate(outf, out); putf(outf, (horizon, $"|"n(UPB self)(f(cell))"|"l$, self, horizon)); close(outf); out ),
- PROC insert glider = # (REF UNIVERSE self, INT row, col)VOID:(
self[row-2, col+1] := TRUE; self[row-1, col+2] := TRUE; self[row, col:col+2] := (TRUE, TRUE, TRUE ) ),
- PROC next = # (REF UNIVERSE self)VOID:(
[0:2, LWB self-1:UPB self+1]BOOL window; # Create a 3 row window into the previous generation #
# Set the universe horizon to be lifeless cells # init row(window[LWB window, ]); window[LWB self, 2 LWB window] := window[LWB self, 2 UPB window] := window[UPB window, 2 LWB window] := window[UPB window, 2 UPB window] := lifeless OF class universe;
# Initialise the first row # window[LWB self, LWB self:UPB self] := self[LWB self, ];
FOR row FROM LWB self TO UPB self DO REF []BOOL next row = window[(row+1) MOD 3, ]; IF row NE UPB self THEN next row[LWB self:UPB self] := self[row+1, ] ELSE init row(next row) FI; FOR col FROM LWB self TO UPB self DO INT live := 0; # Scan for life forms in 3x3 block # FOR row FROM row-1 TO row+1 DO REF[]BOOL window row = window[row MOD 3, ]; FOR col FROM col-1 TO col+1 DO IF window row[col] THEN live +:= 1 FI OD OD; self[row, col] := IF window[row MOD 3, col] THEN #
1. Any live cell with fewer than two live neighbours dies, as if by loneliness. 2. Any live cell with more than three live neighbours dies, as if by overcrowding. 3. Any live cell with two or three live neighbours lives, unchanged, to the next generation. #
live -:= 1; # don't count life in current cell # live = 3 OR live = 2 ELSE #
4. Any lifeless cell with exactly three live neighbours comes to life. #
live = 3 FI OD OD )
);
- Shared static procedure #
PROC init row = (REF [] BOOL xrow)VOID:
FOR col FROM LWB xrow TO UPB xrow DO xrow[col] := lifeless OF class universe OD;
PROC insert gosper gun = (REF [, ] BOOL universe)VOID:(
[, ]CHAR template = ( ("________________________X___________"), ("______________________X X___________"), ("____________XX______XX____________XX"), ("___________X___X____XX____________XX"), ("XX________X_____X___XX______________"), ("XX________X___X_XX____X_X___________"), ("__________X_____X_______X___________"), ("___________X___X____________________"), ("____________XX______________________") ); FOR row TO 1 UPB template DO FOR col TO 2 UPB template DO universe[row, col] := template[row, col]="X" OD OD
);
UNIVERSE conways universe; (init OF class universe)(conways universe);
- Insert a squadron of gliders #
FOR i FROM UPB conways universe OVER 2 BY 5 TO UPB conways universe DO
(insert glider OF class universe)(conways universe, i, ENTIER (UPB conways universe*1.2 - i*0.9))
OD;
- Insert a gosper (repeating) gun #
insert gosper gun(conways universe[5:, :]);
STRING home = REPR 27 +"[H"; TO 564 DO
print((home)); print((repr OF class universe)(conways universe)); (next OF class universe)(conways universe)
OD</lang> Output after 564 iterations:
+----------------------------------------------------------------------------------------------------+ | | | | | | | | | [][] | | [] [] | | [] [][][] [][] [][][] | | [][][][] [][][] [] [][][][] | |[][] [][][][] [][][] [][] | |[][] [] [] [] [] | | [] [][][][] [][] | | [] [][][][] [] | | [] [] | | [][][] | | | | | | | | | | | | [] | | [] [] | | [][] | | | | | | | | [][] | | [] [] [] | | [] [] | | [][][] | | | | | | | | | | | | | | [][] | | [][] | | [] | | [] | | [] | | | | [][][] [][][] | | | | [] | | [] | | [] | | [] | | [] [] | | [] [] | | [][] | +----------------------------------------------------------------------------------------------------+
APL
C
I wrote functions that can be used in a rather general way.
<lang c>
- define CELL(I,J) (field[size*(I)+(J)])
- define ALIVE(I,J) t[size*(I)+(J)] = 1
- define DEAD(I,J) t[size*(I)+(J)] = 0
int count_alive(const char *field, int i, int j, int size) {
int x, y, a=0; for(x=i-1; x <= (i+1) ; x++) { for(y=j-1; y <= (j+1) ; y++) { if ( (x==i) && (y==j) ) continue; if ( (y<size) && (x<size) && (x>=0) && (y>=0) ) { a += CELL(x,y); } } } return a;
}
void evolve(const char *field, char *t, int size) {
int i, j, alive, cs; for(i=0; i < size; i++) { for(j=0; j < size; j++) { alive = count_alive(field, i, j, size); cs = CELL(i,j); if ( cs ) { if ( (alive > 3) || ( alive < 2 ) ) DEAD(i,j); else ALIVE(i,j); } else { if ( alive == 3 ) ALIVE(i,j); else DEAD(i,j); } } }
} </lang>
The function evolve needs a buffer where to store the result, and this buffer must be provided by the user calling the function. An example of usage to test the blinker and the glider is the following.
<lang c>
- include <stdio.h>
/* some additional header needed to use the function evolve provided
previously, or just copy/paste the given code here */
- define BLINKER_SIZE 3
- define BLINKER_GEN 3
char small_blinker[] = {
0,0,0, 1,1,1, 0,0,0
}; char temp_blinker[BLINKER_SIZE*BLINKER_SIZE];
- define FIELD_SIZE 45
- define FIELD_GEN 175
char field[FIELD_SIZE * FIELD_SIZE]; char temp_field[FIELD_SIZE*FIELD_SIZE];
/* set the cell i,j as alive */
- define SCELL(I,J) field[FIELD_SIZE*(I)+(J)] = 1
void dump_field(const char *f, int size) {
int i; for (i=0; i < (size*size); i++) { if ( (i % size) == 0 ) printf("\n"); printf("%c", f[i] ? 'X' : '.'); } printf("\n");
}
int main(int argc, char **argv)
{
int i; char *fa, *fb, *tt, op; /* fast and dirty selection option */ if ( argc > 1 ) { op = *argv[1]; } else { op = 'b'; } switch ( op ) { case 'B': case 'b': /* blinker test */ fa = small_blinker; fb = temp_blinker; for(i=0; i< BLINKER_GEN; i++) { dump_field(fa, BLINKER_SIZE); evolve(fa, fb, BLINKER_SIZE); tt = fb; fb = fa; fa = tt; } return 0; case 'G': case 'g': for(i=0; i < (FIELD_SIZE*FIELD_SIZE) ; i++) field[i]=0; /* prepare the glider */ SCELL(0, 1); SCELL(1, 2); SCELL(2, 0); SCELL(2, 1); SCELL(2, 2); /* evolve */ fa = field; fb = temp_field; for (i=0; i < FIELD_GEN; i++) { dump_field(fa, FIELD_SIZE); evolve(fa, fb, FIELD_SIZE); tt = fb; fb = fa; fa = tt; } return 0; default: fprintf(stderr, "no CA for this\n"); break; } return 1;
} </lang>
The blinker output (reformatted) is simply:
... .X. ... XXX .X. XXX ... .X. ...
D
The implementation keeps universe in ubyte array, alive state is kept as 0x10 bit value, lower 4 bits are used for calculations of number of neighbors.
Output is generated every time it's printed, this probably isn't the best idea.
<lang D> import tango.io.Stdout; import tango.core.Thread; import tango.text.convert.Integer;
class GameOfLife {
private: static const int ClearNeighboursCountMask = 0xf0; static const int NeighboursCountMask = 0xf; static const int AliveMask = 0x10; ubyte[][] universe2d; public: this(int x = 60, int y = 20) { assert (x > 0 && y > 0, "oh noez, universe collapsed!"); universe2d = new ubyte[][](y + 2, x + 2); }
void opIndexAssign(int v, size_t y, size_t x) { if (!v || v == ' ') universe2d[y][x] = 0; else universe2d[y][x] = AliveMask; }
void opIndexAssign(char[][] v, size_t y, size_t x) { foreach (rowIdx, row; v) foreach (cellIdx, cell; row) this[y + rowIdx, x + cellIdx] = cell; }
//
char[] toString() { char[] ret; ret.length = 2*(universe2d[0].length + 1); ret[] = '=';
ret ~= "\n"; foreach (row; universe2d[1 .. $-1]) { ret ~= "|"; foreach (ref cell; row) if (cell & AliveMask) ret ~= "[]"; else ret ~= " "; ret ~= "|\n"; } ret ~= ret[0 .. 2*(universe2d[0].length + 1)];
return ret; }
}
int main(char[][] args) {
auto uni = new GameOfLife(80, 20); char[][] glider1 = [ " #", "# #", " ##" ]; char[][] glider2 = [ "$ ", "$ $", "$$ " ]; char[][] lwss = [ " X X", "X ", "X X", "XXXX " ]; uni.iteration;
uni[3, 2] = glider1; uni[3,15] = glider2; uni[3,19] = glider1; uni[3,32] = glider2;
uni[5,50] = lwss;
// clear screen :> for (int j = 0; j < 100; j++) Stdout.newline;
for (int i = 0; i < 300; i++) { Stdout (uni).newline; uni.iteration; Thread.sleep(0.1); } return 0;
}
</lang>
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
Fortran
<lang fortran> 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 neighbours = Getneighbours(buffer(i-1:i+1, j-1:j+1)) SELECT CASE(neighbours) CASE (0:1) cells(i,j) = .FALSE. CASE (2) ! No change CASE (3) cells(i,j) = .TRUE. CASE (4:8) cells(i,j) = .FALSE. END SELECT END DO END DO END SUBROUTINE Nextgen FUNCTION Getneighbours(neighbourhood) INTEGER :: Getneighbours LOGICAL, INTENT(IN) :: neighbourhood(:,:) INTEGER :: k Getneighbours = 0 DO k = 1, 3 IF (neighbourhood(1,k)) Getneighbours = Getneighbours + 1 END DO DO k = 1, 3, 2 IF (neighbourhood(2,k)) Getneighbours = Getneighbours + 1 END DO DO k = 1, 3 IF (neighbourhood(3,k)) Getneighbours = Getneighbours + 1 END DO END FUNCTION Getneighbours END PROGRAM LIFE_2D</lang>
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 ### ### ### ### ### ###
Haskell
<lang haskell>import Data.Array
type Grid = Array Int Bool
-- The grid is flattened into one dimension for simplicity.
life :: Int -> Int -> Grid -> Grid {- Returns the given Grid advanced by one generation. -} life w h old =
listArray (bounds old) (map f coords) where coords = [(x, y) | y <- [0 .. h - 1], x <- [0 .. w - 1]]
f (x, y) | c && (n == 2 || n == 3) = True | not c && n == 3 = True | otherwise = False 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 < 0 || x == w = False | y < 0 || y == h = False | otherwise = old ! (x + y*w)
count :: [Bool] -> Int count [] = 0 count (False : l) = count l count (True : l) = 1 + count l</lang>
Example of use:
<lang haskell>grid :: [String] -> (Int, Int, Grid) grid l = (width, height, a)
where (width, height) = (length $ head l, length l) a = listArray (0, width * height - 1) $ 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 _ [] = [] split n l = a : split n b
where (a, b) = splitAt n l
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</lang>
Java
<lang 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); } } }</lang> Output:
Generation 0: _#_ _#_ _#_ Generation 1: ___ ### ___ Generation 2: _#_ _#_ _#_
Mathematica
Mathematica has cellular automaton functionality built in, so implementing Conway's Game of Life is a one-liner: <lang Mathematica>
CellularAutomaton[{224,{2,{{2,2,2},{2,1,2},{2,2,2}}},{1,1}}, startconfiguration, steps];
</lang> Example of a glyder progressing 8 steps and showing the 9 frames afterwards as grids of hashes and dots: <lang Mathematica>
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[resultsi/.{1->"#",0->"."}]];,{i,1,Length[results]}]
</lang> gives back:
0 .#... ..#.. ###.. ..... ..... 1 ..... #.#.. .##.. .#... ..... 2 ..... ..#.. #.#.. .##.. ..... 3 ..... .#... ..##. .##.. ..... 4 ..... ..#.. ...#. .###. ..... 5 ..... ..... .#.#. ..##. ..#.. 6 ..... ..... ...#. .#.#. ..##. 7 ..... ..... ..#.. ...## ..##. 8 ..... ..... ...#. ....# ..###
OCaml
<lang 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
</lang>
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 = ()
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.
<lang perl>
#!/usr/bin/perl my ( $rsize, $csize ); $rsize = $ARGV[0]; $csize = $ARGV[1]; $generations = $ARGV[2]; $printed;
sub generate { my @life; foreach my $row ( 1 .. $rsize ) { foreach my $col ( 1 .. $csize ) { if ( rand() < 0.5 ) { $life[$row][$col] = 0; } else { $life[$row][$col] = 1; } } } return @life; }
sub nexgen { my @prev = @_; my @new; foreach my $row ( 1 .. $rsize ) { foreach my $col ( 1 .. $csize ) { $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 ]; if ( $val < 2 ) { $new[$row][$col] = 0; } elsif ( $val < 4 ) { $new[$row][$col] = 1; } else { $new[$row][$col] = 0; } } } return @new; }
sub printlife { if ($printed) {
# Move the cursor up to print over prior generation. foreach my $row ( 1 .. $rsize ) { print "\e[1A"; }
} $printed=1; my @life = @_; foreach my $row ( 1 .. $rsize ) { foreach my $col ( 1 .. $csize ) { if ($life[$row][$col]) {
print "\e[33;45;1m \e[0m"; } else { print "\e[1;34;1m \e[0m"; }
} print "\n"; } }
my @life = generate(); print "Start\n"; printlife(@life); foreach my $stage ( 1 .. $generations ) { print "Generation $stage\n";
print "\e[1A";
@life = nexgen(@life); printlife(@life); } print "\n";
</lang>
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.
<lang python>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</lang>
Sample output:
Generation 0: --- ### --- Generation 1: -#- -#- -#- Generation 2: --- ### ---
Ruby
<lang 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</lang>
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;
Tcl
<lang tcl>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 {5 5} {{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</lang>
blinker generation 1: . # . . # . . # . blinker generation 2: . . . # # # . . . blinker generation 3: . # . . # . . # . glider generation 1: . # . . . . . # . . # # # . . . . . . . . . . . . glider generation 2: . . . . . # . # . . . # # . . . # . . . . . . . . glider generation 3: . . . . . . . # . . # . # . . . # # . . . . . . . glider generation 4: . . . . . . # . . . . . # # . . # # . . . . . . . glider generation 5: . . . . . . . # . . . . . # . . # # # . . . . . .
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.