Conway's Game of Life: Difference between revisions
m →{{header|PureBasic}}: Workaround for Swap-Bug in PB 4.50 |
|||
(455 intermediate revisions by more than 100 users not shown) | |||
Line 1: | Line 1: | ||
{{task|Games}} |
{{task|Games}} |
||
[[Category:Cellular automata]] |
|||
The '''Game of Life''' is a [[wp:cellular automaton|cellular automaton]] devised by the British mathematician [[wp:John Horton Conway|John Horton Conway]] in 1970. It is the best-known example of a cellular automaton. |
|||
The '''Game of Life''' is a [[wp:cellular automaton|cellular automaton]] devised by the British mathematician [[wp:John Horton Conway|John Horton Conway]] in 1970. It is the best-known example of a cellular automaton. |
|||
Conway's game of life is described [[wp:Conway%27s_Game_of_Life|here]]: |
|||
Conway's game of life is described [[wp:Conway%27s_Game_of_Life|here]]: |
|||
A cell '''C''' is represented by a 1 when alive |
A cell '''C''' is represented by a '''1''' when alive, or '''0''' when dead, in an m-by-m (or <big>m</big>×<big>m</big>) square array of cells. |
||
We calculate '''N''' - the sum of live cells in C's [[wp:Moore neighborhood|eight-location neighbourhood]], then cell C is alive or dead in the next generation based on the following table: |
|||
'''C N new C''' |
'''C N new C''' |
||
1 0,1 -> 0 # Lonely |
1 0,1 -> 0 # Lonely |
||
Line 14: | Line 18: | ||
Assume cells beyond the boundary are always dead. |
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. |
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 [[wp:Conway%27s_Game_of_Life#Examples_of_patterns|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. |
|||
;Task: |
|||
Although you should test your implementation on more complex examples such as the [[wp:Conway%27s_Game_of_Life#Examples_of_patterns|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: |
|||
* Its creator John Conway, explains [http://www.youtube.com/watch?v=E8kUJL04ELA the game of life]. Video from numberphile on youtube. |
|||
* John Conway [http://www.youtube.com/watch?v=R9Plq-D1gEk Inventing Game of Life] - Numberphile video. |
|||
;Related task: |
|||
* [[Langton's ant]] - another well known cellular automaton. |
|||
<br><br> |
|||
=={{header|11l}}== |
|||
{{trans|Python}} |
|||
<syntaxhighlight lang="11l">V cellcountx = 6 |
|||
V cellcounty = 5 |
|||
V celltable = [(1, 2) = 1, |
|||
(1, 3) = 1, |
|||
(0, 3) = 1] |
|||
DefaultDict[(Int, Int), Int] universe |
|||
universe[(1, 1)] = 1 |
|||
universe[(2, 1)] = 1 |
|||
universe[(3, 1)] = 1 |
|||
universe[(1, 4)] = 1 |
|||
universe[(2, 4)] = 1 |
|||
universe[(3, 4)] = 1 |
|||
L(i) 4 |
|||
print("\nGeneration "i‘:’) |
|||
L(row) 0 .< cellcounty |
|||
print(‘ ’(0 .< cellcountx).map(col -> [‘. ’, ‘O ’][:universe[(@row, col)]]).join(‘’)) |
|||
DefaultDict[(Int, Int), Int] nextgeneration |
|||
L(row) 0 .< cellcounty |
|||
L(col) 0 .< cellcountx |
|||
nextgeneration[(row, col)] = celltable.get( |
|||
(universe[(row, col)], |
|||
-universe[(row, col)] + sum(multiloop(row-1..row+1, |
|||
col-1..col+1, (r, c) -> :universe[(r, c)])) |
|||
), 0) |
|||
universe = nextgeneration</syntaxhighlight> |
|||
===More optimal solution=== |
|||
<syntaxhighlight lang="11l">V cellcountx = 6 |
|||
V cellcounty = 5 |
|||
V universe = [[0B] * cellcountx] * cellcounty |
|||
universe[1][1] = 1B |
|||
universe[2][1] = 1B |
|||
universe[3][1] = 1B |
|||
universe[1][4] = 1B |
|||
universe[2][4] = 1B |
|||
universe[3][4] = 1B |
|||
V nextgeneration = [[0B] * cellcountx] * cellcounty |
|||
L(i) 4 |
|||
print("\nGeneration "i‘:’) |
|||
L(row) 0 .< cellcounty |
|||
print(‘ ’, end' ‘’) |
|||
L(col) 0 .< cellcountx |
|||
print(I universe[row][col] {‘O ’} E ‘. ’, end' ‘’) |
|||
print() |
|||
L(row) 0 .< cellcounty |
|||
L(col) 0 .< cellcountx |
|||
V s = 0 |
|||
I row > 0 |
|||
s = universe[row-1][col] |
|||
I col > 0 |
|||
s += universe[row-1][col-1] |
|||
I col < cellcountx-1 |
|||
s += universe[row-1][col+1] |
|||
I col > 0 |
|||
s += universe[row][col-1] |
|||
I col < cellcountx-1 |
|||
s += universe[row][col+1] |
|||
I row < cellcounty-1 |
|||
s += universe[row+1][col] |
|||
I col > 0 |
|||
s += universe[row+1][col-1] |
|||
I col < cellcountx-1 |
|||
s += universe[row+1][col+1] |
|||
nextgeneration[row][col] = I universe[row][col] {s C 2..3} E s == 3 |
|||
universe = nextgeneration</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
Generation 0: |
|||
. . . . . . |
|||
. O . . O . |
|||
. O . . O . |
|||
. O . . O . |
|||
. . . . . . |
|||
Generation 1: |
|||
. . . . . . |
|||
. . . . . . |
|||
O O O O O O |
|||
. . . . . . |
|||
. . . . . . |
|||
Generation 2: |
|||
. . . . . . |
|||
. O O O O . |
|||
. O O O O . |
|||
. O O O O . |
|||
. . . . . . |
|||
Generation 3: |
|||
. . O O . . |
|||
. O . . O . |
|||
O . . . . O |
|||
. O . . O . |
|||
. . O O . . |
|||
</pre> |
|||
=={{header|6502 Assembly}}== |
=={{header|6502 Assembly}}== |
||
{{works with|http://www.6502asm.com/ 6502asm.com|1.2}} |
{{works with|http://www.6502asm.com/ 6502asm.com|1.2}} |
||
<syntaxhighlight lang="6502asm">randfill: stx $01 ;$200 for indirect |
|||
<lang 6502asm> |
|||
randfill: stx $01 ;$200 for indirect |
|||
ldx #$02 ;addressing |
ldx #$02 ;addressing |
||
stx $02 |
stx $02 |
||
Line 34: | Line 152: | ||
cmp #$06 |
cmp #$06 |
||
bne randloop |
bne randloop |
||
Line 49: | Line 166: | ||
cmp #$0a |
cmp #$0a |
||
bne clearbyte |
bne clearbyte |
||
Line 69: | Line 185: | ||
cmp #$06 ;copied the |
cmp #$06 ;copied the |
||
bne copybyte ;entire screen |
bne copybyte ;entire screen |
||
Line 137: | Line 252: | ||
bne onecell |
bne onecell |
||
jmp starttick ;run next tick |
jmp starttick ;run next tick |
||
Line 148: | Line 262: | ||
lda $01 |
lda $01 |
||
sta $03 |
sta $03 |
||
rts</ |
rts</syntaxhighlight> |
||
=={{header|68000 Assembly}}== |
|||
I went a little further and created a 40x30 grid, but this implementation is accurate and does have a blinker in it. As always, thanks to Keith of Chibiakumas for the cartridge header and hardware routines. This is the source code for a Sega Genesis game that you can compile with VASM. (It was tested in the Fusion emulator but it should work anywhere.) |
|||
Explanation of the implementation: |
|||
* I'm using the Genesis's foreground tilemap to create the graphics. |
|||
* Grid boundaries are handled by using the classic trick of padding all sides with a value that's not used in the "real" grid, and always counts as a dead cell. |
|||
* Every iteration of the main loop does the same thing: check neighbors of each cell in the grid, write the next generation to a buffer, copy that buffer over the original, and display the output. This way, updates don't impact the outcome of the rest of the cells down the line (which would go against the rules of the game, as all cell births/deaths happen simultaneously.) |
|||
Explanation of macros: |
|||
* pushRegs = <tt>MOVEM.L ___,-(SP)</tt> |
|||
* popRegs = <tt>MOVEM.L (SP)+,___</tt> |
|||
* pushLong = <tt>MOVE.L ___,-(SP)</tt> |
|||
* popLong = <tt>MOVE.L (SP)+,___</tt> |
|||
<syntaxhighlight lang="68000devpac">include "\SrcALL\68000_Macros.asm" |
|||
;Ram Variables |
|||
Cursor_X equ $00FF0000 ;Ram for Cursor Xpos |
|||
Cursor_Y equ $00FF0000+1 ;Ram for Cursor Ypos |
|||
conwayTilemapRam equ $00FF1000 |
|||
conwayBackupTilemapRam equ $00FF2000 |
|||
;Video Ports |
|||
VDP_data EQU $C00000 ; VDP data, R/W word or longword access only |
|||
VDP_ctrl EQU $C00004 ; VDP control, word or longword writes only |
|||
;constants |
|||
ROWLENGTH equ 40 |
|||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
|||
; Traps |
|||
DC.L $FFFFFE00 ;SP register value |
|||
DC.L ProgramStart ;Start of Program Code |
|||
DS.L 7,IntReturn ; bus err,addr err,illegal inst,divzero,CHK,TRAPV,priv viol |
|||
DC.L IntReturn ; TRACE |
|||
DC.L IntReturn ; Line A (1010) emulator |
|||
DC.L IntReturn ; Line F (1111) emulator |
|||
DS.L 4,IntReturn ; Reserverd /Coprocessor/Format err/ Uninit Interrupt |
|||
DS.L 8,IntReturn ; Reserved |
|||
DC.L IntReturn ; spurious interrupt |
|||
DC.L IntReturn ; IRQ level 1 |
|||
DC.L IntReturn ; IRQ level 2 EXT |
|||
DC.L IntReturn ; IRQ level 3 |
|||
DC.L IntReturn ; IRQ level 4 Hsync |
|||
DC.L IntReturn ; IRQ level 5 |
|||
DC.L IntReturn ; IRQ level 6 Vsync |
|||
DC.L IntReturn ; IRQ level 7 |
|||
DS.L 16,IntReturn ; TRAPs |
|||
DS.L 16,IntReturn ; Misc (FP/MMU) |
|||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
|||
; Header |
|||
DC.B "SEGA GENESIS " ;System Name |
|||
DC.B "(C)CHBI " ;Copyright |
|||
DC.B "2019.JAN" ;Date |
|||
DC.B "ChibiAkumas.com " ; Cart Name |
|||
DC.B "ChibiAkumas.com " ; Cart Name (Alt) |
|||
DC.B "GM CHIBI001-00" ;TT NNNNNNNN-RR T=Type (GM=Game) N=game Num R=Revision |
|||
DC.W $0000 ;16-bit Checksum (Address $000200+) |
|||
DC.B "J " ;Control Data (J=3button K=Keyboard 6=6button C=cdrom) |
|||
DC.L $00000000 ;ROM Start |
|||
DC.L $003FFFFF ;ROM Length |
|||
DC.L $00FF0000,$00FFFFFF ;RAM start/end (fixed) |
|||
DC.B " " ;External RAM Data |
|||
DC.B " " ;Modem Data |
|||
DC.B " " ;MEMO |
|||
DC.B "JUE " ;Regions Allowed |
|||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
|||
; Generic Interrupt Handler |
|||
IntReturn: |
|||
rte |
|||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
|||
; Program Start |
|||
ProgramStart: |
|||
;initialize TMSS (TradeMark Security System) |
|||
move.b ($A10001),D0 ;A10001 test the hardware version |
|||
and.b #$0F,D0 |
|||
beq NoTmss ;branch if no TMSS chip |
|||
move.l #'SEGA',($A14000);A14000 disable TMSS |
|||
NoTmss: |
|||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
|||
; Set Up Graphics |
|||
lea VDPSettings,A5 ;Initialize Screen Registers |
|||
move.l #VDPSettingsEnd-VDPSettings,D1 ;length of Settings |
|||
move.w (VDP_ctrl),D0 ;C00004 read VDP status (interrupt acknowledge?) |
|||
move.l #$00008000,d5 ;VDP Reg command (%8rvv) |
|||
NextInitByte: |
|||
move.b (A5)+,D5 ;get next video control byte |
|||
move.w D5,(VDP_ctrl) ;C00004 send write register command to VDP |
|||
; 8RVV - R=Reg V=Value |
|||
add.w #$0100,D5 ;point to next VDP register |
|||
dbra D1,NextInitByte ;loop for rest of block |
|||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
|||
; Set up palette and graphics |
|||
move.l #$C0000000,d0 ;Color 0 |
|||
move.l d0,VDP_Ctrl |
|||
MOVE.W #$0A00,VDP_Data ;BLUE |
|||
move.l #$C01E0000,d0 ;Color 0 |
|||
move.l d0,VDP_Ctrl |
|||
MOVE.W #$00EE,VDP_Data ;YELLOW |
|||
lea Graphics,a0 ;background tiles |
|||
move.w #(GraphicsEnd-Graphics)-1,d1 ;data size |
|||
CLR.L D2 ;start loading at tile 0 of VRAM |
|||
jsr DefineTiles |
|||
clr.b Cursor_X ;Clear Cursor XY |
|||
clr.b Cursor_Y |
|||
;Turn on screen |
|||
move.w #$8144,(VDP_Ctrl);C00004 reg 1 = 0x44 unblank display |
|||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
|||
; TESTING AREA |
|||
;load the tilemaps |
|||
LEA Conway_Tilemap,a3 |
|||
LEA ConwayTilemapRam,A2 |
|||
move.l #(Conway_Backup_Tilemap-Conway_Tilemap),d1 |
|||
JSR LDIR |
|||
LEA Conway_Backup_Tilemap,a3 |
|||
LEA conwayBackupTilemapRam,A2 |
|||
move.l #(Conway_Backup_Tilemap-Conway_Tilemap),d1 |
|||
JSR LDIR |
|||
CLR.B Cursor_X |
|||
CLR.B Cursor_Y |
|||
;print the tilemap once |
|||
LEA ConwayTilemapRam,A3 |
|||
JSR Conway_Print |
|||
main: |
|||
;do the behavior |
|||
jsr Conway_CheckTilemap |
|||
;copy the tilemap |
|||
lea conwayBackupTilemapRam,A3 |
|||
LEA ConwayTilemapRam,A2 |
|||
move.l #(Conway_Backup_Tilemap-Conway_Tilemap),d1 |
|||
jsr LDIR |
|||
CLR.B Cursor_X |
|||
CLR.B Cursor_Y |
|||
;print the tilemap |
|||
LEA ConwayTilemapRam,A3 |
|||
JSR Conway_Print |
|||
;repeat |
|||
JMP main |
|||
Conway_CheckTilemap: |
|||
;since we don't want the partial results of the checks to mess with later ones, we'll copy the changes to a buffer, and then |
|||
;have that buffer clobber the original. That way, all changes to the ecosystem are effectively atomic. |
|||
LEA ConwayTilemapRam,A0 |
|||
LEA conwayBackupTilemapRam,A6 |
|||
LEA (ROWLENGTH+3,A0),A0 ;actual data starts here |
|||
LEA (ROWLENGTH+3,A6),A6 ;actual data starts here |
|||
.loop: |
|||
MOVE.B (A0),D2 |
|||
CMP.B #255,D2 |
|||
BEQ .done |
|||
CMP.B #2,D2 |
|||
BEQ .overhead |
|||
JSR Conway_CheckNeighbors |
|||
pushLong D0 |
|||
JSR popcnt_8 |
|||
MOVE.B D0,D3 |
|||
popLong D0 |
|||
;now implement the table here. |
|||
;neighbor bits in D0 |
|||
;current state in D2 |
|||
;popcnt in D3 |
|||
;pointer to where we are in A0 |
|||
;the only time the current state is relevant to the output, is when popcnt = 2. Otherwise, it's entirely determined by popcnt. |
|||
CMP.B #4,D3 |
|||
BCC .DeadCell |
|||
CMP.B #1,D3 |
|||
BLS .DeadCell |
|||
CMP.B #3,D3 |
|||
BEQ .LiveCell |
|||
;else, must be 2. |
|||
MOVE.B D2,(A6) ;store current value into backup table. |
|||
bra .overhead |
|||
.LiveCell: |
|||
MOVE.B #1,(A6) |
|||
BRA .overhead |
|||
.DeadCell: |
|||
MOVE.B #0,(A6) ;store in backup table. |
|||
.overhead: |
|||
ADDA.L #1,A0 |
|||
ADDA.L #1,A6 |
|||
JMP .loop |
|||
.done: |
|||
RTS |
|||
popcnt_8: |
|||
pushRegs D2-D5 |
|||
MOVEQ #8-1,D5 |
|||
MOVEQ #0,D3 |
|||
.loop: |
|||
ROR.B #1,D0 |
|||
BCC .overhead |
|||
ADDQ.B #1,D3 |
|||
.overhead: |
|||
dbra d5,.loop |
|||
MOVE.B D3,D0 |
|||
popRegs D2-D5 |
|||
RTS |
|||
Conway_CheckNeighbors: |
|||
;A0 = pointer to where we are in the tilemap. |
|||
;returns: D0.B = uUrRdDlL |
|||
;u = top left |
|||
;U = top |
|||
;r = top Right |
|||
;R = Right |
|||
;d = bottom right |
|||
;D = bottom |
|||
;l = bottom Left |
|||
;L = Left |
|||
pushRegs D2-D5/A0 |
|||
MOVEQ #0,D0 |
|||
MOVE.L A0,A1 |
|||
MOVE.L A1,A2 |
|||
SUBA.L #ROWLENGTH+2,A1 ;POINTS ABOVE |
|||
ADDA.L #ROWLENGTH+2,A2 ;POINTS BELOW |
|||
MOVE.B (A1),D1 |
|||
MOVE.B (A2),D2 |
|||
CMP.B #1,D1 |
|||
BNE .noUpper |
|||
BSET #6,D0 |
|||
bra .next |
|||
.noUpper: |
|||
BCLR #6,D0 |
|||
.next: |
|||
CheckLower: |
|||
CMP.B #1,D2 |
|||
BNE .noLower |
|||
BSET #2,D0 |
|||
bra .next |
|||
.noLower: |
|||
BCLR #2,D0 |
|||
.next: |
|||
SUBA.L #1,A0 ;left |
|||
SUBA.L #1,A1 ;upper-left |
|||
SUBA.L #1,A2 ;lower-left |
|||
MOVE.B (A1),D1 |
|||
MOVE.B (A2),D2 |
|||
MOVE.B (A0),D3 |
|||
CheckUpperLeft: |
|||
CMP.B #1,D1 |
|||
BNE .noUpperLeft |
|||
BSET #7,D0 |
|||
bra .next |
|||
.noUpperLeft: |
|||
BCLR #7,D0 |
|||
.next: |
|||
CheckLowerLeft: |
|||
CMP.B #1,D2 |
|||
BNE .noLowerLeft |
|||
BSET #1,D0 |
|||
bra .next |
|||
.noLowerLeft: |
|||
BCLR #1,D0 |
|||
.next: |
|||
CheckLeft: |
|||
CMP.B #1,D3 |
|||
BNE .noLeft |
|||
BSET #0,D0 |
|||
bra .next |
|||
.noLeft: |
|||
BCLR #0,D0 |
|||
.next: |
|||
addA.L #2,A0 |
|||
addA.L #2,A1 |
|||
addA.L #2,A2 |
|||
MOVE.B (A1),D1 |
|||
MOVE.B (A2),D2 |
|||
MOVE.B (A0),D3 |
|||
CheckUpperRight: |
|||
CMP.B #1,D1 |
|||
BNE .noUpperRight |
|||
BSET #5,D0 |
|||
bra .next |
|||
.noUpperRight: |
|||
BCLR #5,D0 |
|||
.next: |
|||
CheckLowerRight: |
|||
CMP.B #1,D2 |
|||
BNE .noLowerRight |
|||
BSET #3,D0 |
|||
bra .next |
|||
.noLowerRight: |
|||
BCLR #3,D0 |
|||
.next: |
|||
CheckRight: |
|||
CMP.B #1,D3 |
|||
BNE .noRight |
|||
BSET #4,D0 |
|||
bra .next |
|||
.noRight: |
|||
BCLR #4,D0 |
|||
.next: |
|||
popRegs D2-D5/A0 |
|||
rts |
|||
Conway_Print: |
|||
;input: A3 = base address of tilemap |
|||
MOVE.B (A3)+,D0 |
|||
CMP.B #255,D0 |
|||
BEQ .done |
|||
CMP.B #2,D0 |
|||
BEQ .skip |
|||
;else, print |
|||
JSR Conway_PrintChar |
|||
.skip |
|||
BRA Conway_Print |
|||
.done |
|||
rts |
|||
Conway_PrintChar: ;Show D0 to screen |
|||
moveM.l d0-d7/a0-a7,-(sp) |
|||
and.l #$FF,d0 ;Keep only 1 byte |
|||
Move.L #$40000003,d5 ;top 4=write, bottom $3=Cxxx range |
|||
MOVEQ #0,D4 ;Tilemap at $C000+ |
|||
Move.B (Cursor_Y),D4 |
|||
rol.L #8,D4 ;move $-FFF to $-FFF---- |
|||
rol.L #8,D4 |
|||
rol.L #7,D4 ;2 bytes per tile * 64 tiles per line |
|||
add.L D4,D5 ;add $4------3 |
|||
Move.B (Cursor_X),D4 |
|||
rol.L #8,D4 ;move $-FFF to $-FFF---- |
|||
rol.L #8,D4 |
|||
rol.L #1,D4 ;2 bytes per tile |
|||
add.L D4,D5 ;add $4------3 |
|||
MOVE.L D5,(VDP_ctrl) ; C00004 write next character to VDP |
|||
MOVE.W D0,(VDP_data) ; C00000 store next word of name data |
|||
addq.b #1,(Cursor_X) ;INC Xpos |
|||
move.b (Cursor_X),d0 |
|||
cmp.b #39,d0 |
|||
bls .nextpixel_Xok |
|||
jsr NewLine ;If we're at end of line, start newline |
|||
.nextpixel_Xok: |
|||
moveM.l (sp)+,d0-d7/a0-a7 |
|||
rts |
|||
;don't forget, these are in ROM so we can't write to them directly. |
|||
;instead, they will be copied to ram and worked with from there. |
|||
Conway_Tilemap: ;(screenwidth + 2) by (screenheight+2) |
|||
DC.B $02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02 |
|||
DC.B $02,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$02 |
|||
DC.B $02,$00,$00,$01,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$01,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$02 |
|||
DC.B $02,$00,$00,$00,$01,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$01,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$02 |
|||
DC.B $02,$00,$01,$01,$01,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$01,$01,$01,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$02 |
|||
DC.B $02,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$02 |
|||
DC.B $02,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$02 |
|||
DC.B $02,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$02 |
|||
DC.B $02,$00,$00,$00,$00,$00,$00,$00,$00,$00,$01,$01,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$02 |
|||
DC.B $02,$00,$00,$00,$00,$00,$00,$00,$00,$01,$00,$00,$01,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$02 |
|||
DC.B $02,$00,$01,$01,$00,$00,$00,$00,$00,$00,$01,$01,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$02 |
|||
DC.B $02,$00,$01,$01,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$02 |
|||
DC.B $02,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$02 |
|||
DC.B $02,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$02 |
|||
DC.B $02,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$02 |
|||
DC.B $02,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$02 |
|||
DC.B $02,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$02 |
|||
DC.B $02,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$02 |
|||
DC.B $02,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$02 |
|||
DC.B $02,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$02 |
|||
DC.B $02,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$02 |
|||
DC.B $02,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$02 |
|||
DC.B $02,$00,$00,$00,$00,$01,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$02 |
|||
DC.B $02,$00,$00,$00,$00,$01,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$02 |
|||
DC.B $02,$00,$00,$00,$00,$01,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$02 |
|||
DC.B $02,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$02 |
|||
DC.B $02,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$02 |
|||
DC.B $02,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$02 |
|||
DC.B $02,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$02 |
|||
DC.B $02,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$02 |
|||
DC.B $02,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$02 |
|||
DC.B $02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02 |
|||
DC.B 255 |
|||
EVEN |
|||
Conway_Backup_Tilemap: ;(screenwidth + 2) by (screenheight+2) |
|||
DC.B $02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02 |
|||
rept 30 |
|||
DC.B $02,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$02 |
|||
endr |
|||
DC.B $02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02 |
|||
DC.B 255 |
|||
EVEN |
|||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
|||
NewLine: |
|||
addq.b #1,(Cursor_Y) ;INC Y |
|||
clr.b (Cursor_X) ;Zero X |
|||
rts |
|||
LDIR: ;COPY D1 BYTES FROM A3 TO A2 |
|||
move.b (a3)+,(a2)+ |
|||
DBRA D1,LDIR |
|||
rts |
|||
DefineTiles: ;Copy D1 bytes of data from A0 to VDP memory D2 |
|||
jsr prepareVram ;Calculate the memory location we want to write |
|||
.again: ; the tile pattern definitions to |
|||
move.l (a0)+,(VDP_data) |
|||
dbra d1,.again |
|||
rts |
|||
prepareVram: ;To select a memory location D2 we need to calculate |
|||
;the command byte... depending on the memory location |
|||
pushall ;$7FFF0003 = Vram $FFFF.... $40000000=Vram $0000 |
|||
move.l d2,d0 |
|||
and.w #%1100000000000000,d0 ;Shift the top two bits to the far right |
|||
rol.w #2,d0 |
|||
and.l #%0011111111111111,d2 ; shift all the other bits left two bytes |
|||
rol.l #8,d2 |
|||
rol.l #8,d2 |
|||
or.l d0,d2 |
|||
or.l #$40000000,d2 ;Set the second bit from the top to 1 |
|||
;#%01000000 00000000 00000000 00000000 |
|||
move.l d2,(VDP_ctrl) |
|||
popallRTS: |
|||
popall |
|||
rts |
|||
Graphics: |
|||
DC.L $0000000F,$0000000F,$0000000F,$0000000F,$0000000F,$0000000F,$0000000F,$FFFFFFFF |
|||
DC.L $FFFFFFFF,$FFFFFFFF,$FFFFFFFF,$FFFFFFFF,$FFFFFFFF,$FFFFFFFF,$FFFFFFFF,$FFFFFFFF |
|||
GraphicsEnd: |
|||
VDPSettings: |
|||
DC.B $04 ; 0 mode register 1 ---H-1M- |
|||
DC.B $04 ; 1 mode register 2 -DVdP--- |
|||
DC.B $30 ; 2 name table base for scroll A (A=top 3 bits) --AAA--- = $C000 |
|||
DC.B $3C ; 3 name table base for window (A=top 4 bits / 5 in H40 Mode) --AAAAA- = $F000 |
|||
DC.B $07 ; 4 name table base for scroll B (A=top 3 bits) -----AAA = $E000 |
|||
DC.B $6C ; 5 sprite attribute table base (A=top 7 bits / 6 in H40) -AAAAAAA = $D800 |
|||
DC.B $00 ; 6 unused register -------- |
|||
DC.B $00 ; 7 background color (P=Palette C=Color) --PPCCCC |
|||
DC.B $00 ; 8 unused register -------- |
|||
DC.B $00 ; 9 unused register -------- |
|||
DC.B $FF ;10 H interrupt register (L=Number of lines) LLLLLLLL |
|||
DC.B $00 ;11 mode register 3 ----IVHL |
|||
DC.B $81 ;12 mode register 4 (C bits both1 = H40 Cell) C---SIIC |
|||
DC.B $37 ;13 H scroll table base (A=Top 6 bits) --AAAAAA = $FC00 |
|||
DC.B $00 ;14 unused register -------- |
|||
DC.B $02 ;15 auto increment (After each Read/Write) NNNNNNNN |
|||
DC.B $01 ;16 scroll size (Horiz & Vert size of ScrollA & B) --VV--HH = 64x32 tiles |
|||
DC.B $00 ;17 window H position (D=Direction C=Cells) D--CCCCC |
|||
DC.B $00 ;18 window V position (D=Direction C=Cells) D--CCCCC |
|||
DC.B $FF ;19 DMA length count low LLLLLLLL |
|||
DC.B $FF ;20 DMA length count high HHHHHHHH |
|||
DC.B $00 ;21 DMA source address low LLLLLLLL |
|||
DC.B $00 ;22 DMA source address mid MMMMMMMM |
|||
DC.B $80 ;23 DMA source address high (C=CMD) CCHHHHHH |
|||
VDPSettingsEnd: |
|||
even</syntaxhighlight> |
|||
{{out}} |
|||
[https://ibb.co/VN4KbSL Screenshot of emulator] |
|||
=={{header|ABAP}}== |
|||
{{works with|ABAP|7.4 SP05 or Above only}} |
|||
For the proposed task use ''10,9;10,10;10,11'' on screen field P_POS. Can be left blank for random filling |
|||
<syntaxhighlight lang="abap"> |
|||
*&---------------------------------------------------------------------* |
|||
*& Report ZCONWAYS_GAME_OF_LIFE |
|||
*&---------------------------------------------------------------------* |
|||
*& by Marcelo Bovo |
|||
*&---------------------------------------------------------------------* |
|||
report zconways_game_of_life line-size 174 no standard page heading |
|||
line-count 40. |
|||
parameters: p_char type c default '#', |
|||
p_pos type string. |
|||
class lcl_game_of_life definition. |
|||
public section. |
|||
data: x_max type i value 174, |
|||
y_max type i value 40, |
|||
character type c value abap_true, |
|||
x type i, |
|||
y type i, |
|||
pos type string, |
|||
offlimits type i value 9998, |
|||
grid(9999) type c. " every X_MAX characters on grid equals one line on screen |
|||
data: o_random_y type ref to cl_abap_random_int, |
|||
o_random_x type ref to cl_abap_random_int. |
|||
methods: constructor, |
|||
play, |
|||
initial_position, |
|||
initial_grid, |
|||
write, |
|||
change_grid. |
|||
endclass. |
|||
class lcl_game_of_life implementation. |
|||
method constructor. |
|||
o_random_y = cl_abap_random_int=>create( seed = cl_abap_random=>create( conv i( sy-uzeit ) )->intinrange( low = 1 high = 999999 ) |
|||
min = 1 |
|||
max = y_max ). |
|||
o_random_x = cl_abap_random_int=>create( seed = cl_abap_random=>create( conv i( |{ sy-datum(4) }{ sy-uzeit }| ) )->intinrange( low = 1 high = 999999 ) |
|||
min = 1 |
|||
max = x_max ). |
|||
endmethod. |
|||
method play. |
|||
"fill initial data ramdonly if user hasnt typed any coordenates |
|||
if pos is initial. |
|||
initial_position( ). |
|||
endif. |
|||
initial_grid( ). |
|||
write( ). |
|||
endmethod. |
|||
method initial_position. |
|||
do cl_abap_random_int=>create( "seed = conv i( sy-uzeit ) |
|||
min = 50 |
|||
max = 800 )->get_next( ) times. |
|||
data(lv_index) = sy-index. |
|||
x = o_random_x->get_next( ). |
|||
y = o_random_y->get_next( ). |
|||
p_pos = |{ p_pos }{ switch char1( lv_index when '1' then space else ';' ) }{ y },{ x }|. |
|||
enddo. |
|||
endmethod. |
|||
method initial_grid. |
|||
"Split coordenates |
|||
split p_pos at ';' into table data(lt_pos_inicial) . |
|||
"Sort By Line(Easy to read) |
|||
sort lt_pos_inicial. |
|||
loop at lt_pos_inicial assigning field-symbol(<pos_inicial>). |
|||
split <pos_inicial> at ',' into data(y_char) data(x_char). |
|||
x = x_char. |
|||
y = y_char. |
|||
"Ensure maximum coordenates are not surpassed |
|||
x = cond #( when x <= x_max then x |
|||
else o_random_x->get_next( ) ). |
|||
y = cond #( when y <= y_max then y |
|||
else o_random_y->get_next( ) ). |
|||
"Write on string grid |
|||
"Every x_max lines represent one line(Y) on the grid |
|||
data(grid_xy) = ( x_max * y ) + x - x_max - 1. |
|||
grid+grid_xy(1) = character. |
|||
endloop. |
|||
endmethod. |
|||
method write. |
|||
skip to line 1. |
|||
"Write every line on screen |
|||
do y_max times. |
|||
data(lv_index) = sy-index - 1. |
|||
"Write whole line(current line plus number of maximum X characters) |
|||
data(grid_xy) = ( lv_index * x_max ). |
|||
write / grid+grid_xy(x_max) no-gap. |
|||
if grid+grid_xy(x_max) is initial. |
|||
skip. |
|||
endif. |
|||
enddo. |
|||
change_grid( ). |
|||
endmethod. |
|||
method change_grid. |
|||
data(grid_aux) = grid. |
|||
clear grid_aux. |
|||
data(grid_size) = strlen( grid ). |
|||
"Validate neighbours based on previous written grid |
|||
"ABC |
|||
"D.F |
|||
"GHI |
|||
do grid_size + x_max times. |
|||
"Doens't write anything beyond borders |
|||
check sy-index <= x_max * y_max. |
|||
data(grid_xy) = sy-index - 1. |
|||
data(neighbours) = 0. |
|||
"Current Line neighbours |
|||
data(d) = grid_xy - 1. |
|||
data(f) = grid_xy + 1. |
|||
"Line above neighbours |
|||
data(b) = grid_xy - x_max. |
|||
data(a) = b - 1. |
|||
data(c) = b + 1. |
|||
"Line Bellow neighbours |
|||
data(h) = grid_xy + x_max. |
|||
data(g) = h - 1. |
|||
data(i) = h + 1. |
|||
"Previous Neighbours |
|||
neighbours += cond #( when a < 0 then 0 else cond #( when grid+a(1) is not initial then 1 else 0 ) ). |
|||
neighbours += cond #( when b < 0 then 0 else cond #( when grid+b(1) is not initial then 1 else 0 ) ). |
|||
neighbours += cond #( when c < 0 then 0 else cond #( when grid+c(1) is not initial then 1 else 0 ) ). |
|||
neighbours += cond #( when d < 0 then 0 else cond #( when grid+d(1) is not initial then 1 else 0 ) ). |
|||
"Next Neighbours |
|||
neighbours += cond #( when f > grid_size then 0 else cond #( when grid+f(1) is not initial then 1 else 0 ) ). |
|||
neighbours += cond #( when g > grid_size then 0 else cond #( when grid+g(1) is not initial then 1 else 0 ) ). |
|||
neighbours += cond #( when h > grid_size then 0 else cond #( when grid+h(1) is not initial then 1 else 0 ) ). |
|||
neighbours += cond #( when i > grid_size then 0 else cond #( when grid+i(1) is not initial then 1 else 0 ) ). |
|||
grid_aux+grid_xy(1) = cond #( when neighbours = 3 or ( neighbours = 2 and grid+grid_xy(1) = character ) |
|||
then character |
|||
else space ). |
|||
enddo. |
|||
grid = grid_aux. |
|||
endmethod. |
|||
endclass. |
|||
start-of-selection. |
|||
set pf-status 'STANDARD_FULLSCREEN'. "Use &REFRESH button with F8 Shortcut to go to next generation |
|||
data(lo_prog) = new lcl_game_of_life( ). |
|||
lo_prog->character = p_char. |
|||
lo_prog->pos = p_pos. |
|||
lo_prog->play( ). |
|||
at user-command. |
|||
case sy-ucomm. |
|||
when 'REFR' or '&REFRESH'. |
|||
sy-lsind = 1. "Prevents LIST_TOO_MANY_LEVELS DUMP |
|||
lo_prog->write( ). |
|||
when others. |
|||
leave list-processing. |
|||
endcase. |
|||
</syntaxhighlight> |
|||
=={{header|ACL2}}== |
|||
<syntaxhighlight lang="lisp">(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)))))</syntaxhighlight> |
|||
{{out}} |
|||
<pre>+------+ |
|||
| [] | |
|||
| [] | |
|||
| [] | |
|||
+------+ |
|||
+------+ |
|||
| | |
|||
|[][][]| |
|||
| | |
|||
+------+ |
|||
+------+ |
|||
| [] | |
|||
| [] | |
|||
| [] | |
|||
+------+</pre> |
|||
=={{header|Ada}}== |
=={{header|Ada}}== |
||
<syntaxhighlight lang="ada"> |
|||
<lang ada>with Ada.Text_IO; use Ada.Text_IO; |
|||
WITH Ada.Text_IO; USE Ada.Text_IO; |
|||
PROCEDURE Life IS |
|||
SUBTYPE Cell IS Natural RANGE 0 .. 1; |
|||
TYPE Petri_Dish IS ARRAY (Positive RANGE <>, Positive RANGE <>) OF Cell; |
|||
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 (Gen : IN OUT Petri_Dish) IS |
|||
Above : |
Above : ARRAY (Gen'Range (2)) OF Cell := (OTHERS => 0); |
||
Left : Cell; |
Left, This : Cell; |
||
BEGIN |
|||
This : Cell; |
|||
FOR I IN Gen'First (1) + 1 .. Gen'Last (1) - 1 LOOP |
|||
begin |
|||
Left := 0; |
|||
for I in Culture'First (1) + 1 .. Culture'Last (1) - 1 loop |
|||
FOR J IN Gen'First (2) + 1 .. Gen'Last (2) - 1 LOOP |
|||
Left := O; |
|||
This := (CASE Above (J - 1) + Above (J) + Above (J + 1) + |
|||
Left + Gen (I, J + 1) + |
|||
Gen (I + 1, J - 1) + Gen (I + 1, J) + Gen (I + 1, J + 1) IS |
|||
WHEN 2 => Gen (I, J), |
|||
WHEN 3 => 1, |
|||
WHEN OTHERS => 0); |
|||
Above (J - 1):= Left; |
|||
Left := Gen (I, J); |
|||
Gen (I, J) := This; |
|||
END LOOP; |
|||
end case; |
|||
Above (J-1) := Left; |
|||
Left := Culture (I, J); |
|||
Culture (I, J) := This; |
|||
end loop; |
|||
Above (Above'Last - 1) := Left; |
Above (Above'Last - 1) := Left; |
||
END LOOP; |
|||
END Step; |
|||
PROCEDURE Put (Gen : Petri_Dish) IS |
|||
BEGIN |
|||
FOR I IN Gen'Range (1) LOOP |
|||
FOR J IN Gen'Range (2) LOOP |
|||
Put ( if Gen (I, J) = 0 then " " else "#"); |
|||
END LOOP; |
|||
when X => Put ('#'); |
|||
end case; |
|||
end loop; |
|||
New_Line; |
New_Line; |
||
END LOOP; |
|||
END Put; |
|||
Blinker : Petri_Dish := (2..4 =>( |
Blinker : Petri_Dish := (2 .. 4 => (0, 0, 1, 0, 0), 1 | 5 => (0, 0, 0, 0, 0)); |
||
Glider : Petri_Dish := |
Glider : Petri_Dish (1..6,1..11):= (2 => (3 => 1, others => 0), |
||
3 => (4 => 1, others => 0), |
|||
( |
4 => (2|3|4=>1, others => 0), |
||
( |
others => (others => 0)); |
||
PROCEDURE Put_And_Step_Generation (N : Positive; Name : String; P : IN OUT Petri_Dish) IS |
|||
(O,X,X,X,O,O,O,O,O,O,O), |
|||
BEGIN |
|||
(O,O,O,O,O,O,O,O,O,O,O), |
|||
FOR Generation IN 1 .. N LOOP |
|||
Put_Line (Name & Generation'Img); |
|||
Put (P); |
|||
begin |
|||
Step (P); |
|||
for Generation in 1..3 loop |
|||
END LOOP; |
|||
Put_Line ("Blinker" & Integer'Image (Generation)); |
|||
END Put_And_Step_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: |
|||
BEGIN |
|||
=== Sample output: === |
|||
Put_And_Step_Generation (3, "Blinker", Blinker); |
|||
Put_And_Step_Generation (5, "Glider", Glider); |
|||
END Life;</syntaxhighlight> |
|||
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: |
|||
{{out}} |
|||
<pre style="height:30ex;overflow:scroll"> |
<pre style="height:30ex;overflow:scroll"> |
||
Blinker 1 |
Blinker 1 |
||
Line 302: | Line 1,211: | ||
=={{header|ALGOL 68}}== |
=={{header|ALGOL 68}}== |
||
See [[Conway's Game of Life/ALGOL 68]] |
See [[Conway's Game of Life/ALGOL 68]] |
||
=={{header|Amazing Hopper}}== |
|||
<p>El programa genera el algoritmo en modo texto.</p> |
|||
<p>Adaptado del programa "Conway's Game of Life" de Vishal Nagda, en Hithub</p> |
|||
<p>https://github.com/vishalnagda1/CONWAYs-Game-of-Life-C/blob/master/game_of_life.c</p> |
|||
<syntaxhighlight lang="text"> |
|||
#include <jambo.h> |
|||
#define SIZER 90 |
|||
#define SIZEC 120 |
|||
Main |
|||
Dim (SIZER, SIZEC) as zeros 'grid, neighbour_count' |
|||
Dim (SIZER, SIZEC) as fill '" ",disp grid' |
|||
c=0, Let( c := Utf8(Chr(254))) |
|||
moi=0, moj=0, poi=0, poj=0 |
|||
m={} |
|||
Set ' 0,1,1 ' Apnd row 'm' |
|||
Set ' 1,1,0 ' Apnd row 'm' |
|||
Set ' 0,1,0 ' Apnd row 'm' |
|||
[44:46, 59:61] Set 'm', Put 'grid' |
|||
Clr all marks |
|||
i=0, j=0 |
|||
Tok sep '""' |
|||
Locate (1,1) |
|||
Loop |
|||
i=1 |
|||
Iterator ( ++i, Less equal(i,SIZER),\ |
|||
j=1, Iterator ( ++j, Less equal(j,SIZEC), \ |
|||
Set 'i,j', Gosub 'Count NBR', [i,j], Put 'neighbour_count' ) ) |
|||
i=1 |
|||
Loop if( Less equal(i,SIZER) ) |
|||
j=1 |
|||
Loop if( Less equal(j,SIZEC) ) |
|||
[i,j] |
|||
If ( Get 'grid' ) |
|||
Get 'neighbour_count' |
|||
When ( Out of range including '1,4' ) { |
|||
Set '0," "', Put 'disp grid', Put 'grid' |
|||
} |
|||
Else |
|||
Get 'neighbour_count' |
|||
When ( Is equal to '3' ) { |
|||
Set '1,c', Put 'disp grid', Put 'grid' |
|||
} |
|||
End If |
|||
++j |
|||
Back |
|||
++i |
|||
Back |
|||
Clr all marks |
|||
Print table 'disp grid' |
|||
Break if ( Key pressed ) |
|||
Back |
|||
Pause |
|||
End |
|||
Subrutines |
|||
Define 'Count NBR, i, j' |
|||
n_count = 0 |
|||
When ( Must be( Minus one 'i' ---Backup to 'moi'---, Minus one 'j' ---Backup to 'moj'--- ) ) { |
|||
When ( [moi,moj]Get 'grid' ) { |
|||
++n_count |
|||
} |
|||
} |
|||
Plus one 'j', Move to 'poj' |
|||
When ( moi ) { |
|||
When ( [ moi, j ] Get 'grid' ) { |
|||
++n_count |
|||
} |
|||
When ( Less equal( poj, SIZEC )) { |
|||
When ( [ moi, poj] Get 'grid' ) { |
|||
++n_count |
|||
} |
|||
} |
|||
} |
|||
When ( moj ) { |
|||
When( [i, moj] Get 'grid' ) { |
|||
++n_count |
|||
} |
|||
} |
|||
When ( Less equal ( poj, SIZEC ) ){ |
|||
When( [i, poj] Get 'grid' ) { |
|||
++n_count |
|||
} |
|||
} |
|||
When ( Less equal (Plus one 'i' ---Backup to 'poi'---, SIZER ) ) { |
|||
When ( [ poi, j] Get 'grid' ) { |
|||
++n_count |
|||
} |
|||
When ( Less equal ( poj, SIZEC) ) { |
|||
When ( [poi, poj] Get 'grid' ) { |
|||
++n_count |
|||
} |
|||
} |
|||
When ( moj ){ |
|||
When ([poi, moj] Get 'grid' ){ |
|||
++n_count |
|||
} |
|||
} |
|||
} |
|||
Return 'n_count' |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
La llamada se realiza desde terminal, con el programa "rxvt" de Linux, para adaptar la terminal a los pixeles y dimensiones adecuados. |
|||
Llamada: |
|||
rxvt -g 270x100 -fn "xft:FantasqueSansMono-Regular:pixelsize=3" -e hopper jm/gamelife.jambo |
|||
</pre> |
|||
[[File:Captura_de_pantalla_de_2022-10-07_04-37-57.png]] |
|||
<p>Version 2</p> |
|||
<p>Me di cuenta de que la subrutina "Count NBR" era un poco lenta, y además, una función que cuente los vecinos en un radio dado, me es útil para hacer juegos, por lo que incluí esa función dentro de las funciones de HOPPER.</p> |
|||
<p>Además, reescribí el programa "a la Hopper", como debió ser desde un principio.</p> |
|||
<syntaxhighlight lang="text"> |
|||
#include <jambo.h> |
|||
#define SIZER 90 |
|||
#define SIZEC 120 |
|||
Main |
|||
Cls |
|||
Hide cursor |
|||
Dim (SIZER, SIZEC), as zeros 'grid, neighbour_count' |
|||
Dim (SIZER, SIZEC), as fill '" ",disp grid' |
|||
c=0 , Let( c := Utf8(Chr(254))) |
|||
m={} |
|||
Set ' 0,1,1 ' Apend row to 'm' |
|||
Set ' 1,1,0 ' Apend row to 'm' |
|||
Set ' 0,1,0 ' Apend row to 'm' |
|||
[44:46, 59:61] Set 'm', Put 'grid' |
|||
Clr all marks |
|||
radio=1, r=0 |
|||
Tok sep '""' |
|||
Locate (1,1) |
|||
Loop |
|||
i=1 |
|||
Iterator ( ++i, Less equal(i,SIZER),\ |
|||
j=1, Iterator ( ++j, Less equal(j,SIZEC), \ |
|||
[i,j], Neighbour count (grid,radio), Put 'neighbour_count' ) ) |
|||
Cartesian ( Greater equal(grid, 1)---Back up to 'r'--- Mul by 'neighbour_count';\ |
|||
Out of range including '1,4' ) |
|||
Get range, Set '0," "', Put 'disp grid', Put 'grid', Forget |
|||
Cartesian ( Not( r ); Mul by 'neighbour_count'; Is equal to '3' ) |
|||
Get range, Set '1,c', Put 'disp grid', Put 'grid', Forget |
|||
Clr range |
|||
Clr all marks |
|||
Print table 'disp grid' |
|||
Break if ( Key pressed ) |
|||
Back |
|||
Pause |
|||
Show cursor |
|||
End |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
La salida es la misma, pero ahora va mucho más rápido... parecen estrellitas explotando :D |
|||
=={{header|APL}}== |
=={{header|APL}}== |
||
[[GNU APL]] (from Wikipedia: https://aplwiki.com/wiki/John_Scholes%27_Conway%27s_Game_of_Life#Translations)<syntaxhighlight lang="apl"> |
|||
Life←{↑↑1 ⍵∨.∧3 4=+/,¯1 0 1∘.⊖¯1 0 1∘.⌽⊂⍵} |
|||
m ← 5 5⍴(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) |
|||
m |
|||
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 m |
|||
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 |
|||
Life Life m |
|||
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 |
|||
</syntaxhighlight> |
|||
[http://www.dyalog.com/dfnsdws/c_life.htm APL2 (Dyalog) Example in one line] |
|||
APL \ 1130 example (very old APL dialect via simulator)<br> |
|||
https://web.archive.org/web/20160313204013/http://www.farnik.com/wiki/uploads/lifeAPL1130.jpg <br> <br> |
|||
From: [http://www.farnik.com/cgi-bin/wiki.pl?Retro_Computing#APL_1130_Sample_Session APL \ 1130 Samples] |
|||
The following APL \ 1130 code will need APL385 font installed to display correctly. <br> |
|||
See [http://www.dyalog.com/apl-font-keyboard.htm Download APL TT Font]<br> |
|||
<syntaxhighlight lang="text"> |
|||
∇LIFE[⎕]∇ |
|||
[0] NG←LIFE CG;W |
|||
[1] W←CG+(¯1⊖CG)+(1⊖CG)+(¯1⌽CG)+(1⌽CG) |
|||
[2] W←W+(1⊖1⌽CG)+(¯1⊖1⌽CG)+(1⊖¯1⌽CG)+(¯1⊖¯1⌽CG) |
|||
[3] NG←(3=W)+(CG∧4=W) |
|||
∇ |
|||
RP←5 5⍴0 0 0 0 0 0 0 1 1 0 0 1 1 0 0 0 0 1 0 0 |
|||
RP |
|||
0 0 0 0 0 |
|||
0 0 1 1 0 |
|||
0 1 1 0 0 |
|||
0 0 1 0 0 |
|||
0 0 0 0 0 |
|||
LIFE RP |
|||
0 0 0 0 0 |
|||
0 1 1 1 0 |
|||
0 1 0 0 0 |
|||
0 1 1 0 0 |
|||
0 0 0 0 0 |
|||
LIFE LIFE RP |
|||
0 0 1 0 0 |
|||
0 1 1 0 0 |
|||
1 0 0 1 0 |
|||
0 1 1 0 0 |
|||
0 0 0 0 0 |
|||
</syntaxhighlight> |
|||
=={{header|AppleScript}}== |
|||
This handler creates and returns a "universe" script object initialised with given "seed" text and dimensions. For convenience, the seed text's visible characters can be anything, the set-up code itself replacing them with "■" characters. The use of the returned universe is demo'd later. |
|||
<syntaxhighlight lang="applescript">use AppleScript version "2.4" -- OS X 10.10 (Yosemite) or later |
|||
use framework "Foundation" -- For the regex at the top of newUniverse() |
|||
use scripting additions |
|||
-- The characters to represent the live and dead cells. |
|||
property live : "■" -- character id 9632 (U+25A0). |
|||
property dead : space |
|||
-- Infinite universes are expensive to maintain, so only a local region of universe is represented here. |
|||
-- Its invisible border is a wall of "dead" cells one cell deep, lined with a two-cell buffer layer into which |
|||
-- objects nominally leaving the region can disappear without being seen to hit the wall or bouncing back. |
|||
property borderThickness : 3 |
|||
on newUniverse(seed, {w, h}) |
|||
-- Replace every visible character in the seed text with "■" and every horizontal space with a space. |
|||
set seed to current application's class "NSMutableString"'s stringWithString:(seed) |
|||
set regex to current application's NSRegularExpressionSearch |
|||
tell seed to replaceOccurrencesOfString:("\\S") withString:(live) options:(regex) range:({0, its |length|()}) |
|||
tell seed to replaceOccurrencesOfString:("\\h") withString:(dead) options:(regex) range:({0, its |length|()}) |
|||
-- Ensure the universe dimensions are at least equal to the number of lines and the length of the longest. |
|||
set seedLines to paragraphs of (seed as text) |
|||
set lineCount to (count seedLines) |
|||
if (lineCount > h) then set h to lineCount |
|||
set seedWidth to 0 |
|||
repeat with thisLine in seedLines |
|||
set lineLength to (count thisLine) |
|||
if (lineLength > seedWidth) then set seedWidth to lineLength |
|||
end repeat |
|||
if (seedWidth > w) then set w to seedWidth |
|||
-- Get a new universe. |
|||
script universe |
|||
-- State lists. These will contain or be lists of 0s and 1s and will include the border cells. |
|||
property newState : {} |
|||
property previousState : {} |
|||
property currentRow : {} |
|||
property rowAbove : {} |
|||
property rowBelow : {} |
|||
property replacementRow : {} |
|||
-- Equivalent text lists. These will only cover what's in the bounded region. |
|||
property lineList : {} |
|||
property characterGrid : {} |
|||
property currentLineCharacters : {} |
|||
-- Precalculated border cell indices. |
|||
property rightInnerBuffer : borderThickness + w + 1 |
|||
property rightOuterBuffer : rightInnerBuffer + borderThickness - 2 |
|||
property bottomInnerBuffer : borderThickness + h + 1 |
|||
property bottomOuterBuffer : bottomInnerBuffer + borderThickness - 2 |
|||
-- Generation counter. |
|||
property counter : 0 |
|||
-- Temporary lists used in the set-up. |
|||
property rowTemplate : {} |
|||
property lineCharacterTemplate : {} |
|||
-- Built-in handlers. Both return text representing a universe state and |
|||
-- a boolean indicating whether or not the state's the same as the previous one. |
|||
on nextState() |
|||
set astid to AppleScript's text item delimiters |
|||
set AppleScript's text item delimiters to "" |
|||
copy newState to previousState |
|||
set currentRow to beginning of my previousState |
|||
set rowBelow to item 2 of my previousState |
|||
-- Check each occupiable cell in each occupiable row of the 'previousState' grid, including the buffer cells. |
|||
-- If warranted by the number of live neighbours, edit the equivalent cell in 'newState' and, |
|||
-- if within the region's bounds, change the corresponding text character too. |
|||
repeat with r from 2 to bottomOuterBuffer |
|||
set rowAbove to currentRow |
|||
set currentRow to rowBelow |
|||
set rowBelow to item (r + 1) of my previousState |
|||
set replacementRow to item r of my newState |
|||
set rowCrossesRegion to ((r comes after borderThickness) and (r comes before bottomInnerBuffer)) |
|||
if (rowCrossesRegion) then set currentLineCharacters to item (r - borderThickness) of my characterGrid |
|||
set lineChanged to false |
|||
repeat with c from 2 to rightOuterBuffer |
|||
set liveNeighbours to ¬ |
|||
(item (c - 1) of my rowAbove) + (item c of my rowAbove) + (item (c + 1) of my rowAbove) + ¬ |
|||
(item (c - 1) of my currentRow) + (item (c + 1) of my currentRow) + ¬ |
|||
(item (c - 1) of my rowBelow) + (item c of my rowBelow) + (item (c + 1) of my rowBelow) |
|||
if (item c of my currentRow is 1) then |
|||
if ((liveNeighbours < 2) or (liveNeighbours > 3)) then |
|||
set item c of my replacementRow to 0 |
|||
if ((c comes after borderThickness) and (c comes before rightInnerBuffer) and (rowCrossesRegion)) then |
|||
set item (c - borderThickness) of my currentLineCharacters to dead |
|||
set lineChanged to true |
|||
else if ((c is 3) or (c is rightOuterBuffer) or (r is 3) or (r is bottomOuterBuffer)) then |
|||
-- This is a fudge to dissolve "bombers" entering the buffer zone. |
|||
set item (c - 1) of my replacementRow to -1 |
|||
set item c of item (r - 1) of my newState to -1 |
|||
end if |
|||
end if |
|||
else if (liveNeighbours is 3) then |
|||
set item c of my replacementRow to 1 |
|||
if ((c comes after borderThickness) and (c comes before rightInnerBuffer) and (rowCrossesRegion)) then |
|||
set item (c - borderThickness) of my currentLineCharacters to live |
|||
set lineChanged to true |
|||
end if |
|||
end if |
|||
end repeat |
|||
if (lineChanged) then set item (r - borderThickness) of my lineList to currentLineCharacters as text |
|||
end repeat |
|||
set AppleScript's text item delimiters to astid |
|||
set counter to counter + 1 |
|||
set last item of my lineList to "Generation " & counter |
|||
return currentState() |
|||
end nextState |
|||
on currentState() |
|||
set noChanges to (newState = previousState) |
|||
if (noChanges) then ¬ |
|||
set last item of my lineList to (last item of my lineList) & " (all dead, still lifes, or left the universe)" |
|||
set astid to AppleScript's text item delimiters |
|||
set AppleScript's text item delimiters to return |
|||
set stateText to lineList as text |
|||
set AppleScript's text item delimiters to astid |
|||
return {stateText, noChanges} |
|||
end currentState |
|||
end script |
|||
-- Set the universe's start conditions. |
|||
-- Build a row template list containing w + 2 * borderThickness zeros |
|||
-- and a line character template list containing w 'dead' characters. |
|||
repeat (borderThickness * 2) times |
|||
set end of universe's rowTemplate to 0 |
|||
end repeat |
|||
repeat w times |
|||
set end of universe's rowTemplate to 0 |
|||
set end of universe's lineCharacterTemplate to dead |
|||
end repeat |
|||
set astid to AppleScript's text item delimiters |
|||
set AppleScript's text item delimiters to "" |
|||
set blankLine to universe's lineCharacterTemplate as text |
|||
-- Use the templates to populate lists representing the universe's conditions. |
|||
-- Firstly the top border rows ('newState' list only). |
|||
repeat borderThickness times |
|||
copy universe's rowTemplate to end of universe's newState |
|||
end repeat |
|||
-- Then enough rows and text lines to centre the input roughly halfway down the grid. |
|||
set headroom to (h - lineCount) div 2 |
|||
repeat headroom times |
|||
copy universe's rowTemplate to end of universe's newState |
|||
copy universe's lineCharacterTemplate to end of universe's characterGrid |
|||
set end of universe's lineList to blankLine |
|||
end repeat |
|||
-- Then the rows and lines representing the input itself, centring it roughly halfway across the grid. |
|||
set textInset to (w - seedWidth) div 2 |
|||
set stateInset to textInset + borderThickness |
|||
repeat with thisLine in seedLines |
|||
copy universe's rowTemplate to universe's currentRow |
|||
copy universe's lineCharacterTemplate to universe's currentLineCharacters |
|||
repeat with c from 1 to (count thisLine) |
|||
set thisCharacter to character c of thisLine |
|||
set item (textInset + c) of universe's currentLineCharacters to thisCharacter |
|||
set item (stateInset + c) of universe's currentRow to (thisCharacter is live) as integer |
|||
end repeat |
|||
set end of universe's newState to universe's currentRow |
|||
set end of universe's characterGrid to universe's currentLineCharacters |
|||
set end of universe's lineList to universe's currentLineCharacters as text |
|||
end repeat |
|||
set AppleScript's text item delimiters to astid |
|||
-- Then the rows and lines beneath and the bottom border. |
|||
repeat (h - (headroom + lineCount)) times |
|||
copy universe's rowTemplate to end of universe's newState |
|||
copy universe's lineCharacterTemplate to end of universe's characterGrid |
|||
set end of universe's lineList to blankLine |
|||
end repeat |
|||
repeat borderThickness times |
|||
copy universe's rowTemplate to end of universe's newState |
|||
end repeat |
|||
-- Add a generation counter display line to the end of the line list. |
|||
set end of universe's lineList to "Generation 0" |
|||
-- Lose the no-longer-needed template lists. |
|||
set universe's rowTemplate to missing value |
|||
set universe's lineCharacterTemplate to universe's rowTemplate |
|||
return universe |
|||
end newUniverse</syntaxhighlight> |
|||
In conjunction with the above, this fulfills the task as set: |
|||
<syntaxhighlight lang="applescript">on RCTask(seed, dimensions, maxGenerations) |
|||
-- Create a universe and start a list with its initial state. |
|||
set universe to newUniverse(seed, dimensions) |
|||
set {stateText} to universe's currentState() |
|||
set output to {stateText} |
|||
-- Add successive states to the list. |
|||
repeat maxGenerations times |
|||
set {stateText, noChanges} to universe's nextState() |
|||
set end of output to stateText |
|||
if (noChanges) then exit repeat |
|||
end repeat |
|||
-- Coerce the states to a single text, each followed by a short line of dashes. |
|||
set astid to AppleScript's text item delimiters |
|||
set AppleScript's text item delimiters to linefeed & "-----" & linefeed & linefeed |
|||
set output to (output as text) & linefeed & "-----" |
|||
set AppleScript's text item delimiters to astid |
|||
return output |
|||
end RCTask |
|||
-- Return text containing the original and three generations of a "blinker" in a 3 x 3 grid. |
|||
return RCTask("***", {3, 3}, 3)</syntaxhighlight> |
|||
{{output}} |
|||
<syntaxhighlight lang="applescript">" |
|||
■■■ |
|||
Generation 0 |
|||
----- |
|||
■ |
|||
■ |
|||
■ |
|||
Generation 1 |
|||
----- |
|||
■■■ |
|||
Generation 2 |
|||
----- |
|||
■ |
|||
■ |
|||
■ |
|||
Generation 3 |
|||
-----"</syntaxhighlight> |
|||
This alternative to the task code runs an animation of a "Gosper glider gun" in TextEdit, the AppleScriptable text editor included with macOS. The animation's achieved by replacing the entire text of a document with successive universe states. It's faster than it sounds, but the universe size specified shouldn't be much greater than 150 x 150 with current machines. |
|||
<syntaxhighlight lang="applescript">on runGame(seed, dimensions, maxGenerations) |
|||
-- Create an RTF file set up for Menlo-Regular 12pt, half spacing, and a reasonable window size. |
|||
set fontName to "Menlo-Regular" |
|||
set fontSize to 12 |
|||
set viewScale to fontSize * 12.4 -- Seems to work well. |
|||
set {w, h} to dimensions |
|||
set RTFHeaders to "{\\rtf1\\ansi\\ansicpg1252\\cocoartf1671\\cocoasubrtf600 |
|||
{\\fonttbl\\f0\\fnil\\fcharset0 " & fontName & ";} |
|||
{\\colortbl;\\red255\\green255\\blue255;} |
|||
{\\*\\expandedcolortbl;;} |
|||
\\margl1440\\margr1440\\vieww" & (w * viewScale as integer) & "\\viewh" & ((h + 1) * viewScale as integer) & "\\viewkind0 |
|||
\\pard\\sl120\\slmult1\\pardirnatural\\partightenfactor0 |
|||
\\f0\\fs" & (fontSize * 2) & " \\cf0 }" -- Contains a space as body text for TextEdit to see as an 'attribute run'. |
|||
set RTFFile to ((path to temporary items as text) & "Conway's Game of Life.rtf") as «class furl» |
|||
set fRef to (open for access RTFFile with write permission) |
|||
try |
|||
set eof fRef to 0 |
|||
write RTFHeaders as «class utf8» to fRef |
|||
close access fRef |
|||
on error errMsg number errNum |
|||
close access fRef |
|||
error errMsg number errNum |
|||
end try |
|||
-- Open the file as a document in TextEdit. |
|||
tell application "TextEdit" |
|||
activate |
|||
tell document "Conway's Game of Life.rtf" to if (it exists) then close saving no |
|||
set CGoLDoc to (open RTFFile) |
|||
end tell |
|||
-- Create a universe and display its initial state in the document window. |
|||
set universe to newUniverse(seed, dimensions) |
|||
set {stateText} to universe's currentState() |
|||
tell application "TextEdit" to set CGoLDoc's first attribute run to stateText |
|||
-- Get and display successive states. |
|||
repeat maxGenerations times |
|||
set {stateText, noChanges} to universe's nextState() |
|||
tell application "TextEdit" to set CGoLDoc's first attribute run to stateText |
|||
if (noChanges) then exit repeat |
|||
end repeat |
|||
end runGame |
|||
set GosperGliderGun to " * |
|||
* * |
|||
** ** ** |
|||
* * ** ** |
|||
** * * ** |
|||
** * * ** * * |
|||
* * * |
|||
* * |
|||
**" |
|||
-- Run for 500 generations in a 100 x 100 universe. |
|||
runGame(GosperGliderGun, {100, 100}, 500)</syntaxhighlight> |
|||
=={{header|ARM Assembly}}== |
|||
{{works with|TI-Nspire}} |
|||
<syntaxhighlight lang="arm assembly"> .string "PRG" |
|||
lcd_ptr .req r4 |
|||
active_fb .req r5 |
|||
inactive_fb .req r6 |
|||
offset_r .req r7 |
|||
backup_fb .req r8 |
|||
@ start |
|||
push {r4-r10, r12, lr} |
|||
ldr lcd_ptr, =0xC0000000 @ address of the LCD controller |
|||
adr offset_r, offsets |
|||
ldrh r0, [offset_r, #6] @ 0xffff is already in memory because -1 is in the offsets table |
|||
str r0, [lcd_ptr, #0x200] @ set up paletted colors: 1 is black, 0 is white |
|||
ldr r2, [lcd_ptr, #0x18] @ load lcd configuration |
|||
bic r2, #14 |
|||
orr r2, #6 @ Set color mode to 8 bpp, paletted |
|||
str r2, [lcd_ptr, #0x18] |
|||
ldr backup_fb, [lcd_ptr, #0x10] @ Save address of OS framebuffer |
|||
@ allocate a buffer for game state / framebuffer |
|||
ldr r0, =153600 @ 320 * 240 * 2 |
|||
add r0, #8 |
|||
svc #5 @ malloc |
|||
push {r0} |
|||
orr inactive_fb, r0, #7 |
|||
add inactive_fb, #1 |
|||
add active_fb, inactive_fb, #76800 |
|||
@ fill buffer with random ones and zeroes |
|||
ldr r10, =76800 |
|||
mov r9, #0 |
|||
1: subs r10, r10, #1 |
|||
strb r9, [active_fb, r10] @ zero other framebuffer |
|||
svc #206 @ rand syscall |
|||
and r0, r0, #1 |
|||
strb r0, [inactive_fb, r10] |
|||
bne 1b |
|||
@ set first and last rows to zero |
|||
mov r2, #320 |
|||
mov r1, #0 |
|||
mov r0, inactive_fb |
|||
push {r1,r2} |
|||
svc #7 @ memset |
|||
pop {r1,r2} |
|||
ldr r3, =76480 |
|||
add r0, r0, r3 |
|||
svc #7 |
|||
@ beginning of main loop, swap framebuffers |
|||
3: ldr r0, =76480 @ 320 * 239 |
|||
str inactive_fb, [lcd_ptr, #0x10] |
|||
mov inactive_fb, active_fb |
|||
ldr active_fb, [lcd_ptr, #0x10] |
|||
@ per-pixel loop |
|||
2: mov r1, #16 @ 8 * 2 |
|||
mov r2, #0 |
|||
sub r0, #1 |
|||
@ loop to count up neighboring living cells |
|||
1: subs r1, #2 |
|||
ldrsh r3, [offset_r, r1] @ cant use lsl #1 |
|||
add r3, r3, r0 |
|||
ldrb r3, [active_fb, r3] |
|||
add r2, r2, r3 |
|||
bne 1b @ at end of loop, r1 and r3 can be discarded |
|||
@ decides whether the cell should live or die based on neighbors |
|||
ldrb r1, [active_fb, r0] |
|||
add r2, r2, r1 |
|||
teq r2, #3 |
|||
moveq r1, #1 |
|||
teqne r2, #4 |
|||
movne r1, #0 |
|||
strb r1, [inactive_fb, r0] |
|||
teq r0, #320 |
|||
bne 2b |
|||
@ checks if the escape key is pressed |
|||
ldr r0, =0x900E001C |
|||
ldr r1, [r0] |
|||
tst r1, #0x80 |
|||
beq 3b |
|||
str backup_fb, [lcd_ptr, #0x10] @ restores OS framebuffer |
|||
pop {r0} |
|||
svc #6 @ free buffer |
|||
pop {r4-r10, r12, pc} |
|||
offsets: |
|||
.hword -321, -320, -319, -1, 1, 319, 320, 321 |
|||
</syntaxhighlight> |
|||
http://i.imgur.com/kV9RirP.gif |
|||
[http://www.dyalog.com/dfnsdws/c_life.htm] |
|||
=={{header|AutoHotkey}}== |
=={{header|AutoHotkey}}== |
||
ahk [http://www.autohotkey.com/forum/viewtopic.php?t=44657&postdays=0&postorder=asc&start=143 discussion] |
ahk [http://www.autohotkey.com/forum/viewtopic.php?t=44657&postdays=0&postorder=asc&start=143 discussion] |
||
< |
<syntaxhighlight lang="autohotkey">rows := cols := 10 ; set grid dimensions |
||
i = -1,0,1, -1,1, -1,0,1 ; neighbors' x-offsets |
i = -1,0,1, -1,1, -1,0,1 ; neighbors' x-offsets |
||
j = -1,-1,-1, 0,0, 1,1,1 ; neighbors' y-offsets |
j = -1,-1,-1, 0,0, 1,1,1 ; neighbors' y-offsets |
||
Line 347: | Line 1,899: | ||
GuiClose: ; exit when GUI is closed |
GuiClose: ; exit when GUI is closed |
||
ExitApp</ |
ExitApp</syntaxhighlight> |
||
=={{header|AWK}}== |
|||
50x20 grid (hardcoded) with empty border, |
|||
filled with random cells, |
|||
running for 220 generations, |
|||
using [http://en.wikipedia.org/wiki/ANSI_escape_code ANSI escape-codes] for output to terminal: |
|||
<syntaxhighlight lang="awk"> |
|||
BEGIN { |
|||
c=220; d=619; i=10000; |
|||
printf("\033[2J"); # Clear screen |
|||
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"); # Home cursor |
|||
for(i=1;i<=1000;i++) # gridsize 50x20 |
|||
{ |
|||
if(n[i]) printf("O"); else printf("."); |
|||
m[i]=n[i]; |
|||
if(!(i%50)) printf("\n"); |
|||
} |
|||
printf("%3d\n",c); # Countdown |
|||
x=30000; while(x--) ; # Delay |
|||
} |
|||
} |
|||
</syntaxhighlight> |
|||
{{out}} Finally: |
|||
<pre> |
|||
.................................................. |
|||
..........................................OO...... |
|||
..........................................O.O..... |
|||
...........................................O...... |
|||
......................................OOO.......OO |
|||
................................................OO |
|||
....................................O.....O....... |
|||
....................................O.....O....... |
|||
....................................O.....O....... |
|||
.................................................. |
|||
......................................OOO......... |
|||
.................................................. |
|||
.................................................. |
|||
.................................................. |
|||
..O............................................... |
|||
.O.O.............................................. |
|||
O.O...........OO...........OO..................... |
|||
.O............OO.........O..O..................... |
|||
.........................OOO...................... |
|||
.................................................. |
|||
0 |
|||
</pre> |
|||
=={{header|Axe}}== |
|||
{{improve|Axe|Improve performance and add a way to interactively seed the map.}} |
|||
This implementation uses the full screen buffer instead of a 3x3 grid. This naive, unoptimized version gets less than 1 FPS. |
|||
<syntaxhighlight lang="axe">Full |
|||
While getKey(0) |
|||
End |
|||
ClrDraw |
|||
.BLINKER |
|||
Pxl-On(45,45) |
|||
Pxl-On(46,45) |
|||
Pxl-On(47,45) |
|||
.GLIDER |
|||
Pxl-On(1,1) |
|||
Pxl-On(2,2) |
|||
Pxl-On(2,3) |
|||
Pxl-On(3,1) |
|||
Pxl-On(3,2) |
|||
Repeat getKey(0) |
|||
DispGraph |
|||
EVOLVE() |
|||
RecallPic |
|||
ClrDrawʳ |
|||
End |
|||
Return |
|||
Lbl EVOLVE |
|||
For(Y,0,63) |
|||
For(X,0,95) |
|||
0→N |
|||
For(B,Y-1,Y+1) |
|||
For(A,X-1,X+1) |
|||
pxl-Test(A,B)?N++ |
|||
End |
|||
End |
|||
pxl_Test(X,Y)?N-- |
|||
If N=3??(N=2?pxl-Test(X,Y)) |
|||
Pxl-On(X,Y)ʳ |
|||
Else |
|||
Pxl-Off(X,Y)ʳ |
|||
End |
|||
End |
|||
End |
|||
Return</syntaxhighlight> |
|||
=={{header|BASIC}}== |
|||
==={{header|BASIC256}}=== |
|||
[[File:Game of life BASIC-256.gif|right|236px|thumb|"Thunderbird" methuselah evolution in the Game of Life (created with BASIC-256)]] |
|||
Saving to PNG files function is omited. |
|||
You can find it in the [[Galton box animation#BASIC256|Galton box animation]] example. |
|||
<syntaxhighlight lang="basic256"># Conway's_Game_of_Life |
|||
X = 59 : Y = 35 : H = 4 |
|||
fastgraphics |
|||
graphsize X*H,Y*H |
|||
dim c(X,Y) : dim cn(X,Y) : dim cl(X,Y) |
|||
</syntaxhighlight><syntaxhighlight lang="basic256"> |
|||
# Thunderbird methuselah |
|||
c[X/2-1,Y/3+1] = 1 : c[X/2,Y/3+1] = 1 : c[X/2+1,Y/3+1] = 1 |
|||
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</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
Stabilized in 243 iterations |
|||
</pre> |
|||
==={{header|BBC BASIC}}=== |
|||
{{works with|BBC BASIC for Windows}} |
|||
<syntaxhighlight lang="bbcbasic"> 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</syntaxhighlight> |
|||
{{out}} |
|||
<BR> |
|||
[[File:Lifebbc.gif]] |
|||
==={{header|CASIO BASIC}}=== |
|||
{{works with|https://community.casiocalc.org/topic/7586-conways-game-of-life-fx-9750gii9860giii/#entry60579 Conway's Game of Life fx-9750GII/9860GI/II|1.1}} |
|||
<syntaxhighlight lang="casiobasic">Filename:JG VIDA |
|||
Cls |
|||
20→D |
|||
D+2→F |
|||
1→E |
|||
{F,F}→Dim Mat A |
|||
{F,F}→Dim Mat B |
|||
{F,F}→Dim Mat C |
|||
Fill(0,Mat A) |
|||
Fill(0,Mat B) |
|||
Fill(0,Mat C) |
|||
"PONDERACION 1"?→G |
|||
"PONDERACION 2"?→H |
|||
For 1→I To D |
|||
For 1→J To D |
|||
RanInt#(1,G)→R |
|||
If R≦H:Then |
|||
0→Mat A[I+1,J+1] |
|||
Else |
|||
1→Mat A[I+1,J+1] |
|||
IfEnd |
|||
Next |
|||
Next |
|||
Goto 2 |
|||
Lbl 1 |
|||
Mat A→Mat C |
|||
Mat B→Mat A |
|||
Lbl 2 |
|||
For 1→I To D |
|||
For 1→J To D |
|||
I+1→K |
|||
J+1→L |
|||
K→M |
|||
L+20→N |
|||
If Mat C[K,L]=0:Then |
|||
If Mat A[K,L]=1:Then For 0→R To 2 |
|||
For 0→S To 2 |
|||
PxlOn 3I+R-2,3J+S-2 |
|||
Next |
|||
Next |
|||
IfEnd |
|||
IfEnd |
|||
If Mat C[K,L]=1:Then |
|||
If Mat A[K,L]=0:Then For 0→R To 2 |
|||
For 0→S To 2 |
|||
PxlOff 3I+R-2,3J+S-2 |
|||
Next |
|||
Next |
|||
IfEnd |
|||
IfEnd |
|||
Next |
|||
Next |
|||
For 1→I To D |
|||
For 1→J To D |
|||
0→C |
|||
I+1→K |
|||
J+1→L |
|||
Mat A[I,J]=1⇨C+1→C |
|||
Mat A[I+1,J]=1⇨C+1→C |
|||
Mat A[I+2,J]=1⇨C+1→C |
|||
Mat A[I+2,J+1]=1⇨C+1→C |
|||
Mat A[I+2,J+2]=1⇨C+1→C |
|||
Mat A[I+1,J+2]=1⇨C+1→C |
|||
Mat A[I,J+2]=1⇨C+1→C |
|||
Mat A[I,J+1]=1⇨C+1→C |
|||
If Mat A[K,L]=1:Then |
|||
C<2⇨0→Mat B[K,L] |
|||
C>3⇨0→Mat B[K,L] |
|||
IfEnd |
|||
If Mat A[K,L]=0:Then |
|||
C=3⇨1→Mat B[K,L] |
|||
IfEnd |
|||
Next |
|||
Next |
|||
Goto 1 |
|||
</syntaxhighlight> |
|||
==={{header|FreeBASIC}}=== |
|||
<syntaxhighlight lang="freebasic">' FreeBASIC Conway's Game of Life |
|||
' May 2015 |
|||
' 07-10-2016 cleanup/little changes |
|||
' moved test inkey outside the ScreenLock - ScreenUnLock block |
|||
' compile: fbc -s gui |
|||
Const As UInteger grid = 300 '480 by 480 |
|||
Const As UInteger gridy = grid |
|||
Const As UInteger gridx = grid |
|||
Const As UInteger pointsize = 5 'pixels |
|||
Const As UInteger steps = 10 |
|||
Dim As UInteger gen, n, neighbours, x, y, was |
|||
Dim As String press |
|||
Const As UByte red = 4 'red is color 6 |
|||
Const As UByte white = 15 'color |
|||
Const As UByte black = 0 'color |
|||
'color 0 normaly is black |
|||
'color 1 normaly is dark blue |
|||
'color 2 normaly is green |
|||
Const As UInteger bot = 35 'this is 35 lines from the top of the page |
|||
Dim As UByte old( grid + 10, grid +10), new_( grid +10, grid +10) |
|||
'Set blinker: |
|||
' old( 160, 160) =1: old( 160, 170) =1 : old( 160, 180) =1 |
|||
'Set blinker: |
|||
' old( 160, 20) =1: old( 160, 30) =1 : old( 160, 40) =1 |
|||
'Set blinker: |
|||
' old( 20, 20) =1: old( 20, 30) =1 : old( 20, 40) =1 |
|||
'Set glider: |
|||
' old( 50, 70) =1: old( 60, 70) =1: old( 70, 70) =1 |
|||
' old( 70, 60) =1: old( 60, 50) =1 |
|||
' http://en.wikipedia.org/wiki/Conway%27s_Game_of_Life |
|||
' Thunderbird methuselah |
|||
'X = 59 : Y = 35 : H = 4 |
|||
'c[X/2-1,Y/3+1] = 1 : c[X/2,Y/3+1] = 1 : c[X/2+1,Y/3+1] = 1 |
|||
'c[X/2,Y/3+3] = 1 : c[X/2,Y/3+4] = 1 : c[X/2,Y/3+5] = 1 |
|||
'xb = 59 : yb = 35 |
|||
' old( Xb/2-1,Yb/3+1) =1: old(Xb/2,Yb/3+1) =1: old(Xb/2+1,Yb/3+1) =1 |
|||
' old( Xb/2,Yb/3+3) =1: old(Xb/2,Yb/3+4) =1 :old(Xb/2,Yb/3+5) = 1 |
|||
'r-pentomino |
|||
' old( 150,140) =1: old( 160,140) =1 |
|||
' old( 140,150) =1 :old( 150,150) =1 |
|||
' old( 150,160) =1 |
|||
'Die Hard around 150 generations |
|||
' old( 150,140) =1: old(160,140) =1 : old(160,150) =1 |
|||
' old( 200,150) =1: old(210,150) =1 : old(210,130) = 1 : old(220,150) = 1 |
|||
'Acorn around 450 generations |
|||
' it looks like this: |
|||
' 0X |
|||
' 000X |
|||
' XX00XXX |
|||
old( 180,200) =1 |
|||
old( 200,210) =1 |
|||
old( 170,220) =1 : old( 180,220) =1 : old( 210,220) =1 : old( 220,220) =1 : old( 230,220) =1 |
|||
Screen 20 'Resolution 800x600 with at least 256 colors |
|||
Color white |
|||
Line (10, 10) - (gridx + 10, gridy + 10),,B 'box from top left to bottom right |
|||
Locate bot, 1 'Use a standard place on the bottom of the page |
|||
Color white |
|||
Print " Welcome to Conway's Game of Life" |
|||
Print " Using a constrained playing field (300x300), the Acorn seed runs" |
|||
Print " for about 450 generations before it becomes stable (or stale)." |
|||
Print " Enter any key to start" |
|||
Beep |
|||
Sleep |
|||
Do ' flush the key input buffer |
|||
press = Inkey |
|||
Loop Until press = "" |
|||
'Print " " |
|||
'Draw initial grid |
|||
For x = 10 To gridX Step steps |
|||
For y = 10 To gridY Step steps |
|||
Color white 'old(x,y) |
|||
If old(x,y) = 1 Then Circle (x + pointsize, y + pointsize), pointsize,,,,, F |
|||
Next y |
|||
Next x |
|||
' |
|||
Locate bot, 1 |
|||
Color white |
|||
Print " Welcome to Conway's Game of Life" |
|||
Print " Using a constrained playing field, the Acorn seed runs for " |
|||
Print " about 450 generations before it becomes stable (or stale). " |
|||
Color red |
|||
Print " Enter spacebar to continue or pause, ESC to stop" |
|||
Sleep |
|||
' |
|||
Do ' flush the key input buffer |
|||
press = Inkey |
|||
Loop Until press = "" |
|||
Do |
|||
gen = gen + 1 |
|||
Locate bot+5,1 |
|||
Color white |
|||
Print " Gen = "; gen |
|||
ScreenLock |
|||
For x = 10 To gridX Step steps |
|||
For y = 10 To gridY Step steps |
|||
'find number of live neighbours |
|||
neighbours = old( x - steps, y - steps) +old( x , y - steps) |
|||
neighbours = neighbours + old( x + steps, y -steps) |
|||
neighbours = neighbours + old( x - steps, y) + old( x + steps, y) |
|||
neighbours = neighbours + old( x - steps, y + steps) |
|||
neighbours = neighbours + old( x, y + steps) +old( x + steps, y + steps) |
|||
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 |
|||
If n = 2 Then Color white |
|||
If n = 1 Then Color red |
|||
If n = 0 Then Color black |
|||
Circle (x + pointsize, y + pointsize), pointsize,,,,, F |
|||
Next y |
|||
Next x |
|||
Color white |
|||
Line (10, 10) - (gridx + 10, gridy + 10),,B 'box from top left to bottom right |
|||
' Locate bot,1 |
|||
' |
|||
't = timer |
|||
'do |
|||
'loop until timer > t + .2 |
|||
ScreenUnlock |
|||
' might not be slow enough |
|||
Sleep 70, 1 ' ignore key press |
|||
press = Inkey |
|||
If press = " " Then |
|||
Do ' flush the key input buffer |
|||
press = Inkey |
|||
Loop Until press = "" |
|||
Do ' wait until a key is pressed |
|||
press = Inkey |
|||
Loop Until press <> "" |
|||
End If |
|||
If press = Chr(27) Then Exit Do |
|||
' mouse click on close window "X" |
|||
If press = Chr(255)+"k" Then End ' stop and close window |
|||
For x =10 To gridX Step steps |
|||
For y =10 To gridY Step steps |
|||
old( x, y) =new_( x, y) |
|||
Next y |
|||
Next x |
|||
Loop ' UNTIL press = CHR(27) 'return to do loop up top until "esc" key is pressed. |
|||
Color white |
|||
Locate bot+3,1 |
|||
Print Space(55) 'clear instructions |
|||
Locate bot+6,1 |
|||
Print " Press any key to exit " |
|||
Sleep |
|||
End</syntaxhighlight> |
|||
==={{header|GFA Basic}}=== |
|||
<syntaxhighlight lang="text"> |
|||
' |
|||
' Conway's Game of Life |
|||
' |
|||
' 30x30 world held in an array size 32x32 |
|||
' world is in indices 1->30, 0 and 31 are always false, for neighbourhoods |
|||
' |
|||
DIM world!(32,32) |
|||
DIM ns%(31,31) ! used to hold the neighbour counts |
|||
clock%=1 |
|||
' |
|||
' run the world |
|||
' |
|||
@setup_world |
|||
@open_window |
|||
DO |
|||
@display_world |
|||
t$=INKEY$ |
|||
EXIT IF t$="q" ! need to hold key down to exit |
|||
@update_world |
|||
DELAY 0.5 ! delay of 0.5s needed in compiled version |
|||
LOOP |
|||
@close_window |
|||
' |
|||
' Setup the world, with a blinker in one corner and a glider in the other |
|||
' |
|||
PROCEDURE setup_world |
|||
ARRAYFILL world!(),FALSE |
|||
' blinker in lower-right |
|||
world!(25,25)=TRUE |
|||
world!(26,25)=TRUE |
|||
world!(27,25)=TRUE |
|||
' glider in top-left |
|||
world!(2,2)=TRUE |
|||
world!(3,3)=TRUE |
|||
world!(3,4)=TRUE |
|||
world!(2,4)=TRUE |
|||
world!(1,4)=TRUE |
|||
RETURN |
|||
' |
|||
' Count the number of neighbours of the point i,j |
|||
' (Assume i/j +/- 1 will not fall out of world) |
|||
' |
|||
FUNCTION count_neighbours(i%,j%) |
|||
LOCAL count%,l% |
|||
count%=0 |
|||
FOR l%=-1 TO 1 |
|||
IF world!(i%+l%,j%-1) |
|||
count%=count%+1 |
|||
ENDIF |
|||
IF world!(i%+l%,j%+1) |
|||
count%=count%+1 |
|||
ENDIF |
|||
NEXT l% |
|||
IF world!(i%-1,j%) |
|||
count%=count%+1 |
|||
ENDIF |
|||
IF world!(i%+1,j%) |
|||
count%=count%+1 |
|||
ENDIF |
|||
RETURN count% |
|||
ENDFUNC |
|||
' |
|||
' Update the world one step |
|||
' |
|||
PROCEDURE update_world |
|||
LOCAL i%,j% |
|||
' compute neighbour counts and store |
|||
FOR i%=1 TO 30 |
|||
FOR j%=1 TO 30 |
|||
ns%(i%,j%)=@count_neighbours(i%,j%) |
|||
NEXT j% |
|||
NEXT i% |
|||
' update the world cells |
|||
FOR i%=1 TO 30 |
|||
FOR j%=1 TO 30 |
|||
IF world!(i%,j%) |
|||
SELECT ns%(i%,j%) |
|||
CASE 0,1 |
|||
world!(i%,j%)=FALSE ! LONELY |
|||
CASE 2,3 |
|||
world!(i%,j%)=TRUE ! LIVES |
|||
CASE 4,5,6,7,8 |
|||
world!(i%,j%)=FALSE ! OVERCROWDED |
|||
ENDSELECT |
|||
ELSE |
|||
IF ns%(i%,j%)=3 |
|||
world!(i%,j%)=TRUE ! BIRTH |
|||
ELSE |
|||
world!(i%,j%)=FALSE ! BARREN |
|||
ENDIF |
|||
ENDIF |
|||
NEXT j% |
|||
NEXT i% |
|||
' update the clock |
|||
clock%=clock%+1 |
|||
RETURN |
|||
' |
|||
' Display the world in window |
|||
' |
|||
PROCEDURE display_world |
|||
LOCAL offsetx%,offsety%,i%,j%,x%,y%,scale% |
|||
@clear_window |
|||
' show clock |
|||
VSETCOLOR 2,0,0,0 |
|||
DEFTEXT 2 |
|||
PRINT AT(5,1);"Clock: ";clock% |
|||
' offset from top-left of display |
|||
offsetx%=10 |
|||
offsety%=10 |
|||
' colour to display active cell |
|||
VSETCOLOR 1,15,0,0 |
|||
DEFFILL 1 |
|||
' scale of display |
|||
scale%=9 |
|||
' display each cell in world |
|||
FOR i%=1 TO 30 |
|||
FOR j%=1 TO 30 |
|||
IF world!(i%,j%) |
|||
' display active cell |
|||
x%=offsetx%+scale%*i% |
|||
y%=offsety%+scale%*j% |
|||
PBOX x%,y%,x%+scale%,y%+scale% |
|||
ENDIF |
|||
NEXT j% |
|||
NEXT i% |
|||
RETURN |
|||
' |
|||
' Manage window for display |
|||
' |
|||
PROCEDURE open_window |
|||
OPENW 1 |
|||
CLEARW 1 |
|||
RETURN |
|||
' |
|||
PROCEDURE clear_window |
|||
VSETCOLOR 0,15,15,15 |
|||
DEFFILL 0 |
|||
PBOX 0,0,300,300 |
|||
RETURN |
|||
' |
|||
PROCEDURE close_window |
|||
CLOSEW 1 |
|||
RETURN |
|||
</syntaxhighlight> |
|||
==={{header|GW-BASIC}}=== |
|||
Allows a blinker to be loaded. It also has a routine for randomising the grid; common objects like blocks, gliders, etc turn up semi-frequently so it won't take long to verify that these all work. |
|||
<syntaxhighlight lang="gwbasic">10 REM Conway's Game of Life |
|||
20 REM 30x30 grid, padded with zeroes as the boundary |
|||
30 DIM WORLD(31, 31, 1) |
|||
40 RANDOMIZE TIMER |
|||
50 CUR = 0 : BUF = 1 |
|||
60 CLS |
|||
70 SCREEN 2 |
|||
80 LOCATE 5,10: PRINT "Press space to perform one iteration" |
|||
90 LOCATE 6,10: PRINT "B to load a blinker" |
|||
100 LOCATE 7,10: PRINT "R to randomise the world" |
|||
110 LOCATE 8,10: PRINT "Q to quit" |
|||
120 K$ = INKEY$ |
|||
130 IF K$=" " THEN GOSUB 250: GOSUB 180 |
|||
140 IF K$="B" OR K$="b" THEN GOSUB 400: GOSUB 180 |
|||
150 IF K$="R" OR K$="r" THEN GOSUB 480: GOSUB 180 |
|||
160 IF K$="Q" OR K$="q" THEN SCREEN 0: END |
|||
170 GOTO 120 |
|||
180 REM draw the world |
|||
190 FOR XX=1 TO 30 |
|||
200 FOR YY=1 TO 30 |
|||
210 PSET (XX, YY), 15*WORLD(XX, YY, CUR) |
|||
220 NEXT YY |
|||
230 NEXT XX |
|||
240 RETURN |
|||
250 REM perform one iteration |
|||
260 FOR XX=1 TO 30 |
|||
270 FOR YY=1 TO 30 |
|||
280 SM=0 |
|||
290 SM = SM + WORLD(XX-1, YY-1, CUR) + WORLD(XX, YY-1, CUR) + WORLD(XX+1, YY-1, CUR) |
|||
300 SM = SM + WORLD(XX-1, YY, CUR) + WORLD(XX+1, YY, CUR) |
|||
310 SM = SM + WORLD(XX-1, YY+1, CUR) + WORLD(XX, YY+1, CUR) + WORLD(XX+1, YY+1, CUR) |
|||
320 IF SM<2 OR SM>3 THEN WORLD(XX, YY, BUF) = 0 |
|||
330 IF SM=3 THEN WORLD(XX, YY, BUF) = 1 |
|||
340 IF SM=2 THEN WORLD(XX,YY,BUF) = WORLD(XX,YY,CUR) |
|||
350 NEXT YY |
|||
360 NEXT XX |
|||
370 CUR = BUF : REM exchange identities of current and buffer |
|||
380 BUF = 1 - BUF |
|||
390 RETURN |
|||
400 REM produces a vertical blinker at the top left corner, and blanks the rest |
|||
410 FOR XX=1 TO 30 |
|||
420 FOR YY=1 TO 30 |
|||
430 WORLD(XX,YY,CUR) = 0 |
|||
440 IF XX=2 AND YY<4 THEN WORLD(XX, YY, CUR) = 1 |
|||
450 NEXT YY |
|||
460 NEXT XX |
|||
470 RETURN |
|||
480 REM randomizes the world with a density of 1/2 |
|||
490 FOR XX = 1 TO 30 |
|||
500 FOR YY = 1 TO 30 |
|||
510 WORLD(XX, YY, CUR) = INT(RND*2) |
|||
520 NEXT YY |
|||
530 NEXT XX |
|||
540 RETURN</syntaxhighlight> |
|||
==={{header|Liberty BASIC}}=== |
|||
It will run slow for grids above say 25! |
|||
<syntaxhighlight lang="lb"> |
|||
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 =0Tail Recursive |
|||
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 |
|||
</syntaxhighlight> |
|||
==={{header|MSX Basic}}=== |
|||
<syntaxhighlight lang="basic"> |
|||
10 DEFINT A-Z |
|||
20 DIM L(16,16), N(16,16) |
|||
30 M = 16 |
|||
40 CLS |
|||
50 PRINT "PRESS A KEY TO START..."; |
|||
60 W = RND(1) |
|||
70 IF INKEY$ = "" THEN 60 |
|||
80 CLS |
|||
100 FOR I=1 TO M |
|||
110 FOR J=1 TO M |
|||
120 IF RND(1)>=.7 THEN N(I,J) = 1 ELSE N(I,J) = 0 |
|||
130 NEXT J |
|||
140 NEXT I |
|||
1000 FOR I=0 TO M+1 |
|||
1010 LOCATE I,0 : PRINT "+"; |
|||
1020 LOCATE I,M+1 : PRINT "+"; |
|||
1030 LOCATE 0,I : PRINT "+"; |
|||
1040 LOCATE M+1,I : PRINT "+"; |
|||
1050 NEXT I |
|||
1080 G=0 |
|||
1090 LOCATE 1,M+3 : PRINT USING "#####";G |
|||
1100 FOR I=1 TO M |
|||
1110 FOR J=1 TO M |
|||
1115 W = N(I,J) : L(I,J) = W |
|||
1120 LOCATE I,J |
|||
1130 IF W=0 THEN PRINT " "; ELSE PRINT "*"; |
|||
1160 NEXT J |
|||
1170 NEXT I |
|||
1180 FOR I=1 TO M |
|||
1190 FOR J=1 TO M |
|||
1200 NC=0 |
|||
1210 FOR K=I-1 TO I+1 |
|||
1215 IF K=0 OR K>M THEN 1260 |
|||
1220 FOR W=J-1 TO J+1 |
|||
1230 IF W=0 OR W>M OR (K=I AND W=J) THEN 1250 |
|||
1240 NC = NC + L(K,W) |
|||
1250 NEXT W |
|||
1260 NEXT K |
|||
1270 IF NC=2 THEN N(I,J)=L(I,J) : GOTO 1300 |
|||
1280 IF NC=3 THEN N(I,J)=1 ELSE N(I,J)=0 |
|||
1300 NEXT J |
|||
1310 NEXT I |
|||
1350 G=G+1 |
|||
1360 GOTO 1090 |
|||
</syntaxhighlight> |
|||
==={{header|PureBasic}}=== |
|||
<syntaxhighlight lang="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() <> ""</syntaxhighlight> |
|||
'''Sample output:'''<br> |
|||
[[File:Game-of-life-PureBasic.gif]] |
|||
==={{header|QBasic}}=== |
|||
El código es de Ben Wagner (bwgames.org)<br> |
|||
The code is from Ben Wagner (bwgames.org)<br> |
|||
Yo solo lo transcrito y comento al español.<br> |
|||
I just transcribed it and comment it in Spanish. |
|||
<syntaxhighlight lang="qbasic">SCREEN 9, 0, 0, 1 |
|||
RANDOMIZE TIMER |
|||
WINDOW (0, 0)-(80, 80) |
|||
'La matrizA es la actual, la matrizB es la siguiente iteración |
|||
'ArrayA is current, arrayB is next iteration |
|||
DIM matrizA(-1 TO 81, -1 TO 81) |
|||
DIM matrizB(-1 TO 81, -1 TO 81) |
|||
'Aleatorizar las celdas de matrizA, |
|||
'Randomize cells in arrayA, |
|||
'y establecer las de matrizB a 0 |
|||
'and set those of matrixB to 0 |
|||
y = 0 |
|||
DO |
|||
x = 0 |
|||
DO |
|||
x = x + 1 |
|||
matrizA(x, y) = INT(RND + .5) |
|||
matrizB(x, y) = 0 |
|||
LOOP UNTIL x > 80 |
|||
y = y + 1 |
|||
LOOP UNTIL y > 80 |
|||
''--- Bucle Principal --- |
|||
'' --- Main Loop --- |
|||
DO |
|||
CLS |
|||
'Dibuja la matriz |
|||
'Draw the matrix |
|||
y = 0 |
|||
DO |
|||
x = 0 |
|||
DO |
|||
IF matrizA(x, y) = 1 THEN LINE (x, y)-(x + 1, y + 1), 1, BF |
|||
x = x + 1 |
|||
LOOP UNTIL x > 80 |
|||
y = y + 1 |
|||
LOOP UNTIL y > 80 |
|||
'Cuenta el recuento de la celda circundante |
|||
'Counts the count of the surrounding cell |
|||
'Luego aplica la operación a la celda |
|||
'Then apply the operation to the cell |
|||
y = 0 |
|||
DO |
|||
x = 0 |
|||
DO |
|||
'Cuenta las células circundantes |
|||
'Count the surrounding cells |
|||
cuenta = 0 |
|||
IF matrizA(x - 1, y + 1) = 1 THEN cuenta = cuenta + 1 |
|||
IF matrizA(x, y + 1) = 1 THEN cuenta = cuenta + 1 |
|||
IF matrizA(x + 1, y + 1) = 1 THEN cuenta = cuenta + 1 |
|||
IF matrizA(x - 1, y) = 1 THEN cuenta = cuenta + 1 |
|||
IF matrizA(x + 1, y) = 1 THEN cuenta = cuenta + 1 |
|||
IF matrizA(x - 1, y - 1) = 1 THEN cuenta = cuenta + 1 |
|||
IF matrizA(x, y - 1) = 1 THEN cuenta = cuenta + 1 |
|||
IF matrizA(x + 1, y - 1) = 1 THEN cuenta = cuenta + 1 |
|||
'Aplica las operaciones |
|||
'Apply the operations |
|||
'Muerte |
|||
'Death |
|||
IF matrizA(x, y) = 1 THEN |
|||
IF cuenta = 2 OR cuenta = 3 THEN matrizB(x, y) = 1 ELSE matrizB(x, y) = 0 |
|||
END IF |
|||
'Nacimiento |
|||
'Birth |
|||
IF matrizA(x, y) = 0 THEN |
|||
IF cuenta = 3 THEN matrizB(x, y) = 1 ELSE matrizB(x, y) = 0 |
|||
END IF |
|||
x = x + 1 |
|||
LOOP UNTIL x > 80 |
|||
y = y + 1 |
|||
LOOP UNTIL y > 80 |
|||
'Actualiza la matriz con la nueva matriz que hemos calculado. |
|||
'Update the matrix with the new matrix that we have calculated. |
|||
y = 0 |
|||
DO |
|||
x = 0 |
|||
DO |
|||
x = x + 1 |
|||
matrizA(x, y) = matrizB(x, y) |
|||
LOOP UNTIL x > 80 |
|||
y = y + 1 |
|||
LOOP UNTIL y > 80 |
|||
PCOPY 0, 1 |
|||
LOOP WHILE INKEY$ = ""</syntaxhighlight> |
|||
==={{header|Sinclair ZX81 BASIC}}=== |
|||
Requires at least 2k of RAM. Expects to find a square array of "0" and "1" characters, <tt>L$()</tt>, giving the initial configuration. You <i>can</i> build this up by issuing commands in immediate mode and then run the program by entering <code>GOTO 1000</code>, but it's probably easier—assuming you can spare some RAM—to write the setup into the program using line numbers below <tt>1000</tt>. |
|||
The graphics character in lines <tt>1030</tt> to <tt>1060</tt> can be obtained by typing <code>SHIFT</code><code>9</code> then <code>SHIFT</code><code>H</code>, and the one in line <tt>1130</tt> by typing <code>SHIFT</code><code>9</code> then <code>SPACE</code>. |
|||
<syntaxhighlight lang="basic">1000 LET M=LEN L$(1) |
|||
1010 DIM N$(M,M) |
|||
1020 FOR I=0 TO M+1 |
|||
1030 PRINT AT I,0;"▩" |
|||
1040 PRINT AT I,M+1;"▩" |
|||
1050 PRINT AT 0,I;"▩" |
|||
1060 PRINT AT M+1,I;"▩" |
|||
1070 NEXT I |
|||
1080 LET G=0 |
|||
1090 PRINT AT 1,M+3;G |
|||
1100 FOR I=1 TO M |
|||
1110 FOR J=1 TO M |
|||
1120 IF L$(I,J)="0" THEN GOTO 1150 |
|||
1130 PRINT AT I,J;"■" |
|||
1140 GOTO 1160 |
|||
1150 PRINT AT I,J;" " |
|||
1160 NEXT J |
|||
1170 NEXT I |
|||
1180 FOR I=1 TO M |
|||
1190 FOR J=1 TO M |
|||
1200 LET N=0 |
|||
1210 FOR K=I-1 TO I+1 |
|||
1220 FOR L=J-1 TO J+1 |
|||
1230 IF K=0 OR K>M OR L=0 OR L>M OR (K=I AND L=J) THEN GOTO 1250 |
|||
1240 LET N=N+VAL L$(K,L) |
|||
1250 NEXT L |
|||
1260 NEXT K |
|||
1270 LET N$(I,J)=L$(I,J) |
|||
1280 IF N<=1 OR N>=4 THEN LET N$(I,J)="0" |
|||
1290 IF N=3 THEN LET N$(I,J)="1" |
|||
1300 NEXT J |
|||
1310 NEXT I |
|||
1320 FOR I=1 TO M |
|||
1330 LET L$(I)=N$(I) |
|||
1340 NEXT I |
|||
1350 LET G=G+1 |
|||
1360 GOTO 1090</syntaxhighlight> |
|||
To run the blinker, add this code: |
|||
<syntaxhighlight lang="basic">10 DIM L$(3,3) |
|||
20 LET L$(1)="000" |
|||
30 LET L$(2)="111" |
|||
40 LET L$(3)="000"</syntaxhighlight> |
|||
A screenshot of it running can be found [http://www.edmundgriffiths.com/zx81lifeblinker.jpg here]. |
|||
To try a random starting configuration on a 16x16 grid, use this: |
|||
<syntaxhighlight lang="basic">10 DIM L$(16,16) |
|||
20 FOR I=1 TO 16 |
|||
30 FOR J=1 TO 16 |
|||
40 LET L$(I,J)="0" |
|||
50 IF RND>=.7 THEN LET L$(I,J)="1" |
|||
60 NEXT J |
|||
70 NEXT I</syntaxhighlight> |
|||
A screenshot is [http://www.edmundgriffiths.com/zx81liferandom.jpg here]. |
|||
==={{header|TI-83 BASIC}}=== |
|||
This implementation is loosely based on the [http://www.processing.org/learning/topics/conway.html 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. |
|||
<syntaxhighlight lang="ti83b"> 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 |
|||
</syntaxhighlight> |
|||
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). |
|||
<syntaxhighlight lang="ti83b">PROGRAM:PIC2LIFE |
|||
:For(I,0,17,1) |
|||
:For(J,0,9,1) |
|||
:pxl-Test(J,I)→[A](I+1,J+1) |
|||
:End |
|||
:End |
|||
</syntaxhighlight> |
|||
==={{header|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. <!-- I wrote this program, but I am not the inventor of this technique; I saw something like it in the WireWorld system mentioned in http://kpreid.livejournal.com/4424.html . --> |
|||
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. |
|||
<syntaxhighlight lang="ti89b">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</syntaxhighlight> |
|||
=={{header|Batch File}}== |
|||
This code takes three parameters: <code>m chance iterations</code> |
|||
Where,<br> |
|||
m - The length and width of the array of cells<br> |
|||
chance - The percent chance of any cell within the set array initially being alive. Full numbers only.<br> |
|||
iterations - The amount of iterations of evolution the array goes through to display.<br> |
|||
If no parameters are parsed, it defaults to 5 iterations of the blinking example. |
|||
<syntaxhighlight lang="dos"> |
|||
@echo off |
|||
setlocal enabledelayedexpansion |
|||
if "%1"=="" ( |
|||
call:_blinkerArray |
|||
) else ( |
|||
call:_randomArray %* |
|||
) |
|||
for /l %%i in (1,1,%iterations%) do ( |
|||
call:_setStatus |
|||
call:_display |
|||
for /l %%m in (1,1,%m%) do ( |
|||
for /l %%n in (1,1,%m%) do ( |
|||
call:_evolution %%m %%n |
|||
) |
|||
) |
|||
) |
|||
:_blinkerArray |
|||
for /l %%m in (0,1,4) do ( |
|||
for /l %%n in (0,1,4) do ( |
|||
set cell[%%m][%%n]=0 |
|||
) |
|||
) |
|||
set cell[2][1]=1 |
|||
set cell[2][2]=1 |
|||
set cell[2][3]=1 |
|||
set iterations=5 |
|||
set m=3 |
|||
set cellsaddone=4 |
|||
exit /b |
|||
:_randomArray |
|||
set cellsaddone=%1+1 |
|||
for /l %%m in (0,1,%cellsaddone%) do for /l %%n in (0,1,%cellsaddone%) do set cell[%%m][%%n]=0 |
|||
for /l %%m in (1,1,%1) do ( |
|||
for /l %%n in (1,1,%1) do ( |
|||
set /a cellrandom=!random! %% 101 |
|||
set cell[%%m][%%n]=0 |
|||
if !cellrandom! leq %2 set cell[%%m][%%n]=1 |
|||
) |
|||
) |
|||
set iterations=%3 |
|||
set m=%1 |
|||
exit /b |
|||
:_setStatus |
|||
for /l %%m in (0,1,%cellsaddone%) do ( |
|||
for /l %%n in (0,1,%cellsaddone%) do ( |
|||
if !cell[%%m][%%n]!==1 set cellstatus[%%m][%%n]=alive |
|||
if !cell[%%m][%%n]!==0 set cellstatus[%%m][%%n]=dead |
|||
) |
|||
) |
|||
exit /b |
|||
:_evolution |
|||
set /a lowerm=%1-1 |
|||
set /a upperm=%1+1 |
|||
set /a lowern=%2-1 |
|||
set /a uppern=%2+1 |
|||
set numm=%1 |
|||
set numn=%2 |
|||
set sum=0 |
|||
for /l %%m in (%lowerm%,1,%upperm%) do ( |
|||
for /l %%n in (%lowern%,1,%uppern%) do ( |
|||
if %%m==%numm% ( |
|||
if %%n==%numn% ( |
|||
set /a sum=!sum! |
|||
) else ( |
|||
if !cellstatus[%%m][%%n]!==alive set /a sum+=1 |
|||
) |
|||
) else ( |
|||
if !cellstatus[%%m][%%n]!==alive set /a sum+=1 |
|||
) |
|||
) |
|||
) |
|||
goto:!cell[%numm%][%numn%]! |
|||
exit /b |
|||
:0 |
|||
set alive=3 |
|||
set death=0 1 2 4 5 6 7 8 |
|||
for %%i in (%alive%) do if %sum%==%%i set cell[%numm%][%numn%]=1 |
|||
for %%i in (%death%) do if %sum%==%%i set cell[%numm%][%numn%]=0 |
|||
exit /b |
|||
:1 |
|||
set alive=2 3 |
|||
set death=0 1 4 5 6 7 8 |
|||
for %%i in (%alive%) do if %sum%==%%i set cell[%1][%2]=1 |
|||
for %%i in (%death%) do if %sum%==%%i set cell[%1][%2]=0 |
|||
exit /b |
|||
:_display |
|||
echo. |
|||
for /l %%m in (1,1,%m%) do ( |
|||
set m%%m= |
|||
for /l %%n in (1,1,%m%) do set m%%m=!m%%m! !cell[%%m][%%n]! |
|||
echo !m%%m! |
|||
) |
|||
exit /b |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
Blinking example: |
|||
<pre> |
|||
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 |
|||
0 1 0 |
|||
0 1 0 |
|||
0 1 0 |
|||
0 0 0 |
|||
1 1 1 |
|||
0 0 0 |
|||
</pre> |
|||
{{in}} |
|||
<pre> |
|||
10 35 5 |
|||
</pre> |
|||
{{out}} |
|||
<pre> |
|||
1 1 0 0 0 0 1 1 0 0 |
|||
1 1 0 0 1 1 0 1 1 0 |
|||
1 0 1 1 0 0 0 1 0 0 |
|||
0 1 0 1 0 1 0 1 0 1 |
|||
1 0 0 0 1 0 0 1 0 1 |
|||
0 0 0 1 0 0 0 0 0 0 |
|||
0 0 0 0 0 0 1 1 0 0 |
|||
0 0 1 0 0 0 0 0 0 0 |
|||
1 0 1 0 0 0 0 0 1 1 |
|||
1 1 0 0 1 0 0 1 1 0 |
|||
1 1 0 0 0 1 1 1 1 0 |
|||
0 0 0 1 1 1 0 0 1 0 |
|||
1 0 0 1 0 1 0 1 0 0 |
|||
1 1 0 1 0 0 0 1 0 0 |
|||
0 0 1 1 1 0 1 0 0 0 |
|||
0 0 0 0 0 0 1 1 1 0 |
|||
0 0 0 0 0 0 0 0 0 0 |
|||
0 1 0 0 0 0 0 1 1 0 |
|||
1 0 1 1 0 0 0 1 1 1 |
|||
1 1 0 0 0 0 0 1 1 1 |
|||
0 0 0 0 0 1 1 1 1 0 |
|||
1 1 1 1 0 0 0 0 1 0 |
|||
1 1 0 1 0 1 0 1 1 0 |
|||
1 1 0 0 0 1 0 1 0 0 |
|||
0 1 1 1 1 1 1 0 1 0 |
|||
0 0 0 1 0 1 1 1 0 0 |
|||
0 0 0 0 0 0 1 0 0 0 |
|||
0 1 1 0 0 0 0 1 0 1 |
|||
1 0 1 0 0 0 1 0 0 0 |
|||
1 1 1 0 0 0 0 1 0 1 |
|||
0 1 1 0 0 0 1 1 1 0 |
|||
1 0 0 1 0 1 0 0 0 1 |
|||
0 0 0 1 0 0 0 1 1 0 |
|||
0 0 0 0 0 0 0 0 0 0 |
|||
1 1 0 1 0 0 0 0 1 0 |
|||
0 0 0 1 0 0 0 0 0 0 |
|||
0 0 1 0 0 1 0 0 1 0 |
|||
0 1 1 0 0 0 1 1 0 0 |
|||
1 0 0 1 0 0 1 1 0 0 |
|||
1 0 1 0 0 0 0 0 0 0 |
|||
0 1 1 0 0 0 1 1 1 0 |
|||
0 1 0 1 1 0 0 0 0 1 |
|||
0 0 0 0 1 0 0 0 1 0 |
|||
0 0 1 0 0 0 0 1 1 0 |
|||
0 0 1 0 0 0 0 0 0 0 |
|||
0 1 0 1 1 0 0 0 0 0 |
|||
0 1 1 1 0 0 1 1 0 0 |
|||
0 1 1 1 0 1 0 0 1 0 |
|||
1 0 0 1 0 0 1 1 0 0 |
|||
0 1 0 0 0 0 0 0 0 0 |
|||
</pre> |
|||
=={{header|Befunge}}== |
|||
Takes as input the width and height of the universe, followed by the pattern (which is terminated by the end of file). If your interpreter can't easily redirect the input from a file, or doesn't handle end-of-file detection, you can also type in the pattern manually and mark the end of input with a <kbd>~</kbd> character. |
|||
The pattern format itself is fairly lenient in what it accepts. You can use either space or <kbd>.</kbd> for dead cells, and <kbd>o</kbd>, <kbd>O</kbd>, <kbd>*</kbd> or <kbd>#</kbd> for live cells. This should make it fairly easy to cut and paste a number of existing formats, including the [http://www.conwaylife.com/wiki/Life_1.05 Life 1.05 format] and the [http://www.conwaylife.com/wiki/Plaintext Plaintext .cells format] used on the [http://www.conwaylife.com/wiki/ LifeWiki] website (comments aren't supported though, so make sure to copy just the pattern itself). |
|||
In Befunge-93, the maximum value for the width and height of the universe is 127, but there is an additional constraint of 4080 cells in total, so the largest universe would really be something like 120x34 or 68x60. Befunge-98 has no real limit on the size, although in practice a much larger universe will probably be unbearably slow. |
|||
<syntaxhighlight lang="befunge">00p10p20p30p&>40p&>50p60p>$#v~>:55+-vv+`1:%3:+*g04p03< >3/"P"%\56v>p\56*8*/8+:v |
|||
v5\`\"~"::-*3p06!:!-+67:_^#!<*<!g06!<>1+70g*\:3/"P"%v^ ^::+*g04%<*0v`1:%3\gp08< |
|||
>6*`*#v_55+-#v_p10g1+10p>^pg08g07+gp08:+8/*8*65\p07:<^ >/10g-50g^87>+1+:01p/8/v |
|||
>%#74#<-!!70p 00g::1+00p:20g\-:0`*+20p10g::30g\-:0`*+^ ^2+2+g03*<*:v+g06p09:%2< |
|||
.v,:*93"[2J"0<>"H["39*,,,50g0v!:-1,+55$_:40g3*20g+2+2/\-40g%50g3^/%\ >:3-\3-90v |
|||
O>"l52?[">:#,_^v/3+2:*g05g04$_>:10p40g0^!:-1,g+4\0%2/+1+`1:%3\g+8<^: $v10!*-g<< |
|||
g+70g80gp:#v_$^>1-:::"P"%\"P"/8+:10v >/10g+1-50g+50g%40g*+::3/"P"^>!|>g*70g80g |
|||
:p00%g04:-1<<$_^#!:pg01%"P"\*8%8gp<< ^3\%g04+g04-1+g00%3:%9+4:-1p06\<90p01/g04</syntaxhighlight> |
|||
{{in}} |
|||
Here's an example of what the input could look like for the [http://www.conwaylife.com/wiki/Blinker Blinker pattern] in a 5x5 universe: |
|||
<pre>5 |
|||
5 |
|||
OOO</pre> |
|||
And for a more complicated example, this is the [http://www.conwaylife.com/wiki/Queen_bee Queen bee pattern] in a 50x30 universe: |
|||
<pre>50 |
|||
30 |
|||
...* |
|||
..*.* |
|||
.*...* |
|||
..*** |
|||
**...**</pre> |
|||
{{out}} |
|||
In order to produce an animated view of the universe evolving, we use a few basic ANSI escape sequences to reset the cursor position between frames. Without ANSI support, you'll just see the individual frames scrolling past with a bit of junk inbetween. The output shown below is just an extract of the first three generations of the Blinker in a 5x5 universe. |
|||
<pre>..... ..... ..... |
|||
..... ..O.. ..... |
|||
.OOO. ..O.. .OOO. |
|||
..... ..O.. ..... |
|||
..... ..... .....</pre> |
|||
=={{header|Brainf***}}== |
|||
A life-program written in [http://www.df.lth.se/~lft/brainfuck Brainf***] |
|||
With [http://www.linusakesson.net/programming/brainfuck/output.txt Example-Output]. |
|||
=={{header|Brat}}== |
|||
<syntaxhighlight lang="brat">width = 3 |
|||
height = 3 |
|||
rounds = 3 |
|||
universe = [[0 1 0] |
|||
[0 1 0] |
|||
[0 1 0]] |
|||
next = height.of({width.of(0)}) |
|||
cell = { x, y | |
|||
true? x < width && { x >= 0 && { y >= 0 && { y < height }}} |
|||
{ |
|||
universe[y][x] |
|||
} |
|||
{ 0 } |
|||
} |
|||
neighbors = { x, y | |
|||
cell(x - 1, y - 1) + |
|||
cell(x, y - 1) + |
|||
cell(x + 1, y - 1) + |
|||
cell(x + 1, y) + |
|||
cell(x + 1, y + 1) + |
|||
cell(x, y + 1) + |
|||
cell(x - 1, y + 1) + |
|||
cell(x - 1, y) |
|||
} |
|||
set_next = { x, y, v | |
|||
next[y][x] = v |
|||
} |
|||
step = { |
|||
universe.each_with_index { row, y | |
|||
row.each_with_index { c, x | |
|||
n = neighbors(x, y) |
|||
when { n < 2 } { set_next x,y, 0 } |
|||
{ n > 3 } { set_next x, y, 0 } |
|||
{ n == 3 } { set_next x, y, 1 } |
|||
{ true } { set_next x, y, c } |
|||
} |
|||
} |
|||
u2 = universe |
|||
universe = next |
|||
next = u2 |
|||
} |
|||
display = { |
|||
p universe.map({ r | |
|||
r.map({ n | true? n == 0, '-', "O" }).join |
|||
}).join("\n") |
|||
} |
|||
rounds.times { |
|||
display |
|||
p |
|||
step |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
-O- |
|||
-O- |
|||
-O- |
|||
--- |
|||
OOO |
|||
--- |
|||
-O- |
|||
-O- |
|||
-O- |
|||
</pre> |
|||
=={{header|BQN}}== |
|||
<syntaxhighlight lang="bqn">Life←{ |
|||
r←¯1(⌽⎉1)¯1⌽(2+≢𝕩)↑𝕩 |
|||
s←∨´ (1∾<r) ∧ 3‿4 = <+´⥊ ¯1‿0‿1 (⌽⎉1)⌜ ¯1‿0‿1 ⌽⌜ <r |
|||
1(↓⎉1) ¯1(↓⎉1) 1↓ ¯1↓s |
|||
} |
|||
blinker←>⟨0‿0‿0,1‿1‿1,0‿0‿0⟩ |
|||
(<".#") ⊏¨˜ Life⍟(↕3) blinker</syntaxhighlight> |
|||
{{out}} |
|||
<pre>┌─ |
|||
· ┌─ ┌─ ┌─ |
|||
╵"... ╵".#. ╵"... |
|||
### .#. ### |
|||
..." .#." ..." |
|||
┘ ┘ ┘ |
|||
┘</pre> |
|||
=={{header|C}}== |
=={{header|C}}== |
||
Play game of life on your console: <code>gcc -std=c99 -Wall game.c; ./a.out [width] [height]</code> |
|||
See [[Conway's Game of Life/C]] |
|||
<syntaxhighlight lang="c">#include <stdio.h> |
|||
#include <stdlib.h> |
|||
#include <unistd.h> |
|||
#define for_x for (int x = 0; x < w; x++) |
|||
=={{header|Clojure}}== |
|||
#define for_y for (int y = 0; y < h; y++) |
|||
In keeping with idiomatic Clojure, the solution is implemented as discrete, composable functions and datastructures rather than one big blob of code. |
|||
#define for_xy for_x for_y |
|||
<lang lisp>(defstruct grid :w :h :cells) |
|||
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) |
|||
(defn get-cell |
|||
{ |
|||
"Returns the value at x,y. The grid is treated as a torus, such that both x and |
|||
unsigned (*univ)[w] = u; |
|||
y coordinates will wrap around if greater than width and height respectively." |
|||
unsigned new[h][w]; |
|||
[grid x y] |
|||
(let [x (mod x (:w grid)) |
|||
y (mod y (:h grid))] |
|||
(-> grid :cells (nth y) (nth x)))) |
|||
for_y for_x { |
|||
(defn neighbors |
|||
int n = 0; |
|||
"Returns a lazy sequence of all neighbors of the specified cell." |
|||
for (int y1 = y - 1; y1 <= y + 1; y1++) |
|||
[grid x y] |
|||
for (int x1 = x - 1; x1 <= x + 1; x1++) |
|||
(for [j [(dec y) y (inc y)] |
|||
if (univ[(y1 + h) % h][(x1 + w) % w]) |
|||
n++; |
|||
:when (not (and (= i x) (= j y)))] |
|||
(get-cell grid i j))) |
|||
if (univ[y][x]) n--; |
|||
(defn evolve-cell |
|||
new[y][x] = (n == 3 || (n == 2 && univ[y][x])); |
|||
"Returns the new state of the specifed cell." |
|||
} |
|||
[grid x y] |
|||
for_y for_x univ[y][x] = new[y][x]; |
|||
(let [c (get-cell grid x y) |
|||
} |
|||
n (reduce + (neighbors grid x y))] |
|||
(if (or (and (zero? c) (= 3 n)) |
|||
(and (= 1 c) (or (= 2 n) (= 3 n)))) |
|||
1 0))) |
|||
void game(int w, int h) |
|||
(defn evolve-grid |
|||
{ |
|||
"Returns a new grid whose cells have all been evolved." |
|||
unsigned univ[h][w]; |
|||
[grid] |
|||
for_xy univ[y][x] = rand() < RAND_MAX / 10 ? 1 : 0; |
|||
(assoc grid :cells |
|||
while (1) { |
|||
(vec (for [y (range (:h grid))] |
|||
show(univ, w, h); |
|||
(vec (for [x (range (:w grid))] |
|||
evolve(univ, w, h); |
|||
(evolve-cell grid x y))))))) |
|||
usleep(200000); |
|||
} |
|||
} |
|||
int main(int c, char **v) |
|||
(defn generations [grid] |
|||
{ |
|||
"Returns a lazy sequence of the grid, and all subsequent generations." |
|||
int w = 0, h = 0; |
|||
(iterate evolve-grid grid))</lang> |
|||
if (c > 1) w = atoi(v[1]); |
|||
The above does the work of creating subsequent generations of an initial grid. Now we add in some functions to create and display the grids: |
|||
if (c > 2) h = atoi(v[2]); |
|||
<lang lisp>(defn make-grid [w h & row-patterns] |
|||
if (w <= 0) w = 30; |
|||
(let [cells (vec (for [rp row-patterns] |
|||
if (h <= 0) h = 30; |
|||
(vec (mapcat #(take %1 (repeat %2)) rp (cycle [0 1])))))] |
|||
game(w, h); |
|||
(if (and (= h (count cells)) |
|||
}</syntaxhighlight> |
|||
(every? #(= w (count %)) cells)) |
|||
Also see [[Conway's Game of Life/C]] |
|||
(struct grid w h cells) |
|||
(throw (IllegalArgumentException. "Resulting cells do not match expected width/height."))))) |
|||
===C for Arduino=== |
|||
(defn display-row [row] |
|||
Play game of life on your arduino (using FastLED) - based on the C example. |
|||
(do (dorun (map print (map #(if (zero? %) " . " "[X]") row))) (println))) |
|||
<syntaxhighlight lang="c"> |
|||
#include <FastLED.h> |
|||
#define LED_PIN 3 |
|||
(defn display-grid [grid] |
|||
#define LED_TYPE WS2812B |
|||
(dorun (map display-row (:cells grid)))) |
|||
#define WIDTH 20 |
|||
#define HEIGHT 15 |
|||
#define NUM_LEDS (WIDTH*HEIGHT) |
|||
#define BRIGHTNESS 100 |
|||
#define COLOR_ORDER GRB |
|||
#define SERPENTINE 1 |
|||
#define FRAMERATE 1 |
|||
#define SCALE 1 |
|||
CRGB leds[NUM_LEDS]; |
|||
(defn display-grids [grids] |
|||
(dorun |
|||
(interleave |
|||
(repeatedly println) |
|||
(map display-grid grids))))</lang> |
|||
Thus, running: |
|||
<lang lisp>(def blinker (make-grid 5 5 [5] [5] [1 3 1] [5] [5])) |
|||
(display-grids (take 3 (generations blinker)))</lang> |
|||
Outputs: |
|||
<pre style="height:15ex;overflow:scroll"> |
|||
. . . . . |
|||
. . . . . |
|||
. [X][X][X] . |
|||
. . . . . |
|||
. . . . . |
|||
#include <stdio.h> |
|||
. . . . . |
|||
#include <stdlib.h> |
|||
. . [X] . . |
|||
#include <unistd.h> |
|||
. . [X] . . |
|||
. . [X] . . |
|||
. . . . . |
|||
#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 |
|||
. [X][X][X] . |
|||
. . . . . |
|||
const int w = WIDTH, h = HEIGHT; |
|||
. . . . . |
|||
unsigned univ[h][w]; |
|||
int getLEDpos(int x, int y){ // for a serpentine raster |
|||
return (y%2 || !SERPENTINE) ? y*WIDTH + x : y*WIDTH + (WIDTH - 1 - x); |
|||
} |
|||
void show(void *u, int w, int h) |
|||
{ |
|||
int (*univ)[w] = u; |
|||
for_xy { |
|||
leds[getLEDpos(x, y)] = univ[y][x] ? CRGB::White: CRGB::Black; |
|||
} |
|||
FastLED.show(); |
|||
} |
|||
void evolve(void *u, int w, int h) |
|||
{ |
|||
unsigned (*univ)[w] = u; |
|||
unsigned newU[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--; |
|||
newU[y][x] = (n == 3 || (n == 2 && univ[y][x])); |
|||
} |
|||
for_y for_x univ[y][x] = newU[y][x]; |
|||
} |
|||
void setup(){ |
|||
//Initialize leds after safety period |
|||
FastLED.delay(500); |
|||
FastLED.addLeds<LED_TYPE, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection( TypicalLEDStrip ); |
|||
FastLED.setBrightness( BRIGHTNESS ); |
|||
//Seed random with analog noise |
|||
randomSeed(analogRead(0)); |
|||
for_xy univ[y][x] = random() %10 <= 1 ? 1 : 0; |
|||
} |
|||
void loop(){ |
|||
show(univ, w, h); |
|||
evolve(univ, w, h); |
|||
FastLED.delay(1000/FRAMERATE); |
|||
} |
|||
</syntaxhighlight> |
|||
===C for Arduino=== |
|||
Play game of life on your arduino (using two MAX7219 led 'screens') - based on the C example. |
|||
<syntaxhighlight lang="c"> |
|||
#include <MaxMatrix.h> |
|||
int DIN = 11; // DIN pin of MAX7219 module |
|||
int CS = 12; // CS pin of MAX7219 module |
|||
int CLK = 13; // CLK pin of MAX7219 module |
|||
int DIN2 = 8; // DIN pin of MAX7219 module |
|||
int CS2 = 9; // CS pin of MAX7219 module |
|||
int CLK2 = 10; // CLK pin of MAX7219 module |
|||
int maxInUse = 1; |
|||
//setup two screens |
|||
MaxMatrix m(DIN, CS, CLK, maxInUse); |
|||
MaxMatrix m2(DIN2, CS2, CLK2, maxInUse); |
|||
void setup() { |
|||
randomSeed(analogRead(0)); |
|||
m.init(); // MAX7219 initialization |
|||
m.setIntensity(0); // initial led matrix intensity, 0-15 |
|||
m.clear(); // Clears the display |
|||
m2.init(); // MAX7219 initialization |
|||
m2.setIntensity(0); // initial led matrix intensity, 0-15 |
|||
m2.clear(); // Clears the display |
|||
} |
|||
void loop() { |
|||
game(16,8);//w,h |
|||
} |
|||
void setDot(int x,int y,bool isOn){ |
|||
if(x<8){ |
|||
m.setDot(x,y,isOn); |
|||
}else{ |
|||
m2.setDot(x-8,y,isOn); |
|||
} |
|||
} |
|||
void show(void *u, int w, int h){ |
|||
int (*univ)[w] = u; |
|||
for (int y = 0; y < h; y++){ |
|||
for (int x = 0; x < w; x++){ |
|||
bool sh=(univ[y][x]==1); |
|||
setDot(x,y,sh); |
|||
} |
|||
} |
|||
} |
|||
void evolve(void *u, int w, int h){ |
|||
unsigned (*univ)[w] = u; |
|||
unsigned newar[h][w]; |
|||
for (int y = 0; y < h; y++){ |
|||
for (int x = 0; x < w; 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--; |
|||
newar[y][x] = (n == 3 || (n == 2 && univ[y][x])); |
|||
} |
|||
} |
|||
for (int y = 0; y < h; y++){ |
|||
for (int x = 0; x < w; x++){ |
|||
univ[y][x] = newar[y][x]; |
|||
} |
|||
} |
|||
} |
|||
void game(int w, int h) { |
|||
unsigned univ[h][w]; |
|||
for (int x = 0; x < w; x++){ |
|||
for (int y = 0; y < h; y++){ |
|||
univ[y][x] = random(0, 100)>65 ? 1 : 0; |
|||
} |
|||
} |
|||
int sc=0; |
|||
while (1) { |
|||
show(univ, w, h); |
|||
evolve(univ, w, h); |
|||
delay(150); |
|||
sc++;if(sc>150)break; |
|||
} |
|||
} |
|||
</syntaxhighlight> |
|||
=={{header|C sharp|C#}}== |
|||
<syntaxhighlight lang="csharp"> |
|||
using System; |
|||
using System.Text; |
|||
using System.Threading; |
|||
namespace ConwaysGameOfLife |
|||
{ |
|||
// Plays Conway's Game of Life on the console with a random initial state. |
|||
class Program |
|||
{ |
|||
// The delay in milliseconds between board updates. |
|||
private const int DELAY = 50; |
|||
// The cell colors. |
|||
private const ConsoleColor DEAD_COLOR = ConsoleColor.White; |
|||
private const ConsoleColor LIVE_COLOR = ConsoleColor.Black; |
|||
// The color of the cells that are off of the board. |
|||
private const ConsoleColor EXTRA_COLOR = ConsoleColor.Gray; |
|||
private const char EMPTY_BLOCK_CHAR = ' '; |
|||
private const char FULL_BLOCK_CHAR = '\u2588'; |
|||
// Holds the current state of the board. |
|||
private static bool[,] board; |
|||
// The dimensions of the board in cells. |
|||
private static int width = 32; |
|||
private static int height = 32; |
|||
// True if cell rules can loop around edges. |
|||
private static bool loopEdges = true; |
|||
static void Main(string[] args) |
|||
{ |
|||
// Use initializeRandomBoard for a larger, random board. |
|||
initializeDemoBoard(); |
|||
initializeConsole(); |
|||
// Run the game until the Escape key is pressed. |
|||
while (!Console.KeyAvailable || Console.ReadKey(true).Key != ConsoleKey.Escape) { |
|||
Program.drawBoard(); |
|||
Program.updateBoard(); |
|||
// Wait for a bit between updates. |
|||
Thread.Sleep(DELAY); |
|||
} |
|||
} |
|||
// Sets up the Console. |
|||
private static void initializeConsole() |
|||
{ |
|||
Console.BackgroundColor = EXTRA_COLOR; |
|||
Console.Clear(); |
|||
Console.CursorVisible = false; |
|||
// Each cell is two characters wide. |
|||
// Using an extra row on the bottom to prevent scrolling when drawing the board. |
|||
int width = Math.Max(Program.width, 8) * 2 + 1; |
|||
int height = Math.Max(Program.height, 8) + 1; |
|||
Console.SetWindowSize(width, height); |
|||
Console.SetBufferSize(width, height); |
|||
Console.BackgroundColor = DEAD_COLOR; |
|||
Console.ForegroundColor = LIVE_COLOR; |
|||
} |
|||
// Creates the initial board with a random state. |
|||
private static void initializeRandomBoard() |
|||
{ |
|||
var random = new Random(); |
|||
Program.board = new bool[Program.width, Program.height]; |
|||
for (var y = 0; y < Program.height; y++) { |
|||
for (var x = 0; x < Program.width; x++) { |
|||
// Equal probability of being true or false. |
|||
Program.board[x, y] = random.Next(2) == 0; |
|||
} |
|||
} |
|||
} |
|||
// Creates a 3x3 board with a blinker. |
|||
private static void initializeDemoBoard() |
|||
{ |
|||
Program.width = 3; |
|||
Program.height = 3; |
|||
Program.loopEdges = false; |
|||
Program.board = new bool[3, 3]; |
|||
Program.board[1, 0] = true; |
|||
Program.board[1, 1] = true; |
|||
Program.board[1, 2] = true; |
|||
} |
|||
// Draws the board to the console. |
|||
private static void drawBoard() |
|||
{ |
|||
// One Console.Write call is much faster than writing each cell individually. |
|||
var builder = new StringBuilder(); |
|||
for (var y = 0; y < Program.height; y++) { |
|||
for (var x = 0; x < Program.width; x++) { |
|||
char c = Program.board[x, y] ? FULL_BLOCK_CHAR : EMPTY_BLOCK_CHAR; |
|||
// Each cell is two characters wide. |
|||
builder.Append(c); |
|||
builder.Append(c); |
|||
} |
|||
builder.Append('\n'); |
|||
} |
|||
// Write the string to the console. |
|||
Console.SetCursorPosition(0, 0); |
|||
Console.Write (builder.ToString()); |
|||
} |
|||
// Moves the board to the next state based on Conway's rules. |
|||
private static void updateBoard() |
|||
{ |
|||
// A temp variable to hold the next state while it's being calculated. |
|||
bool[,] newBoard = new bool[Program.width, Program.height]; |
|||
for (var y = 0; y < Program.height; y++) { |
|||
for (var x = 0; x < Program.width; x++) { |
|||
var n = countLiveNeighbors(x, y); |
|||
var c = Program.board[x, y]; |
|||
// A live cell dies unless it has exactly 2 or 3 live neighbors. |
|||
// A dead cell remains dead unless it has exactly 3 live neighbors. |
|||
newBoard[x, y] = c && (n == 2 || n == 3) || !c && n == 3; |
|||
} |
|||
} |
|||
// Set the board to its new state. |
|||
Program.board = newBoard; |
|||
} |
|||
// Returns the number of live neighbors around the cell at position (x,y). |
|||
private static int countLiveNeighbors(int x, int y) |
|||
{ |
|||
// The number of live neighbors. |
|||
int value = 0; |
|||
// This nested loop enumerates the 9 cells in the specified cells neighborhood. |
|||
for (var j = -1; j <= 1; j++) { |
|||
// If loopEdges is set to false and y+j is off the board, continue. |
|||
if (!Program.loopEdges && y + j < 0 || y + j >= Program.height) { |
|||
continue; |
|||
} |
|||
// Loop around the edges if y+j is off the board. |
|||
int k = (y + j + Program.height) % Program.height; |
|||
for (var i = -1; i <= 1; i++) { |
|||
// If loopEdges is set to false and x+i is off the board, continue. |
|||
if (!Program.loopEdges && x + i < 0 || x + i >= Program.width) { |
|||
continue; |
|||
} |
|||
// Loop around the edges if x+i is off the board. |
|||
int h = (x + i + Program.width) % Program.width; |
|||
// Count the neighbor cell at (h,k) if it is alive. |
|||
value += Program.board[h, k] ? 1 : 0; |
|||
} |
|||
} |
|||
// Subtract 1 if (x,y) is alive since we counted it as a neighbor. |
|||
return value - (Program.board[x, y] ? 1 : 0); |
|||
} |
|||
} |
|||
} |
|||
</syntaxhighlight> |
|||
Output: |
|||
<syntaxhighlight lang="text"> |
|||
Frame 1: Frame 2: Frame 3: |
|||
██ |
|||
██████ ██ ██████ |
|||
██ |
|||
</syntaxhighlight> |
|||
=={{header|C++}}== |
|||
Because Nobody else included a version with Graphics, Here is a simple implementation using SFML for graphic rendering |
|||
<syntaxhighlight lang="c"> |
|||
#include <iostream> |
|||
#include <vector> |
|||
#include <SFML/Graphics.hpp> |
|||
#include <thread> |
|||
#include <chrono> |
|||
using namespace std; |
|||
class Life { |
|||
private: |
|||
int ticks; |
|||
bool pass; |
|||
int height; |
|||
int width; |
|||
bool **board; |
|||
bool **buffer; |
|||
vector<pair<float,float>> liveCoords; |
|||
void init(); |
|||
void lives(int x, int y); |
|||
void dies(int x, int y); |
|||
bool isAlive(bool **curr, int x, int y); |
|||
int checkNeighbors(bool **curr, int x, int y); |
|||
void evaluatePosition(bool** curr, int x, int y); |
|||
public: |
|||
Life(int w = 100, int h = 50, int seed = 1337); |
|||
Life(const Life& life); |
|||
~Life(); |
|||
vector<pair<float,float>> doTick(); |
|||
Life& operator=(const Life& life); |
|||
}; |
|||
void Life::init() { |
|||
board = new bool*[height]; |
|||
buffer = new bool*[height]; |
|||
for (int y = 0; y < height; y++) { |
|||
board[y] = new bool[width]; |
|||
buffer[y] = new bool[width]; |
|||
for (int x = 0; x < width; x++) { |
|||
board[y][x] = false; |
|||
buffer[y][x] = false; |
|||
} |
|||
} |
|||
} |
|||
Life::Life(int w, int h, int seed) { |
|||
width = w; |
|||
height = h; |
|||
init(); |
|||
for (int i = 0; i < seed; i++) { |
|||
board[rand() % height][rand() % width] = true; |
|||
} |
|||
pass = true; |
|||
} |
|||
Life::Life(const Life& life) { |
|||
width = life.width; |
|||
height = life.height; |
|||
init(); |
|||
for (int y = 0; y < height; y++) { |
|||
for (int x = 0; x < width; x++) { |
|||
board[y][x] = life.board[y][x]; |
|||
buffer[y][x] = life.buffer[y][x]; |
|||
} |
|||
} |
|||
} |
|||
Life& Life::operator=(const Life& life) { |
|||
width = life.width; |
|||
height = life.height; |
|||
init(); |
|||
for (int y = 0; y < height; y++) { |
|||
for (int x = 0; x < width; x++) { |
|||
board[y][x] = life.board[y][x]; |
|||
buffer[y][x] = life.buffer[y][x]; |
|||
} |
|||
} |
|||
return *this; |
|||
} |
|||
Life::~Life() { |
|||
for (int i = 0; i < height; i++) { |
|||
delete [] board[i]; |
|||
delete [] buffer[i]; |
|||
} |
|||
delete [] board; |
|||
delete [] buffer; |
|||
} |
|||
vector<pair<float,float>> Life::doTick() { |
|||
liveCoords.clear(); |
|||
bool **currentGeneration = pass ? board:buffer; |
|||
for (int y = 0; y < height; y++) { |
|||
for (int x = 0; x < width; x++) { |
|||
evaluatePosition(currentGeneration, x, y); |
|||
} |
|||
} |
|||
pass = !pass; |
|||
ticks++; |
|||
return liveCoords; |
|||
} |
|||
bool Life::isAlive(bool **curr, int x, int y) { |
|||
return curr[y][x]; |
|||
} |
|||
int Life::checkNeighbors(bool **curr, int x, int y) { |
|||
int lc = 0; |
|||
int dx[8] = {-1, 0, 1,1,1,-1, 0,-1}; |
|||
int dy[8] = {-1,-1,-1,0,1, 1, 1, 0}; |
|||
for (int i = 0; i < 8; i++) { |
|||
int nx = ((dx[i]+x)+width) % width; |
|||
int ny = ((dy[i]+y)+height) % height; |
|||
lc += isAlive(curr, nx, ny); |
|||
} |
|||
return lc; |
|||
} |
|||
void Life::lives(int x, int y) { |
|||
if (!pass) { |
|||
board[y][x] = true; |
|||
} else { |
|||
buffer[y][x] = true; |
|||
} |
|||
liveCoords.push_back(make_pair((float)x,(float)y)); |
|||
} |
|||
void Life::dies(int x, int y) { |
|||
if (!pass) { |
|||
board[y][x] = false; |
|||
} else { |
|||
buffer[y][x] = false; |
|||
} |
|||
} |
|||
void Life::evaluatePosition(bool** generation, int x, int y) { |
|||
int lc = checkNeighbors(generation, x, y); |
|||
if (isAlive(generation, x, y)) { |
|||
if (lc == 2 || lc == 3) { |
|||
lives(x, y); |
|||
} else { |
|||
dies(x, y); |
|||
} |
|||
} else { |
|||
if (lc == 3) { |
|||
lives(x, y); |
|||
} else { |
|||
dies(x, y); |
|||
} |
|||
} |
|||
} |
|||
class App { |
|||
private: |
|||
void sleep(); |
|||
void drawLiveCells(); |
|||
void render(); |
|||
void handleEvent(sf::Event& event); |
|||
void saveDisplay(); |
|||
int width; |
|||
int height; |
|||
Life life; |
|||
bool isRecording; |
|||
int tick; |
|||
sf::RenderWindow* window; |
|||
sf::RenderTexture* texture; |
|||
public: |
|||
App(int w = 100, int h = 50); |
|||
void start(); |
|||
}; |
|||
App::App(int w, int h) { |
|||
height = h; |
|||
width = w; |
|||
life = Life(width, height); |
|||
isRecording = false; |
|||
tick = 0; |
|||
} |
|||
void App::start() { |
|||
sf::Event event; |
|||
window = new sf::RenderWindow(sf::VideoMode(width*10, height*10), "The Game of Life"); |
|||
texture = new sf::RenderTexture(); |
|||
texture->create(width*10, height*10); |
|||
window->setFramerateLimit(60); |
|||
while (window->isOpen()) { |
|||
while (window->pollEvent(event)) { |
|||
handleEvent(event); |
|||
} |
|||
render(); |
|||
tick++; |
|||
} |
|||
delete window; |
|||
delete texture; |
|||
} |
|||
void App::handleEvent(sf::Event& event) { |
|||
if (event.type == sf::Event::Closed) { |
|||
window->close(); |
|||
} |
|||
if (event.type == sf::Event::KeyPressed) { |
|||
switch (event.key.code) { |
|||
case sf::Keyboard::R: |
|||
life = Life(width, height); |
|||
break; |
|||
case sf::Keyboard::S: |
|||
isRecording = !isRecording; |
|||
break; |
|||
case sf::Keyboard::Q: |
|||
case sf::Keyboard::Escape: |
|||
window->close(); |
|||
break; |
|||
default: |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
void App::sleep() { |
|||
std::this_thread::sleep_for(350ms); |
|||
} |
|||
void App::drawLiveCells() { |
|||
float XSCALE = 10.0, YSCALE = 10.0; |
|||
sf::RectangleShape rect; |
|||
rect.setSize(sf::Vector2f(XSCALE, YSCALE)); |
|||
rect.setFillColor(sf::Color::Green); |
|||
auto coords = life.doTick(); |
|||
texture->clear(sf::Color::Black); |
|||
for (auto m : coords) { |
|||
rect.setPosition(m.first*XSCALE, m.second*YSCALE); |
|||
texture->draw(rect); |
|||
} |
|||
texture->display(); |
|||
} |
|||
void App::render() { |
|||
drawLiveCells(); |
|||
window->clear(); |
|||
sf::Sprite sprite(texture->getTexture()); |
|||
window->draw(sprite); |
|||
window->display(); |
|||
if (isRecording) saveDisplay(); |
|||
sleep(); |
|||
} |
|||
void App::saveDisplay() { |
|||
string name = "tick" + to_string(tick) + ".png"; |
|||
sf::Image image = texture->getTexture().copyToImage(); |
|||
image.saveToFile(name); |
|||
} |
|||
int main(int argc, char* argv[]) { |
|||
srand(time(0)); |
|||
App app; |
|||
app.start(); |
|||
return 0; |
|||
} |
|||
</syntaxhighlight> |
|||
And the output of the above program: |
|||
[[File:Game of Life.gif|thumb|Life]] |
|||
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. |
|||
<syntaxhighlight lang="c">#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); |
|||
} |
|||
</syntaxhighlight> |
|||
{{out}} first a glider, then a blinker, over a few iterations |
|||
(reformatted for convenience). |
|||
<pre> |
|||
.X.. .... .... .... .... |
|||
..X. X.X. ..X. .X.. ..X. |
|||
XXX. .XX. X.X. ..XX ...X |
|||
.... .X.. .XX. .XX. .XXX |
|||
==== ==== ==== ==== ==== |
|||
.X.. .... .X.. |
|||
.X.. XXX. .X.. |
|||
.X.. .... .X.. |
|||
.... .... .... |
|||
==== ==== ==== |
|||
</pre> |
</pre> |
||
Similarly we can simply jump to a particular generation: |
|||
<lang lisp>(def figure-eight (make-grid 10 10 [10] [10] [2 3 5] [2 3 5] [2 3 5] [5 3 2] [5 3 2] [5 3 2] [10] [10])) |
|||
(display-grid figure-eight) |
|||
(display-grid (nth (generations figure-eight) 7))</lang> |
|||
Outputs: |
|||
<pre style="height:15ex;overflow:scroll"> |
|||
. . . . . . . . . . |
|||
. . . . . . . . . . |
|||
. . [X][X][X] . . . . . |
|||
. . [X][X][X] . . . . . |
|||
. . [X][X][X] . . . . . |
|||
. . . . . [X][X][X] . . |
|||
. . . . . [X][X][X] . . |
|||
. . . . . [X][X][X] . . |
|||
. . . . . . . . . . |
|||
. . . . . . . . . . |
|||
=== Alternate version === |
|||
. . . . . . . . . . |
|||
Another aproach - a pretty simple one.<br /> |
|||
. . . . . . . . . . |
|||
This version allows you to start the automata with different set of rules. Just for the fun of it. |
|||
. . [X][X] . . . . . . |
|||
<syntaxhighlight lang="cpp"> |
|||
. . [X][X] . [X] . . . . |
|||
#include <algorithm> |
|||
. . . . . . [X] . . . |
|||
#include <vector> |
|||
. . . [X] . . . . . . |
|||
#include <iostream> |
|||
. . . . [X] . [X][X] . . |
|||
#include <string> |
|||
. . . . . . [X][X] . . |
|||
. . . . . . . . . . |
|||
typedef unsigned char byte; |
|||
. . . . . . . . . . |
|||
class world { |
|||
public: |
|||
world( int x, int y ) : _wid( x ), _hei( y ) { |
|||
int s = _wid * _hei * sizeof( byte ); |
|||
_cells = new byte[s]; |
|||
memset( _cells, 0, s ); |
|||
} |
|||
~world() { |
|||
delete [] _cells; |
|||
} |
|||
int wid() const { |
|||
return _wid; |
|||
} |
|||
int hei() const { |
|||
return _hei; |
|||
} |
|||
byte at( int x, int y ) const { |
|||
return _cells[x + y * _wid]; |
|||
} |
|||
void set( int x, int y, byte c ) { |
|||
_cells[x + y * _wid] = c; |
|||
} |
|||
void swap( world* w ) { |
|||
memcpy( _cells, w->_cells, _wid * _hei * sizeof( byte ) ); |
|||
} |
|||
private: |
|||
int _wid, _hei; |
|||
byte* _cells; |
|||
}; |
|||
class rule { |
|||
public: |
|||
rule( world* w ) : wrd( w ) { |
|||
wid = wrd->wid(); |
|||
hei = wrd->hei(); |
|||
wrdT = new world( wid, hei ); |
|||
} |
|||
~rule() { |
|||
if( wrdT ) delete wrdT; |
|||
} |
|||
bool hasLivingCells() { |
|||
for( int y = 0; y < hei; y++ ) |
|||
for( int x = 0; x < wid; x++ ) |
|||
if( wrd->at( x, y ) ) return true; |
|||
std::cout << "*** All cells are dead!!! ***\n\n"; |
|||
return false; |
|||
} |
|||
void swapWrds() { |
|||
wrd->swap( wrdT ); |
|||
} |
|||
void setRuleB( std::vector<int>& birth ) { |
|||
_birth = birth; |
|||
} |
|||
void setRuleS( std::vector<int>& stay ) { |
|||
_stay = stay; |
|||
} |
|||
void applyRules() { |
|||
int n; |
|||
for( int y = 0; y < hei; y++ ) { |
|||
for( int x = 0; x < wid; x++ ) { |
|||
n = neighbours( x, y ); |
|||
if( wrd->at( x, y ) ) { |
|||
wrdT->set( x, y, inStay( n ) ? 1 : 0 ); |
|||
} else { |
|||
wrdT->set( x, y, inBirth( n ) ? 1 : 0 ); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
private: |
|||
int neighbours( int xx, int yy ) { |
|||
int n = 0, nx, ny; |
|||
for( int y = -1; y < 2; y++ ) { |
|||
for( int x = -1; x < 2; x++ ) { |
|||
if( !x && !y ) continue; |
|||
nx = ( wid + xx + x ) % wid; |
|||
ny = ( hei + yy + y ) % hei; |
|||
n += wrd->at( nx, ny ) > 0 ? 1 : 0; |
|||
} |
|||
} |
|||
return n; |
|||
} |
|||
bool inStay( int n ) { |
|||
return( _stay.end() != find( _stay.begin(), _stay.end(), n ) ); |
|||
} |
|||
bool inBirth( int n ) { |
|||
return( _birth.end() != find( _birth.begin(), _birth.end(), n ) ); |
|||
} |
|||
int wid, hei; |
|||
world *wrd, *wrdT; |
|||
std::vector<int> _stay, _birth; |
|||
}; |
|||
class cellular { |
|||
public: |
|||
cellular( int w, int h ) : rl( 0 ) { |
|||
wrd = new world( w, h ); |
|||
} |
|||
~cellular() { |
|||
if( rl ) delete rl; |
|||
delete wrd; |
|||
} |
|||
void start( int r ) { |
|||
rl = new rule( wrd ); |
|||
gen = 1; |
|||
std::vector<int> t; |
|||
switch( r ) { |
|||
case 1: // conway |
|||
t.push_back( 2 ); t.push_back( 3 ); rl->setRuleS( t ); |
|||
t.clear(); t.push_back( 3 ); rl->setRuleB( t ); |
|||
break; |
|||
case 2: // amoeba |
|||
t.push_back( 1 ); t.push_back( 3 ); t.push_back( 5 ); t.push_back( 8 ); rl->setRuleS( t ); |
|||
t.clear(); t.push_back( 3 ); t.push_back( 5 ); t.push_back( 7 ); rl->setRuleB( t ); |
|||
break; |
|||
case 3: // life34 |
|||
t.push_back( 3 ); t.push_back( 4 ); rl->setRuleS( t ); |
|||
rl->setRuleB( t ); |
|||
break; |
|||
case 4: // maze |
|||
t.push_back( 1 ); t.push_back( 2 ); t.push_back( 3 ); t.push_back( 4 ); t.push_back( 5 ); rl->setRuleS( t ); |
|||
t.clear(); t.push_back( 3 ); rl->setRuleB( t ); |
|||
break; |
|||
} |
|||
/* just for test - shoud read from a file */ |
|||
/* GLIDER */ |
|||
wrd->set( 6, 1, 1 ); wrd->set( 7, 2, 1 ); |
|||
wrd->set( 5, 3, 1 ); wrd->set( 6, 3, 1 ); |
|||
wrd->set( 7, 3, 1 ); |
|||
/* BLINKER */ |
|||
wrd->set( 1, 3, 1 ); wrd->set( 2, 3, 1 ); |
|||
wrd->set( 3, 3, 1 ); |
|||
/******************************************/ |
|||
generation(); |
|||
} |
|||
private: |
|||
void display() { |
|||
system( "cls" ); |
|||
int wid = wrd->wid(), |
|||
hei = wrd->hei(); |
|||
std::cout << "+" << std::string( wid, '-' ) << "+\n"; |
|||
for( int y = 0; y < hei; y++ ) { |
|||
std::cout << "|"; |
|||
for( int x = 0; x < wid; x++ ) { |
|||
if( wrd->at( x, y ) ) std::cout << "#"; |
|||
else std::cout << "."; |
|||
} |
|||
std::cout << "|\n"; |
|||
} |
|||
std::cout << "+" << std::string( wid, '-' ) << "+\n"; |
|||
std::cout << "Generation: " << gen << "\n\nPress [RETURN] for the next generation..."; |
|||
std::cin.get(); |
|||
} |
|||
void generation() { |
|||
do { |
|||
display(); |
|||
rl->applyRules(); |
|||
rl->swapWrds(); |
|||
gen++; |
|||
} |
|||
while ( rl->hasLivingCells() ); |
|||
} |
|||
rule* rl; |
|||
world* wrd; |
|||
int gen; |
|||
}; |
|||
int main( int argc, char* argv[] ) { |
|||
cellular c( 20, 12 ); |
|||
std::cout << "\n\t*** CELLULAR AUTOMATA ***" << "\n\n Which one you want to run?\n\n\n"; |
|||
std::cout << " [1]\tConway's Life\n [2]\tAmoeba\n [3]\tLife 34\n [4]\tMaze\n\n > "; |
|||
int o; |
|||
do { |
|||
std::cin >> o; |
|||
} |
|||
while( o < 1 || o > 4 ); |
|||
std::cin.ignore(); |
|||
c.start( o ); |
|||
return system( "pause" ); |
|||
} |
|||
</syntaxhighlight> |
|||
{{out}}<pre> |
|||
+--------------------+ +--------------------+ +--------------------+ +--------------------+ |
|||
|....................| |....................| |....................| |....................| |
|||
|......#.............| |....................| |....................| |....................| |
|||
|.......#............| |..#..#.#............| |.......#............| |..#...#.............| |
|||
|.###.###............| |..#...##............| |.###.#.#............| |..#....##...........| |
|||
|....................| |..#...#.............| |......##............| |..#...##............| |
|||
|....................| |....................| |....................| |....................| |
|||
|....................| |....................| |....................| |....................| |
|||
|....................| |....................| |....................| |....................| |
|||
+--------------------+ +--------------------+ +--------------------+ +--------------------+ |
|||
Generation: 1 Generation: 2 Generation: 3 Generation: 4 |
|||
</pre> |
</pre> |
||
=== Simple Without Classes === |
|||
=={{header|Common Lisp}}== |
|||
Shows a glider over 20 generations |
|||
Board edges wrap around to simulate infinite board |
|||
<syntaxhighlight lang="cpp"> |
|||
#include <iostream> |
|||
#include <vector> |
|||
#include <numeric> |
|||
// ---------------------------------------------------------------------------- |
|||
<lang lisp>(defun next-life (array &optional results) |
|||
using Row = std::vector<int>; |
|||
using Cells = std::vector<Row>; |
|||
// ---------------------------------------------------------------------------- |
|||
Cells board = { |
|||
{0,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,0,0,0}, |
|||
{0,0,0,1,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,0,0,0,0,0}, |
|||
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, |
|||
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, |
|||
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, |
|||
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, |
|||
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, |
|||
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, |
|||
}; |
|||
int numRows = 10; |
|||
int numCols = 20; |
|||
// ---------------------------------------------------------------------------- |
|||
int getNeighbor(int row, int col, Cells& board) { |
|||
// use modulus to get wrapping effect at board edges |
|||
return board.at((row + numRows) % numRows).at((col + numCols) % numCols); |
|||
} |
|||
int getCount(int row, int col, Cells& board) { |
|||
int count = 0; |
|||
std::vector<int> deltas {-1, 0, 1}; |
|||
for (int dc : deltas) { |
|||
for (int dr : deltas) { |
|||
if (dr || dc) { |
|||
count += getNeighbor(row + dr, col + dc, board); |
|||
} |
|||
} |
|||
} |
|||
return count; |
|||
} |
|||
void showCell(int cell) { |
|||
std::cout << (cell ? "*" : " "); |
|||
} |
|||
void showRow(const Row& row) { |
|||
std::cout << "|"; |
|||
for (int cell : row) {showCell(cell);} |
|||
std::cout << "|\n"; |
|||
} |
|||
void showCells(Cells board) { |
|||
for (const Row& row : board) { showRow(row); } |
|||
} |
|||
int tick(Cells& board, int row, int col) { |
|||
int count = getCount(row, col, board); |
|||
bool birth = !board.at(row).at(col) && count == 3; |
|||
bool survive = board.at(row).at(col) && (count == 2 || count == 3); |
|||
return birth || survive; |
|||
} |
|||
void updateCells(Cells& board) { |
|||
Cells original = board; |
|||
for (int row = 0; row < numRows; row++) { |
|||
for (int col = 0; col < numCols; col++) { |
|||
board.at(row).at(col) = tick(original, row, col); |
|||
} |
|||
} |
|||
} |
|||
int main () { |
|||
for (int gen = 0; gen < 20; gen++) { |
|||
std::cout << "\ngeneration " << gen << ":\n"; |
|||
showCells(board); |
|||
updateCells(board); |
|||
} |
|||
} |
|||
</syntaxhighlight> |
|||
{{out}}<pre> |
|||
generation 0: |
|||
| | |
|||
| * | |
|||
| * | |
|||
| *** | |
|||
| | |
|||
| | |
|||
| | |
|||
| | |
|||
| | |
|||
| | |
|||
generation 1: |
|||
| | |
|||
| | |
|||
| * * | |
|||
| ** | |
|||
| * | |
|||
| | |
|||
| | |
|||
| | |
|||
| | |
|||
| | |
|||
generation 2: |
|||
| | |
|||
| | |
|||
| * | |
|||
| * * | |
|||
| ** | |
|||
| | |
|||
| | |
|||
| | |
|||
| | |
|||
| | |
|||
generation 3: |
|||
| | |
|||
| | |
|||
| * | |
|||
| ** | |
|||
| ** | |
|||
| | |
|||
| | |
|||
| | |
|||
| | |
|||
| | |
|||
... |
|||
</pre> |
|||
=={{header|Chapel}}== |
|||
Compile and run with <code>chpl gol.chpl; ./gol --gridWidth [x] --gridHeight [y]</code>. |
|||
<syntaxhighlight lang="chapel"> |
|||
config const gridHeight : int = 3; |
|||
config const gridWidth : int = 3; |
|||
enum state { dead = 0, alive = 1 }; |
|||
class ConwaysGameofLife |
|||
{ |
|||
var gridDomain : domain(2, int); |
|||
var computeDomain : subdomain(gridDomain); |
|||
var grid : [gridDomain] state; |
|||
proc init(height : int, width : int) |
|||
{ |
|||
this.gridDomain = {0..#height+2, 0..#width+2}; |
|||
this.computeDomain = this.gridDomain.expand(-1); |
|||
} |
|||
proc step() : void |
|||
{ |
|||
var tempGrid: [this.computeDomain] state; |
|||
forall (i,j) in this.computeDomain |
|||
{ |
|||
var isAlive = this.grid[i,j] == state.alive; |
|||
var numAlive = (+ reduce this.grid[i-1..i+1, j-1..j+1]:int) - if isAlive then 1 else 0; |
|||
tempGrid[i,j] = if ( (2 == numAlive && isAlive) || numAlive == 3 ) then state.alive else state.dead ; |
|||
} |
|||
this.grid[this.computeDomain] = tempGrid; |
|||
} |
|||
proc this(i : int, j : int) ref : state |
|||
{ |
|||
return this.grid[i,j]; |
|||
} |
|||
proc prettyPrint() : string |
|||
{ |
|||
var str : string; |
|||
for i in this.gridDomain.dim(0) |
|||
{ |
|||
if i == 0 || i == gridDomain.dim(0).last |
|||
{ |
|||
for j in this.gridDomain.dim(1) |
|||
{ |
|||
str += "-"; |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
for j in this.gridDomain.dim(1) |
|||
{ |
|||
if j == 0 || j == this.gridDomain.dim(1).last |
|||
{ |
|||
str += "|"; |
|||
} |
|||
else |
|||
{ |
|||
str += if this.grid[i,j] == state.alive then "#" else " "; |
|||
} |
|||
} |
|||
} |
|||
str += "\n"; |
|||
} |
|||
return str; |
|||
} |
|||
} |
|||
proc main() |
|||
{ |
|||
var game = new ConwaysGameofLife(gridHeight, gridWidth); |
|||
game[gridHeight/2 + 1, gridWidth/2 ] = state.alive; |
|||
game[gridHeight/2 + 1, gridWidth/2 + 1 ] = state.alive; |
|||
game[gridHeight/2 + 1, gridWidth/2 + 2 ] = state.alive; |
|||
for i in 1..3 |
|||
{ |
|||
writeln(game.prettyPrint()); |
|||
game.step(); |
|||
} |
|||
} |
|||
</syntaxhighlight> |
|||
Output: |
|||
<pre> |
|||
----- |
|||
| | |
|||
|###| |
|||
| | |
|||
----- |
|||
----- |
|||
| # | |
|||
| # | |
|||
| # | |
|||
----- |
|||
----- |
|||
| | |
|||
|###| |
|||
| | |
|||
----- |
|||
</pre> |
|||
=={{header|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. |
|||
<syntaxhighlight lang="lisp">(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]}) |
|||
</syntaxhighlight> |
|||
=={{header|COBOL}}== |
|||
<syntaxhighlight lang="cobolfree">identification division. |
|||
program-id. game-of-life-program. |
|||
data division. |
|||
working-storage section. |
|||
01 grid. |
|||
05 cell-table. |
|||
10 row occurs 5 times. |
|||
15 cell pic x value space occurs 5 times. |
|||
05 next-gen-cell-table. |
|||
10 next-gen-row occurs 5 times. |
|||
15 next-gen-cell pic x occurs 5 times. |
|||
01 counters. |
|||
05 generation pic 9. |
|||
05 current-row pic 9. |
|||
05 current-cell pic 9. |
|||
05 living-neighbours pic 9. |
|||
05 neighbour-row pic 9. |
|||
05 neighbour-cell pic 9. |
|||
05 check-row pic s9. |
|||
05 check-cell pic s9. |
|||
procedure division. |
|||
control-paragraph. |
|||
perform blinker-paragraph varying current-cell from 2 by 1 |
|||
until current-cell is greater than 4. |
|||
perform show-grid-paragraph through life-paragraph |
|||
varying generation from 0 by 1 |
|||
until generation is greater than 2. |
|||
stop run. |
|||
blinker-paragraph. |
|||
move '#' to cell(3,current-cell). |
|||
show-grid-paragraph. |
|||
display 'GENERATION ' generation ':'. |
|||
display ' +---+'. |
|||
perform show-row-paragraph varying current-row from 2 by 1 |
|||
until current-row is greater than 4. |
|||
display ' +---+'. |
|||
display ''. |
|||
life-paragraph. |
|||
perform update-row-paragraph varying current-row from 2 by 1 |
|||
until current-row is greater than 4. |
|||
move next-gen-cell-table to cell-table. |
|||
show-row-paragraph. |
|||
display ' |' with no advancing. |
|||
perform show-cell-paragraph varying current-cell from 2 by 1 |
|||
until current-cell is greater than 4. |
|||
display '|'. |
|||
show-cell-paragraph. |
|||
display cell(current-row,current-cell) with no advancing. |
|||
update-row-paragraph. |
|||
perform update-cell-paragraph varying current-cell from 2 by 1 |
|||
until current-cell is greater than 4. |
|||
update-cell-paragraph. |
|||
move 0 to living-neighbours. |
|||
perform check-row-paragraph varying check-row from -1 by 1 |
|||
until check-row is greater than 1. |
|||
evaluate living-neighbours, |
|||
when 2 move cell(current-row,current-cell) to next-gen-cell(current-row,current-cell), |
|||
when 3 move '#' to next-gen-cell(current-row,current-cell), |
|||
when other move space to next-gen-cell(current-row,current-cell), |
|||
end-evaluate. |
|||
check-row-paragraph. |
|||
add check-row to current-row giving neighbour-row. |
|||
perform check-cell-paragraph varying check-cell from -1 by 1 |
|||
until check-cell is greater than 1. |
|||
check-cell-paragraph. |
|||
add check-cell to current-cell giving neighbour-cell. |
|||
if cell(neighbour-row,neighbour-cell) is equal to '#', |
|||
and check-cell is not equal to zero or check-row is not equal to zero, |
|||
then add 1 to living-neighbours. |
|||
end program game-of-life-program.</syntaxhighlight> |
|||
{{out}} |
|||
<pre>GENERATION 0: |
|||
+---+ |
|||
| | |
|||
|###| |
|||
| | |
|||
+---+ |
|||
GENERATION 1: |
|||
+---+ |
|||
| # | |
|||
| # | |
|||
| # | |
|||
+---+ |
|||
GENERATION 2: |
|||
+---+ |
|||
| | |
|||
|###| |
|||
| | |
|||
+---+</pre> |
|||
=={{header|Common Lisp}}== |
|||
<syntaxhighlight lang="lisp">(defun next-life (array &optional results) |
|||
(let* ((dimensions (array-dimensions array)) |
(let* ((dimensions (array-dimensions array)) |
||
(results (or results (make-array dimensions :element-type 'bit)))) |
(results (or results (make-array dimensions :element-type 'bit)))) |
||
Line 507: | Line 4,943: | ||
(terpri out) (print-grid world out) |
(terpri out) (print-grid world out) |
||
(psetq world (next-life world result) |
(psetq world (next-life world result) |
||
result world))))</ |
result world))))</syntaxhighlight> |
||
< |
<syntaxhighlight lang="lisp">(run-life (make-array '(3 3) |
||
:element-type 'bit |
:element-type 'bit |
||
:initial-contents '((0 0 0) |
:initial-contents '((0 0 0) |
||
(1 1 1) |
(1 1 1) |
||
(0 0 0))) |
(0 0 0))) |
||
3)</ |
3)</syntaxhighlight> |
||
produces |
produces |
||
Line 529: | Line 4,965: | ||
### |
### |
||
+++</pre> |
+++</pre> |
||
A version using a sparse list of living cells rather than an explicit board. |
|||
<syntaxhighlight lang="lisp">(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)))</syntaxhighlight> |
|||
=={{header|D}}== |
=={{header|D}}== |
||
<syntaxhighlight lang="d">import std.stdio, std.string, std.algorithm, std.array, std.conv; |
|||
struct GameOfLife { |
|||
The implementation keeps universe in ubyte array, |
|||
enum Cell : char { dead = ' ', alive = '#' } |
|||
alive state is kept as 0x10 bit value, lower 4 bits are |
|||
Cell[][] grid, newGrid; |
|||
used for calculations of number of neighbors. |
|||
this(in int x, in int y) pure nothrow @safe { |
|||
Output is generated every time it's printed, this probably |
|||
grid = new typeof(grid)(y + 2, x + 2); |
|||
isn't the best idea. |
|||
newGrid = new typeof(grid)(y + 2, x + 2); |
|||
} |
|||
void opIndexAssign(in string[] v, in size_t y, in size_t x) |
|||
<lang D>import tango.io.Stdout; |
|||
pure /*nothrow*/ @safe /*@nogc*/ { |
|||
import tango.core.Thread; |
|||
foreach (immutable nr, row; v) |
|||
import tango.text.convert.Integer; |
|||
foreach (immutable nc, state; row) |
|||
grid[y + nr][x + nc] = state.to!Cell; |
|||
} |
|||
void iteration() pure nothrow @safe @nogc { |
|||
class GameOfLife { |
|||
newGrid[0][] = Cell.dead; |
|||
private: |
|||
newGrid[$ - 1][] = Cell.dead; |
|||
static const int ClearNeighboursCountMask = 0xf0; |
|||
foreach (row; newGrid) |
|||
static const int NeighboursCountMask = 0xf; |
|||
row[0] = row[$ - 1] = Cell.dead; |
|||
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); |
|||
} |
|||
foreach (immutable r; 1 .. grid.length - 1) |
|||
void opIndexAssign(int v, size_t y, size_t x) |
|||
foreach (immutable c; 1 .. grid[0].length - 1) { |
|||
uint count = 0; |
|||
foreach (immutable i; -1 .. 2) |
|||
else universe2d[y][x] = AliveMask; |
|||
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); |
|||
void opIndexAssign(char[][] v, size_t y, size_t x) |
|||
} |
|||
{ |
|||
foreach (rowIdx, row; v) |
|||
foreach (cellIdx, cell; row) |
|||
this[y + rowIdx, x + cellIdx] = cell; |
|||
} |
|||
string toString() const pure /*nothrow @safe*/ { |
|||
// {{{ |
|||
auto ret = "-".replicate(grid[0].length - 1) ~ "\n"; |
|||
void iteration() |
|||
foreach (const row; grid[1 .. $ - 1]) |
|||
ret ~= "|%(%c%)|\n".format(row[1 .. $ - 1]); |
|||
// clear borders |
|||
return ret ~ "-".replicate(grid[0].length - 1); |
|||
foreach (ref cell; universe2d[0]) cell = 0; |
|||
} |
|||
foreach (ref cell; universe2d[$-1]) cell = 0; |
|||
} |
|||
foreach (ref row; universe2d) row[0] = row[$-1] = 0; |
|||
void main() /*@safe*/ { |
|||
// let each alive cell tell it's neighoburs |
|||
immutable glider1 = [" #", "# #", " ##"]; |
|||
immutable glider2 = ["# ", "# #", "## "]; |
|||
// mind, that rowIdx and cellIdx starts from 0... |
|||
foreach (rowIdx, row; universe2d[1 .. $-1]) |
|||
foreach (cellIdx, ref cell; row[1 .. $-1]) |
|||
if (cell & AliveMask) |
|||
foreach (i, nRow; universe2d[rowIdx .. rowIdx+3]) |
|||
foreach (j, ref nCell; nRow[cellIdx .. cellIdx+3]) |
|||
if (i != 1 || j != 1) |
|||
nCell++; |
|||
auto uni = GameOfLife(60, 20); |
|||
foreach (rowIdx, row; universe2d[1 .. $-1]) |
|||
uni[3, 2] = glider1; |
|||
foreach (cellIdx, ref cell; row[1 .. $-1]) |
|||
uni[3, 15] = glider2; |
|||
if ((cell & NeighboursCountMask) == 3 || cell == (AliveMask|2)) |
|||
uni[3, 19] = glider1; |
|||
cell = AliveMask; |
|||
uni[3, 32] = glider2; |
|||
else |
|||
uni[5, 50] = [" # #", "# ", "# #", "#### "]; |
|||
uni.writeln; |
|||
} |
|||
// }}} |
|||
foreach (immutable _; 0 .. 20) { |
|||
char[] toString() |
|||
uni.iteration; |
|||
uni.writeln; |
|||
} |
|||
ret.length = 2*(universe2d[0].length + 1); |
|||
}</syntaxhighlight> |
|||
ret[] = '='; |
|||
{{out|Output, first iteration}} |
|||
<pre>------------------------------------------------------------- |
|||
| | |
|||
| | |
|||
| # # # # | |
|||
| # # # # # # # # | |
|||
| ## ## ## ## # # | |
|||
| # | |
|||
| # # | |
|||
| #### | |
|||
| | |
|||
| | |
|||
| | |
|||
| | |
|||
| | |
|||
| | |
|||
| | |
|||
| | |
|||
| | |
|||
| | |
|||
| | |
|||
| | |
|||
-------------------------------------------------------------</pre> |
|||
===Faster Version=== |
|||
Same output. |
|||
<syntaxhighlight lang="d">import std.stdio, std.string, std.algorithm, std.typetuple, |
|||
std.array, std.conv; |
|||
struct GameOfLife { |
|||
ret ~= "\n"; |
|||
enum Cell : char { dead = ' ', alive = '#' } |
|||
foreach (row; universe2d[1 .. $-1]) { |
|||
Cell[] grid, newGrid; |
|||
ret ~= "|"; |
|||
immutable size_t nCols; |
|||
foreach (ref cell; row) |
|||
if (cell & AliveMask) ret ~= "[]"; |
|||
else ret ~= " "; |
|||
ret ~= "|\n"; |
|||
} |
|||
ret ~= ret[0 .. 2*(universe2d[0].length + 1)]; |
|||
this(in int nx, in int ny) pure nothrow @safe { |
|||
return ret; |
|||
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; TypeTuple!(-1, 0, 1)) |
|||
/*static*/ foreach (immutable j; TypeTuple!(-1, 0, 1)) |
|||
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 = new GameOfLife(80, 20); |
|||
char[][] glider1 = [ " #", "# #", " ##" ]; |
|||
auto uni = GameOfLife(60, 20); |
|||
char[][] glider2 = [ "$ ", "$ $", "$$ " ]; |
|||
uni[3, 2] = glider1; |
|||
char[][] lwss = [ " X X", "X ", "X X", "XXXX " ]; |
|||
uni[3, 15] = glider2; |
|||
uni[3, 19] = glider1; |
|||
uni[3, 32] = glider2; |
|||
uni[5, 50] = [" # #", "# ", "# #", "#### "]; |
|||
uni.writeln; |
|||
foreach (immutable _; 0 .. 20) { |
|||
uni.iteration; |
uni.iteration; |
||
uni.writeln; |
|||
} |
|||
}</syntaxhighlight> |
|||
=={{header|Dart}}== |
|||
uni[3, 2] = glider1; |
|||
<syntaxhighlight lang="dart">/** |
|||
uni[3,15] = glider2; |
|||
* States of a cell. A cell is either [ALIVE] or [DEAD]. |
|||
uni[3,19] = glider1; |
|||
* The state contains its [symbol] for printing. |
|||
uni[3,32] = glider2; |
|||
*/ |
|||
class State { |
|||
const State(this.symbol); |
|||
static final ALIVE = const State('#'); |
|||
uni[5,50] = lwss; |
|||
static final DEAD = const State(' '); |
|||
final String symbol; |
|||
// clear screen :> |
|||
} |
|||
for (int j = 0; j < 100; j++) Stdout.newline; |
|||
/** |
|||
for (int i = 0; i < 300; i++) |
|||
* The "business rule" of the game. Depending on the count of neighbours, |
|||
{ |
|||
* the [cellState] changes. |
|||
Stdout (uni).newline; |
|||
*/ |
|||
uni.iteration; |
|||
class Rule { |
|||
Thread.sleep(0.1); |
|||
Rule(this.cellState); |
|||
reactToNeighbours(int neighbours) { |
|||
if (neighbours == 3) { |
|||
cellState = State.ALIVE; |
|||
} else if (neighbours != 2) { |
|||
cellState = State.DEAD; |
|||
} |
} |
||
} |
|||
return 0; |
|||
}</lang> |
|||
var cellState; |
|||
=={{header|E}}== |
|||
} |
|||
/** |
|||
* 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)];</syntaxhighlight> |
|||
Test cases driving the design of this code: |
|||
<syntaxhighlight lang="dart">#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); |
|||
}); |
|||
}); |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
# |
|||
# |
|||
# |
|||
### |
|||
# |
|||
# |
|||
# |
|||
### |
|||
</pre> |
|||
=={{header|Delphi}}== |
|||
{{libheader| System.SysUtils}} |
|||
{{libheader| Velthuis.Console}} |
|||
{{Trans|Go}} |
|||
Thanks Rudy Velthuis for the [https://github.com/rvelthuis/Consoles Velthuis.Console] library. |
|||
<syntaxhighlight lang="delphi"> |
|||
program game_of_life; |
|||
{$APPTYPE CONSOLE} |
|||
uses |
|||
System.SysUtils, |
|||
Velthuis.Console; // CrlScr |
|||
type |
|||
TBoolMatrix = TArray<TArray<Boolean>>; |
|||
TField = record |
|||
s: TBoolMatrix; |
|||
w, h: Integer; |
|||
procedure SetValue(x, y: Integer; b: boolean); |
|||
function Next(x, y: Integer): boolean; |
|||
function State(x, y: Integer): boolean; |
|||
class function NewField(w1, h1: Integer): TField; static; |
|||
end; |
|||
TLife = record |
|||
a, b: TField; |
|||
w, h: Integer; |
|||
class function NewLife(w1, h1: Integer): TLife; static; |
|||
procedure Step; |
|||
function ToString: string; |
|||
end; |
|||
{ TField } |
|||
class function TField.NewField(w1, h1: Integer): TField; |
|||
var |
|||
s1: TBoolMatrix; |
|||
begin |
|||
SetLength(s1, h1); |
|||
for var i := 0 to High(s1) do |
|||
SetLength(s1[i], w1); |
|||
with Result do |
|||
begin |
|||
s := s1; |
|||
w := w1; |
|||
h := h1; |
|||
end; |
|||
end; |
|||
function TField.Next(x, y: Integer): boolean; |
|||
var |
|||
_on: Integer; |
|||
begin |
|||
_on := 0; |
|||
for var i := -1 to 1 do |
|||
for var j := -1 to 1 do |
|||
if self.State(x + i, y + j) and not ((j = 0) and (i = 0)) then |
|||
inc(_on); |
|||
Result := (_on = 3) or (_on = 2) and self.State(x, y); |
|||
end; |
|||
procedure TField.SetValue(x, y: Integer; b: boolean); |
|||
begin |
|||
self.s[y, x] := b; |
|||
end; |
|||
function TField.State(x, y: Integer): boolean; |
|||
begin |
|||
while y < 0 do |
|||
inc(y, self.h); |
|||
while x < 0 do |
|||
inc(x, self.w); |
|||
result := self.s[y mod self.h, x mod self.w] |
|||
end; |
|||
{ TLife } |
|||
class function TLife.NewLife(w1, h1: Integer): TLife; |
|||
var |
|||
a1: TField; |
|||
begin |
|||
a1 := TField.NewField(w1, h1); |
|||
for var i := 0 to (w1 * h1 div 2) do |
|||
a1.SetValue(Random(w1), Random(h1), True); |
|||
with Result do |
|||
begin |
|||
a := a1; |
|||
b := TField.NewField(w1, h1); |
|||
w := w1; |
|||
h := h1; |
|||
end; |
|||
end; |
|||
procedure TLife.Step; |
|||
var |
|||
tmp: TField; |
|||
begin |
|||
for var y := 0 to self.h - 1 do |
|||
for var x := 0 to self.w - 1 do |
|||
self.b.SetValue(x, y, self.a.Next(x, y)); |
|||
tmp := self.a; |
|||
self.a := self.b; |
|||
self.b := tmp; |
|||
end; |
|||
function TLife.ToString: string; |
|||
begin |
|||
result := ''; |
|||
for var y := 0 to self.h - 1 do |
|||
begin |
|||
for var x := 0 to self.w - 1 do |
|||
begin |
|||
var b: char := ' '; |
|||
if self.a.State(x, y) then |
|||
b := '*'; |
|||
result := result + b; |
|||
end; |
|||
result := result + #10; |
|||
end; |
|||
end; |
|||
begin |
|||
Randomize; |
|||
var life := TLife.NewLife(80, 15); |
|||
for var i := 1 to 300 do |
|||
begin |
|||
life.Step; |
|||
ClrScr; |
|||
writeln(life.ToString); |
|||
sleep(30); |
|||
end; |
|||
readln; |
|||
end.</syntaxhighlight> |
|||
{{out}} |
|||
<pre> * *** * * * |
|||
* ** ** * |
|||
**** * * * * |
|||
*** * * ** * * |
|||
*** ** * * * |
|||
* * * |
|||
** ** **** ** * |
|||
****** ** * * |
|||
* * * *** ** *** |
|||
** * * |
|||
* * ** |
|||
* * * |
|||
* * * * |
|||
* *** * * * ** |
|||
* *** * * * ***</pre> |
|||
=={{header|E}}== |
|||
Just does three generations of a blinker in a dead-boundary grid, as specified. ([[User:Kevin Reid]] has graphical and wrapping versions.) |
Just does three generations of a blinker in a dead-boundary grid, as specified. ([[User:Kevin Reid]] has graphical and wrapping versions.) |
||
< |
<syntaxhighlight lang="e">def gridWidth := 3 |
||
def gridHeight := 3 |
def gridHeight := 3 |
||
def X := 0..!gridWidth |
def X := 0..!gridWidth |
||
Line 708: | Line 5,672: | ||
currentFrame := frame |
currentFrame := frame |
||
println(currentFrame) |
println(currentFrame) |
||
}</ |
}</syntaxhighlight> |
||
=={{header|EasyLang}}== |
|||
[https://easylang.online/apps/game-of-life.html Run it] |
|||
<syntaxhighlight lang=text> |
|||
n = 70 |
|||
time = 0.1 |
|||
# |
|||
nx = n + 1 |
|||
subr init |
|||
for r = 1 to n |
|||
for c = 1 to n |
|||
i = r * nx + c |
|||
if randomf < 0.3 |
|||
f[i] = 1 |
|||
. |
|||
. |
|||
. |
|||
. |
|||
f = 100 / n |
|||
subr show |
|||
clear |
|||
for r = 1 to n |
|||
for c = 1 to n |
|||
if f[r * nx + c] = 1 |
|||
move c * f - f r * f - f |
|||
rect f * 0.9 f * 0.9 |
|||
. |
|||
. |
|||
. |
|||
. |
|||
subr update |
|||
swap f[] p[] |
|||
for r = 1 to n |
|||
sm = 0 |
|||
i = r * nx + 1 |
|||
sr = p[i - nx] + p[i] + p[i + nx] |
|||
for c = 1 to n |
|||
sl = sm |
|||
sm = sr |
|||
in = i + 1 |
|||
sr = p[in - nx] + p[in] + p[in + nx] |
|||
s = sl + sm + sr |
|||
if s = 3 or s = 4 and p[i] = 1 |
|||
f[i] = 1 |
|||
else |
|||
f[i] = 0 |
|||
. |
|||
i = in |
|||
. |
|||
. |
|||
. |
|||
on timer |
|||
update |
|||
show |
|||
timer time |
|||
. |
|||
on mouse_down |
|||
c = mouse_x div f + 1 |
|||
r = mouse_y div f + 1 |
|||
i = r * nx + c |
|||
f[i] = 1 - f[i] |
|||
show |
|||
timer 3 |
|||
. |
|||
len f[] nx * nx + nx |
|||
len p[] nx * nx + nx |
|||
init |
|||
timer 0 |
|||
</syntaxhighlight> |
|||
=={{header|eC}}== |
|||
{{libheader|Ecere}} |
|||
<syntaxhighlight lang="ec"> |
|||
import "ecere" |
|||
define seed = 12345; |
|||
define popInit = 1000; |
|||
define width = 100; |
|||
define height = 100; |
|||
define cellWidth = 4; |
|||
define cellHeight = 4; |
|||
Array<byte> grid { size = width * height }; |
|||
Array<byte> newState { size = width * height }; |
|||
class GameOfLife : Window |
|||
{ |
|||
caption = $"Conway's Game of Life"; |
|||
background = lightBlue; |
|||
borderStyle = sizable; |
|||
hasMaximize = true; |
|||
hasMinimize = true; |
|||
hasClose = true; |
|||
clientSize = { width * cellWidth, height * cellHeight }; |
|||
Timer tickTimer |
|||
{ |
|||
delay = 0.05, started = true, userData = this; |
|||
bool DelayExpired() |
|||
{ |
|||
int y, x, ix = 0; |
|||
for(y = 0; y < height; y++) |
|||
{ |
|||
for(x = 0; x < width; x++, ix++) |
|||
{ |
|||
int nCount = 0; |
|||
byte alive; |
|||
if(x > 0 && y > 0 && grid[ix - width - 1]) nCount++; |
|||
if( y > 0 && grid[ix - width ]) nCount++; |
|||
if(x < width-1 && y > 0 && grid[ix - width + 1]) nCount++; |
|||
if(x > 0 && grid[ix - 1]) nCount++; |
|||
if(x < width - 1 && grid[ix + 1]) nCount++; |
|||
if(x > 0 && y < height-1 && grid[ix + width - 1]) nCount++; |
|||
if( y < height-1 && grid[ix + width ]) nCount++; |
|||
if(x < width-1 && y < height-1 && grid[ix + width + 1]) nCount++; |
|||
if(grid[ix]) |
|||
alive = nCount >= 2 && nCount <= 3; // Death |
|||
else |
|||
alive = nCount == 3; // Birth |
|||
newState[ix] = alive; |
|||
} |
|||
} |
|||
memcpy(grid.array, newState.array, width * height); |
|||
Update(null); |
|||
return true; |
|||
} |
|||
}; |
|||
void OnRedraw(Surface surface) |
|||
{ |
|||
int x, y; |
|||
int ix = 0; |
|||
surface.background = navy; |
|||
for(y = 0; y < height; y++) |
|||
{ |
|||
for(x = 0; x < width; x++, ix++) |
|||
{ |
|||
if(grid[ix]) |
|||
{ |
|||
int sy = y * cellHeight; |
|||
int sx = x * cellWidth; |
|||
surface.Area(sx, sy, sx + cellWidth-1, sy + cellHeight-1); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
bool OnCreate() |
|||
{ |
|||
int i; |
|||
RandomSeed(seed); |
|||
for(i = 0; i < popInit; i++) |
|||
{ |
|||
int x = GetRandom(0, width-1); |
|||
int y = GetRandom(0, height-1); |
|||
grid[y * width + x] = 1; |
|||
} |
|||
return true; |
|||
} |
|||
} |
|||
GameOfLife life {}; |
|||
</syntaxhighlight> |
|||
=={{header|Egel}}== |
|||
<syntaxhighlight lang="egel">import "prelude.eg" |
|||
import "io.ego" |
|||
using System |
|||
using List |
|||
using IO |
|||
def boardsize = 5 |
|||
def empty = [ X Y -> 0 ] |
|||
def insert = |
|||
[ X Y BOARD -> |
|||
[ X0 Y0 -> if and (X0 == X) (Y0 == Y) then 1 |
|||
else BOARD X0 Y0 ] ] |
|||
def coords = |
|||
let R = fromto 0 (boardsize - 1) in |
|||
[ XX YY -> map (\X -> map (\Y -> X Y) YY) XX ] R R |
|||
def printcell = |
|||
[ 0 -> print ". " |
|||
| _ -> print "* " ] |
|||
def printboard = |
|||
[ BOARD -> |
|||
let M = map [XX -> let _ = map [(X Y) -> printcell (BOARD X Y)] XX in print "\n" ] coords in |
|||
nop ] |
|||
def count = |
|||
[ BOARD, X, Y -> |
|||
(BOARD (X - 1) (Y - 1)) + (BOARD (X) (Y - 1)) + (BOARD (X+1) (Y - 1)) + |
|||
(BOARD (X - 1) Y) + (BOARD (X+1) Y) + |
|||
(BOARD (X - 1) (Y+1)) + (BOARD (X) (Y+1)) + (BOARD (X+1) (Y+1)) ] |
|||
def next = |
|||
[ 0 N -> if N == 3 then 1 else 0 |
|||
| _ N -> if or (N == 2) (N == 3) then 1 else 0 ] |
|||
def updateboard = |
|||
[ BOARD -> |
|||
let XX = map (\(X Y) -> X Y (BOARD X Y) (count BOARD X Y)) (flatten coords) in |
|||
let YY = map (\(X Y C N) -> X Y (next C N)) XX in |
|||
foldr [(X Y 0) BOARD -> BOARD | (X Y _) BOARD -> insert X Y BOARD ] empty YY ] |
|||
def blinker = |
|||
(insert 1 2) @ (insert 2 2) @ (insert 3 2) |
|||
def main = |
|||
let GEN0 = blinker empty in |
|||
let GEN1 = updateboard GEN0 in |
|||
let GEN2 = updateboard GEN1 in |
|||
let _ = map [ G -> let _ = print "generation:\n" in printboard G ] {GEN0, GEN1, GEN2} in |
|||
nop |
|||
</syntaxhighlight> |
|||
=={{header|Elena}}== |
|||
ELENA 6.x, using cellular library |
|||
<syntaxhighlight lang="elena">import extensions; |
|||
import system'threading; |
|||
import system'text; |
|||
import cellular; |
|||
const int maxX = 48; |
|||
const int maxY = 28; |
|||
const int DELAY = 50; |
|||
sealed class Model |
|||
{ |
|||
Space _space; |
|||
RuleSet _ruleSet; |
|||
bool _started; |
|||
Func<Space, object> OnUpdate : event; |
|||
constructor newRandomset(RuleSet transformSet) |
|||
{ |
|||
_space := IntMatrixSpace.allocate(maxY, maxX, randomSet); |
|||
_ruleSet := transformSet; |
|||
_started := false |
|||
} |
|||
private onUpdate() |
|||
{ |
|||
OnUpdate.?(_space) |
|||
} |
|||
run() |
|||
{ |
|||
if (_started) |
|||
{ |
|||
_space.update(_ruleSet) |
|||
} |
|||
else |
|||
{ |
|||
_started := true |
|||
}; |
|||
self.onUpdate() |
|||
} |
|||
} |
|||
singleton gameOfLifeRuleSet : RuleSet |
|||
{ |
|||
int proceed(Space s, int x, int y) |
|||
{ |
|||
int cell := s.at(x, y); |
|||
int number := s.LiveCell(x, y, 1); // NOTE : number of living cells around the self includes the cell itself |
|||
if (cell == 0 && number == 3) |
|||
{ |
|||
^ 1 |
|||
} |
|||
else if (cell == 1 && (number == 4 || number == 3)) |
|||
{ |
|||
^ 1 |
|||
} |
|||
else |
|||
{ |
|||
^ 0 |
|||
} |
|||
} |
|||
} |
|||
public extension presenterOp : Space |
|||
{ |
|||
print() |
|||
{ |
|||
console.setCursorPosition(0, 0); |
|||
int columns := self.Columns; |
|||
int rows := self.Rows; |
|||
auto line := new TextBuilder(); |
|||
for(int i := 0; i < rows; i += 1) |
|||
{ |
|||
line.clear(); |
|||
for(int j := 0; j < columns; j += 1) |
|||
{ |
|||
int cell := self.at(i, j); |
|||
line.write((cell == 0).iif(" ","o")); |
|||
}; |
|||
console.writeLine(line.Value) |
|||
} |
|||
} |
|||
} |
|||
public program() |
|||
{ |
|||
auto model := Model.newRandomset(gameOfLifeRuleSet); |
|||
console.clear(); |
|||
model.OnUpdate := (Space sp){ sp.print() }; |
|||
until (console.KeyAvailable) |
|||
{ |
|||
model.run(); |
|||
threadControl.sleep(DELAY) |
|||
}; |
|||
console.readChar() |
|||
}</syntaxhighlight> |
|||
=={{header|Elixir}}== |
|||
{{works with|Elixir|1.2}} |
|||
{{trans|Ruby}} |
|||
<syntaxhighlight lang="elixir">defmodule Conway do |
|||
def game_of_life(name, size, generations, initial_life\\nil) do |
|||
board = seed(size, initial_life) |
|||
print_board(board, name, size, 0) |
|||
reason = generate(name, size, generations, board, 1) |
|||
case reason do |
|||
:all_dead -> "no more life." |
|||
:static -> "no movement" |
|||
_ -> "specified lifetime ended" |
|||
end |
|||
|> IO.puts |
|||
IO.puts "" |
|||
end |
|||
defp new_board(n) do |
|||
for x <- 1..n, y <- 1..n, into: %{}, do: {{x,y}, 0} |
|||
end |
|||
defp seed(n, points) do |
|||
if points do |
|||
points |
|||
else # randomly seed board |
|||
(for x <- 1..n, y <- 1..n, do: {x,y}) |> Enum.take_random(10) |
|||
end |
|||
|> Enum.reduce(new_board(n), fn pos,acc -> %{acc | pos => 1} end) |
|||
end |
|||
defp generate(_, _, generations, _, gen) when generations < gen, do: :ok |
|||
defp generate(name, size, generations, board, gen) do |
|||
new = evolve(board, size) |
|||
print_board(new, name, size, gen) |
|||
cond do |
|||
barren?(new) -> :all_dead |
|||
board == new -> :static |
|||
true -> generate(name, size, generations, new, gen+1) |
|||
end |
|||
end |
|||
defp evolve(board, n) do |
|||
for x <- 1..n, y <- 1..n, into: %{}, do: {{x,y}, fate(board, x, y, n)} |
|||
end |
|||
defp fate(board, x, y, n) do |
|||
irange = max(1, x-1) .. min(x+1, n) |
|||
jrange = max(1, y-1) .. min(y+1, n) |
|||
sum = ((for i <- irange, j <- jrange, do: board[{i,j}]) |> Enum.sum) - board[{x,y}] |
|||
cond do |
|||
sum == 3 -> 1 |
|||
sum == 2 and board[{x,y}] == 1 -> 1 |
|||
true -> 0 |
|||
end |
|||
end |
|||
defp barren?(board) do |
|||
Enum.all?(board, fn {_,v} -> v == 0 end) |
|||
end |
|||
defp print_board(board, name, n, generation) do |
|||
IO.puts "#{name}: generation #{generation}" |
|||
Enum.each(1..n, fn y -> |
|||
Enum.map(1..n, fn x -> if board[{x,y}]==1, do: "#", else: "." end) |
|||
|> IO.puts |
|||
end) |
|||
end |
|||
end |
|||
Conway.game_of_life("blinker", 3, 2, [{2,1},{2,2},{2,3}]) |
|||
Conway.game_of_life("glider", 4, 4, [{2,1},{3,2},{1,3},{2,3},{3,3}]) |
|||
Conway.game_of_life("random", 5, 10)</syntaxhighlight> |
|||
{{out}} |
|||
<pre style="height: 60ex; overflow: scroll"> |
|||
blinker: generation 0 |
|||
.#. |
|||
.#. |
|||
.#. |
|||
blinker: generation 1 |
|||
... |
|||
### |
|||
... |
|||
blinker: generation 2 |
|||
.#. |
|||
.#. |
|||
.#. |
|||
specified lifetime ended |
|||
glider: generation 0 |
|||
.#.. |
|||
..#. |
|||
###. |
|||
.... |
|||
glider: generation 1 |
|||
.... |
|||
#.#. |
|||
.##. |
|||
.#.. |
|||
glider: generation 2 |
|||
.... |
|||
..#. |
|||
#.#. |
|||
.##. |
|||
glider: generation 3 |
|||
.... |
|||
.#.. |
|||
..## |
|||
.##. |
|||
glider: generation 4 |
|||
.... |
|||
..#. |
|||
...# |
|||
.### |
|||
specified lifetime ended |
|||
random: generation 0 |
|||
.#... |
|||
#.#.. |
|||
#...# |
|||
###.# |
|||
..#.. |
|||
random: generation 1 |
|||
.#... |
|||
#.... |
|||
#.#.. |
|||
#.#.. |
|||
..##. |
|||
random: generation 2 |
|||
..... |
|||
#.... |
|||
#.... |
|||
..#.. |
|||
.###. |
|||
random: generation 3 |
|||
..... |
|||
..... |
|||
.#... |
|||
..##. |
|||
.###. |
|||
random: generation 4 |
|||
..... |
|||
..... |
|||
..#.. |
|||
...#. |
|||
.#.#. |
|||
random: generation 5 |
|||
..... |
|||
..... |
|||
..... |
|||
...#. |
|||
..#.. |
|||
random: generation 6 |
|||
..... |
|||
..... |
|||
..... |
|||
..... |
|||
..... |
|||
no more life. |
|||
</pre> |
|||
=={{header|Emacs Lisp}}== |
|||
<syntaxhighlight lang="lisp">#!/usr/bin/env emacs -script |
|||
;; -*- lexical-binding: t -*- |
|||
;; run: ./conways-life conways-life.config |
|||
(require 'cl-lib) |
|||
(defconst blinker '("***")) |
|||
(defconst toad '(".***" "***.")) |
|||
(defconst pentomino-p '(".**" ".**" ".*.")) |
|||
(defconst pi-heptomino '("***" "*.*" "*.*")) |
|||
(defconst glider '(".*." "..*" "***")) |
|||
(defconst pre-pulsar '("***...***" "*.*...*.*" "***...***")) |
|||
(defconst ship '("**." "*.*" ".**")) |
|||
(defconst pentadecathalon '("**********")) |
|||
(defconst clock '("..*." "*.*." ".*.*" ".*..")) |
|||
(defmacro swap (a b) |
|||
`(setq ,b (prog1 ,a (setq ,a ,b)))) |
|||
(cl-defstruct world rows cols data) |
|||
(defun new-world (rows cols) |
|||
(make-world :rows rows :cols cols :data (make-vector (* rows cols) nil))) |
|||
(defmacro world-pt (w r c) |
|||
`(+ (* (mod ,r (world-rows ,w)) (world-cols ,w)) |
|||
(mod ,c (world-cols ,w)))) |
|||
(defmacro world-ref (w r c) |
|||
`(aref (world-data ,w) (world-pt ,w ,r ,c))) |
|||
(defun print-world (world) |
|||
(dotimes (r (world-rows world)) |
|||
(dotimes (c (world-cols world)) |
|||
(princ (format "%c" (if (world-ref world r c) ?* ?.)))) |
|||
(terpri))) |
|||
(defun insert-pattern (world row col shape) |
|||
(let ((r row) |
|||
(c col)) |
|||
(unless (listp shape) |
|||
(setq shape (symbol-value shape))) |
|||
(dolist (row-data shape) |
|||
(dolist (col-data (mapcar 'identity row-data)) |
|||
(setf (world-ref world r c) (not (or (eq col-data ?.)))) |
|||
(setq c (1+ c))) |
|||
(setq r (1+ r)) |
|||
(setq c col)))) |
|||
(defun neighbors (world row col) |
|||
(let ((n 0)) |
|||
(dolist (offset '((1 . 1) (1 . 0) (1 . -1) (0 . 1) (0 . -1) (-1 . 1) (-1 . 0) (-1 . -1))) |
|||
(when (world-ref world (+ row (car offset)) (+ col (cdr offset))) |
|||
(setq n (1+ n)))) |
|||
n)) |
|||
(defun advance-generation (old new) |
|||
(dotimes (r (world-rows old)) |
|||
(dotimes (c (world-cols old)) |
|||
(let ((n (neighbors old r c))) |
|||
(setf (world-ref new r c) |
|||
(if (world-ref old r c) |
|||
(or (= n 2) (= n 3)) |
|||
(= n 3))))))) |
|||
(defun read-config (file-name) |
|||
(with-temp-buffer |
|||
(insert-file-contents-literally file-name) |
|||
(read (current-buffer)))) |
|||
(defun get-config (key config) |
|||
(let ((val (assoc key config))) |
|||
(if (null val) |
|||
(error (format "missing value for %s" key)) |
|||
(cdr val)))) |
|||
(defun insert-patterns (world patterns) |
|||
(dolist (p patterns) |
|||
(apply 'insert-pattern (cons world p)))) |
|||
(defun simulate-life (file-name) |
|||
(let* ((config (read-config file-name)) |
|||
(rows (get-config 'rows config)) |
|||
(cols (get-config 'cols config)) |
|||
(generations (get-config 'generations config)) |
|||
(a (new-world rows cols)) |
|||
(b (new-world rows cols))) |
|||
(insert-patterns a (get-config 'patterns config)) |
|||
(dotimes (g generations) |
|||
(princ (format "generation %d\n" g)) |
|||
(print-world a) |
|||
(advance-generation a b) |
|||
(swap a b)))) |
|||
(simulate-life (elt command-line-args-left 0))</syntaxhighlight> |
|||
Configuration file, which defines the size starting patterns and |
|||
how long the simulation will run. |
|||
<syntaxhighlight lang="lisp">((rows . 8) |
|||
(cols . 10) |
|||
(generations . 3) |
|||
(patterns |
|||
;; Blinker is defined in the script. |
|||
(1 1 blinker) |
|||
;; This is a custom pattern. |
|||
(4 4 (".***" |
|||
"***."))))</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
generation 0 |
|||
.......... |
|||
.***...... |
|||
.......... |
|||
.......... |
|||
.....***.. |
|||
....***... |
|||
.......... |
|||
.......... |
|||
generation 1 |
|||
..*....... |
|||
..*....... |
|||
..*....... |
|||
......*... |
|||
....*..*.. |
|||
....*..*.. |
|||
.....*.... |
|||
.......... |
|||
generation 2 |
|||
.......... |
|||
.***...... |
|||
.......... |
|||
.......... |
|||
.....***.. |
|||
....***... |
|||
.......... |
|||
.......... |
|||
</pre> |
|||
=={{header|Erlang}}== |
|||
<syntaxhighlight lang="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)). |
|||
</syntaxhighlight> |
|||
=={{header|ERRE}}== |
|||
This is a simple implementation of Conway's game of Life with an endless world. Test pattern configuration is 'glider'. |
|||
<syntaxhighlight lang="erre"> |
|||
PROGRAM LIFE |
|||
!$INTEGER |
|||
!$KEY |
|||
!for C-64 compatibility |
|||
CONST Xmax=38,Ymax=20 |
|||
DIM x,y,N |
|||
DIM WORLD[39,21],NextWORLD[39,21] |
|||
BEGIN |
|||
! 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 |
|||
!------------------------------------------ |
|||
PRINT(CHR$(12);"Press any key to interrupt") |
|||
LOOP |
|||
PRINT(CHR$(11);) PRINT |
|||
PRINT(STRING$(Xmax+2,"-")) |
|||
!---------- endless world --------- |
|||
FOR y=1 TO Ymax DO |
|||
WORLD[0,y]=WORLD[Xmax,y] |
|||
WORLD[Xmax+1,y]=WORLD[1,y] |
|||
END FOR |
|||
FOR x=1 TO Xmax DO |
|||
WORLD[x,0]=WORLD[x,Ymax] |
|||
WORLD[x,Ymax+1]=WORLD[x,1] |
|||
END FOR |
|||
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 DO |
|||
PRINT("|";) |
|||
FOR x=1 TO Xmax DO |
|||
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=N+WORLD[x,y+1]+WORLD[x+1,y-1]+WORLD[x+1,y]+WORLD[x+1,y+1] |
|||
IF (WORLD[x,y]<>0 AND (N=2 OR N=3)) OR (WORLD[x,y]=0 AND N=3) THEN |
|||
NextWORLD[x,y]=1 |
|||
ELSE |
|||
NextWORLD[x,y]=0 |
|||
END IF |
|||
END FOR |
|||
PRINT("|") |
|||
END FOR |
|||
PRINT(STRING$(Xmax+2,"-")) |
|||
PAUSE(0.1) |
|||
FOR x=0 TO Xmax+1 DO |
|||
FOR y=0 TO Ymax+1 DO |
|||
WORLD[x,y]=NextWORLD[x,y] |
|||
NextWORLD[x,y]=0 |
|||
END FOR |
|||
END FOR |
|||
REPEAT |
|||
GET(A$) |
|||
UNTIL A$<>"" |
|||
EXIT IF A$=CHR$(27) |
|||
END LOOP |
|||
PRINT("Press any key to exit") |
|||
REPEAT |
|||
UNTIL GETKEY$<>"" |
|||
END PROGRAM |
|||
</syntaxhighlight> |
|||
=={{header|F Sharp|F#}}== |
=={{header|F Sharp|F#}}== |
||
The following F# implementation uses {{libheader|Windows Presentation Foundation}} for visualization and is easily compiled into a standalone executable: |
The following F# implementation uses {{libheader|Windows Presentation Foundation}} for visualization and is easily compiled into a standalone executable: |
||
< |
<syntaxhighlight lang="fsharp">let count (a: _ [,]) x y = |
||
let m, n = a.GetLength 0, a.GetLength 1 |
let m, n = a.GetLength 0, a.GetLength 1 |
||
let mutable c = 0 |
let mutable c = 0 |
||
Line 746: | Line 6,643: | ||
Media.CompositionTarget.Rendering.Add update |
Media.CompositionTarget.Rendering.Add update |
||
Window(Content=image, Title="Game of Life") |
Window(Content=image, Title="Game of Life") |
||
|> (Application()).Run |> ignore</ |
|> (Application()).Run |> ignore</syntaxhighlight> |
||
=={{header|Fermat}}== |
|||
<syntaxhighlight lang="fermat">;{Conway's Game of Life in Fermat} |
|||
;{square grid with wrap-around boundaries} |
|||
size:=50; {how big a grid do you want? This fits my screen OK, change this for your own screen} |
|||
Array w1[size,size], w2[size,size]; {set up an active world and a 'scratchpad' world} |
|||
act:=1; |
|||
buf:=2; |
|||
%[1]:=[w1]; {Fermat doesn't have 3D arrays in the normal sense--} |
|||
%[2]:=[w2]; {we need to use the somewhat odd "array of arrays" functionality} |
|||
Func Cls = for i = 1 to size do !!; od.; {"clear screen" by printing a bunch of newlines} |
|||
Func Draw = {draw the active screen} |
|||
for i = 1 to size do |
|||
for j = 1 to size do |
|||
if %[act][i, j] = 1 then !('# ') else !('. ') fi; |
|||
od; |
|||
!; |
|||
od; |
|||
.; |
|||
Func Rnd = {randomize the grid with a density of 40% live cells} |
|||
for i = 1 to size do |
|||
for j = 1 to size do |
|||
if Rand|5<2 then %[act][i, j] := 1 else %[act][i, j] := 0 fi; |
|||
od; |
|||
od; |
|||
Cls; |
|||
Draw; |
|||
.; |
|||
Func Blinker = {clears the screen except for a blinker in the top left corner} |
|||
for i = 1 to size do |
|||
for j = 1 to size do |
|||
%[act][i, j] := 0; |
|||
od; |
|||
od; |
|||
%[act][1,2] := 1; |
|||
%[act][2,2] := 1; |
|||
%[act][3,2] := 1; |
|||
Cls; |
|||
Draw; |
|||
.; |
|||
Func Iter = {do one iteration} |
|||
for i = 1 to size do |
|||
if i = 1 then im := size else im := i - 1 fi; {handle wrap around} |
|||
if i = size then ip := 1 else ip := i + 1 fi; |
|||
for j = 1 to size do |
|||
if j = 1 then jm := size else jm := j - 1 fi; |
|||
if j = size then jp := 1 else jp := j + 1 fi; |
|||
neigh := %[act][im, jm]; {count neigbours} |
|||
neigh :+ (%[act][im, j ]); |
|||
neigh :+ (%[act][im, jp]); |
|||
neigh :+ (%[act][i , jm]); |
|||
neigh :+ (%[act][i , jp]); |
|||
neigh :+ (%[act][ip, jm]); |
|||
neigh :+ (%[act][ip, j ]); |
|||
neigh :+ (%[act][ip, jp]); |
|||
if neigh < 2 or neigh > 3 then %[buf][i, j] := 0 fi; {alive and dead rules} |
|||
if neigh = 2 then %[buf][i, j] := %[act][i, j] fi; |
|||
if neigh = 3 then %[buf][i, j] := 1 fi; |
|||
od; |
|||
od; |
|||
Swap(act, buf); {rather than copying the scratch over into the active, just exchange their identities} |
|||
Cls; |
|||
Draw; |
|||
.; |
|||
choice := 9; |
|||
while choice <> 0 do {really rough menu, not the point of this exercise} |
|||
?choice; |
|||
if choice=4 then Cls fi; |
|||
if choice=3 then Blinker fi; |
|||
if choice=2 then Rnd fi; |
|||
if choice=1 then Iter fi; |
|||
od; |
|||
!!'John Horton Conway (26 December 1937 – 11 April 2020)'; |
|||
</syntaxhighlight> |
|||
{{out}} One iteration starting from random soup, showing a few well-known objects (blinker, block, glider, beehive, loaf) |
|||
<pre> |
|||
# . . . . . . . . . # . . # . . . . . . . . # . . . . . . . . . . . . . . . . . . . . . . # # . . . |
|||
. . . . . . . # . . # . . . # . . . . # # . # # . . # # # . . . . . . . . . . . . . . . # # . # . # |
|||
. . . . . . # . # # . . . # . . . . . # # . # . . . # . . # . . . . . . . . . . . . . . # # . . # . |
|||
. . . . . . # . # . # . . # . . . . . # . # # . . # . . . # . . . . # # . . . . . . . . . . . . . . |
|||
. . . . . . . # . # # . . # . . . # # # # . . . . # . . # . . . . # . . # . . . . . . . . . . . . . |
|||
. . . . . . . . . . . . # . . . . # . . . . . . . . . . . . . . . . # . . # . . . . . . . . . . . . |
|||
. . . . . . . . . . . . . . . . . # # # . . . . . . # # . . . . . . # . . # . . . . . . . . . . . . |
|||
. . . . . . . . . . . . . . . . . . # . . . . . . . . . . . . . . . # . # . . . . . . . . . . . . . |
|||
. . . . # # # . . . . . . . . . . . # . . . . # # . . . . . . . . . . # . . . . . . . . . . . . . . |
|||
. . . . . . . . . . . . . . . . # # # # . . # . # . . . . . . . . . . . . . . . . . . . . . . . . . |
|||
. . . . . . . . . . . . . . . . # # . . # . . # . . . . . . . . . . . . . . . . . . . . . . . . . . |
|||
. . . . . . # # # . . . . . . . # # . . . . . # . . . . . . . . . . # . . . . . . . . . . . . . . . |
|||
. . . . . . # # # . . . . . . . . . . . # # . . . . . . . . . . . # . # . . . . . . . . . . . . . . |
|||
. . . . . # . . # . # # . . . . . . # # . # . . . . . . . . . . . # . . # . . . . . . . . . . . . . |
|||
. . . . . . # # . # # . . . . . . . . # # . . . . . . . . . . . . . # # . . . . . . . . . . . . . . |
|||
. . . . . . # # # # # . . . . . . . . # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . |
|||
. . . . . . . # . . # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . # # . . . . . . . . |
|||
. . . . . . . . # # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . # # . . . . . . . |
|||
. . . . . . . . # # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . # # . . . . . . . |
|||
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . # # # . . . . . . . |
|||
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . # # . . . . . . . . |
|||
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . # . # # . . . . . . # # # |
|||
# . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . # # . . . . . . # . . . |
|||
# . . . . . . # # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . # # . # |
|||
. . . . . . # . . # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . |
|||
. . . . . . . # # . . . . . . . . . . . . . . . . . . . . . . . . . . . . # # . . . . . . . . . . . |
|||
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . # . # . . . . . . . . . . . |
|||
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . # . . . . . . . . . . . |
|||
# . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . # # # |
|||
# # . . . . . . . # # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . # . # # |
|||
. # # . . . . . . # # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . # # . . # |
|||
. # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . # . # . # # . . . . . . . # # . . . |
|||
# . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . # . # . # . . . . . . . . . # # . . |
|||
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . # . . . . # . # # . . . . . . . . . |
|||
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . # . . . # # # # . . . . . . . . . |
|||
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . # # # . . # # . . . . . . . . . |
|||
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . |
|||
. . . . . . . . . . . . . . # # # . . . . . . . . . . . . . . . . . . . . . . . . # . . . . . . . . |
|||
. . . . . . . . . . . . . . . . . . . . . . . . . . . . # # # . . . . . . . . . . # . . . . . . . . |
|||
. . . . . . . . . . . . # . . . . . . . . . . . . . . # . # . # # . # . . . . . . # . . . . . . . . |
|||
. . . . . . . . . . . . # . . . . . . . . . . . . . # . . . . . . . . # . . . . . . . . . . . . . . |
|||
. . . . . . . . . . . . # . . . . . . . . . . . . # . . # # # # . . . # . . . . . . . . . . . . . . |
|||
. . . . . . . . . . . . . . . . . . . . . . . . . # # # . . . . . . # . . . . . . . . . . . . . . . |
|||
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . # # . |
|||
. . . . . . . . . . . . . . . . . # # . . . . . . . . . . . . . . . . . . . . . . . . . . . # . # . |
|||
. . . . . . . . . . . . # # . . # . . . . . . . . . . . . . . . . . . . . . . . . . . . . # . . # . |
|||
. . . . . . . . . . . . # . # . . # . . . . . . . . . . . . . . # . . . . . . . . . . . . # . . . . |
|||
. . . . . . . . . . . . # . . . . . . # . . . . . . . . . . . # . . # . . . . . . . . . # # . # . . |
|||
. . . . . . . . . . . . . . . . . . # . . . . . . . . . . . . . # # . . . . . . . . . . . . . . # # |
|||
. . . . . . . . . . . # . # . . . . # . . # . . . . . . . . . . . . . . . . . . . . . . . # # # . # |
|||
</pre> |
|||
=={{header|Forth}}== |
=={{header|Forth}}== |
||
'''gencell''' uses an optimization for the core Game of Life rules: new state = (old state | neighbors == 3). |
'''gencell''' uses an optimization for the core Game of Life rules: new state = (old state | neighbors == 3). |
||
<syntaxhighlight lang="forth"> \ The fast wrapping requires dimensions that are powers of 2. |
|||
1 6 lshift constant w \ 64 |
1 6 lshift constant w \ 64 |
||
1 4 lshift constant h \ 16 |
1 4 lshift constant h \ 16 |
||
Line 829: | Line 6,862: | ||
** |
** |
||
* |
* |
||
Generation 1 ok |
Generation 1 ok</syntaxhighlight> |
||
=={{header|Fortran}}== |
=={{header|Fortran}}== |
||
{{works with|Fortran|90 and later}} |
{{works with|Fortran|90 and later}} |
||
< |
<syntaxhighlight lang="fortran"> PROGRAM LIFE_2D |
||
IMPLICIT NONE |
IMPLICIT NONE |
||
Line 853: | Line 6,886: | ||
CALL Drawgen(cells(1:gridsize, 1:gridsize), generation) |
CALL Drawgen(cells(1:gridsize, 1:gridsize), generation) |
||
DO generation = 1, 8 |
DO generation = 1, 8 |
||
CALL |
CALL NextgenV2(cells) |
||
CALL Drawgen(cells(1:gridsize, 1:gridsize), generation) |
CALL Drawgen(cells(1:gridsize, 1:gridsize), generation) |
||
END DO |
END DO |
||
Line 877: | Line 6,910: | ||
END SUBROUTINE Drawgen |
END SUBROUTINE Drawgen |
||
SUBROUTINE Nextgen(cells) |
|||
LOGICAL, INTENT(IN OUT) :: cells(0:,0:) |
LOGICAL, INTENT(IN OUT) :: cells(0:,0:) |
||
LOGICAL :: buffer(0:SIZE(cells, 1)-1, 0:SIZE(cells, 2)-1) |
LOGICAL :: buffer(0:SIZE(cells, 1)-1, 0:SIZE(cells, 2)-1) |
||
INTEGER :: neighbours, i, j |
INTEGER :: neighbours, i, j |
||
buffer = cells ! Store current status |
buffer = cells ! Store current status |
||
DO |
DO j = 1, SIZE(cells, 2)-2 |
||
DO |
DO i = 1, SIZE(cells, 1)-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 DO |
END DO |
||
END SUBROUTINE Nextgen |
END SUBROUTINE Nextgen |
||
!########################################################################### |
|||
FUNCTION Getneighbours(neighbourhood) |
|||
! In this version instead of cycling through all points an integer array |
|||
INTEGER :: Getneighbours |
|||
! is used the sum the live neighbors of all points. The sum is done with |
|||
LOGICAL, INTENT(IN) :: neighbourhood(:,:) |
|||
! the entire array cycling through the eight positions of the neighbors. |
|||
INTEGER :: k |
|||
! Executing a grid size of 10000 in 500 generations this version gave a |
|||
! speedup of almost 4 times. |
|||
Getneighbours = 0 |
|||
!########################################################################### |
|||
DO k = 1, 3 |
|||
PURE SUBROUTINE NextgenV2(cells) |
|||
IF (neighbourhood(1,k)) Getneighbours = Getneighbours + 1 |
|||
LOGICAL, INTENT(IN OUT) :: cells(:,:) |
|||
END DO |
|||
INTEGER(KIND=1) :: buffer(1:SIZE(cells, 1)-2,1:SIZE(cells, 2)-2) |
|||
DO k = 1, 3, 2 |
|||
INTEGER :: gridsize, i, j |
|||
IF (neighbourhood(2,k)) Getneighbours = Getneighbours + 1 |
|||
gridsize=SIZE(cells, 1) |
|||
buffer=0 |
|||
IF (neighbourhood(3,k)) Getneighbours = Getneighbours + 1 |
|||
DO j=-1, 1 |
|||
END FUNCTION Getneighbours |
|||
DO i=-1,1 |
|||
IF(i==0 .AND. j==0) CYCLE |
|||
END PROGRAM LIFE_2D</lang> |
|||
WHERE(cells(i+2:gridsize-i-1,j+2:gridsize-j-1)) buffer=buffer+1 |
|||
=== Sample output: === |
|||
END DO |
|||
END DO |
|||
WHERE(buffer<2 .or. buffer>3) cells(2:gridsize-1,2:gridsize-1) = .FALSE. |
|||
WHERE(buffer==3) cells(2:gridsize-1,2:gridsize-1) = .TRUE. |
|||
END SUBROUTINE NextgenV2 |
|||
!########################################################################### |
|||
END PROGRAM LIFE_2D</syntaxhighlight> |
|||
{{out}} |
|||
<pre style="height:45ex;overflow:scroll"> |
<pre style="height:45ex;overflow:scroll"> |
||
Blinker |
Blinker |
||
Line 1,044: | Line 7,088: | ||
### |
### |
||
### |
### |
||
</pre> |
|||
=={{header|Frink}}== |
|||
Simple solution using two dictionaries (a display and a toggle) to store grid and track changes. The program outputs an animation of the game. |
|||
<syntaxhighlight lang="frink"> |
|||
start = now[] |
|||
// Generate a random 10x10 grid with "1" being on and "0" being off |
|||
instructions = ["1000100110","0001100010","1000111101","1001111110","0000110011","1111000001","0100001110","1011101001","1001011000","1101110111"] |
|||
// Create dictionary of starting positions. |
|||
rowCounter = 0 |
|||
display = new dict |
|||
for instructionStr = instructions |
|||
{ |
|||
rowCounter = rowCounter + 1 |
|||
columnCounter = 0 |
|||
instructionArr = charList[instructionStr] |
|||
for instruction = instructionArr |
|||
{ |
|||
columnCounter = columnCounter + 1 |
|||
arr = [rowCounter,columnCounter] |
|||
if instruction == "1" |
|||
display@arr = 1 |
|||
else |
|||
display@arr = 0 |
|||
} |
|||
} |
|||
// Create toggle dictionary to track changes. It starts off with everything off. |
|||
toggle = new dict |
|||
multifor[x,y] = [new range[1,10],new range[1,10]] |
|||
{ |
|||
arr = [x,y] |
|||
toggle@arr = 0 |
|||
} |
|||
// Animate the game of life |
|||
a = new Animation[3/s] |
|||
win = undef |
|||
// Loop through 10 changes to the grid. The starting points will tick down to two stable unchanging shapes in 10 steps. |
|||
for i = 1 to 12 // 12 steps so animation will pause on final state. |
|||
{ |
|||
// Graphics item for this frame of the animation. |
|||
g = new graphics |
|||
g.backgroundColor[1,1,1] |
|||
// Add in a transparent shape to prevent the image from jiggle to automatic scaling. |
|||
g.color[0,0,0,0] // Transparent black |
|||
g.fillRectSides[-1, -1, 12, 12] // Set minimum size |
|||
g.clipRectSides[-1, -1, 12, 12] // Set maximum size |
|||
g.color[0,0,0] // Color back to default black |
|||
multifor[x1,y1] = [new range[1,10],new range[1,10]] |
|||
{ |
|||
tval = [x1,y1] |
|||
// This is programmed with a hard edge. Points beyond the border aren't considered. |
|||
xmax = min[x1+1,10] |
|||
xmin = max[x1-1,1] |
|||
ymax = min[y1+1,10] |
|||
ymin = max[y1-1,1] |
|||
// Range will be 8 surrounding cells or cells up to border. |
|||
pointx = new range[xmin,xmax] |
|||
pointy = new range[ymin,ymax] |
|||
pointsum = 0 |
|||
status = 0 |
|||
// Process each surrounded point |
|||
multifor[x2,y2] = [pointx,pointy] |
|||
{ |
|||
// Assign the array to a variable so it can be used as a dictionary reference. |
|||
point = [x2,y2] |
|||
if x2 == x1 && y2 == y1 |
|||
{ |
|||
status = display@point |
|||
} else // Calculate the total of surrounding points |
|||
{ |
|||
pointsum = pointsum + display@point |
|||
} |
|||
} |
|||
// Animate if the point is on. |
|||
if status == 1 |
|||
{ |
|||
g.color[0,0,0] |
|||
g.fillEllipseCenter[x1,y1,1,1] |
|||
} |
|||
toggle@tval = status // This will be overwritten if needed by neighbor check conditions below. |
|||
// Check if off point has 3 on point neighbors |
|||
if status == 0 && pointsum == 3 |
|||
{ |
|||
toggle@tval = 1 |
|||
} |
|||
// Check if on point has between 2 and 3 on point neighbors |
|||
if status == 1 && (pointsum < 2 || pointsum > 3) |
|||
{ |
|||
toggle@tval = 0 |
|||
} |
|||
} |
|||
// Add the current frame to the animation |
|||
a.add[g] |
|||
// Replace the current display with the toggle values. |
|||
for toggleKeys = keys[toggle] |
|||
{ |
|||
val = toggle@toggleKeys |
|||
display@toggleKeys = val |
|||
} |
|||
} |
|||
// Write the animation file |
|||
a.write["FrinkAnimationGoL.gif",400,400] |
|||
end = now[] |
|||
println["Program run time: " + ((end - start)*1.0 -> "seconds")] |
|||
</syntaxhighlight> |
|||
=={{header|FunL}}== |
|||
<syntaxhighlight lang="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()</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
···· |
|||
***· |
|||
···· |
|||
···· |
|||
----- |
|||
·*·· |
|||
·*·· |
|||
·*·· |
|||
···· |
|||
----- |
|||
···· |
|||
***· |
|||
···· |
|||
···· |
|||
----- |
|||
·*·· |
|||
··*· |
|||
***· |
|||
···· |
|||
----- |
|||
···· |
|||
*·*· |
|||
·**· |
|||
·*·· |
|||
----- |
|||
···· |
|||
··*· |
|||
*·*· |
|||
·**· |
|||
----- |
|||
···· |
|||
·*·· |
|||
··** |
|||
·**· |
|||
----- |
|||
···· |
|||
··*· |
|||
···* |
|||
·*** |
|||
----- |
|||
</pre> |
|||
=={{header|Furor}}== |
|||
<syntaxhighlight lang="furor"> |
|||
// Life simulator (game). Console (CLI) version. |
|||
// It is a 'cellular automaton', and was invented by Cambridge mathematician John Conway. |
|||
// The Rules |
|||
// For a space that is 'populated': |
|||
// Each cell with one or no neighbors dies, as if by solitude. |
|||
// Each cell with four or more neighbors dies, as if by overpopulation. |
|||
// Each cell with two or three neighbors survives. |
|||
// For a space that is 'empty' or 'unpopulated' |
|||
// Each cell with three neighbors becomes populated. |
|||
// ----------------------------------------------------- |
|||
#g |
|||
// Get the terminal-resolution: |
|||
terminallines -- sto tlin |
|||
terminalcolumns sto tcol |
|||
// ............................. |
|||
// Verify the commandline parameters: |
|||
argc 3 < { #s ."Usage: " 0 argv print SPACE 1 argv print ." lifeshape-file.txt\n" end } |
|||
2 argv 'f !istrue { #s ."The given file ( " 2 argv print ." ) doesn't exist!\n" end } |
|||
startingshape 2 argv filetolist // read the file into the list |
|||
startingshape maxlinelength sto maxlinlen |
|||
neighbour @tlin @tcol createlist // Generate the stringarray for the neighbour-calculations |
|||
livingspace @tlin @tcol createlist // Generate the stringarray for the actual generations |
|||
cellscreen @tlin @tcol createscreen // Generate the stringarray for the visible livingspace |
|||
// Calculate offset for the shape ( it must be put to the centre): |
|||
@tlin startingshape~ - 2 / sto originlin |
|||
@tcol @maxlinlen - 2 / sto origincol |
|||
startingshape {{| |
|||
{{}} {{|}} {{-}} [[]] 32 > { 1 }{ 0 } sto emblem |
|||
livingspace @originlin {{|}} + @origincol {{-}} + @emblem [[^]] |
|||
|}} |
|||
cursoroff |
|||
// ================================================================== |
|||
{... // infinite loop starts |
|||
sbr §renderingsbr |
|||
topleft cellscreen !printlist |
|||
."Generation: " {...} print fflush // print the number of the generations. |
|||
neighbour 0 filluplist // fill up the neighbour list with zero value |
|||
// Calculate neighbourhoods |
|||
neighbour {{| |
|||
{{|}} {{-}} sbr §neighbors |
|||
{{}} {{|}} {{-}} @n [[^]] // store the neighbournumber |
|||
|}} |
|||
// Now, kill everybody if the neighbors are less than 2 or more than 3: |
|||
neighbour {{| |
|||
{{|}} {{-}} sbr §killsbr |
|||
|}} |
|||
// Generate the newborn cells: |
|||
neighbour {{| |
|||
{{}} {{|}} {{-}} [[]] 3 == { livingspace {{|}} {{-}} 1 [[^]] } |
|||
|}} |
|||
50000 usleep |
|||
//2 sleep |
|||
...} // infinite loop ends |
|||
// ================================================================== |
|||
end |
|||
killsbr: |
|||
sto innerindex sto outerindex |
|||
neighbour @outerindex @innerindex [[]] 2 < then §kill |
|||
neighbour @outerindex @innerindex [[]] 3 > then §kill |
|||
rts |
|||
kill: livingspace @outerindex @innerindex 0 [[^]] rts |
|||
// ========================================================== |
|||
neighbors: // This subroutine calculates the quantity of neighborhood |
|||
sto y sto x zero n |
|||
livingspace @x ? @tlin -- @y ? @tcol -- [[]] sum n // upleft corner |
|||
livingspace @x ? @tlin -- @y [[]] sum n // upmid corner |
|||
livingspace @x ? @tlin -- @y ++ dup @tcol == { drop 0 } [[]] sum n // upright corner |
|||
livingspace @x @y ? @tcol -- [[]] sum n // midleft corner |
|||
livingspace @x @y ++ dup @tcol == { drop 0 } [[]] sum n // midright corner |
|||
livingspace @x ++ dup @tlin == { drop 0 } @y ? @tcol -- [[]] sum n // downleft corner |
|||
livingspace @x ++ dup @tlin == { drop 0 } @y [[]] sum n // downmid corner |
|||
livingspace @x ++ dup @tlin == { drop 0 } @y ++ dup @tcol == { drop 0 } [[]] sum n // downright corner |
|||
rts |
|||
// ========================================================== |
|||
renderingsbr: |
|||
livingspace {{| |
|||
cellscreen {{|}} {{-}} |
|||
{{}} {{|}} {{-}} [[]] { '* }{ 32 } [[^]] |
|||
|}} |
|||
rts |
|||
{ „startingshape” } |
|||
{ „livingspace” } |
|||
{ „cellscreen” } |
|||
{ „innerindex” } |
|||
{ „outerindex” } |
|||
{ „maxlinlen” } |
|||
{ „neighbour” } |
|||
{ „originlin” } |
|||
{ „origincol” } |
|||
{ „emblem” } |
|||
{ „tlin” } |
|||
{ „tcol” } |
|||
{ „x” } { „y” } { „n” } |
|||
</syntaxhighlight> |
|||
=={{header|Peri}}== |
|||
<syntaxhighlight lang="peri"> |
|||
###sysinclude standard.uh |
|||
###sysinclude args.uh |
|||
###sysinclude str.uh |
|||
###sysinclude system.uh |
|||
// Life simulator (game). |
|||
// It is a 'cellular automaton', and was invented by Cambridge mathematician John Conway. |
|||
// The Rules |
|||
// For a space that is 'populated': |
|||
// Each cell with one or no neighbors dies, as if by solitude. |
|||
// Each cell with four or more neighbors dies, as if by overpopulation. |
|||
// Each cell with two or three neighbors survives. |
|||
// For a space that is 'empty' or 'unpopulated' |
|||
// Each cell with three neighbors becomes populated. |
|||
// -------------------------------------------------------------------------------------------- |
|||
#g |
|||
// Get the terminal-resolution: |
|||
terminallines -- sto tlin |
|||
terminalcolumns sto tcol |
|||
// ............................. |
|||
// Verify the commandline parameters: |
|||
argc 3 < { #s ."Usage: " 0 argv print SPACE 1 argv print ." lifeshape-file.txt\n" end } |
|||
2 argv 'f inv istrue { #s ."The given file ( " 2 argv print ." ) doesn't exist!\n" end } |
|||
2 argv filetolist sto startingshape // read the file into the list |
|||
[[startingshape]]~~~ sto maxlinlen |
|||
@tlin @tcol [[mem]] sto neighbour // Generate the stringarray for the neighbour-calculations |
|||
@tlin @tcol [[mem]] sto livingspace // Generate the stringarray for the actual generations |
|||
@tlin @tcol [[mem]]! sto cellscreen // Generate the stringarray for the visible livingspace |
|||
// Calculate offset for the shape ( it must be put to the centre): |
|||
@tlin startingshape~ - 2 / sto originlin |
|||
@tcol @maxlinlen - 2 / sto origincol |
|||
startingshape~ shaperead: {{ {{}} [[startingshape]]~ {{ {{}}§shaperead {{}} [[startingshape]][] |
|||
32 > { 1 }{ 0 } sto emblem |
|||
@originlin {{}}§shaperead + @origincol {{}} + @emblem inv [[livingspace]][] |
|||
}} |
|||
}} |
|||
cursoroff |
|||
// ================================================================== |
|||
{.. // infinite loop starts |
|||
sbr §renderingsbr |
|||
topleft @cellscreen ![[print]] |
|||
."Generation: " {..} print fflush // print the number of the generations. |
|||
0 [[neighbour]][^] // fill up the neighbour list with zero value |
|||
// Calculate neighbourhoods |
|||
@tlin linloop: {{ // loop for every lines |
|||
@tcol {{ // loop for every columns |
|||
{{}}§linloop {{}} sbr §neighbors |
|||
{{}}§linloop {{}} @nn inv [[neighbour]][] // store the neighbournumber |
|||
}} }} |
|||
// Now, kill everybody if the neighbors are less than 2 or more than 3: |
|||
@tlin linloop2: {{ // loop for every lines |
|||
@tcol {{ // loop for every columns |
|||
{{}}§linloop2 {{}} [[neighbour]][] 2 < then §kill |
|||
{{}}§linloop2 {{}} [[neighbour]][] 3 > then §kill |
|||
{{<}} // Continue the inner loop |
|||
kill: {{}}§linloop2 {{}} 0 inv [[livingspace]][] |
|||
}} }} |
|||
@tlin linloop3: {{ // loop for every lines |
|||
@tcol {{ // loop for every columns |
|||
// Generate the newborn cells: |
|||
{{}}§linloop3 {{}} [[neighbour]][] 3 == { {{}}§linloop3 {{}} 1 inv [[livingspace]][] } |
|||
}} }} |
|||
50000 inv sleep |
|||
..} // infinite loop ends |
|||
// ================================================================== |
|||
end |
|||
// ========================================================== |
|||
neighbors: // This subroutine calculates the quantity of neighborhood |
|||
sto xx sto yy zero nn |
|||
@yy ? @tlin -- @xx ? @tcol -- [[livingspace]][] sum nn // upleft corner |
|||
@yy @xx ? @tcol -- [[livingspace]][] sum nn // upmid corner |
|||
@yy ++? tlin @xx ? @tcol -- [[livingspace]][] sum nn // upright corner |
|||
@yy ? @tlin -- @xx [[livingspace]][] sum nn // midleft corner |
|||
@yy ++? tlin @xx [[livingspace]][] sum nn // midright corner |
|||
@yy ? @tlin -- @xx ++? tcol [[livingspace]][] sum nn // downleft corner |
|||
@yy @xx ++? tcol [[livingspace]][] sum nn // downmid corner |
|||
@yy ++? tlin @xx ++? tcol [[livingspace]][] sum nn // downright corner |
|||
rts |
|||
// ========================================================== |
|||
renderingsbr: |
|||
@tlin livingspaceloop: {{ @tcol {{ |
|||
{{}}§livingspaceloop {{}} |
|||
{{}}§livingspaceloop {{}} [[livingspace]][] { '* }{ 32 } inv [[cellscreen]][] |
|||
}} |
|||
}} |
|||
rts |
|||
{ „tlin” } |
|||
{ „tcol” } |
|||
{ „startingshape” } |
|||
{ „maxlinlen” } |
|||
{ „livingspace” } |
|||
{ „neighbour” } |
|||
{ „originlin” } |
|||
{ „origincol” } |
|||
{ „emblem” } |
|||
{ „cellscreen” } |
|||
{ „xx” } { „yy” } { „nn” } |
|||
</syntaxhighlight> |
|||
=={{header|Futhark}}== |
|||
{{incorrect|Futhark|Futhark's syntax has changed, so this example will not compile}} |
|||
<syntaxhighlight lang="futhark"> |
|||
fun bint(b: bool): int = if b then 1 else 0 |
|||
fun intb(x: int): bool = if x == 0 then False else True |
|||
fun to_bool_board(board: [][]int): [][]bool = |
|||
map (fn (r: []int): []bool => map intb r) board |
|||
fun to_int_board(board: [][]bool): [][]int = |
|||
map (fn (r: []bool): []int => map bint r) board |
|||
fun cell_neighbors(i: int, j: int, board: [n][m]bool): int = |
|||
unsafe |
|||
let above = (i - 1) % n |
|||
let below = (i + 1) % n |
|||
let right = (j + 1) % m |
|||
let left = (j - 1) % m in |
|||
bint board[above,left] + bint board[above,j] + bint board[above,right] + |
|||
bint board[i,left] + bint board[i,right] + |
|||
bint board[below,left] + bint board[below,j] + bint board[below,right] |
|||
fun all_neighbours(board: [n][m]bool): [n][m]int = |
|||
map (fn (i: int): []int => |
|||
map (fn (j: int): int => cell_neighbors(i,j,board)) (iota m)) |
|||
(iota n) |
|||
fun iteration(board: [n][m]bool): [n][m]bool = |
|||
let lives = all_neighbours(board) in |
|||
zipWith (fn (lives_r: []int) (board_r: []bool): []bool => |
|||
zipWith (fn (neighbors: int) (alive: bool): bool => |
|||
if neighbors < 2 |
|||
then False |
|||
else if neighbors == 3 then True |
|||
else if alive && neighbors < 4 then True |
|||
else False) |
|||
lives_r board_r) |
|||
lives board |
|||
fun main(int_board: [][]int, iterations: int): [][]int = |
|||
-- We accept the board as integers for convenience, and then we |
|||
-- convert to booleans here. |
|||
let board = to_bool_board int_board in |
|||
loop (board) = for i < iterations do |
|||
iteration board in |
|||
to_int_board board |
|||
</syntaxhighlight> |
|||
=={{header|FutureBasic}}== |
|||
This is just the basic central routine. |
|||
<syntaxhighlight lang="futurebasic"> |
|||
Short a(4, 4), c(4, 4) // Initialize arrays of cells and a working copy |
|||
void local fn seed |
|||
Short x, y |
|||
// Blinker |
|||
a(1, 2) = 1 : a(2, 2) = 1 : a(3, 2) = 1 |
|||
for y = 1 to 3 : for x = 1 to 3 |
|||
print a(x, y); // Draw array |
|||
next : print : next : print |
|||
end fn |
|||
void local fn nextGen |
|||
Short x, y, dx, dy, n |
|||
// Calculate next generation on temporary board |
|||
for y = 1 to 3 : for x = 1 to 3 |
|||
c(x, y) = 0 // Initialize |
|||
n = -a(x, y) // Don't count center cell |
|||
for dy = -1 to 1 : for dx = -1 to 1 |
|||
n += a(x + dx, y + dy) // Count the neighbours |
|||
next : next |
|||
c(x, y) = ( n == 3 ) or ( n == 2 and a(x, y) ) // Conway’s rule |
|||
next : next |
|||
// Copy temp array to actual array and draw |
|||
for y = 1 to 3 : for x = 1 to 3 |
|||
a(x, y) = c(x, y) // Copy |
|||
print a(x, y); // Draw |
|||
next : print : next : print |
|||
end fn |
|||
fn seed |
|||
fn nextGen |
|||
fn nextGen |
|||
handleevents // Go into Mac event loop |
|||
</syntaxhighlight> |
|||
Just three generations, as requested: |
|||
<pre> |
|||
000 |
|||
111 |
|||
000 |
|||
010 |
|||
010 |
|||
010 |
|||
000 |
|||
111 |
|||
000 |
|||
</pre> |
|||
=={{header|Go}}== |
|||
<syntaxhighlight lang="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) |
|||
} |
|||
}</syntaxhighlight> |
|||
Running this program will compute and draw the first 300 "frames". The final frame looks like this: |
|||
<pre> |
|||
** **** * |
|||
* ** |
|||
* ** |
|||
* * * |
|||
* ** **** ** * |
|||
* ** * ** |
|||
* ** * |
|||
** * |
|||
**** * * ** * |
|||
*** ** * ** **** * * ** |
|||
* ** * ** * ** ** |
|||
* *** *** |
|||
* ** |
|||
** ** ** |
|||
** * * * * |
|||
</pre> |
|||
=={{header|Groovy}}== |
|||
<syntaxhighlight lang="groovy"> |
|||
class GameOfLife { |
|||
int generations |
|||
int dimensions |
|||
def board |
|||
GameOfLife(generations = 5, dimensions = 5) { |
|||
this.generations = generations |
|||
this.dimensions = dimensions |
|||
this.board = createBlinkerBoard() |
|||
} |
|||
static def createBlinkerBoard() { |
|||
[ |
|||
[].withDefault{0}, |
|||
[0,0,1].withDefault{0}, |
|||
[0,0,1].withDefault{0}, |
|||
[0,0,1].withDefault{0} |
|||
].withDefault{[]} |
|||
} |
|||
static def createGliderBoard() { |
|||
[ |
|||
[].withDefault{0}, |
|||
[0,0,1].withDefault{0}, |
|||
[0,0,0,1].withDefault{0}, |
|||
[0,1,1,1].withDefault{0} |
|||
].withDefault{[]} |
|||
} |
|||
static def getValue(board, point) { |
|||
def x,y |
|||
(x,y) = point |
|||
if(x < 0 || y < 0) { |
|||
return 0 |
|||
} |
|||
board[x][y] ? 1 : 0 |
|||
} |
|||
static def countNeighbors(board, point) { |
|||
def x,y |
|||
(x,y) = point |
|||
def neighbors = 0 |
|||
neighbors += getValue(board, [x-1,y-1]) |
|||
neighbors += getValue(board, [x-1,y]) |
|||
neighbors += getValue(board, [x-1,y+1]) |
|||
neighbors += getValue(board, [x,y-1]) |
|||
neighbors += getValue(board, [x,y+1]) |
|||
neighbors += getValue(board, [x+1,y-1]) |
|||
neighbors += getValue(board, [x+1,y]) |
|||
neighbors += getValue(board, [x+1,y+1]) |
|||
neighbors |
|||
} |
|||
static def conwaysRule(currentValue, neighbors) { |
|||
def newValue = 0 |
|||
if(neighbors == 3 || (currentValue && neighbors == 2)) { |
|||
newValue = 1 |
|||
} |
|||
newValue |
|||
} |
|||
static def createNextGeneration(currentBoard, dimensions) { |
|||
def newBoard = [].withDefault{[].withDefault{0}} |
|||
(0..(dimensions-1)).each { row -> |
|||
(0..(dimensions-1)).each { column -> |
|||
def point = [row, column] |
|||
def currentValue = getValue(currentBoard, point) |
|||
def neighbors = countNeighbors(currentBoard, point) |
|||
newBoard[row][column] = conwaysRule(currentValue, neighbors) |
|||
} |
|||
} |
|||
newBoard |
|||
} |
|||
static def printBoard(generationCount, board, dimensions) { |
|||
println "Generation ${generationCount}" |
|||
println '*' * 80 |
|||
(0..(dimensions-1)).each { row -> |
|||
(0..(dimensions-1)).each { column -> |
|||
print board[row][column] ? 'X' : '.' |
|||
} |
|||
print System.getProperty('line.separator') |
|||
} |
|||
println '' |
|||
} |
|||
def start() { |
|||
(1..generations).each { generation -> |
|||
printBoard(generation, this.board, this.dimensions) |
|||
this.board = createNextGeneration(this.board, this.dimensions) |
|||
} |
|||
} |
|||
} |
|||
// Blinker |
|||
def game = new GameOfLife() |
|||
game.start() |
|||
// Glider |
|||
game = new GameOfLife(10, 10) |
|||
game.board = game.createGliderBoard() |
|||
game.start() |
|||
</syntaxhighlight> |
|||
The output of this program: |
|||
<pre style="height:40ex;overflow:scroll"> |
|||
Generation 1 |
|||
******************************************************************************** |
|||
..... |
|||
..X.. |
|||
..X.. |
|||
..X.. |
|||
..... |
|||
Generation 2 |
|||
******************************************************************************** |
|||
..... |
|||
..... |
|||
.XXX. |
|||
..... |
|||
..... |
|||
Generation 3 |
|||
******************************************************************************** |
|||
..... |
|||
..X.. |
|||
..X.. |
|||
..X.. |
|||
..... |
|||
Generation 4 |
|||
******************************************************************************** |
|||
..... |
|||
..... |
|||
.XXX. |
|||
..... |
|||
..... |
|||
Generation 5 |
|||
******************************************************************************** |
|||
..... |
|||
..X.. |
|||
..X.. |
|||
..X.. |
|||
..... |
|||
Generation 1 |
|||
******************************************************************************** |
|||
.......... |
|||
..X....... |
|||
...X...... |
|||
.XXX...... |
|||
.......... |
|||
.......... |
|||
.......... |
|||
.......... |
|||
.......... |
|||
.......... |
|||
Generation 2 |
|||
******************************************************************************** |
|||
.......... |
|||
.......... |
|||
.X.X...... |
|||
..XX...... |
|||
..X....... |
|||
.......... |
|||
.......... |
|||
.......... |
|||
.......... |
|||
.......... |
|||
Generation 3 |
|||
******************************************************************************** |
|||
.......... |
|||
.......... |
|||
...X...... |
|||
.X.X...... |
|||
..XX...... |
|||
.......... |
|||
.......... |
|||
.......... |
|||
.......... |
|||
.......... |
|||
Generation 4 |
|||
******************************************************************************** |
|||
.......... |
|||
.......... |
|||
..X....... |
|||
...XX..... |
|||
..XX...... |
|||
.......... |
|||
.......... |
|||
.......... |
|||
.......... |
|||
.......... |
|||
Generation 5 |
|||
******************************************************************************** |
|||
.......... |
|||
.......... |
|||
...X...... |
|||
....X..... |
|||
..XXX..... |
|||
.......... |
|||
.......... |
|||
.......... |
|||
.......... |
|||
.......... |
|||
Generation 6 |
|||
******************************************************************************** |
|||
.......... |
|||
.......... |
|||
.......... |
|||
..X.X..... |
|||
...XX..... |
|||
...X...... |
|||
.......... |
|||
.......... |
|||
.......... |
|||
.......... |
|||
Generation 7 |
|||
******************************************************************************** |
|||
.......... |
|||
.......... |
|||
.......... |
|||
....X..... |
|||
..X.X..... |
|||
...XX..... |
|||
.......... |
|||
.......... |
|||
.......... |
|||
.......... |
|||
Generation 8 |
|||
******************************************************************************** |
|||
.......... |
|||
.......... |
|||
.......... |
|||
...X...... |
|||
....XX.... |
|||
...XX..... |
|||
.......... |
|||
.......... |
|||
.......... |
|||
.......... |
|||
Generation 9 |
|||
******************************************************************************** |
|||
.......... |
|||
.......... |
|||
.......... |
|||
....X..... |
|||
.....X.... |
|||
...XXX.... |
|||
.......... |
|||
.......... |
|||
.......... |
|||
.......... |
|||
Generation 10 |
|||
******************************************************************************** |
|||
.......... |
|||
.......... |
|||
.......... |
|||
.......... |
|||
...X.X.... |
|||
....XX.... |
|||
....X..... |
|||
.......... |
|||
.......... |
|||
.......... |
|||
</pre> |
</pre> |
||
=={{header|Haskell}}== |
=={{header|Haskell}}== |
||
< |
<syntaxhighlight lang="haskell">import Data.Array.Unboxed |
||
type Grid = |
type Grid = UArray (Int,Int) Bool |
||
-- The grid is |
-- The grid is indexed by (y, x). |
||
life :: Int -> Int -> Grid -> Grid |
life :: Int -> Int -> Grid -> Grid |
||
{- Returns the given Grid advanced by one generation. -} |
{- Returns the given Grid advanced by one generation. -} |
||
life w h old = |
life w h old = |
||
listArray |
listArray b (map f (range b)) |
||
where b@((y1,x1),(y2,x2)) = bounds old |
|||
where coords = [(x, y) | y <- [0 .. h - 1], x <- [0 .. w - 1]] |
|||
f (y, x) = ( c && (n == 2 || n == 3) ) || ( not c && n == 3 ) |
|||
f (x, y) | c && (n == 2 || n == 3) = True |
|||
| not c && n == 3 = True |
|||
| otherwise = False |
|||
where c = get x y |
where c = get x y |
||
n = count [get (x + x') (y + y') | |
n = count [get (x + x') (y + y') | |
||
Line 1,066: | Line 8,075: | ||
not (x' == 0 && y' == 0)] |
not (x' == 0 && y' == 0)] |
||
get x y | x < |
get x y | x < x1 || x > x2 = False |
||
| y < |
| y < y1 || y > y2 = False |
||
| otherwise = old ! (x |
| otherwise = old ! (y, x) |
||
count :: [Bool] -> Int |
count :: [Bool] -> Int |
||
count |
count = length . filter id</syntaxhighlight> |
||
count (False : l) = count l |
|||
count (True : l) = 1 + count l</lang> |
|||
Example of use: |
Example of use: |
||
<lang |
<syntaxhighlight lang="haskell">import Data.List (unfoldr) |
||
grid :: [String] -> (Int, Int, Grid) |
|||
grid l = (width, height, a) |
grid l = (width, height, a) |
||
where (width, height) = (length $ head l, length l) |
where (width, height) = (length $ head l, length l) |
||
a = listArray ( |
a = listArray ((1, 1), (height, width)) $ concatMap f l |
||
f = map g |
f = map g |
||
g '.' = False |
g '.' = False |
||
Line 1,092: | Line 8,101: | ||
split :: Int -> [a] -> [[a]] |
split :: Int -> [a] -> [[a]] |
||
split n = takeWhile (not . null) . unfoldr (Just . splitAt n) |
|||
split _ [] = [] |
|||
split n l = a : split n b |
|||
where (a, b) = splitAt n l |
|||
blinker = grid |
blinker = grid |
||
Line 1,116: | Line 8,123: | ||
printGrid w g |
printGrid w g |
||
main = printLife 10 glider</ |
main = printLife 10 glider</syntaxhighlight> |
||
Here's the gridless version. It could probably be improved with some light use of <code>Data.Set</code>, but I leave that as an exercise for the reader. Note that the function <code>lifeStep</code> 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. |
|||
<syntaxhighlight lang="haskell">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</syntaxhighlight> |
|||
=={{header|HolyC}}== |
|||
[https://gist.github.com/bcoles/25d4ebf66fdf5fc5d6e4 Conway's Game of Life in HolyC] for TempleOS ported from [http://rosettacode.org/wiki/Conway's_Game_of_Life#C Conway's_Game_of_Life#C]. |
|||
Also see the [https://web.archive.org/web/20170325003302/http://www.templeos.org/Wb/Demo/Graphics/Life.html TempleOS implementation] of Conway's Game of Life which makes use of the graphics engine. |
|||
=={{header|Icon}} and {{header|Unicon}}== |
|||
<syntaxhighlight lang="icon">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</syntaxhighlight> |
|||
A sample run: |
|||
<pre>->life 3 3 |
|||
Enter the starting pattern, end with EOF |
|||
*** |
|||
--- |
|||
*** |
|||
--- |
|||
--- |
|||
* |
|||
* |
|||
* |
|||
--- |
|||
--- |
|||
*** |
|||
--- |
|||
-> |
|||
</pre> |
|||
=={{header|INTERCAL}}== |
|||
{{works with|https://web.archive.org/web/20010213211806/http://www.webcom.com/nazgul/pit/life.i the Pit|1}} |
|||
=={{header|J}}== |
=={{header|J}}== |
||
'''Solution:''' |
'''Solution:''' |
||
< |
<syntaxhighlight lang="j">pad=: 0,0,~0,.0,.~] |
||
life=: ( |
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,.~]) |
|||
</syntaxhighlight> |
|||
In other words, given a life instance, the next generation can be found by: |
|||
'''Example:''' |
|||
<lang j> life^:0 1 2 #:0 7 0 |
|||
#. adding extra empty cells, surrounding the life instance, |
|||
#. tessellating the result, finding every overlapping 3 by 3 subinstance, |
|||
#. totaling the number of live cells in each subinstance, |
|||
#. treating a subinstance as a live cell [[wp:Iff|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): |
|||
<syntaxhighlight lang="j"> life^:0 1 2 #:0 7 0 |
|||
0 0 0 |
0 0 0 |
||
1 1 1 |
1 1 1 |
||
Line 1,135: | Line 8,316: | ||
0 0 0 |
0 0 0 |
||
1 1 1 |
1 1 1 |
||
0 0 0 |
0 0 0 |
||
</syntaxhighlight> |
|||
'''Example''' (showing start and six following generations of a glider) |
|||
<syntaxhighlight lang="j"> blocks=: (2 2$2) ((7 u:' ▗▖▄▝▐▞▟▘▚▌▙▀▜▛█') {~ #.@,);._3 >.&.-:@$ {. ]</syntaxhighlight> |
|||
<pre style="line-height: 1"> blocks"2 life^:(i.7) 4 5{.#:1 5 3 |
|||
▖▌ |
|||
▝▘ |
|||
▝▄ |
|||
▝▘ |
|||
▚ |
|||
▝▀ |
|||
▗▗ |
|||
▛ |
|||
▗ |
|||
▝▟ |
|||
▖ |
|||
▟▘ |
|||
▗ |
|||
▄▌</pre> |
|||
=={{header|JAMES II/Rule-based Cellular Automata}}== |
=={{header|JAMES II/Rule-based Cellular Automata}}== |
||
{{libheader|JAMES II}} |
{{libheader|JAMES II}} |
||
< |
<syntaxhighlight lang="j2carules">@caversion 1; |
||
dimensions 2; |
dimensions 2; |
||
Line 1,163: | Line 8,372: | ||
ALIVE |
ALIVE |
||
*/ |
*/ |
||
rule{DEAD}:ALIVE{3}->ALIVE;</ |
rule{DEAD}:ALIVE{3}->ALIVE;</syntaxhighlight> |
||
Animated output for the blinker example: |
Animated output for the blinker example: |
||
Line 1,169: | Line 8,378: | ||
=={{header|Java}}== |
=={{header|Java}}== |
||
< |
<syntaxhighlight lang="java">public class GameOfLife{ |
||
public static void main(String[] args){ |
public static void main(String[] args){ |
||
String[] dish= { |
String[] dish= { |
||
Line 1,259: | Line 8,468: | ||
} |
} |
||
} |
} |
||
}</ |
}</syntaxhighlight> |
||
{{out}} |
|||
Output: |
|||
<pre>Generation 0: |
<pre>Generation 0: |
||
_#_ |
_#_ |
||
Line 1,274: | Line 8,483: | ||
_#_</pre> |
_#_</pre> |
||
===Stretch=== |
|||
=={{header|Mathematica}}== |
|||
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. |
|||
<syntaxhighlight lang="java"> |
|||
//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(); |
|||
} |
|||
} |
|||
</syntaxhighlight> |
|||
Glider Gun design. Save it in GOLglidergun.txt and uncomment the setCustomConfig function. |
|||
<pre> |
|||
____________________________________________________________ |
|||
_________________________*__________________________________ |
|||
_______________________*_*__________________________________ |
|||
_____________**______**____________**_______________________ |
|||
____________*___*____**____________**_______________________ |
|||
_**________*_____*___**_____________________________________ |
|||
_**________*___*_**____*_*__________________________________ |
|||
___________*_____*_______*__________________________________ |
|||
____________*___*___________________________________________ |
|||
_____________**_____________________________________________ |
|||
____________________________________________________________ |
|||
____________________________________________________________ |
|||
____________________________________________________________ |
|||
____________________________________________________________ |
|||
____________________________________________________________ |
|||
____________________________________________________________ |
|||
____________________________________________________________ |
|||
____________________________________________________________ |
|||
____________________________________________________________ |
|||
____________________________________________________________ |
|||
</pre> |
|||
===Swing=== |
|||
See [[Conway's Game of Life/Java/Swing]] |
|||
===Java 10=== |
|||
<syntaxhighlight lang="java"> |
|||
import static java.util.List.of; |
|||
class GameOfLife { |
|||
boolean[][] board = new boolean[3][3]; |
|||
GameOfLife() {} |
|||
GameOfLife(String[] board) { |
|||
set((i, j, s) -> board[i].charAt(j * 2) == '■'); |
|||
} |
|||
void set(Setter setter) { |
|||
for (int i = 0; i < board.length; i++) { |
|||
for (int j = 0; j < board[i].length; j++) { |
|||
board[i][j] = setter.set(i, j, board[i][j]); |
|||
} |
|||
} |
|||
} |
|||
void get(Getter getter) { |
|||
set((i, j, s) -> { |
|||
getter.get(i, j, s); |
|||
return s; |
|||
}); |
|||
} |
|||
int countNeighbors(int i, int j) { |
|||
var counter = new Getter() { |
|||
int count; |
|||
@Override |
|||
public void get(int li, int lj, boolean state) { |
|||
if (distance(i, j, li, lj) == 1 && board[li][lj]) |
|||
count++; |
|||
} |
|||
}; |
|||
get(counter); |
|||
return counter.count; |
|||
} |
|||
int distance(int i, int j, int li, int lj) { |
|||
return Math.max( |
|||
Math.abs(i - li), |
|||
Math.abs(j - lj)); |
|||
} |
|||
GameOfLife makeNextGeneration() { |
|||
var n = new GameOfLife(); |
|||
n.set((i, j, s) -> { |
|||
var alive = board[i][j]; |
|||
int c = countNeighbors(i, j); |
|||
if (alive) { |
|||
return c == 2 || c == 3; |
|||
} else { |
|||
return c == 3; |
|||
} |
|||
}); |
|||
return n; |
|||
} |
|||
void print() { |
|||
get((i, j, s) -> { |
|||
if (j == 0) |
|||
System.out.println(); |
|||
System.out.print(s ? "■ " : "□ "); |
|||
}); |
|||
} |
|||
interface Setter { |
|||
boolean set(int i, int j, boolean state); |
|||
} |
|||
interface Getter { |
|||
void get(int i, int j, boolean state); |
|||
} |
|||
public static void main(String[] args) { |
|||
String[] board = { |
|||
"□ ■ □ ", |
|||
"□ ■ □ ", |
|||
"□ ■ □ ", |
|||
}; |
|||
var gol = new GameOfLife(board); |
|||
for (var generation : of(0, 1, 2)) { |
|||
gol.print(); |
|||
System.out.println("\n"); |
|||
gol = gol.makeNextGeneration(); |
|||
} |
|||
} |
|||
} |
|||
</syntaxhighlight> |
|||
Outputs: |
|||
<pre> |
|||
□ ■ □ |
|||
□ ■ □ |
|||
□ ■ □ |
|||
□ □ □ |
|||
■ ■ ■ |
|||
□ □ □ |
|||
□ ■ □ |
|||
□ ■ □ |
|||
□ ■ □ |
|||
</pre> |
|||
=={{header|JavaScript}}== |
|||
{{works with|SpiderMonkey}} |
|||
{{works with|V8}} |
|||
<syntaxhighlight lang="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() { |
|||
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();</syntaxhighlight> |
|||
{{out}} |
|||
<pre style="height:30ex;overflow:scroll">--- |
|||
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 |
|||
</pre> |
|||
{{libheader|HTML5}} |
|||
Essentially the same as the above straight [[JavaScript]] but displayed in an [[HTML5]] Canvas. |
|||
<syntaxhighlight lang="javascript"> |
|||
<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></syntaxhighlight> |
|||
{{out}} for 3x3 Blinker: |
|||
[[File:Blinker.gif]] |
|||
'''More functional style''': |
|||
<syntaxhighlight lang="javascript"> |
|||
const _ = require('lodash'); |
|||
/////////////////// |
|||
// LODASH IMPORT // |
|||
/////////////////// |
|||
// import all lodash functions to the main namespace, but isNaN not to cause conflicts |
|||
_.each(_.keys(_), k => global[k === 'isNaN' ? '_isNaN' : k] = _[k]); |
|||
/////////////// |
|||
// FUNCTIONS // |
|||
/////////////// |
|||
const WORLD_WIDTH = 3, |
|||
WORLD_HEIGHT = 3, |
|||
displayWorld = (world) => console.log(map(world, x => x.join(' ')).join('\n') + '\n'), |
|||
aliveNeighbours = (world, x, y) => chain(range(-1, 2)) |
|||
.reduce((acc, i) => acc.concat(map(range(-1, 2), ii => [i, ii])), []) |
|||
.reject(partial(isEqual, [0, 0])) |
|||
.map(i => { |
|||
try { |
|||
return world[x + i[0]][y + i[1]]; |
|||
} catch (err) { |
|||
return null; |
|||
} |
|||
}) |
|||
.compact() |
|||
.value() |
|||
.length, |
|||
isAlive = (cell, numAliveNeighbours) => (cell === 1 && inRange(numAliveNeighbours, 2, 4)) || (cell === 0 && numAliveNeighbours === 3) ? 1 : 0, |
|||
updateWorld = (world) => map(world, (row, rowidx) => map(row, (cell, colidx) => isAlive(cell, aliveNeighbours(world, rowidx, colidx)))); |
|||
// let world = map(range(WORLD_WIDTH), partial(ary(map, 2), range(WORLD_HEIGHT), partial(random, 0, 1, false))); |
|||
let world = [[0, 0, 0], [1, 1, 1], [0, 0, 0]]; |
|||
setInterval(() => { |
|||
world = updateWorld(world) |
|||
displayWorld(world); |
|||
}, 1000); |
|||
</syntaxhighlight> |
|||
'''ES6 + ''': |
|||
<syntaxhighlight lang="javascript"> |
|||
const alive = 1; |
|||
const dead = 0; |
|||
const conwaysGameOfLife = (game) => { |
|||
const newGame = [] |
|||
for (let y = 0; y < game.length; y += 1) { |
|||
const newRow = [] |
|||
for (let x = 0; x < game[y].length; x += 1) { |
|||
const cell = game[y][x]; |
|||
const prevX = x > 0 ? x - 1 : x; |
|||
const nextX = x < game[y].length - 1 ? x + 2 : x + 1; |
|||
const counter = |
|||
(game[y - 1] ? game[y - 1].slice(prevX, nextX).reduce((acc, v) => acc + v) : 0) + |
|||
(game[y][x - 1] || 0) + (game[y][x + 1] || 0) + |
|||
(game[y + 1] ? game[y + 1].slice(prevX, nextX).reduce((acc, v) => acc + v) : 0) |
|||
cell === alive |
|||
? counter > 1 && counter <= 3 |
|||
? newRow.push(alive) |
|||
: newRow.push(dead) |
|||
: counter === 3 |
|||
? newRow.push(alive) |
|||
: newRow.push(dead) |
|||
} |
|||
newGame.push(newRow) |
|||
} |
|||
return newGame |
|||
} |
|||
const generateGame = (height, width) => { |
|||
return Array.from({ length: height }, (v, k) => ( |
|||
Array.from({ length: width}, (v, k) => { |
|||
return (Math.random() * 100 | 0) < 50 ? dead : alive |
|||
}) |
|||
)) |
|||
} |
|||
const output = (game) =>{ |
|||
process.stdout.write('\033c'); |
|||
let screen = ''; |
|||
for (let i = 0; i < game.length; i += 1) { |
|||
screen += game[i].join('') |
|||
screen += '\n' |
|||
} |
|||
console.log(screen) |
|||
} |
|||
const setup = ((game) => { |
|||
return () => { |
|||
setInterval(() => { |
|||
output(game) |
|||
const newGame = conwaysGameOfLife(game) |
|||
game = newGame |
|||
}, 1000) |
|||
} |
|||
}) |
|||
// for random game |
|||
// const game = generateGame(10, 10) |
|||
// glider test |
|||
const game = [ |
|||
[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] |
|||
]; |
|||
const run = setup(game); |
|||
run() |
|||
</syntaxhighlight> |
|||
=={{header|jq}}== |
|||
{{works with|jq|1.4}} |
|||
In this implementation, a "world" is simply a suitably constructed string as illustrated by world3 and world11 below. The "game" can be played either by creating separate frames (using frames(n)) |
|||
or by calling animation(n; sleep) with sleep approximately equal to the number of milliseconds between refreshes. |
|||
<syntaxhighlight lang="jq"># Notes on the implementation: |
|||
# 1. For efficiency, the implementation requires that the world |
|||
# has boundaries, as illustrated in the examples. |
|||
# 2. For speed, the simulation uses the exploded string. |
|||
# 3. The ASCII values of the "alive" and "empty" symbols are |
|||
# hardcoded: "." => 46; " " => 32 |
|||
# 4. To adjust the refresh rate, adjust the input to "spin". |
|||
def lines: split("\n")|length; |
|||
def cols: split("\n")[0]|length + 1; # allow for the newline |
|||
# Is there a "." (46) at [x,y] relative to position i, |
|||
# assuming the width is w? |
|||
# Input is an array; result is 0 or 1 so we can easily count the total. |
|||
def isAlive(x; y; i; w): if .[i+ w*y + x] == 46 then 1 else 0 end; |
|||
def neighborhood(i;w): |
|||
isAlive(-1; -1; i; w) + isAlive(0; -1; i; w) + isAlive(1; -1; i; w) + |
|||
isAlive(-1; 0; i; w) + isAlive(1; 0; i; w) + |
|||
isAlive(-1; 1; i; w) + isAlive(0; 1; i; w) + isAlive(1; 1; i; w) ; |
|||
# The basic rules: |
|||
def evolve(cell; sum) : |
|||
if cell == 46 then if sum == 2 or sum == 3 then 46 else 32 end |
|||
elif cell == 32 then if sum == 3 then 46 else 32 end |
|||
else cell |
|||
end ; |
|||
# [world, lines, cols] | next(w) => [world, lines, cols] |
|||
def next: |
|||
.[0] as $world | .[1] as $lines | .[2] as $w |
|||
| reduce range(0; $world|length) as $i |
|||
($world; |
|||
.[$i] as $c |
|||
| if $c == 32 or $c == 46 then |
|||
# updates are "simultaneous" i.e. relative to $world, not "." |
|||
($world | neighborhood($i; $w)) as $sum |
|||
| evolve($c; $sum) as $next |
|||
| if $c == $next then . else .[$i] = $next end |
|||
else . |
|||
end ) |
|||
| [., $lines, $w] ; |
|||
</syntaxhighlight> |
|||
'''Animation''': |
|||
<syntaxhighlight lang="jq"># "clear screen": |
|||
def cls: "\u001b[2J"; |
|||
# Input: an integer; 1000 ~ 1 sec |
|||
def spin: |
|||
reduce range(1; 500 * .) as $i |
|||
(0; . + ($i|cos)*($i|cos) + ($i|sin)*($i|sin) ) |
|||
| "" ; |
|||
# Animate n steps; |
|||
# if "sleep" is non-negative then cls and |
|||
# sleep about "sleep" ms between frames. |
|||
def animate(n; sleep): |
|||
if n == 0 then empty |
|||
else (if sleep >= 0 then cls else "" end), |
|||
(.[0]|implode), n, "\n", |
|||
(sleep|spin), |
|||
( next|animate(n-1; sleep) ) |
|||
end ; |
|||
# Input: a string representing the initial state |
|||
def animation(n; sleep): |
|||
[ explode, lines, cols] | animate(n; sleep) ; |
|||
# Input: a string representing the initial state |
|||
def frames(n): animation(n; -1); |
|||
</syntaxhighlight> |
|||
'''Examples''': |
|||
<syntaxhighlight lang="jq">def world3: |
|||
"+---+\n" + |
|||
"| |\n" + |
|||
"|...|\n" + |
|||
"| |\n" + |
|||
"+---+\n" ; |
|||
def world11: |
|||
"+-----------+\n" + |
|||
"| |\n" + |
|||
"| .. |\n" + |
|||
"| ... |\n" + |
|||
"| .. |\n" + |
|||
"| |\n" + |
|||
"+-----------+\n" ;</syntaxhighlight> |
|||
'''Task''': |
|||
<syntaxhighlight lang="jq">world3 | frames(3)</syntaxhighlight> |
|||
{{Out}} |
|||
<div style="overflow:scroll; height:200px;"> |
|||
<syntaxhighlight lang="sh">$ jq -n -r -f Game_of_life.jq |
|||
+---+ |
|||
| | |
|||
|...| |
|||
| | |
|||
+---+ |
|||
3 |
|||
+---+ |
|||
| . | |
|||
| . | |
|||
| . | |
|||
+---+ |
|||
2 |
|||
+---+ |
|||
| | |
|||
|...| |
|||
| | |
|||
+---+ |
|||
1</syntaxhighlight></div> |
|||
'''Animation example''' |
|||
<syntaxhighlight lang="jq"># Animation of 100 frames with approximately 1 second between each update: |
|||
world11 | animation(100; 1000)</syntaxhighlight> |
|||
=={{header|Jsish}}== |
|||
From Javascript, SpiderMonkey entry. |
|||
<syntaxhighlight lang="javascript">/* Conway's game of life, in Jsish */ |
|||
function GameOfLife () { |
|||
this.title = "Conway's Game of Life"; |
|||
this.cls = "\u001B[H\u001B[2J"; |
|||
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(0); |
|||
}; |
|||
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 += " "; |
|||
} |
|||
puts(l); |
|||
} |
|||
}; |
|||
this.start = function() { |
|||
for (var t = 0; t < this.turns; t++) { |
|||
sleep(500); |
|||
printf(this.cls); |
|||
puts(this.title + "\n---\nTurn "+(t+1)); |
|||
this.print(); |
|||
this.nextGen(); |
|||
} |
|||
}; |
|||
} |
|||
var game = new GameOfLife(); |
|||
if (Interp.conf('unitTest')) { |
|||
game.init(3,3,3); |
|||
game.title="---\n3x3 Blinker over three turns."; |
|||
game.board = [ |
|||
[0,0,0], |
|||
[1,1,1], |
|||
[0,0,0]]; |
|||
game.cls=""; |
|||
game.start(); |
|||
} else { |
|||
game.init(3,3,3); |
|||
game.title="---\n3x3 Blinker over three turns."; |
|||
game.board = [ |
|||
[0,0,0], |
|||
[1,1,1], |
|||
[0,0,0]]; |
|||
game.start(); |
|||
game.init(5,10,6); |
|||
game.title="---\n10x6 Glider over five turns."; |
|||
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(); |
|||
var steps = (console.args[0]) ? parseInt(console.args[0]) || 1 : 50; |
|||
game.init(steps, 32,16); |
|||
game.title="---\nRandom 32x16, " + steps + " step" + ((steps === 1) ? "" : "s"); |
|||
game.start(); |
|||
} |
|||
/* |
|||
=!EXPECTSTART!= |
|||
--- |
|||
3x3 Blinker over three turns. |
|||
--- |
|||
Turn 1 |
|||
XXX |
|||
--- |
|||
3x3 Blinker over three turns. |
|||
--- |
|||
Turn 2 |
|||
X |
|||
X |
|||
X |
|||
--- |
|||
3x3 Blinker over three turns. |
|||
--- |
|||
Turn 3 |
|||
XXX |
|||
=!EXPECTEND!= |
|||
*/</syntaxhighlight> |
|||
{{out}} |
|||
<pre>prompt$ jsish -u conwaysGame.jsi |
|||
[PASS] conwaysGame.jsi</pre> |
|||
=={{header|Julia}}== |
|||
{{works with|julia|0.3.5}} |
|||
Using the '''CellularAutomata''' package: https://github.com/natj/CellularAutomata.jl |
|||
<syntaxhighlight lang="cpp">julia> Pkg.add("CellularAutomata") |
|||
INFO: Installing CellularAutomata v0.1.2 |
|||
INFO: Package database updated |
|||
julia> using CellularAutomata |
|||
julia> gameOfLife{T<:Int}(n::T, m::T, gen::T) = CA2d([3], [2,3], int(randbool(n, m)), gen) |
|||
gameOfLife (generic function with 1 method) |
|||
julia> gameOfLife(15, 30, 5) |
|||
30x15x5 Cellular Automaton</syntaxhighlight> |
|||
# ## # ###### ### ### ## |
|||
# #### # # # ## ##### |
|||
## # # ## ### ## # # ## |
|||
# # ##### # # # ## # # # |
|||
## ## # # ## ### |
|||
# #### ## ## ### # # # # # # |
|||
# ## ##### # ##### # ## ### # |
|||
### # ## #### ## # |
|||
#### # ## ## ### ### ### |
|||
## #### ####### # ## # |
|||
# ## ##### ## #### # ##### |
|||
## ## ##### # # # # # # # |
|||
# # ## ## ## ## ##### # |
|||
## # # # # ############# |
|||
# ## # # ### ## ## |
|||
# ### # |
|||
# # ## # # # # |
|||
### # # # ## |
|||
# # # # # # ## ## #### |
|||
### # # # ## # # |
|||
# ## # # # # |
|||
#### # |
|||
## # |
|||
# ## # ## # |
|||
# # |
|||
# # # ## # |
|||
## # # # # # |
|||
# ## # |
|||
## # # ## # # |
|||
# # ## |
|||
# ## |
|||
## # # # # |
|||
## # # # ## # # ##### |
|||
# # ## # # ## |
|||
## ## # # ## |
|||
### # # ## ##### |
|||
# # |
|||
# ## ## |
|||
# ## |
|||
# # # |
|||
# # # # |
|||
##### ## # # ## |
|||
# # # # ## |
|||
# # ### # ### |
|||
# ### #### |
|||
## ### |
|||
#### # # # ## ## |
|||
## # ### # ## ## # |
|||
# ### ## ## # |
|||
## ### # ## |
|||
# # ##### #### #### |
|||
## ## # ##### |
|||
# #### |
|||
# # # |
|||
# ## |
|||
## # # ## |
|||
# # ## ## ## |
|||
# # # # |
|||
##### # ## # # |
|||
# # # # # |
|||
## # ## # |
|||
## # # ### ## |
|||
# # ## # ### # # |
|||
## ## # ## # |
|||
#### # ## |
|||
## # # ## |
|||
# # ## ## |
|||
# ### # #### |
|||
# #### ## |
|||
# # # |
|||
### ## ## # |
|||
## ## ## # ## |
|||
# # ## # # # # |
|||
####### #### ### |
|||
## # |
|||
=== GPU calculation based version === |
|||
Requires a CUDA compatible graphics card and uses the ArrayFire library. |
|||
<syntaxhighlight lang="julia">using ArrayFire |
|||
using Images |
|||
using LinearAlgebra |
|||
const blinker = [0 0 0; 1 1 1; 0 0 0] |
|||
const glider = [0 0 1; 1 0 1; 0 1 1] |
|||
const lwss = [0 1 1 1 1; 1 0 0 0 1; 0 0 0 0 1; 1 0 0 1 0] |
|||
const glidergun = [ |
|||
0 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 0 0 0 0 0 0 0 0 0 0 0 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 0 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 1 1 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0; |
|||
0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0; |
|||
0 1 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0; |
|||
0 1 1 0 0 0 0 0 0 0 0 1 0 0 0 1 0 1 1 0 0 0 0 1 0 1 0 0 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 1 0 0 0 0 0 0 0 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 1 0 0 0 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 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] |
|||
function lifegame(fname, initializer, mapsize=30, imgsteps=50, upscaleratio=20) |
|||
kernel = convert(Array{Float32}, [1 1 1; 1 0 1; 1 1 1]) |> AFArray |
|||
initialstate = zeros(Bool, mapsize, mapsize) |
|||
mid = div(mapsize, 2) |
|||
(xlen, ylen), (halfx, halfy) = size(initializer), div.(size(initializer), 2) |
|||
x1, x2 = mid - halfx, isodd(xlen) ? mid + halfx : mid + halfx - 1 |
|||
y1, y2 = mid - halfy, isodd(ylen) ? mid + halfy : mid + halfy - 1 |
|||
initialstate[x1:x2, y1:y2] .= initializer |
|||
state = initialstate .+ Float32(.0) |> AFArray |
|||
img = zeros(Float32, mapsize * upscaleratio, mapsize * upscaleratio, imgsteps) |
|||
for i in 1:imgsteps |
|||
nb = convolve2(state, kernel, UInt32(0), UInt32(0)) |
|||
a = (nb == 2) |
|||
b = (nb == 3) |
|||
state = ((state .* a .+ b) > 0) + Float32(0) |
|||
frame = imresize(state, ratio=upscaleratio) |
|||
img[:, :, i] .= frame |
|||
end |
|||
save(fname, img) |
|||
end |
|||
lifegame("blinker.gif", blinker) |
|||
lifegame("glider.gif", glider) |
|||
lifegame("lwss.gif", lwss) |
|||
lifegame("glidergun.gif", glidergun, 90, 200) |
|||
</syntaxhighlight> |
|||
=={{header|Kotlin}}== |
|||
This is based on the Go entry but has been altered in several respects. |
|||
In particular, it now allows for the blinker and glider patterns as well as an initially random pattern. |
|||
Also any cells beyond the boundary are now treated as dead as per the current task description. |
|||
The particular random pattern used now needs only 99 generations to reach stability. |
|||
<syntaxhighlight lang="scala">// version 1.2.0 |
|||
import java.util.Random |
|||
val rand = Random(0) // using a seed to produce same output on each run |
|||
enum class Pattern { BLINKER, GLIDER, RANDOM } |
|||
class Field(val w: Int, val h: Int) { |
|||
val s = List(h) { BooleanArray(w) } |
|||
operator fun set(x: Int, y: Int, b: Boolean) { |
|||
s[y][x] = b |
|||
} |
|||
fun next(x: Int, y: Int): Boolean { |
|||
var on = 0 |
|||
for (i in -1..1) { |
|||
for (j in -1..1) { |
|||
if (state(x + i, y + j) && !(j == 0 && i == 0)) on++ |
|||
} |
|||
} |
|||
return on == 3 || (on == 2 && state(x, y)) |
|||
} |
|||
fun state(x: Int, y: Int): Boolean { |
|||
if ((x !in 0 until w) || (y !in 0 until h)) return false |
|||
return s[y][x] |
|||
} |
|||
} |
|||
class Life(val pattern: Pattern) { |
|||
val w: Int |
|||
val h: Int |
|||
var a: Field |
|||
var b: Field |
|||
init { |
|||
when (pattern) { |
|||
Pattern.BLINKER -> { |
|||
w = 3 |
|||
h = 3 |
|||
a = Field(w, h) |
|||
b = Field(w, h) |
|||
a[0, 1] = true |
|||
a[1, 1] = true |
|||
a[2, 1] = true |
|||
} |
|||
Pattern.GLIDER -> { |
|||
w = 4 |
|||
h = 4 |
|||
a = Field(w, h) |
|||
b = Field(w, h) |
|||
a[1, 0] = true |
|||
a[2, 1] = true |
|||
for (i in 0..2) a[i, 2] = true |
|||
} |
|||
Pattern.RANDOM -> { |
|||
w = 80 |
|||
h = 15 |
|||
a = Field(w, h) |
|||
b = Field(w, h) |
|||
for (i in 0 until w * h / 2) { |
|||
a[rand.nextInt(w), rand.nextInt(h)] = true |
|||
} |
|||
} |
|||
} |
|||
} |
|||
fun step() { |
|||
for (y in 0 until h) { |
|||
for (x in 0 until w) { |
|||
b[x, y] = a.next(x, y) |
|||
} |
|||
} |
|||
val t = a |
|||
a = b |
|||
b = t |
|||
} |
|||
override fun toString(): String { |
|||
val sb = StringBuilder() |
|||
for (y in 0 until h) { |
|||
for (x in 0 until w) { |
|||
val c = if (a.state(x, y)) '#' else '.' |
|||
sb.append(c) |
|||
} |
|||
sb.append('\n') |
|||
} |
|||
return sb.toString() |
|||
} |
|||
} |
|||
fun main(args: Array<String>) { |
|||
val lives = listOf( |
|||
Triple(Life(Pattern.BLINKER), 3, "BLINKER"), |
|||
Triple(Life(Pattern.GLIDER), 4, "GLIDER"), |
|||
Triple(Life(Pattern.RANDOM), 100, "RANDOM") |
|||
) |
|||
for ((game, gens, title) in lives) { |
|||
println("$title:\n") |
|||
repeat(gens + 1) { |
|||
println("Generation: $it\n$game") |
|||
Thread.sleep(30) |
|||
game.step() |
|||
} |
|||
println() |
|||
} |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
In the interests of brevity, only generations 0 and 100 are shown for the 'random' pattern: |
|||
<pre> |
|||
BLINKER: |
|||
Generation: 0 |
|||
... |
|||
### |
|||
... |
|||
Generation: 1 |
|||
.#. |
|||
.#. |
|||
.#. |
|||
Generation: 2 |
|||
... |
|||
### |
|||
... |
|||
Generation: 3 |
|||
.#. |
|||
.#. |
|||
.#. |
|||
GLIDER: |
|||
Generation: 0 |
|||
.#.. |
|||
..#. |
|||
###. |
|||
.... |
|||
Generation: 1 |
|||
.... |
|||
#.#. |
|||
.##. |
|||
.#.. |
|||
Generation: 2 |
|||
.... |
|||
..#. |
|||
#.#. |
|||
.##. |
|||
Generation: 3 |
|||
.... |
|||
.#.. |
|||
..## |
|||
.##. |
|||
Generation: 4 |
|||
.... |
|||
..#. |
|||
...# |
|||
.### |
|||
RANDOM: |
|||
Generation: 0 |
|||
....###....#.#....###...#.#..#.###...#..#......#.#..#####......######...##.#.##. |
|||
####..###.#....#.#####.##.....####.##..####.####.........#.#.###...#.##.#.#..... |
|||
..##.##.#.##...#..#.#..#.#.#.#..####.#...#..##....#..##.........#.#..#....#...#. |
|||
..##..#..##.#.#.....#.##.##...#####...##.##.....##...#.....##......###..##.#..## |
|||
......##..........#.#.#.......#..#.##.#.##....#.#...#.#.#.#.....#..#.......#.#.# |
|||
.#.#.....#####..#..##............#.#.#...###..#...##.....#..##...#.#.##.#..##... |
|||
.##.#.#.##.#..#####.....##..#.####..#.#..#...#.#..#...#.#.#.#....#.#..#.#.#..##. |
|||
.#..#..#.....#...###..###.....####..........##.##.####.....#..##..####..#...##.. |
|||
..##.#...#.#..#.#....#..#...##.#..##.......#.#..##..##..##.#.....##.#......#.#.# |
|||
#.######..#.#.#.###.#....###.....#.....#..#......####.#.#..#....#...#.......#.#. |
|||
#...#..###.##....#.#..##..#..#.#.#...#..#....#....##...#..#..#.#....#...##....#. |
|||
#....##.......#.####.##..#....#.#....#....#######.#..####.#..#.#.##....#.#####.. |
|||
...#.......##..##.##......##....#..#.####.......#..#.#..##.###.#.#.#.#..##.....# |
|||
#.##..####....#..#..##..#.#..#..#....#.###.##.....#.....##....#.#..#.##.....##.. |
|||
...#.#..#.#.....###...#..##.#.....#......#...........###...#.#....#..#.#..##.#.# |
|||
Generation: 100 |
|||
................................................................................ |
|||
................................................................................ |
|||
.........................................#...................................... |
|||
......................#.................#.#..................................... |
|||
.##..................#.#................#.#..................................... |
|||
.##..................#.#.................#...#.................................. |
|||
......................#.....................#.#................................. |
|||
...........................................#..#................................. |
|||
............................................##.................................. |
|||
.............##................................................................. |
|||
............#..#................................................................ |
|||
.##..........#.#.......................................##..........#............ |
|||
.##...........#........................................##.........#.#........... |
|||
..........##.......................................................##........... |
|||
..........##.................................................................... |
|||
</pre> |
|||
=={{header|Lambdatalk}}== |
|||
Lambdatalk works in any web browsers coming with javascript and canvas. |
|||
<syntaxhighlight lang="scheme"> |
|||
{center |
|||
{input {@ type="button" value="start random" |
|||
onclick="LIFE.startstop(this,'random')"}} |
|||
{input {@ type="button" value="start cross" |
|||
onclick="LIFE.startstop(this,'cross')"}} |
|||
{canvas {@ id="life" width="330" height="330" |
|||
style="box-shadow:0 0 8px #000"}} |
|||
} |
|||
calling the following javascript code |
|||
{script |
|||
var LIFE = (function() { |
|||
var board = [], |
|||
xmax = 8, ymax = 8, |
|||
interval = null, |
|||
delay = 1000; |
|||
var isdefined = function(x,y) { |
|||
return typeof board[x] !== 'undefined' |
|||
&& typeof board[x][y] !== 'undefined' |
|||
&& board[x][y]; |
|||
}; |
|||
var alive = function(c,n) { |
|||
if (n < 2 || n > 3) c = 0; |
|||
if (n === 3) c = 1; |
|||
return c |
|||
}; |
|||
var neighbours = function(x,y) { |
|||
var n = 0; |
|||
if (isdefined(x-1,y-1)) n++; |
|||
if (isdefined(x ,y-1)) n++; |
|||
if (isdefined(x+1,y-1)) n++; |
|||
if (isdefined(x-1,y )) n++; |
|||
if (isdefined(x+1,y )) n++; |
|||
if (isdefined(x-1,y+1)) n++; |
|||
if (isdefined(x ,y+1)) n++; |
|||
if (isdefined(x+1,y+1)) n++; |
|||
return n |
|||
}; |
|||
var nextGen = function() { |
|||
var next = []; |
|||
for (var x = 0; x < xmax; x++) { |
|||
next[x] = []; |
|||
for (var y = 0; y < ymax; y++) |
|||
next[x][y] = alive( board[x][y], neighbours(x,y) ); |
|||
} |
|||
board = next |
|||
}; |
|||
var print = function(ctx,dw,dh) { |
|||
for (var x = 0; x < xmax; x++) { |
|||
for (var y = 0; y < ymax; y++) { |
|||
ctx.fillStyle = (board[x][y])? "#fff" : "#888"; |
|||
ctx.fillRect(y*dh+1,x*dw+1,dh-1,dw-1) |
|||
} |
|||
} |
|||
}; |
|||
var init = function (type,w,h) { |
|||
xmax = w; |
|||
ymax = h; |
|||
delay = (type === "random")? 100 : 1000; |
|||
for (var x = 0; x < xmax; x++) { |
|||
board[x] = []; |
|||
for (var y = 0; y < ymax; y++) { |
|||
if (type === "random") { |
|||
board[x][y] = Math.round(Math.random()); |
|||
} else { |
|||
board[x][y] = 0; |
|||
if (x === Math.floor(w/2)) board[x][y] = 1; |
|||
if (y === Math.floor(h/2)) board[x][y] = 1; |
|||
} |
|||
} |
|||
} |
|||
}; |
|||
var run = function(ctx,dw,dh) { |
|||
print(ctx,dw,dh); |
|||
nextGen() |
|||
}; |
|||
var startstop = function(id,type) { |
|||
var can = document.getElementById('life').getContext('2d'); |
|||
if (interval === null) { |
|||
id.value = "stop"; |
|||
init(type,33,33); |
|||
interval = window.setInterval( run, delay, can, 10, 10 ); |
|||
} else { |
|||
id.value = (type === "random")? "start_random" : "start_cross"; |
|||
window.clearInterval( interval ); |
|||
interval = null; |
|||
} |
|||
}; |
|||
return { startstop } |
|||
}) (); // end LIFE |
|||
} |
|||
</syntaxhighlight> |
|||
Results can be seen in http://lambdaway.free.fr/lambdaspeech/?view=life |
|||
and in http://lambdaway.free.fr/lambdawalks/?view=conway |
|||
=={{header|Lua}}== |
|||
A slight modernization of my original "life.lua" for Lua 3.x circa 2000 -- heck, we didn't even have for loops back then! :D ''(a copy can be found in the 4.0 source distro if interested in comparing syntax)'' |
|||
<syntaxhighlight lang="lua">local function T2D(w,h) local t={} for y=1,h do t[y]={} for x=1,w do t[y][x]=0 end end return t end |
|||
local Life = { |
|||
new = function(self,w,h) |
|||
return setmetatable({ w=w, h=h, gen=1, curr=T2D(w,h), next=T2D(w,h)}, {__index=self}) |
|||
end, |
|||
set = function(self, coords) |
|||
for i = 1, #coords, 2 do |
|||
self.curr[coords[i+1]][coords[i]] = 1 |
|||
end |
|||
end, |
|||
evolve = function(self) |
|||
local curr, next = self.curr, self.next |
|||
local ym1, y, yp1 = self.h-1, self.h, 1 |
|||
for i = 1, self.h do |
|||
local xm1, x, xp1 = self.w-1, self.w, 1 |
|||
for j = 1, self.w do |
|||
local sum = curr[ym1][xm1] + curr[ym1][x] + curr[ym1][xp1] + |
|||
curr[y][xm1] + curr[y][xp1] + |
|||
curr[yp1][xm1] + curr[yp1][x] + curr[yp1][xp1] |
|||
next[y][x] = ((sum==2) and curr[y][x]) or ((sum==3) and 1) or 0 |
|||
xm1, x, xp1 = x, xp1, xp1+1 |
|||
end |
|||
ym1, y, yp1 = y, yp1, yp1+1 |
|||
end |
|||
self.curr, self.next, self.gen = self.next, self.curr, self.gen+1 |
|||
end, |
|||
render = function(self) |
|||
print("Generation "..self.gen..":") |
|||
for y = 1, self.h do |
|||
for x = 1, self.w do |
|||
io.write(self.curr[y][x]==0 and "□ " or "■ ") |
|||
end |
|||
print() |
|||
end |
|||
end |
|||
}</syntaxhighlight> |
|||
Example usage. Coordinates wrap to simulate an infinite universe, so here a glider/lwss are evolved through one complete period, then advanced forward until returning to starting conditions. |
|||
<syntaxhighlight lang="lua">print("GLIDER:") |
|||
local life = Life:new(5,5) |
|||
life:set({ 2,1, 3,2, 1,3, 2,3, 3,3 }) |
|||
for i = 1, 5 do |
|||
life:render() |
|||
life:evolve() |
|||
end |
|||
for i = 6,20 do life:evolve() end |
|||
life:render() |
|||
print() |
|||
print("LWSS:") |
|||
life = Life:new(10,7) |
|||
life:set({ 2,2, 5,2, 6,3, 2,4, 6,4, 3,5, 4,5, 5,5, 6,5 }) |
|||
for i = 1, 5 do |
|||
life:render() |
|||
life:evolve() |
|||
end |
|||
for i = 6,20 do life:evolve() end |
|||
life:render()</syntaxhighlight> |
|||
{{out}} |
|||
<pre>GLIDER: |
|||
Generation 1: |
|||
□ ■ □ □ □ |
|||
□ □ ■ □ □ |
|||
■ ■ ■ □ □ |
|||
□ □ □ □ □ |
|||
□ □ □ □ □ |
|||
Generation 2: |
|||
□ □ □ □ □ |
|||
■ □ ■ □ □ |
|||
□ ■ ■ □ □ |
|||
□ ■ □ □ □ |
|||
□ □ □ □ □ |
|||
Generation 3: |
|||
□ □ □ □ □ |
|||
□ □ ■ □ □ |
|||
■ □ ■ □ □ |
|||
□ ■ ■ □ □ |
|||
□ □ □ □ □ |
|||
Generation 4: |
|||
□ □ □ □ □ |
|||
□ ■ □ □ □ |
|||
□ □ ■ ■ □ |
|||
□ ■ ■ □ □ |
|||
□ □ □ □ □ |
|||
Generation 5: |
|||
□ □ □ □ □ |
|||
□ □ ■ □ □ |
|||
□ □ □ ■ □ |
|||
□ ■ ■ ■ □ |
|||
□ □ □ □ □ |
|||
Generation 21: |
|||
□ ■ □ □ □ |
|||
□ □ ■ □ □ |
|||
■ ■ ■ □ □ |
|||
□ □ □ □ □ |
|||
□ □ □ □ □ |
|||
LWSS: |
|||
Generation 1: |
|||
□ □ □ □ □ □ □ □ □ □ |
|||
□ ■ □ □ ■ □ □ □ □ □ |
|||
□ □ □ □ □ ■ □ □ □ □ |
|||
□ ■ □ □ □ ■ □ □ □ □ |
|||
□ □ ■ ■ ■ ■ □ □ □ □ |
|||
□ □ □ □ □ □ □ □ □ □ |
|||
□ □ □ □ □ □ □ □ □ □ |
|||
Generation 2: |
|||
□ □ □ □ □ □ □ □ □ □ |
|||
□ □ □ □ □ □ □ □ □ □ |
|||
□ □ □ □ ■ ■ □ □ □ □ |
|||
□ □ ■ ■ □ ■ ■ □ □ □ |
|||
□ □ ■ ■ ■ ■ □ □ □ □ |
|||
□ □ □ ■ ■ □ □ □ □ □ |
|||
□ □ □ □ □ □ □ □ □ □ |
|||
Generation 3: |
|||
□ □ □ □ □ □ □ □ □ □ |
|||
□ □ □ □ □ □ □ □ □ □ |
|||
□ □ □ ■ ■ ■ ■ □ □ □ |
|||
□ □ ■ □ □ □ ■ □ □ □ |
|||
□ □ □ □ □ □ ■ □ □ □ |
|||
□ □ ■ □ □ ■ □ □ □ □ |
|||
□ □ □ □ □ □ □ □ □ □ |
|||
Generation 4: |
|||
□ □ □ □ □ □ □ □ □ □ |
|||
□ □ □ □ ■ ■ □ □ □ □ |
|||
□ □ □ ■ ■ ■ ■ □ □ □ |
|||
□ □ □ ■ ■ □ ■ ■ □ □ |
|||
□ □ □ □ □ ■ ■ □ □ □ |
|||
□ □ □ □ □ □ □ □ □ □ |
|||
□ □ □ □ □ □ □ □ □ □ |
|||
Generation 5: |
|||
□ □ □ □ □ □ □ □ □ □ |
|||
□ □ □ ■ □ □ ■ □ □ □ |
|||
□ □ □ □ □ □ □ ■ □ □ |
|||
□ □ □ ■ □ □ □ ■ □ □ |
|||
□ □ □ □ ■ ■ ■ ■ □ □ |
|||
□ □ □ □ □ □ □ □ □ □ |
|||
□ □ □ □ □ □ □ □ □ □ |
|||
Generation 21: |
|||
□ □ □ □ □ □ □ □ □ □ |
|||
□ ■ □ □ ■ □ □ □ □ □ |
|||
□ □ □ □ □ ■ □ □ □ □ |
|||
□ ■ □ □ □ ■ □ □ □ □ |
|||
□ □ ■ ■ ■ ■ □ □ □ □ |
|||
□ □ □ □ □ □ □ □ □ □ |
|||
□ □ □ □ □ □ □ □ □ □</pre> |
|||
=={{header|ksh}}== |
|||
<syntaxhighlight lang="ksh"> |
|||
#!/bin/ksh |
|||
# # Version AJM 93u+ 2012-08-01 |
|||
# Conway's Game of Life |
|||
# # Variables: |
|||
# |
|||
integer RAND_MAX=32767 |
|||
LIFE="�[07m �[m" |
|||
NULL=" " |
|||
typeset -a char=( "$NULL" "$LIFE" ) |
|||
# # Input x y or default to 30x30, positive integers only |
|||
# |
|||
integer h=${1:-30} ; h=$(( h<=0 ? 30 : h )) # Height (y) |
|||
integer w=${2:-30} ; w=$(( w<=0 ? 30 : w )) # Width (x) |
|||
# # Functions: |
|||
# |
|||
# # Function _display(map, h, w) |
|||
# |
|||
function _display { |
|||
typeset _dmap ; nameref _dmap="$1" |
|||
typeset _h _w ; integer _h=$2 _w=$3 |
|||
typeset _x _y ; integer _x _y |
|||
printf %s "�[H" |
|||
for (( _y=0; _y<_h; _y++ )); do |
|||
for (( _x=0; _x<_w; _x++ )); do |
|||
printf %s "${char[_dmap[_y][_x]]}" |
|||
done |
|||
printf "%s\n" |
|||
done |
|||
} |
|||
# # Function _evolve(h, w) |
|||
# |
|||
_evolve() { |
|||
typeset _h _w ; integer _h=$1 _w=$2 |
|||
typeset _x _y _n _y1 _x1 ; integer _x _y _n _y1 _x1 |
|||
typeset _new _newdef ; typeset -a _new |
|||
for (( _y=0; _y<_h; _y++ )); do |
|||
for (( _x=0; _x<_w; _x++ )); do |
|||
_n=0 |
|||
for (( _y1=_y-1; _y1<=_y+1; _y1++ )); do |
|||
for (( _x1=_x-1; _x1<=_x+1; _x1++ )); do |
|||
(( _map[$(( (_y1 + _h) % _h ))][$(( (_x1 + _w) % _w ))] )) && (( _n++ )) |
|||
done |
|||
done |
|||
(( _map[_y][_x] )) && (( _n-- )) |
|||
_new[_y][_x]=$(( (_n==3) || (_n==2 && _map[_y][_x]) )) |
|||
done |
|||
done |
|||
for (( _y=0; _y<_h; _y++ )); do |
|||
for (( _x=0; _x<_w; _x++ )); do |
|||
_map[_y][_x]=${_new[_y][_x]} |
|||
done |
|||
done |
|||
} |
|||
# # Function _game(h, w) |
|||
# |
|||
function _game { |
|||
typeset _h ; integer _h=$1 |
|||
typeset _w ; integer _w=$2 |
|||
typeset _x _y ; integer _x _y |
|||
typeset -a _map |
|||
for (( _y=0 ; _y<_h ; _y++ )); do |
|||
for (( _x=0 ; _x<_h ; _x++ )); do |
|||
_map[_x][_y]=$(( RANDOM < RAND_MAX / 10 ? 1 : 0 )) # seed map |
|||
done |
|||
done |
|||
while : ; do |
|||
_display _map ${_h} ${_w} |
|||
_evolve ${_h} ${_w} |
|||
sleep 0.2 |
|||
done |
|||
} |
|||
###### |
|||
# main # |
|||
###### |
|||
_game ${h} ${w} |
|||
</syntaxhighlight> |
|||
=={{header|M2000 Interpreter}}== |
|||
<syntaxhighlight lang="m2000 interpreter"> |
|||
Module Life { |
|||
Font "courier new" |
|||
Form 40, 18 |
|||
Cls 3,0 |
|||
Double |
|||
Report 2, "Game of Life" |
|||
Normal |
|||
Cls 5, 2 |
|||
Const Mx=20, My=10 |
|||
Dim A(0 to Mx+1, 0 to My+1)=0 |
|||
Flush |
|||
REM Data (2,2),(2,3),(3,3),(4,3),(5,4),(,3,4),(5,3),(6,2),(8,5),(5,8) |
|||
Data (5,3) |
|||
Data (5,4) |
|||
Data (5,5) |
|||
generation=1 |
|||
While not empty |
|||
(A, B)=Array |
|||
A(A,B)=1 |
|||
End While |
|||
Display() |
|||
Do |
|||
k$=Key$ |
|||
If k$=chr$(13) Then exit |
|||
A()=@GetNext() |
|||
refresh 500 |
|||
Display() |
|||
Until A()#Sum()=0 |
|||
K$=Key$ |
|||
Cls, 0 |
|||
End |
|||
Function GetNext() |
|||
Local B() |
|||
B()=A() ' copy array |
|||
Local B=B() ' get a pointer |
|||
B*=0 ' make all element zero |
|||
Local i, j, tot |
|||
For j=1 to My |
|||
For i=1 to Mx |
|||
tot=-A(i,j) |
|||
For k=j-1 to j+1 |
|||
For m=i-1 to i+1 |
|||
tot+=A(m, k) |
|||
Next |
|||
Next |
|||
If A(i,j)=1 Then |
|||
If tot=2 or tot=3 Then B(i,j)=1 |
|||
Else.if tot=3 Then |
|||
B(i,j)=1 |
|||
End If |
|||
Next |
|||
Next |
|||
=B() |
|||
End Function |
|||
Sub Display() |
|||
Cursor 0,2 |
|||
move ! ' move graphic to character cursor |
|||
Fill scale.x, scale.y-pos.Y, 1, 5 |
|||
Print "Generation:";Generation |
|||
Generation++ |
|||
Local i, j |
|||
For j=1 to My |
|||
Print @(width div 2-Mx div 2); |
|||
For i=1 to Mx |
|||
Print If$(A(I,J)=1->"■", "□"); |
|||
Next |
|||
Print |
|||
Next |
|||
Print |
|||
Report 2, "Press enter to exit or any other key for Next generation" |
|||
End Sub |
|||
} |
|||
Life |
|||
</syntaxhighlight> |
|||
=={{header|MANOOL}}== |
|||
Straightforward implementation useful for benchmarking: |
|||
<syntaxhighlight lang="manool"> |
|||
{ {extern "manool.org.18/std/0.3/all"} in |
|||
: let { N = 40; M = 80 } in |
|||
: let { G = 99 } in |
|||
: let |
|||
{ Display = |
|||
{ proc { B } as |
|||
: for { I = Range[N]$ } do |
|||
: do Out.WriteLine[] after |
|||
: for { J = Range[M]$ } do |
|||
Out.Write[{if B[I; J] <> 0 then "*" else " "}] |
|||
} |
|||
} |
|||
in |
|||
: var { B = {array N of: array M of 0} } in |
|||
-- initialization |
|||
B[19; 41] = 1 |
|||
B[20; 40] = 1 |
|||
B[21; 40] = 1 |
|||
B[22; 40] = 1 |
|||
B[22; 41] = 1 |
|||
B[22; 42] = 1 |
|||
B[22; 43] = 1 |
|||
B[19; 44] = 1 |
|||
-- end of initialization |
|||
Out.WriteLine["Before:"]; Display[B] |
|||
{ var { NextB = B } in |
|||
: repeat G do |
|||
: do {assign B = NextB; NextB = B} after |
|||
: for { I = Range[N]$ } do |
|||
: var { Up = (I - 1).Mod[N]; Down = (I + 1).Mod[N] } in |
|||
: for { J = Range[M]$ } do |
|||
: var { Left = (J - 1).Mod[M]; Right = (J + 1).Mod[M] } in |
|||
: var |
|||
{ Count = |
|||
B[Up ; Left ] + |
|||
B[Up ; J ] + |
|||
B[Up ; Right] + |
|||
B[I ; Right] + |
|||
B[Down; Right] + |
|||
B[Down; J ] + |
|||
B[Down; Left ] + |
|||
B[I ; Left ] |
|||
} |
|||
in |
|||
NextB[I; J] = |
|||
{ if Count == 2 then B[I; J] else |
|||
: if Count == 3 then 1 else |
|||
0 |
|||
} |
|||
} |
|||
Out.WriteLine["After " G " generations:"]; Display[B] |
|||
} |
|||
</syntaxhighlight> |
|||
Using comprehension notation: |
|||
<syntaxhighlight lang="manool"> |
|||
{ {extern "manool.org.18/std/0.3/all"} in |
|||
: let { N = 40; M = 80 } in |
|||
: let { G = 99 } in |
|||
: let |
|||
{ Display = |
|||
{ proc { B } as |
|||
: for { I = Range[N]$ } do |
|||
: do Out.WriteLine[] after |
|||
: for { J = Range[M]$ } do |
|||
Out.Write[{if B[I; J] <> 0 then "*" else " "}] |
|||
} |
|||
} |
|||
in |
|||
: var { B = {array N of: array M of 0} } in |
|||
-- initialization |
|||
B[19; 41] = 1 |
|||
B[20; 40] = 1 |
|||
B[21; 40] = 1 |
|||
B[22; 40] = 1 |
|||
B[22; 41] = 1 |
|||
B[22; 42] = 1 |
|||
B[22; 43] = 1 |
|||
B[19; 44] = 1 |
|||
-- end of initialization |
|||
Out.WriteLine["Before:"]; Display[B] |
|||
{ repeat G do |
|||
B = |
|||
{ array for { I = Range[N]$ } of |
|||
: var { Up = (I - 1).Mod[N]; Down = (I + 1).Mod[N] } in |
|||
: array for { J = Range[M]$ } of |
|||
: var { Left = (J - 1).Mod[M]; Right = (J + 1).Mod[M] } in |
|||
: var |
|||
{ Count = |
|||
B[Up ; Left ] + |
|||
B[Up ; J ] + |
|||
B[Up ; Right] + |
|||
B[I ; Right] + |
|||
B[Down; Right] + |
|||
B[Down; J ] + |
|||
B[Down; Left ] + |
|||
B[I ; Left ] |
|||
} |
|||
in |
|||
: if Count == 2 then B[I; J] else |
|||
: if Count == 3 then 1 else |
|||
0 |
|||
} |
|||
} |
|||
Out.WriteLine["After " G " generations:"]; Display[B] |
|||
} |
|||
</syntaxhighlight> |
|||
=={{header|Mathematica}} / {{header|Wolfram Language}}== |
|||
Mathematica has cellular automaton functionality built in, so implementing Conway's Game of Life is a one-liner: |
Mathematica has cellular automaton functionality built in, so implementing Conway's Game of Life is a one-liner: |
||
< |
<syntaxhighlight lang="mathematica">CellularAutomaton[{224,{2,{{2,2,2},{2,1,2},{2,2,2}}},{1,1}}, startconfiguration, steps];</syntaxhighlight> |
||
Example of a glyder progressing 8 steps and showing the 9 frames afterwards as grids of hashes and dots: |
Example of a glyder progressing 8 steps and showing the 9 frames afterwards as grids of hashes and dots: |
||
< |
<syntaxhighlight 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[results[[i]]/.{1->"#",0->"."}]];,{i,1,Length[results]}]</ |
Do[Print[i-1];Print[Grid[results[[i]]/.{1->"#",0->"."}]];,{i,1,Length[results]}]</syntaxhighlight> |
||
gives back: |
gives back: |
||
<pre style="height:30ex;overflow:scroll"> |
<pre style="height:30ex;overflow:scroll"> |
||
Line 1,346: | Line 10,547: | ||
</pre> |
</pre> |
||
=={{header| |
=={{header|MATLAB}}== |
||
MATLAB has a builtin Game of Life GUI. Type <syntaxhighlight lang="matlab">life</syntaxhighlight> to run it. To view the code, type |
|||
<syntaxhighlight lang="matlab">open(fullfile(matlabroot, 'toolbox', 'matlab', 'demos', 'life.m'))</syntaxhighlight> |
|||
<lang ocaml>let get g x y = |
|||
Here is an example code, more simple (runs the game of life for N generations in a square of side S) : |
|||
<syntaxhighlight lang="matlab">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</syntaxhighlight> |
|||
=={{header|Maxima}}== |
|||
<syntaxhighlight lang="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])</syntaxhighlight> |
|||
Blinker over three generations |
|||
<syntaxhighlight lang="maxima"> |
|||
three_all_alive: matrix([0,1,0],[0,1,0],[0,1,0])$ |
|||
with_slider_draw(n,makelist(j,j,0,2),image(gen(three_all_alive,n),0,0,30,30)); |
|||
</syntaxhighlight> |
|||
[[File:BlinkerMaxima.gif|thumb|center]] |
|||
=={{header|MiniScript}}== |
|||
This GUI implementation is for use with [http://miniscript.org/MiniMicro Mini Micro]. |
|||
<syntaxhighlight lang="miniscript">// Conway's Game of Life |
|||
clear |
|||
rows = 64; rowRange = range(0, rows-1) |
|||
cols = 96; colRange = range(0, cols-1) |
|||
// prepare two tile displays, in display layers 4 and 5 |
|||
img = Image.create(2, 1); img.setPixel 1, 0, color.white |
|||
grids = [] |
|||
for dispIdx in [4,5] |
|||
display(dispIdx).mode = displayMode.tile |
|||
td = display(dispIdx) |
|||
td.cellSize = 9 // size of cells on screen |
|||
td.extent = [cols, rows] |
|||
td.overlap = -1 // adds a small gap between cells |
|||
td.tileSet = img; td.tileSetTileSize = 1 |
|||
td.clear 0 |
|||
grids.push td |
|||
end for |
|||
// initialize to a random state |
|||
for x in colRange |
|||
for y in rowRange |
|||
td.setCell x, y, rnd > 0.5 |
|||
end for |
|||
end for |
|||
curIdx = 5 |
|||
// main loop |
|||
while true |
|||
yield |
|||
td = grids[curIdx-4] |
|||
newIdx = 4 + (curIdx == 4) |
|||
newTd = grids[newIdx-4] |
|||
for x in colRange |
|||
for y in rowRange |
|||
// get sum of neighboring states |
|||
sum = 0 |
|||
for i in [x-1, x, x+1] |
|||
if i < 0 or i >= cols then continue |
|||
for j in [y-1, y, y+1] |
|||
if j < 0 or j >= rows then continue |
|||
if i==x and j==y then continue |
|||
sum = sum + td.cell(i,j) |
|||
end for |
|||
end for |
|||
// Now, update the cell based on current state |
|||
// and neighboring sum. |
|||
if td.cell(x,y) then |
|||
newTd.setCell x, y, (sum == 2 or sum == 3) |
|||
else |
|||
newTd.setCell x, y, sum == 3 |
|||
end if |
|||
end for |
|||
end for |
|||
display(newIdx).mode = displayMode.tile |
|||
display(curIdx).mode = displayMode.off |
|||
curIdx = newIdx |
|||
end while</syntaxhighlight> |
|||
=={{header|Nim}}== |
|||
{{trans|C}} |
|||
<syntaxhighlight lang="nim">import os, strutils, random |
|||
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 rand(9) < 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</syntaxhighlight> |
|||
=={{header|OCaml}}== |
|||
<syntaxhighlight lang="ocaml">let get g x y = |
|||
try g.(x).(y) |
try g.(x).(y) |
||
with _ -> 0 |
with _ -> 0 |
||
Line 1,393: | Line 10,777: | ||
else print_char 'o' |
else print_char 'o' |
||
done; |
done; |
||
print_newline() |
print_newline () |
||
done</ |
done</syntaxhighlight> |
||
put the code above in a file named "life.ml", and then use it in the ocaml toplevel like this: |
put the code above in a file named "life.ml", and then use it in the ocaml toplevel like this: |
||
Line 1,454: | Line 10,838: | ||
.......... |
.......... |
||
- : unit = () |
- : unit = () |
||
</pre> |
|||
=== A graphical version === |
|||
This implementation has 6 starting patterns (most get quite large) and a random option, and you can set the grid size. |
|||
<syntaxhighlight lang="ocaml">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</syntaxhighlight> |
|||
Compile with: <pre>opam install graphics && ocamlopt -o life -I $(ocamlfind query graphics) graphics.cmxa life.ml</pre> and run with <pre>./life acorn 250 250</pre> If you run the blinker it will probably blink too fast to see unless you choose a large grid size. |
|||
=={{header|Octave}}== |
|||
1st order two variable recurrence relation m-file, will also run under MATLAB. |
|||
<syntaxhighlight lang="matlab"> |
|||
clear all |
|||
x=55; % Size of the Lattice (same as LAWE) |
|||
z(1:1:x^2)=0; % Initialise the binary lattice |
|||
z_prime=z; % prepare the z prime |
|||
idx=7*x+2; % Origin |
|||
z(idx+4)=1; % Populate the binary lattice with the Gosper Glider |
|||
z(idx+x+4)=1; |
|||
z(idx+1+4)=1; |
|||
z(idx+x+1+4)=1; |
|||
z(idx+14)=1; |
|||
z(idx+14+x)=1; |
|||
z(idx+14+x+x)=1; |
|||
z(idx+15+x+x+x)=1; |
|||
z(idx+15-x)=1; |
|||
z(idx+16-x-x)=1; |
|||
z(idx+16+x+x+x+x)=1; |
|||
z(idx+17-x-x)=1; |
|||
z(idx+17+x+x+x+x)=1; |
|||
z(idx+18+x)=1; |
|||
z(idx+19-x)=1; |
|||
z(idx+19+x+x+x)=1; |
|||
z(idx+20)=1; |
|||
z(idx+20+x)=1; |
|||
z(idx+20+x+x)=1; |
|||
z(idx+21+x)=1; |
|||
z(idx+24)=1; |
|||
z(idx+25)=1; |
|||
z(idx+24-x)=1; |
|||
z(idx+25-x)=1; |
|||
z(idx+24-x-x)=1; |
|||
z(idx+25-x-x)=1; |
|||
z(idx+26-x-x-x)=1; |
|||
z(idx+26+x)=1; |
|||
z(idx+28-x-x-x)=1; |
|||
z(idx+28+x)=1; |
|||
z(idx+28-x-x-x-x)=1; |
|||
z(idx+28+x+x)=1; |
|||
z(idx+38)=1; |
|||
z(idx+38-x)=1; |
|||
z(idx+39)=1; |
|||
z(idx-x+39)=1; |
|||
for k=1:1:80; % Number of frames |
|||
for n=x+2:1:x^2-x-1; % one time pass |
|||
theta=0; % Sum the surrounding area (top equation) |
|||
for c=n-1:1:n+1; |
|||
for m=-1:1:1; |
|||
theta=theta+z(m*x+c); |
|||
endfor |
|||
endfor |
|||
z_prime(n)= round(1/5*(z(n)*acos(sin(pi/4*(theta-z(n)-9/2))) + (1-z(n)) * acos(sin(pi/4*(theta-z(n) + 3))))); % bottom equation |
|||
endfor |
|||
figure(2) |
|||
title('GOL','FontWeight','bold'); |
|||
xlabel("X"); |
|||
ylabel("Y"); |
|||
imagesc(reshape(z,x,x)') |
|||
z=z_prime; |
|||
pause % each press advances the algorithm one step |
|||
endfor |
|||
</syntaxhighlight> |
|||
=={{header|Ol}}== |
|||
{{libheader|OpenGL}} |
|||
<syntaxhighlight lang="scheme"> |
|||
#!/usr/bin/ol |
|||
(import (otus random!)) |
|||
(define MAX 65536) ; should be power of two |
|||
; size of game board (should be less than MAX) |
|||
(define WIDTH 170) |
|||
(define HEIGHT 96) |
|||
; helper function |
|||
(define (hash x y) |
|||
(let ((x (mod (+ x WIDTH) WIDTH)) |
|||
(y (mod (+ y HEIGHT) HEIGHT))) |
|||
(+ (* y MAX) x))) |
|||
; helper function |
|||
(define neighbors '( |
|||
(-1 . -1) ( 0 . -1) ( 1 . -1) |
|||
(-1 . 0) ( 1 . 0) |
|||
(-1 . 1) ( 0 . 1) ( 1 . 1) |
|||
)) |
|||
; dead-or-alive cell test |
|||
(define (alive gen x y) |
|||
(case (fold (lambda (f xy) |
|||
(+ f (get gen (hash (+ x (car xy)) (+ y (cdr xy))) 0))) |
|||
0 neighbors) |
|||
(2 |
|||
(get gen (hash x y) #false)) |
|||
(3 |
|||
#true))) |
|||
; --------------- |
|||
(import (lib gl2)) |
|||
(gl:set-window-title "Convey's The game of Life") |
|||
(glShadeModel GL_SMOOTH) |
|||
(glClearColor 0.11 0.11 0.11 1) |
|||
(glOrtho 0 WIDTH 0 HEIGHT 0 1) |
|||
(glPointSize (/ 854 WIDTH)) |
|||
; generate random field |
|||
(gl:set-userdata |
|||
(list->ff (map (lambda (i) (let ((x (rand! WIDTH)) (y (rand! HEIGHT))) |
|||
(cons (hash x y) 1))) (iota 2000)))) |
|||
; main game loop |
|||
(gl:set-renderer (lambda (mouse) |
|||
(let ((generation (gl:get-userdata))) |
|||
(glClear GL_COLOR_BUFFER_BIT) |
|||
; draw the cells |
|||
(glColor3f 0.2 0.5 0.2) |
|||
(glBegin GL_POINTS) |
|||
(ff-fold (lambda (st key value) |
|||
(glVertex2f (mod key MAX) |
|||
(div key MAX)) |
|||
) #f generation) |
|||
(glEnd) |
|||
(gl:set-userdata |
|||
; next cells generation |
|||
(ff-fold (lambda (st key value) |
|||
(let ((x (mod key MAX)) |
|||
(y (div key MAX))) |
|||
(fold (lambda (st key) |
|||
(let ((x (+ x (car key))) |
|||
(y (+ y (cdr key)))) |
|||
(if (alive generation x y) (put st (hash x y) 1) st))) |
|||
(if (alive generation x y) (put st (hash x y) 1) st) ; the cell |
|||
neighbors))) |
|||
#empty generation))))) |
|||
</syntaxhighlight> |
|||
=={{header|ooRexx}}== |
|||
<syntaxhighlight lang="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 |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre>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 |
|||
</pre> |
</pre> |
||
=={{header|Oz}}== |
=={{header|Oz}}== |
||
< |
<syntaxhighlight lang="oz">declare |
||
Rules = [rule(c:1 n:[0 1] new:0) %% Lonely |
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:[4 5 6 7 8] new:0) %% Overcrowded |
||
Line 1,560: | Line 11,642: | ||
{System.showInfo "\nGen. "#I} |
{System.showInfo "\nGen. "#I} |
||
{ForAll {ShowG Gi} System.showInfo} |
{ForAll {ShowG Gi} System.showInfo} |
||
end</ |
end</syntaxhighlight> |
||
=={{header|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. |
|||
<syntaxhighlight lang="parigp">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))</syntaxhighlight> |
|||
=={{header|Pascal}}== |
|||
Uses crt for console output. |
|||
Can make use of a "torus"-world. |
|||
Optimized for speed on a Haswell CPU |
|||
(without PrintGen ~ 8.5 Cpu-cyles/coordinate ) |
|||
<syntaxhighlight lang="pascal">program Gol; |
|||
// Game of life |
|||
{$IFDEF FPC} |
|||
//save as gol.pp/gol.pas |
|||
{$Mode delphi} |
|||
{$ELSE} |
|||
//for Delphi save as gol.dpr |
|||
{$Apptype Console} |
|||
{$ENDIF} |
|||
uses |
|||
crt; |
|||
const |
|||
colMax = 76; |
|||
rowMax = 22; |
|||
dr = colMax+2; // element count of one row |
|||
cDelay = 20; // delay in ms |
|||
(* |
|||
expand field by one row/column before and after |
|||
for easier access no special treatment of torus |
|||
*) |
|||
type |
|||
tFldElem = byte;//0..1 |
|||
tpFldElem = ^tFldElem; |
|||
tRow = array[0..colMax+1] of tFldElem; |
|||
tpRow = ^tRow; |
|||
tBoard = array[0..rowMax+1] of tRow; |
|||
tpBoard = ^tBoard; |
|||
tpBoards = array[0..1] of tpBoard; |
|||
type |
|||
tIntArr = array[0..2*dr+2] of tFldElem; |
|||
tpIntArr = ^tIntArr; |
|||
var |
|||
aBoard,bBoard : tBoard; |
|||
pBoards :tpBoards; |
|||
gblActBoard : byte; |
|||
gblUseTorus :boolean; |
|||
gblGenCnt : integer; |
|||
procedure PrintGen; |
|||
const |
|||
cChar: array[0..1] of char = (' ','#'); |
|||
var |
|||
p0 : tpIntArr; |
|||
col,row: integer; |
|||
s : string[colMax]; |
|||
begin |
|||
setlength(s,colmax); |
|||
gotoxy(1,1); |
|||
writeln(gblGenCnt:10); |
|||
For row := 1 to rowMax do |
|||
begin |
|||
p0 := @pBoards[gblActBoard]^[row,0];; |
|||
For col := 1 to colMax do |
|||
s[col] := cChar[p0[col]]; |
|||
writeln(s); |
|||
end; |
|||
delay(cDelay); |
|||
end; |
|||
procedure Init0(useTorus:boolean); |
|||
begin |
|||
gblUseTorus := useTorus; |
|||
gblGenCnt := 0; |
|||
fillchar(aBoard,SizeOf(aBoard),#0); |
|||
pBoards[0] := @aBoard; |
|||
pBoards[1] := @bBoard; |
|||
gblActBoard := 0; |
|||
clrscr; |
|||
end; |
|||
procedure InitRandom(useTorus:boolean); |
|||
var |
|||
col,row : integer; |
|||
begin |
|||
Init0(useTorus); |
|||
For row := 1 to rowMax do |
|||
For col := 1 to colMax do |
|||
aBoard[row,col]:= tFldElem(random>0.9); |
|||
end; |
|||
procedure InitBlinker(useTorus:boolean); |
|||
var |
|||
col,row : integer; |
|||
begin |
|||
Init0(useTorus); |
|||
For col := 1 to colMax do |
|||
begin |
|||
IF (col+2) mod 4 = 0 then |
|||
begin |
|||
For row := 1 to rowmax do |
|||
IF row mod 4 <> 0 then |
|||
aBoard[row,col]:= 1; |
|||
end; |
|||
end; |
|||
end; |
|||
procedure Torus; |
|||
var |
|||
p0 : tpIntArr; |
|||
row: integer; |
|||
begin |
|||
//copy column 1-> colMax+1 and colMax-> 0 |
|||
p0 := @pBoards[gblActBoard]^[1,0]; |
|||
For row := 1 to rowMax do |
|||
begin |
|||
p0^[0] := p0^[colMax]; |
|||
p0^[colmax+1] := p0^[1]; |
|||
//next row |
|||
p0 := Pointer(PtrUint(p0)+SizeOf(tRow)); |
|||
end; |
|||
//copy row 1-> rowMax+1 |
|||
move(pBoards[gblActBoard]^[1,0],pBoards[gblActBoard]^[rowMax+1,0],sizeof(trow)); |
|||
//copy row rowMax-> 0 |
|||
move(pBoards[gblActBoard]^[rowMax,0],pBoards[gblActBoard]^[0,0],sizeof(trow)); |
|||
end; |
|||
function Survive(p: tpIntArr):tFldElem; |
|||
//p points to actual_board [row-1,col-1] |
|||
//calculates the sum of alive around [row,col] aka p^[dr+1] |
|||
//really fast using fpc 2.6.4 no element on stack |
|||
const |
|||
cSurvives : array[boolean,0..8] of byte = |
|||
//0,1,2,3,4,5,6,7,8 sum of alive neighbours |
|||
((0,0,0,1,0,0,0,0,0), {alive =false 1->born} |
|||
(0,0,1,1,0,0,0,0,0)); {alive =true 0->die } |
|||
var |
|||
sum : integer; |
|||
begin |
|||
// row above |
|||
// sum := byte(aBoard[row-1,col-1])+byte(aBoard[row-1,col])+byte(aBoard[row-1,col+1]); |
|||
sum := integer(p^[ 0])+integer(p^[ 1])+integer(p^[ 2]); |
|||
sum := sum+integer(p^[ dr+0]) +integer(p^[ dr+2]); |
|||
sum := sum+integer(p^[2*dr+0])+integer(p^[2*dr+1])+integer(p^[2*dr+2]); |
|||
survive := cSurvives[boolean(p^[dr+1]),sum]; |
|||
end; |
|||
procedure NextGen; |
|||
var |
|||
p0,p1 : tpFldElem; |
|||
row: NativeInt; |
|||
col :NativeInt; |
|||
begin |
|||
if gblUseTorus then |
|||
Torus; |
|||
p1 := @pBoards[1-gblActBoard]^[1,1]; |
|||
//One row above and one column before because of survive |
|||
p0 := @pBoards[ gblActBoard]^[0,0]; |
|||
For row := rowMax-1 downto 0 do |
|||
begin |
|||
For col := colMax-1 downto 0 do |
|||
begin |
|||
p1^ := survive(tpIntArr(p0)); |
|||
inc(p0); |
|||
inc(p1); |
|||
end; |
|||
// jump over the borders |
|||
inc(p1,2); |
|||
inc(p0,2); |
|||
end; |
|||
//aBoard := bBoard; |
|||
gblActBoard :=1-gblActBoard; |
|||
inc(gblGenCnt); |
|||
end; |
|||
begin |
|||
InitBlinker(false); |
|||
repeat |
|||
PrintGen; |
|||
NextGen; |
|||
until keypressed; |
|||
PrintGen; |
|||
end. |
|||
</syntaxhighlight> |
|||
=={{header|Perl}}== |
=={{header|Perl}}== |
||
Line 1,570: | Line 11,851: | ||
would do 15 iterations over 5 rows and 10 columns. |
would do 15 iterations over 5 rows and 10 columns. |
||
< |
<syntaxhighlight lang="perl">my ($width, $height, $generations) = @ARGV; |
||
my $printed; |
my $printed; |
||
Line 1,628: | Line 11,909: | ||
printlife @life; |
printlife @life; |
||
} |
} |
||
print "\n";</ |
print "\n";</syntaxhighlight> |
||
Another version, takes up the whole area of your terminal. Using warping edges.<syntaxhighlight lang="perl">my $w = `tput cols` - 1; |
|||
=={{header|Perl 6}}== |
|||
my $h = `tput lines` - 1; |
|||
{{works with|Rakudo|#22 "Thousand Oaks"}} |
|||
my $r = "\033[H"; |
|||
my @universe = map([ map(rand(1) < .1, 1 .. $w) ], 1 .. $h); |
|||
<lang perl6>class Grid { |
|||
sub iterate { |
|||
has Int $.width; |
|||
my @new = map([ map(0, 1 .. $w) ], 1 .. $h); |
|||
has Int $.height; |
|||
for my $i (0 .. $h - 1) { |
|||
has @!a; |
|||
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] |
|||
multi method new (@a) { |
|||
? ($neighbor == 2 or $neighbor == 3) |
|||
# Makes a new grid with @!a equal to @a. |
|||
: $neighbor == 3; |
|||
Grid.bless(*, |
|||
}} |
|||
width => @a[0].elems, height => @a.elems, |
|||
@universe = @new; |
|||
a => @a) |
|||
} |
|||
} |
|||
while(1) { |
|||
multi method new (Str $s) { |
|||
print $r; |
|||
# Interprets the string as a grid. |
|||
print map((map($_ ? "#" : " ", @$_), "\n"), @universe); |
|||
Grid.new(map |
|||
iterate; |
|||
{ [map { $^c eq '#' ?? True !! False }, split '', $^l] }, |
|||
}</syntaxhighlight> |
|||
grep /\N/, split "\n", $s) |
|||
===Next Generation in a Single Substitution Operator=== |
|||
} |
|||
<syntaxhighlight lang="perl">#!/usr/bin/perl |
|||
print $_ = <<'' =~ tr/./ /r; # test case, dots only for layout |
|||
method clone { Grid.new(map { [$^x.clone] }, @!a) } |
|||
..... |
|||
..... |
|||
.###. |
|||
..... |
|||
..... |
|||
my $gap = /..\n/ ? qr/.{$-[0]}/s : die "too small"; |
|||
method Str { |
|||
my $neighborhood = qr/(?<=(...) $gap (.)) . (?=(.) $gap (...))/x; |
|||
[~] map |
|||
{ [~] map({ $^c ?? '#' !! '.' }, |$^row), "\n" }, |
|||
@!a |
|||
} |
|||
for my $generation ( 2 .. 4 ) |
|||
method alive (Int $row, Int $col --> Bool) { |
|||
{ |
|||
0 <= $row < $.height and 0 <= $col < $.width |
|||
s/$neighborhood/ substr " $&# ", "$1$2$3$4" =~ tr|#||, 1 /ge; |
|||
and @!a[$row][$col]; |
|||
print; |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
### |
|||
# |
|||
# |
|||
# |
|||
### |
|||
# |
|||
# |
|||
# |
|||
=={{header|Phix}}== |
|||
method nextgen { |
|||
{{trans|basic256}} |
|||
my $prev = self.clone; |
|||
{{libheader|Phix/pGUI}} |
|||
for ^$.height -> $row { |
|||
{{libheader|Phix/online}} |
|||
for ^$.width -> $col { |
|||
You can run this online [http://phix.x10.mx/p2js/conways_game_of_life.htm here]. |
|||
my $v = [+] |
|||
<!--<syntaxhighlight lang="phix">(phixonline)--> |
|||
map({ $prev.alive($^r, $^c) }, |
|||
<span style="color: #000080;font-style:italic;">-- |
|||
($col - 1, $col, $col + 1 X |
|||
-- demo\rosetta\Conways_Game_of_Life.exw |
|||
$row - 1, $row, $row + 1)), |
|||
-- ===================================== |
|||
-$prev.alive($row, $col); |
|||
--</span> |
|||
@!a[$row][$col] = |
|||
<span style="color: #008080;">with</span> <span style="color: #008080;">javascript_semantics</span> |
|||
$prev.alive($row, $col) && $v == 2 || $v == 3; |
|||
<span style="color: #008080;">include</span> <span style="color: #000000;">pGUI</span><span style="color: #0000FF;">.</span><span style="color: #000000;">e</span> |
|||
} |
|||
} |
|||
<span style="color: #008080;">constant</span> <span style="color: #000000;">title</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">"Conway's Game of Life"</span> |
|||
} |
|||
<span style="color: #004080;">Ihandle</span> <span style="color: #000000;">dlg</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">canvas</span> |
|||
}</lang> |
|||
<span style="color: #004080;">Ihandln</span> <span style="color: #000000;">hTimer</span> <span style="color: #0000FF;">=</span> <span style="color: #004600;">NULL</span> |
|||
<span style="color: #004080;">cdCanvas</span> <span style="color: #000000;">cddbuffer</span> |
|||
<span style="color: #004080;">sequence</span> <span style="color: #000000;">c</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{},</span> <span style="color: #000080;font-style:italic;">-- cells</span> |
|||
<span style="color: #000000;">cn</span><span style="color: #0000FF;">,</span> <span style="color: #000080;font-style:italic;">-- new cells</span> |
|||
<span style="color: #000000;">cl</span> <span style="color: #000080;font-style:italic;">-- last cells</span> |
|||
<span style="color: #008080;">procedure</span> <span style="color: #000000;">draw</span><span style="color: #0000FF;">(</span><span style="color: #004080;">integer</span> <span style="color: #000000;">what</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #004080;">integer</span> <span style="color: #000000;">x</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">floor</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">c</span><span style="color: #0000FF;">)/</span><span style="color: #000000;">2</span><span style="color: #0000FF;">)+</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span> |
|||
<span style="color: #000000;">y</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">floor</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">c</span><span style="color: #0000FF;">[</span><span style="color: #000000;">1</span><span style="color: #0000FF;">])/</span><span style="color: #000000;">2</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #008080;">switch</span> <span style="color: #000000;">what</span> <span style="color: #008080;">do</span> |
|||
<span style="color: #008080;">case</span> <span style="color: #008000;">' '</span><span style="color: #0000FF;">:</span> <span style="color: #000000;">c</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">sq_mul</span><span style="color: #0000FF;">(</span><span style="color: #000000;">c</span><span style="color: #0000FF;">,</span><span style="color: #000000;">0</span><span style="color: #0000FF;">)</span> <span style="color: #000080;font-style:italic;">-- Clear</span> |
|||
<span style="color: #008080;">case</span> <span style="color: #008000;">'+'</span><span style="color: #0000FF;">:</span> <span style="color: #000000;">c</span><span style="color: #0000FF;">[</span><span style="color: #000000;">x</span><span style="color: #0000FF;">][</span><span style="color: #000000;">y</span><span style="color: #0000FF;">-</span><span style="color: #000000;">1</span><span style="color: #0000FF;">..</span><span style="color: #000000;">y</span><span style="color: #0000FF;">+</span><span style="color: #000000;">1</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">repeat</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #000000;">3</span><span style="color: #0000FF;">)</span> <span style="color: #000080;font-style:italic;">-- Blinker</span> |
|||
<span style="color: #008080;">case</span> <span style="color: #008000;">'G'</span><span style="color: #0000FF;">:</span> <span style="color: #0000FF;">{</span> <span style="color: #000000;">c</span><span style="color: #0000FF;">[</span><span style="color: #000000;">x</span><span style="color: #0000FF;">+</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #000000;">y</span><span style="color: #0000FF;">+</span><span style="color: #000000;">4</span><span style="color: #0000FF;">],</span> |
|||
<span style="color: #000000;">c</span><span style="color: #0000FF;">[</span><span style="color: #000000;">x</span><span style="color: #0000FF;">+</span><span style="color: #000000;">2</span><span style="color: #0000FF;">,</span><span style="color: #000000;">y</span><span style="color: #0000FF;">+</span><span style="color: #000000;">2</span><span style="color: #0000FF;">],</span> <span style="color: #000000;">c</span><span style="color: #0000FF;">[</span><span style="color: #000000;">x</span><span style="color: #0000FF;">+</span><span style="color: #000000;">2</span><span style="color: #0000FF;">,</span><span style="color: #000000;">y</span><span style="color: #0000FF;">+</span><span style="color: #000000;">4</span><span style="color: #0000FF;">],</span> |
|||
<span style="color: #000000;">c</span><span style="color: #0000FF;">[</span><span style="color: #000000;">x</span><span style="color: #0000FF;">+</span><span style="color: #000000;">3</span><span style="color: #0000FF;">,</span><span style="color: #000000;">y</span><span style="color: #0000FF;">+</span><span style="color: #000000;">3</span><span style="color: #0000FF;">],</span> <span style="color: #000000;">c</span><span style="color: #0000FF;">[</span><span style="color: #000000;">x</span><span style="color: #0000FF;">+</span><span style="color: #000000;">3</span><span style="color: #0000FF;">,</span><span style="color: #000000;">y</span><span style="color: #0000FF;">+</span><span style="color: #000000;">4</span><span style="color: #0000FF;">]}</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">repeat</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #000000;">5</span><span style="color: #0000FF;">)</span> <span style="color: #000080;font-style:italic;">-- Glider</span> |
|||
<span style="color: #008080;">case</span> <span style="color: #008000;">'T'</span><span style="color: #0000FF;">:</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">c</span><span style="color: #0000FF;">[</span><span style="color: #000000;">x</span><span style="color: #0000FF;">-</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #000000;">y</span><span style="color: #0000FF;">+</span><span style="color: #000000;">1</span><span style="color: #0000FF;">],</span><span style="color: #000000;">c</span><span style="color: #0000FF;">[</span><span style="color: #000000;">x</span><span style="color: #0000FF;">,</span><span style="color: #000000;">y</span><span style="color: #0000FF;">+</span><span style="color: #000000;">1</span><span style="color: #0000FF;">],</span><span style="color: #000000;">c</span><span style="color: #0000FF;">[</span><span style="color: #000000;">x</span><span style="color: #0000FF;">+</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #000000;">y</span><span style="color: #0000FF;">+</span><span style="color: #000000;">1</span><span style="color: #0000FF;">],</span> |
|||
<span style="color: #000000;">c</span><span style="color: #0000FF;">[</span><span style="color: #000000;">x</span> <span style="color: #0000FF;">,</span><span style="color: #000000;">y</span><span style="color: #0000FF;">+</span><span style="color: #000000;">3</span><span style="color: #0000FF;">],</span><span style="color: #000000;">c</span><span style="color: #0000FF;">[</span><span style="color: #000000;">x</span><span style="color: #0000FF;">,</span><span style="color: #000000;">y</span><span style="color: #0000FF;">+</span><span style="color: #000000;">4</span><span style="color: #0000FF;">],</span><span style="color: #000000;">c</span><span style="color: #0000FF;">[</span><span style="color: #000000;">x</span> <span style="color: #0000FF;">,</span><span style="color: #000000;">y</span><span style="color: #0000FF;">+</span><span style="color: #000000;">5</span><span style="color: #0000FF;">]}</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">repeat</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #000000;">6</span><span style="color: #0000FF;">)</span> <span style="color: #000080;font-style:italic;">-- Thunderbird</span> |
|||
<span style="color: #008080;">case</span> <span style="color: #008000;">'X'</span><span style="color: #0000FF;">:</span> <span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">0</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">min</span><span style="color: #0000FF;">(</span><span style="color: #000000;">x</span><span style="color: #0000FF;">,</span><span style="color: #000000;">y</span><span style="color: #0000FF;">)-</span><span style="color: #000000;">1</span> <span style="color: #008080;">do</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #000000;">c</span><span style="color: #0000FF;">[</span><span style="color: #000000;">x</span><span style="color: #0000FF;">-</span><span style="color: #000000;">i</span><span style="color: #0000FF;">,</span><span style="color: #000000;">y</span><span style="color: #0000FF;">+</span><span style="color: #000000;">i</span><span style="color: #0000FF;">],</span><span style="color: #000000;">c</span><span style="color: #0000FF;">[</span><span style="color: #000000;">x</span><span style="color: #0000FF;">+</span><span style="color: #000000;">i</span><span style="color: #0000FF;">,</span><span style="color: #000000;">y</span><span style="color: #0000FF;">+</span><span style="color: #000000;">i</span><span style="color: #0000FF;">],</span> |
|||
<span style="color: #000000;">c</span><span style="color: #0000FF;">[</span><span style="color: #000000;">x</span><span style="color: #0000FF;">-</span><span style="color: #000000;">i</span><span style="color: #0000FF;">,</span><span style="color: #000000;">y</span><span style="color: #0000FF;">-</span><span style="color: #000000;">i</span><span style="color: #0000FF;">],</span><span style="color: #000000;">c</span><span style="color: #0000FF;">[</span><span style="color: #000000;">x</span><span style="color: #0000FF;">+</span><span style="color: #000000;">i</span><span style="color: #0000FF;">,</span><span style="color: #000000;">y</span><span style="color: #0000FF;">-</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]}</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">repeat</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #000000;">4</span><span style="color: #0000FF;">)</span> <span style="color: #000080;font-style:italic;">-- Cross</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">switch</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span> |
|||
<span style="color: #004080;">atom</span> <span style="color: #000000;">t1</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">time</span><span style="color: #0000FF;">()+</span><span style="color: #000000;">1</span> |
|||
<span style="color: #004080;">integer</span> <span style="color: #000000;">fps</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">0</span> |
|||
<span style="color: #008080;">function</span> <span style="color: #000000;">redraw_cb</span><span style="color: #0000FF;">(</span><span style="color: #004080;">Ihandle</span> <span style="color: #000080;font-style:italic;">/*ih*/</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #004080;">integer</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">width</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">height</span><span style="color: #0000FF;">}</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">IupGetIntInt</span><span style="color: #0000FF;">(</span><span style="color: #000000;">canvas</span><span style="color: #0000FF;">,</span> <span style="color: #008000;">"DRAWSIZE"</span><span style="color: #0000FF;">),</span> |
|||
<span style="color: #000080;font-style:italic;">-- limit to 10K cells (performance target of 10fps) |
|||
-- n here is the cell size in pixels (min of 1x1)</span> |
|||
<span style="color: #000000;">n</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">ceil</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">sqrt</span><span style="color: #0000FF;">(</span><span style="color: #000000;">width</span><span style="color: #0000FF;">*</span><span style="color: #000000;">height</span><span style="color: #0000FF;">/</span><span style="color: #000000;">10000</span><span style="color: #0000FF;">)),</span> |
|||
<span style="color: #000000;">w</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">floor</span><span style="color: #0000FF;">(</span><span style="color: #000000;">width</span><span style="color: #0000FF;">/</span><span style="color: #000000;">n</span><span style="color: #0000FF;">)+</span><span style="color: #000000;">2</span><span style="color: #0000FF;">,</span> <span style="color: #000080;font-style:italic;">-- (see cx below)</span> |
|||
<span style="color: #000000;">h</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">floor</span><span style="color: #0000FF;">(</span><span style="color: #000000;">height</span><span style="color: #0000FF;">/</span><span style="color: #000000;">n</span><span style="color: #0000FF;">)+</span><span style="color: #000000;">2</span><span style="color: #0000FF;">,</span> |
|||
<span style="color: #000000;">alive</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">0</span> |
|||
<span style="color: #000080;font-style:italic;">-- keep w, h odd for symmetry (plus I suspect the |
|||
-- "Cross" code above may now crash without this)</span> |
|||
<span style="color: #000000;">w</span> <span style="color: #0000FF;">-=</span> <span style="color: #7060A8;">even</span><span style="color: #0000FF;">(</span><span style="color: #000000;">w</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #000000;">h</span> <span style="color: #0000FF;">-=</span> <span style="color: #7060A8;">even</span><span style="color: #0000FF;">(</span><span style="color: #000000;">h</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #008080;">if</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">c</span><span style="color: #0000FF;">)!=</span><span style="color: #000000;">w</span> |
|||
<span style="color: #008080;">or</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">c</span><span style="color: #0000FF;">[</span><span style="color: #000000;">1</span><span style="color: #0000FF;">])!=</span><span style="color: #000000;">h</span> <span style="color: #008080;">then</span> |
|||
<span style="color: #000000;">c</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">repeat</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">repeat</span><span style="color: #0000FF;">(</span><span style="color: #000000;">0</span><span style="color: #0000FF;">,</span><span style="color: #000000;">h</span><span style="color: #0000FF;">),</span><span style="color: #000000;">w</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #000000;">cn</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">deep_copy</span><span style="color: #0000FF;">(</span><span style="color: #000000;">c</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #000000;">cl</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">deep_copy</span><span style="color: #0000FF;">(</span><span style="color: #000000;">c</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #000000;">draw</span><span style="color: #0000FF;">(</span><span style="color: #008000;">'X'</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #008080;">if</span> <span style="color: #000000;">hTimer</span><span style="color: #0000FF;">!=</span><span style="color: #004600;">NULL</span> <span style="color: #008080;">then</span> |
|||
<span style="color: #7060A8;">IupStoreAttribute</span><span style="color: #0000FF;">(</span><span style="color: #000000;">hTimer</span><span style="color: #0000FF;">,</span> <span style="color: #008000;">"RUN"</span><span style="color: #0000FF;">,</span> <span style="color: #008000;">"YES"</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span> |
|||
<span style="color: #7060A8;">cdCanvasActivate</span><span style="color: #0000FF;">(</span><span style="color: #000000;">cddbuffer</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #000080;font-style:italic;">-- |
|||
-- There is a "dead border" of 1 cell all around the edge of c (etc) which |
|||
-- we never display or update. If we have got this right/an exact fit, then |
|||
-- w*n should be exactly 2n too wide, whereas in the worst case there is an |
|||
-- (2n-1) pixel border, which we split between left and right, ditto cy. |
|||
--</span> |
|||
<span style="color: #004080;">integer</span> <span style="color: #000000;">cx</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">n</span><span style="color: #0000FF;">+</span><span style="color: #7060A8;">floor</span><span style="color: #0000FF;">((</span><span style="color: #000000;">width</span><span style="color: #0000FF;">-</span><span style="color: #000000;">w</span><span style="color: #0000FF;">*</span><span style="color: #000000;">n</span><span style="color: #0000FF;">)/</span><span style="color: #000000;">2</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #008080;">for</span> <span style="color: #000000;">x</span><span style="color: #0000FF;">=</span><span style="color: #000000;">2</span> <span style="color: #008080;">to</span> <span style="color: #000000;">w</span><span style="color: #0000FF;">-</span><span style="color: #000000;">1</span> <span style="color: #008080;">do</span> |
|||
<span style="color: #004080;">integer</span> <span style="color: #000000;">cy</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">n</span><span style="color: #0000FF;">+</span><span style="color: #7060A8;">floor</span><span style="color: #0000FF;">((</span><span style="color: #000000;">height</span><span style="color: #0000FF;">-</span><span style="color: #000000;">h</span><span style="color: #0000FF;">*</span><span style="color: #000000;">n</span><span style="color: #0000FF;">)/</span><span style="color: #000000;">2</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #008080;">for</span> <span style="color: #000000;">y</span><span style="color: #0000FF;">=</span><span style="color: #000000;">2</span> <span style="color: #008080;">to</span> <span style="color: #000000;">h</span><span style="color: #0000FF;">-</span><span style="color: #000000;">1</span> <span style="color: #008080;">do</span> |
|||
<span style="color: #004080;">integer</span> <span style="color: #000000;">cxy</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">c</span><span style="color: #0000FF;">[</span><span style="color: #000000;">x</span><span style="color: #0000FF;">,</span><span style="color: #000000;">y</span><span style="color: #0000FF;">],</span> |
|||
<span style="color: #000000;">clxy</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">cl</span><span style="color: #0000FF;">[</span><span style="color: #000000;">x</span><span style="color: #0000FF;">,</span><span style="color: #000000;">y</span><span style="color: #0000FF;">],</span> |
|||
<span style="color: #000000;">cnxy</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">-</span><span style="color: #000000;">cxy</span> |
|||
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">x</span><span style="color: #0000FF;">-</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #000000;">x</span><span style="color: #0000FF;">+</span><span style="color: #000000;">1</span> <span style="color: #008080;">do</span> |
|||
<span style="color: #008080;">for</span> <span style="color: #000000;">j</span><span style="color: #0000FF;">=</span><span style="color: #000000;">y</span><span style="color: #0000FF;">-</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #000000;">y</span><span style="color: #0000FF;">+</span><span style="color: #000000;">1</span> <span style="color: #008080;">do</span> |
|||
<span style="color: #000000;">cnxy</span> <span style="color: #0000FF;">+=</span> <span style="color: #000000;">c</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">,</span><span style="color: #000000;">j</span><span style="color: #0000FF;">]</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span> |
|||
<span style="color: #004080;">integer</span> <span style="color: #000000;">live</span> <span style="color: #0000FF;">=</span> <span style="color: #008080;">iff</span><span style="color: #0000FF;">(</span><span style="color: #000000;">cxy</span><span style="color: #0000FF;">?</span><span style="color: #000000;">cnxy</span><span style="color: #0000FF;">>=</span><span style="color: #000000;">2</span> <span style="color: #008080;">and</span> <span style="color: #000000;">cnxy</span><span style="color: #0000FF;"><=</span><span style="color: #000000;">3</span><span style="color: #0000FF;">:</span><span style="color: #000000;">cnxy</span><span style="color: #0000FF;">==</span><span style="color: #000000;">3</span><span style="color: #0000FF;">),</span> |
|||
<span style="color: #000000;">colour</span> <span style="color: #0000FF;">=</span> <span style="color: #008080;">iff</span><span style="color: #0000FF;">(</span><span style="color: #000000;">live</span><span style="color: #0000FF;">?</span><span style="color: #008080;">iff</span><span style="color: #0000FF;">(</span><span style="color: #000000;">cxy</span><span style="color: #0000FF;">?</span><span style="color: #008080;">iff</span><span style="color: #0000FF;">(</span><span style="color: #000000;">clxy</span><span style="color: #0000FF;">?</span><span style="color: #004600;">CD_PURPLE</span> <span style="color: #000080;font-style:italic;">-- adult</span> |
|||
<span style="color: #0000FF;">:</span><span style="color: #004600;">CD_GREEN</span><span style="color: #0000FF;">)</span> <span style="color: #000080;font-style:italic;">-- newborn</span> |
|||
<span style="color: #0000FF;">:</span><span style="color: #008080;">iff</span><span style="color: #0000FF;">(</span><span style="color: #000000;">clxy</span><span style="color: #0000FF;">?</span><span style="color: #004600;">CD_RED</span> <span style="color: #000080;font-style:italic;">-- old</span> |
|||
<span style="color: #0000FF;">:</span><span style="color: #004600;">CD_YELLOW</span><span style="color: #0000FF;">))</span> <span style="color: #000080;font-style:italic;">-- shortlived</span> |
|||
<span style="color: #0000FF;">:</span><span style="color: #004600;">CD_BLACK</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #000000;">cn</span><span style="color: #0000FF;">[</span><span style="color: #000000;">x</span><span style="color: #0000FF;">,</span><span style="color: #000000;">y</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">live</span> |
|||
<span style="color: #000000;">alive</span> <span style="color: #0000FF;">+=</span> <span style="color: #000000;">live</span> |
|||
<span style="color: #7060A8;">cdCanvasSetForeground</span><span style="color: #0000FF;">(</span><span style="color: #000000;">cddbuffer</span><span style="color: #0000FF;">,</span><span style="color: #000000;">colour</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #7060A8;">cdCanvasBox</span><span style="color: #0000FF;">(</span><span style="color: #000000;">cddbuffer</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">cx</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">cx</span><span style="color: #0000FF;">+</span><span style="color: #000000;">n</span><span style="color: #0000FF;">-</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">cy</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">cy</span><span style="color: #0000FF;">+</span><span style="color: #000000;">n</span><span style="color: #0000FF;">-</span><span style="color: #000000;">1</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #000000;">cy</span> <span style="color: #0000FF;">+=</span> <span style="color: #000000;">n</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span> |
|||
<span style="color: #000000;">cx</span> <span style="color: #0000FF;">+=</span> <span style="color: #000000;">n</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span> |
|||
<span style="color: #7060A8;">cdCanvasFlush</span><span style="color: #0000FF;">(</span><span style="color: #000000;">cddbuffer</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #008080;">if</span> <span style="color: #008080;">not</span> <span style="color: #000000;">alive</span> <span style="color: #008080;">then</span> |
|||
<span style="color: #7060A8;">IupStoreAttribute</span><span style="color: #0000FF;">(</span><span style="color: #000000;">hTimer</span><span style="color: #0000FF;">,</span> <span style="color: #008000;">"RUN"</span><span style="color: #0000FF;">,</span> <span style="color: #008000;">"NO"</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #7060A8;">IupStoreAttribute</span><span style="color: #0000FF;">(</span><span style="color: #000000;">dlg</span><span style="color: #0000FF;">,</span> <span style="color: #008000;">"TITLE"</span><span style="color: #0000FF;">,</span> <span style="color: #008000;">"died"</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #008080;">elsif</span> <span style="color: #000000;">c</span><span style="color: #0000FF;">=</span><span style="color: #000000;">cn</span> <span style="color: #008080;">then</span> |
|||
<span style="color: #7060A8;">IupStoreAttribute</span><span style="color: #0000FF;">(</span><span style="color: #000000;">hTimer</span><span style="color: #0000FF;">,</span> <span style="color: #008000;">"RUN"</span><span style="color: #0000FF;">,</span> <span style="color: #008000;">"NO"</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #7060A8;">IupStoreAttribute</span><span style="color: #0000FF;">(</span><span style="color: #000000;">dlg</span><span style="color: #0000FF;">,</span> <span style="color: #008000;">"TITLE"</span><span style="color: #0000FF;">,</span> <span style="color: #008000;">"stable"</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #008080;">else</span> |
|||
<span style="color: #000000;">cl</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">c</span> |
|||
<span style="color: #000000;">c</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">deep_copy</span><span style="color: #0000FF;">(</span><span style="color: #000000;">cn</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #000000;">fps</span> <span style="color: #0000FF;">+=</span> <span style="color: #000000;">1</span> |
|||
<span style="color: #008080;">if</span> <span style="color: #7060A8;">time</span><span style="color: #0000FF;">()>=</span><span style="color: #000000;">t1</span> <span style="color: #008080;">then</span> |
|||
<span style="color: #7060A8;">IupSetStrAttribute</span><span style="color: #0000FF;">(</span><span style="color: #000000;">dlg</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"TITLE"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"%s (%d FPS)"</span><span style="color: #0000FF;">,{</span><span style="color: #000000;">title</span><span style="color: #0000FF;">,</span><span style="color: #000000;">fps</span><span style="color: #0000FF;">})</span> |
|||
<span style="color: #000000;">t1</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">time</span><span style="color: #0000FF;">()+</span><span style="color: #000000;">1</span> |
|||
<span style="color: #000000;">fps</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">0</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span> |
|||
<span style="color: #008080;">return</span> <span style="color: #004600;">IUP_DEFAULT</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span> |
|||
<span style="color: #008080;">function</span> <span style="color: #000000;">map_cb</span><span style="color: #0000FF;">(</span><span style="color: #004080;">Ihandle</span> <span style="color: #000000;">ih</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #004080;">cdCanvas</span> <span style="color: #000000;">cdcanvas</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">cdCreateCanvas</span><span style="color: #0000FF;">(</span><span style="color: #004600;">CD_IUP</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">ih</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #000000;">cddbuffer</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">cdCreateCanvas</span><span style="color: #0000FF;">(</span><span style="color: #004600;">CD_DBUFFER</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">cdcanvas</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #7060A8;">cdCanvasSetBackground</span><span style="color: #0000FF;">(</span><span style="color: #000000;">cddbuffer</span><span style="color: #0000FF;">,</span> <span style="color: #004600;">CD_WHITE</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #7060A8;">cdCanvasSetForeground</span><span style="color: #0000FF;">(</span><span style="color: #000000;">cddbuffer</span><span style="color: #0000FF;">,</span> <span style="color: #004600;">CD_RED</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #008080;">return</span> <span style="color: #004600;">IUP_DEFAULT</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span> |
|||
<span style="color: #008080;">function</span> <span style="color: #000000;">key_cb</span><span style="color: #0000FF;">(</span><span style="color: #004080;">Ihandle</span> <span style="color: #000080;font-style:italic;">/*ih*/</span><span style="color: #0000FF;">,</span> <span style="color: #004080;">atom</span> <span style="color: #000000;">c</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #008080;">if</span> <span style="color: #000000;">c</span><span style="color: #0000FF;">=</span><span style="color: #004600;">K_ESC</span> <span style="color: #008080;">then</span> <span style="color: #008080;">return</span> <span style="color: #004600;">IUP_CLOSE</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span> <span style="color: #000080;font-style:italic;">-- (standard practice for me)</span> |
|||
<span style="color: #008080;">if</span> <span style="color: #000000;">c</span><span style="color: #0000FF;">=</span><span style="color: #004600;">K_F5</span> <span style="color: #008080;">then</span> <span style="color: #008080;">return</span> <span style="color: #004600;">IUP_DEFAULT</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span> <span style="color: #000080;font-style:italic;">-- (let browser reload work)</span> |
|||
<span style="color: #008080;">if</span> <span style="color: #7060A8;">find</span><span style="color: #0000FF;">(</span><span style="color: #000000;">c</span><span style="color: #0000FF;">,</span><span style="color: #008000;">" "</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">then</span> <span style="color: #000000;">draw</span><span style="color: #0000FF;">(</span><span style="color: #008000;">' '</span><span style="color: #0000FF;">)</span> <span style="color: #000080;font-style:italic;">-- Clear</span> |
|||
<span style="color: #008080;">elsif</span> <span style="color: #7060A8;">find</span><span style="color: #0000FF;">(</span><span style="color: #000000;">c</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"+bB"</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">then</span> <span style="color: #000000;">draw</span><span style="color: #0000FF;">(</span><span style="color: #008000;">'+'</span><span style="color: #0000FF;">)</span> <span style="color: #000080;font-style:italic;">-- Blinker</span> |
|||
<span style="color: #008080;">elsif</span> <span style="color: #7060A8;">find</span><span style="color: #0000FF;">(</span><span style="color: #000000;">c</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"gG"</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">then</span> <span style="color: #000000;">draw</span><span style="color: #0000FF;">(</span><span style="color: #008000;">'G'</span><span style="color: #0000FF;">)</span> <span style="color: #000080;font-style:italic;">-- Glider</span> |
|||
<span style="color: #008080;">elsif</span> <span style="color: #7060A8;">find</span><span style="color: #0000FF;">(</span><span style="color: #000000;">c</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"tT"</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">then</span> <span style="color: #000000;">draw</span><span style="color: #0000FF;">(</span><span style="color: #008000;">'T'</span><span style="color: #0000FF;">)</span> <span style="color: #000080;font-style:italic;">-- Thunderbird</span> |
|||
<span style="color: #008080;">elsif</span> <span style="color: #7060A8;">find</span><span style="color: #0000FF;">(</span><span style="color: #000000;">c</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"xX"</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">then</span> <span style="color: #000000;">draw</span><span style="color: #0000FF;">(</span><span style="color: #008000;">'X'</span><span style="color: #0000FF;">)</span> <span style="color: #000080;font-style:italic;">-- Cross</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span> |
|||
<span style="color: #7060A8;">IupStoreAttribute</span><span style="color: #0000FF;">(</span><span style="color: #000000;">hTimer</span><span style="color: #0000FF;">,</span> <span style="color: #008000;">"RUN"</span><span style="color: #0000FF;">,</span> <span style="color: #008000;">"YES"</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #008080;">return</span> <span style="color: #004600;">IUP_CONTINUE</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span> |
|||
<span style="color: #008080;">function</span> <span style="color: #000000;">timer_cb</span><span style="color: #0000FF;">(</span><span style="color: #004080;">Ihandle</span> <span style="color: #000080;font-style:italic;">/*ih*/</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #7060A8;">IupUpdate</span><span style="color: #0000FF;">(</span><span style="color: #000000;">canvas</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #008080;">return</span> <span style="color: #004600;">IUP_IGNORE</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span> |
|||
<span style="color: #008080;">procedure</span> <span style="color: #000000;">main</span><span style="color: #0000FF;">()</span> |
|||
<span style="color: #7060A8;">IupOpen</span><span style="color: #0000FF;">()</span> |
|||
<span style="color: #000000;">canvas</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">IupCanvas</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"RASTERSIZE=390x390"</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #7060A8;">IupSetCallbacks</span><span style="color: #0000FF;">(</span><span style="color: #000000;">canvas</span><span style="color: #0000FF;">,</span> <span style="color: #0000FF;">{</span><span style="color: #008000;">"MAP_CB"</span><span style="color: #0000FF;">,</span> <span style="color: #7060A8;">Icallback</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"map_cb"</span><span style="color: #0000FF;">),</span> |
|||
<span style="color: #008000;">"ACTION"</span><span style="color: #0000FF;">,</span> <span style="color: #7060A8;">Icallback</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"redraw_cb"</span><span style="color: #0000FF;">)})</span> |
|||
<span style="color: #000000;">dlg</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">IupDialog</span><span style="color: #0000FF;">(</span><span style="color: #000000;">canvas</span><span style="color: #0000FF;">,</span><span style="color: #008000;">`TITLE="%s", MINSIZE=350x55`</span><span style="color: #0000FF;">,</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">title</span><span style="color: #0000FF;">})</span> |
|||
<span style="color: #000080;font-style:italic;">-- (above MINSIZE prevents the title from getting squished)</span> |
|||
<span style="color: #7060A8;">IupSetCallback</span><span style="color: #0000FF;">(</span><span style="color: #000000;">dlg</span><span style="color: #0000FF;">,</span> <span style="color: #008000;">"KEY_CB"</span><span style="color: #0000FF;">,</span> <span style="color: #7060A8;">Icallback</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"key_cb"</span><span style="color: #0000FF;">))</span> |
|||
<span style="color: #7060A8;">IupShow</span><span style="color: #0000FF;">(</span><span style="color: #000000;">dlg</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #7060A8;">IupSetAttribute</span><span style="color: #0000FF;">(</span><span style="color: #000000;">canvas</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"RASTERSIZE"</span><span style="color: #0000FF;">,</span><span style="color: #004600;">NULL</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #000080;font-style:italic;">-- hTimer = IupTimer(Icallback("timer_cb"),40) -- 25fps (maybe)</span> |
|||
<span style="color: #000000;">hTimer</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">IupTimer</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">Icallback</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"timer_cb"</span><span style="color: #0000FF;">),</span><span style="color: #000000;">100</span><span style="color: #0000FF;">)</span> <span style="color: #000080;font-style:italic;">-- 10fps</span> |
|||
<span style="color: #008080;">if</span> <span style="color: #7060A8;">platform</span><span style="color: #0000FF;">()!=</span><span style="color: #004600;">JS</span> <span style="color: #008080;">then</span> |
|||
<span style="color: #7060A8;">IupMainLoop</span><span style="color: #0000FF;">()</span> |
|||
<span style="color: #7060A8;">IupClose</span><span style="color: #0000FF;">()</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span> |
|||
<span style="color: #000000;">main</span><span style="color: #0000FF;">()</span> |
|||
<!--</syntaxhighlight>--> |
|||
=={{header|Picat}}== |
|||
An example of use: |
|||
===Blinker=== |
|||
<syntaxhighlight lang="picat">go => |
|||
Rows = 3, |
|||
Cols = 3, |
|||
println(blinker), |
|||
pattern(blinker, Pattern,I,J), |
|||
life(fill(Rows,Cols,Pattern,I,J)), |
|||
nl. |
|||
fill(Rows, Cols, Obj) = fill(Rows, Cols, Obj,1,1). |
|||
<lang perl6>my Grid $glider .= new ' |
|||
fill(Rows, Cols, Obj,OffsetI,OffsetJ) = Grid => |
|||
............ |
|||
Grid = new_array(Rows,Cols), bind_vars(Grid,0), |
|||
............ |
|||
foreach(I in 1..Obj.length, J in 1..Obj[1].length) |
|||
............ |
|||
Grid[I+OffsetI-1,J+OffsetJ-1] := Obj[I,J] |
|||
.......###.. |
|||
end. |
|||
.......#.... |
|||
........#... |
|||
............'; |
|||
% We stop whenever a fixpoint/cycle is detected |
|||
loop { |
|||
life(S) => |
|||
say $glider; |
|||
Rows = S.length, |
|||
Cols = S[1].length, |
|||
println([rows=Rows, cols=Cols]), |
|||
}</lang> |
|||
print_grid(S), |
|||
Seen = new_map(), % detect fixpoint and cycle |
|||
Count = 0, |
|||
while (not Seen.has_key(S)) |
|||
Seen.put(S,1), |
|||
T = new_array(Rows,Cols), |
|||
foreach(I in 1..Rows, J in 1..Cols) |
|||
Sum = sum([S[I+A,J+B] : A in -1..1,B in -1..1, |
|||
I+A > 0, J+B > 0, |
|||
I+A =< Rows, J+B =< Cols]) - S[I,J], |
|||
C = rules(S[I,J], Sum), |
|||
T[I,J] := C |
|||
end, |
|||
print_grid(T), |
|||
S := T, |
|||
Count := Count + 1 |
|||
end, |
|||
printf("%d generations\n", Count). |
|||
print_grid(G) => |
|||
foreach(I in 1..G.length) |
|||
foreach(J in 1..G[1].length) |
|||
if G[I,J] == 1 then print("#") else print(".") end |
|||
end, |
|||
nl |
|||
end, |
|||
nl. |
|||
% The Rules of Life |
|||
rules(This,Sum) = 1, (This == 1, member(Sum,[2,3]); This == 0, Sum == 3) => true. |
|||
rules(_This, _Sum) = 0 => true. |
|||
% |
|||
% The patterns |
|||
% pattern(Name, Pattern, OffsetI, OffsetJ) |
|||
% where Offset* is the recommended offsets in a grid. |
|||
% |
|||
% For the task |
|||
pattern(blinker, Pattern,I,J) ?=> |
|||
Pattern = [[0,0,0], |
|||
[1,1,1], |
|||
[0,0,0]], |
|||
I=1,J=1.</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
blinker |
|||
[rows = 3,cols = 3] |
|||
... |
|||
### |
|||
... |
|||
.#. |
|||
.#. |
|||
.#. |
|||
... |
|||
### |
|||
... |
|||
2 generations</pre> |
|||
===Testing some more forms=== |
|||
<syntaxhighlight lang="picat">go2 => |
|||
Rows = 20, |
|||
Cols = 20, |
|||
Names = [blinker2, p4, p5, glider, figure_eight], |
|||
foreach(Name in Names ) |
|||
pattern(Name, Pattern,I,J), |
|||
println(Name), |
|||
life(fill(Rows,Cols,Pattern,I,J)), |
|||
nl |
|||
end, |
|||
nl. |
|||
% Increase the recommended offset |
|||
pattern(blinker2, Pattern,I,J) ?=> |
|||
Pattern = [[0,0,0], |
|||
[1,1,1], |
|||
[0,0,0]], |
|||
I=5,J=5. |
|||
pattern(p4, Pattern,I,J) ?=> |
|||
Pattern = [[0,0,0,0], |
|||
[1,1,1,1], |
|||
[1,1,1,1], |
|||
[0,0,0,0]], |
|||
I=10,J=10. |
|||
pattern(p5, Pattern,I,J) ?=> |
|||
Pattern = [[0,0,0,0,0], |
|||
[1,1,1,1,1], |
|||
[1,1,1,1,1], |
|||
[1,1,1,1,1], |
|||
[0,0,0,0,0]], |
|||
I=10,J=10. |
|||
pattern(glider, Pattern,I,J) ?=> |
|||
Pattern = [[0,0,1], |
|||
[1,0,1], |
|||
[0,1,1]], |
|||
I=5,J=5. |
|||
pattern(figure_eight, Pattern,I,J) => |
|||
Pattern = [[1,1,1,0,0,0], |
|||
[1,1,1,0,0,0], |
|||
[1,1,1,0,0,0], |
|||
[0,0,0,1,1,1], |
|||
[0,0,0,1,1,1], |
|||
[0,0,0,1,1,1]], |
|||
I=10,J=10.</syntaxhighlight> |
|||
=={{header|PicoLisp}}== |
=={{header|PicoLisp}}== |
||
Line 1,702: | Line 12,286: | ||
an array of multiply linked objects, and are also used in the chess program and |
an array of multiply linked objects, and are also used in the chess program and |
||
other games in the distribution. |
other games in the distribution. |
||
< |
<syntaxhighlight lang="picolisp">(load "@lib/simul.l") |
||
(de life (DX DY . Init) |
(de life (DX DY . Init) |
||
Line 1,731: | Line 12,315: | ||
(=: life (: next)) ) ) ) ) ) |
(=: life (: next)) ) ) ) ) ) |
||
(life 5 5 b3 c3 d3)</ |
(life 5 5 b3 c3 d3)</syntaxhighlight> |
||
Output: |
Output: |
||
<pre> 5 |
<pre> 5 |
||
Line 1,752: | Line 12,336: | ||
a b c d e</pre> |
a b c d e</pre> |
||
=={{header| |
=={{header|PL/I}}== |
||
<syntaxhighlight lang="pl/i">(subscriptrange): |
|||
<lang PureBasic>EnableExplicit |
|||
Conway: procedure options (main); /* 20 November 2013 */ |
|||
Define.i x, y ,Xmax ,Ymax ,N |
|||
/* A grid of (1:100, 1:100) is desired; the array GRID is defined as (0:101, 0:101), */ |
|||
Xmax = 13 : Ymax = 20 |
|||
/* to satisfy the requirement that elements off-grid are zero. */ |
|||
Dim world.i(Xmax+1,Ymax+1) |
|||
declare n fixed binary; /* grid size) */ |
|||
Dim Nextworld.i(Xmax+1,Ymax+1) |
|||
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;</syntaxhighlight> |
|||
Results: |
|||
<pre> |
|||
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 |
|||
</pre> |
|||
=={{header|Pointless}}== |
|||
<syntaxhighlight lang="pointless">----------------------------------------------------------- |
|||
-- Print 100 simulated states of conway's game of life |
|||
-- for a glider starting pattern on a wrapping grid |
|||
-- Print generation number along with cells |
|||
output = |
|||
initCells |
|||
|> iterate(updateCells) |
|||
|> take(130) |
|||
|> enumerate |
|||
|> map(showPair) |
|||
|> printFrames |
|||
initCells = |
|||
[[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] |
|||
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] |
|||
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] |
|||
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] |
|||
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] |
|||
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] |
|||
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] |
|||
[0 0 0 0 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 0 0 0 0 1 0 0 0 0 0 0 0] |
|||
[0 0 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 0 0 0 0 0 0 0 0 0 0 0 0 0] |
|||
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] |
|||
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] |
|||
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] |
|||
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]] |
|||
width = length(initCells[0]) |
|||
height = length(initCells) |
|||
positions = |
|||
for y in range(0, height - 1) |
|||
for x in range(0, width - 1) |
|||
yield (x, y) |
|||
----------------------------------------------------------- |
|||
-- Update each cell in the grid according to its position, |
|||
-- and convert the resulting list back to a 2D array |
|||
updateCells(cells) = |
|||
positions |
|||
|> map(tick(cells)) |
|||
|> toNDArray((height, width)) |
|||
----------------------------------------------------------- |
|||
-- Get the new value for a cell at a give position |
|||
-- based on the current cell values in the grid |
|||
tick(cells, pos) = toInt(survive or birth) where { |
|||
survive = cells[y][x] == 1 and count in {2, 3} |
|||
birth = cells[y][x] == 0 and count == 3 |
|||
count = getCount(x, y, cells) |
|||
(x, y) = pos |
|||
} |
|||
----------------------------------------------------------- |
|||
-- Get the number of live neighbors of a given position |
|||
getCount(x, y, cells) = sum( |
|||
for dx in [-1, 0, 1] |
|||
for dy in [-1, 0, 1] |
|||
when (dx != 0 or dy != 0) |
|||
yield getNeighbor(x + dx, y + dy, cells) |
|||
) |
|||
getNeighbor(x, y, cells) = cells[y % height][x % width] |
|||
----------------------------------------------------------- |
|||
-- Print the board and generation number given pairs |
|||
-- of (gen, cells) from the enumerate function |
|||
showPair(pair) = |
|||
format("{}\ngeneration: {}", [showCells(cells), gen]) |
|||
where (gen, cells) = pair |
|||
showCells(cells) = |
|||
toList(cells) |
|||
|> map(showRow) |
|||
|> join("\n") |
|||
showRow(row) = |
|||
format("|{}|", [map(showCell, toList(row)) |> join("")]) |
|||
showCell(cell) = |
|||
if cell == 1 then "*" else " "</syntaxhighlight> |
|||
=={{header|PostScript}}== |
|||
<syntaxhighlight lang="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</syntaxhighlight> |
|||
=={{header|Prolog}}== |
|||
<syntaxhighlight lang="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(_,[]) :- !. |
|||
</syntaxhighlight> |
|||
; 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 |
|||
CopyMemory(@Nextworld(0,0), @world(0,0),(Xmax+2)*(Ymax+2)*SizeOf(Integer)) ; Workaround for Swap-Bug in PB 4.50 |
|||
Dim Nextworld.i(Xmax+1,Ymax+1) |
|||
Until Inkey() <> "" |
|||
PrintN("Press any key to exit"): Repeat: Until Inkey() <> ""</lang> |
|||
'''Sample output:'''<br> |
'''Sample output:'''<br> |
||
[[File: |
[[File:100x100.gif]] |
||
=={{header|Processing}}== |
|||
Inspired by Daniel Shiffman's book The Nature of Code (http://natureofcode.com) |
|||
<syntaxhighlight lang="java">boolean play = true; |
|||
int cellSize = 10; |
|||
int cols, rows; |
|||
int lastCell = 0; |
|||
int sample = 10; |
|||
// Game of life board |
|||
int[][] grid; |
|||
void setup() { |
|||
size(800, 500); |
|||
noStroke(); |
|||
// Calculate cols, rows and init array |
|||
cols = width/cellSize; |
|||
rows = height/cellSize; |
|||
grid = new int[cols][rows]; |
|||
init(-1); // randomized start |
|||
println("Press 'space' to start/stop"); |
|||
println("'e' to clear all cells"); |
|||
println("'b' demonstrate 'blinker'"); |
|||
println("'g' demonstrate glider"); |
|||
println("'r' to randomize grid"); |
|||
println("'+' and '-' to change speed"); |
|||
} |
|||
void draw() { |
|||
background(0); |
|||
for ( int i = 0; i < cols; i++) { |
|||
for ( int j = 0; j < rows; j++) { |
|||
if ((grid[i][j] == 1)) fill(255); |
|||
else fill(0); |
|||
rect(i*cellSize, j*cellSize, cellSize, cellSize); |
|||
} |
|||
} |
|||
if (play && frameCount%sample==0 && !mousePressed) { |
|||
generate(); |
|||
} |
|||
} |
|||
void generate() { |
|||
int[][] nextGrid = new int[cols][rows]; |
|||
for (int x = 0; x < cols; x++) { |
|||
for (int y = 0; y < rows; y++) { |
|||
int ngbs = countNgbs(x, y); |
|||
// the classic Conway rules |
|||
if ((grid[x][y] == 1) && (ngbs < 2)) nextGrid[x][y] = 0; // solitude |
|||
else if ((grid[x][y] == 1) && (ngbs > 3)) nextGrid[x][y] = 0; // crowded |
|||
else if ((grid[x][y] == 0) && (ngbs == 3)) nextGrid[x][y] = 1; // cell born |
|||
else nextGrid[x][y] = grid[x][y]; // keep |
|||
} |
|||
} |
|||
grid = nextGrid; |
|||
} |
|||
int countNgbs(int x, int y) { |
|||
int ngbCount = 0; |
|||
for (int i = -1; i <= 1; i++) { |
|||
for (int j = -1; j <= 1; j++) { |
|||
// 'united' borders |
|||
ngbCount += grid[(x+i+cols)%cols][(y+j+rows)%rows]; |
|||
} |
|||
} |
|||
// cell taken out of count |
|||
ngbCount -= grid[x][y]; |
|||
return ngbCount; |
|||
} |
|||
void init(int option) { |
|||
int state; |
|||
for (int x = 0; x < cols; x++) { |
|||
for (int y = 0; y < rows; y++) { |
|||
if (option == -1) { |
|||
state = int(random(2)); |
|||
} else { |
|||
state = option; |
|||
} |
|||
grid[x][y] = state; |
|||
} |
|||
} |
|||
} |
|||
void keyReleased() { |
|||
if (key == 'r') { |
|||
init(-1); // randomize grid |
|||
} |
|||
if (key == 'e') { |
|||
init(0); // empty grid |
|||
} |
|||
if (key == 'g') { |
|||
int glider[][] = { |
|||
{0, 1, 0}, |
|||
{0, 0, 1}, |
|||
{1, 1, 1}}; |
|||
setNine(10, 10, glider); |
|||
} |
|||
if (key == 'b') { |
|||
int blinker[][] = { |
|||
{0, 1, 0}, |
|||
{0, 1, 0}, |
|||
{0, 1, 0}}; |
|||
setNine(10, 10, blinker); |
|||
} |
|||
if (key == ' ') { |
|||
play = !play; |
|||
} |
|||
if (key == '+' || key == '=') { |
|||
sample=max(sample-1, 1); |
|||
} |
|||
if (key == '-') { |
|||
sample++; |
|||
} |
|||
} |
|||
void setNine(int x, int y, int nine[][]) { |
|||
for (int i = 0; i <= 2; i++) { |
|||
for (int j = 0; j <= 2; j++) { |
|||
grid[(x+i+cols)%cols][(y+j+rows)%rows] = nine[i][j]; |
|||
} |
|||
} |
|||
} |
|||
void mousePressed() { |
|||
paint(); |
|||
} |
|||
void mouseDragged() { |
|||
paint(); |
|||
} |
|||
void paint() { |
|||
int x = mouseX/cellSize; |
|||
int y = mouseY/cellSize; |
|||
int p = y*cols + x; |
|||
if (p!=lastCell) { |
|||
lastCell=p; |
|||
int states[] = {1, 0}; |
|||
grid[x][y] = states[grid[x][y]]; // invert |
|||
} |
|||
}</syntaxhighlight> |
|||
==={{header|Processing Python mode}}=== |
|||
<syntaxhighlight lang="python">cell_size = 10 |
|||
sample = 10 |
|||
play = False # simulation is running |
|||
last_cell = 0 |
|||
def setup(): |
|||
global grid, next_grid, rows, cols |
|||
size(800, 500) |
|||
rows = height / cell_size |
|||
cols = width / cell_size |
|||
grid = empty_grid() |
|||
next_grid = empty_grid() |
|||
randomize_grid() |
|||
println("Press 'space' to start/stop") |
|||
println("'e' to clear all cells") |
|||
println("'b' demonstrate 'blinker'") |
|||
println("'g' demonstrate glider") |
|||
println("'r' to randomize grid") |
|||
println("'+' and '-' to change speed") |
|||
def draw(): |
|||
background(0) |
|||
for i in range(cols): |
|||
x = i * cell_size |
|||
for j in range(rows): |
|||
y = j * cell_size |
|||
current_state = grid[i][j] |
|||
fill(255) |
|||
noStroke() |
|||
if current_state: |
|||
rect(x, y, cell_size, cell_size) |
|||
if play: |
|||
ngbs_alive = calc_ngbs_alive(i, j) |
|||
result = rule(current_state, ngbs_alive) |
|||
next_grid[i][j] = result |
|||
if play and frameCount % sample == 0 and not mousePressed: |
|||
step() |
|||
def rule(current, ngbs): |
|||
""" classic Conway's Game of Life rule """ |
|||
if ngbs < 2 or ngbs > 3: |
|||
return 0 # dies / dead |
|||
elif ngbs == 3: |
|||
return 1 # born / alive |
|||
else: |
|||
return current # stays the same (ngbs == 2) |
|||
def calc_ngbs_alive(i, j): |
|||
NEIGHBOURS = ((-1, 00), (01, 00), # a tuple describing the neighbourhood of a cell |
|||
(-1, -1), (00, -1), |
|||
(01, -1), (-1, 01), |
|||
(00, 01), (01, 01)) |
|||
alive = 0 |
|||
for iv, jv in NEIGHBOURS: |
|||
alive += grid[(i + iv) % cols][(j + jv) % rows] |
|||
return alive |
|||
def empty_grid(): |
|||
grid = [] |
|||
for _ in range(cols): |
|||
grid.append([0] * rows) |
|||
return grid |
|||
def randomize_grid(): |
|||
from random import choice |
|||
for i in range(cols): |
|||
for j in range(rows): |
|||
grid[i][j] = choice((0, 1)) |
|||
def step(): |
|||
global grid, next_grid |
|||
grid = next_grid |
|||
next_grid = empty_grid() |
|||
def keyReleased(): |
|||
global grid, play, sample |
|||
if key == "e": |
|||
grid = empty_grid() |
|||
if key == "r": |
|||
randomize_grid() |
|||
if key == "g": |
|||
grid[10][10:13] = [0, 1, 0] |
|||
grid[11][10:13] = [0, 0, 1] |
|||
grid[12][10:13] = [1, 1, 1] |
|||
if key == "b": |
|||
grid[10][10:13] = [0, 1, 0] |
|||
grid[11][10:13] = [0, 1, 0] |
|||
grid[12][10:13] = [0, 1, 0] |
|||
if key == " ": |
|||
play = not play |
|||
if str(key) in '+=': |
|||
sample = max(sample - 1, 1); |
|||
if key == '-': |
|||
sample += 1 |
|||
def mousePressed(): |
|||
paint() |
|||
def mouseDragged(): |
|||
paint() |
|||
def paint(): |
|||
global last_cell |
|||
i, j = mouseX // cell_size, mouseY // cell_size |
|||
p = j * cols + i |
|||
if p != last_cell: |
|||
last_cell = p |
|||
grid[i][j] = (1, 0)[grid[i][j]]</syntaxhighlight> |
|||
=={{header|Python}}== |
=={{header|Python}}== |
||
===Using defaultdict=== |
|||
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 <u>celltable</u> to be initialized to just those keys with a value of 1. |
|||
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 <u>celltable</u> to be initialized to just those keys |
|||
with a value of 1. |
|||
Python allows many types other than strings and ints to be keys |
|||
Python allows many types other than strings and ints to be keys in a [http://www.rosettacode.org/wiki/Creating_a_Hash_from_Two_Arrays#Python dictionary]. The example uses a dictionary with keys that are a [[Two-dimensional_array_(runtime)#Python|two entry tuple]] to represent the <u>universe</u>, which also returns a default value of zero. This simplifies the calculation '''N''' as out-of-bounds indexing of <u>universe</u> returns zero. |
|||
in a [[Creating a Hash from Two Arrays#Python|dictionary]]. |
|||
The example uses a dictionary with keys that are a [[Two-dimensional array (runtime)#Python|two entry tuple]] to represent the <u>universe</u>, |
|||
which also returns a default value of zero. |
|||
This simplifies the calculation '''N''' as out-of-bounds indexing |
|||
of <u>universe</u> returns zero. |
|||
< |
<syntaxhighlight lang="python">import random |
||
from collections import defaultdict |
from collections import defaultdict |
||
Line 1,857: | Line 13,077: | ||
for i in range(maxgenerations): |
for i in range(maxgenerations): |
||
print |
print("\nGeneration %3i:" % ( i, )) |
||
for row in range(cellcount[1]): |
for row in range(cellcount[1]): |
||
print |
print(" ", ''.join(str(universe[(row,col)]) |
||
for col in range(cellcount[0])).replace( |
for col in range(cellcount[0])).replace( |
||
'0', printdead).replace('1', printlive) |
'0', printdead).replace('1', printlive)) |
||
nextgeneration = defaultdict(int) |
nextgeneration = defaultdict(int) |
||
for row in range(cellcount[1]): |
for row in range(cellcount[1]): |
||
Line 1,871: | Line 13,091: | ||
for c in range(col-1, col+2) ) |
for c in range(col-1, col+2) ) |
||
) ] |
) ] |
||
universe = nextgeneration</ |
universe = nextgeneration</syntaxhighlight> |
||
{{out}} (sample) |
|||
=== Sample output: === |
|||
<pre style="height:30ex;overflow:scroll"> |
<pre style="height:30ex;overflow:scroll"> |
||
Generation 0: |
Generation 0: |
||
Line 1,888: | Line 13,108: | ||
### |
### |
||
---</pre> |
---</pre> |
||
===Boardless approach=== |
|||
A version using the boardless approach. |
|||
A world is represented as a set of (x, y) coordinates of all the alive cells. |
|||
<syntaxhighlight lang="python">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 = {c for c in counts |
|||
if counts[c] == 3 or (counts[c] == 2 and c in world)} |
|||
neighboring_cells = [(-1, -1), (-1, 0), (-1, 1), |
|||
( 0, -1), ( 0, 1), |
|||
( 1, -1), ( 1, 0), ( 1, 1)] |
|||
def offset(cells, delta): |
|||
"Slide/offset all the cells by delta, a (dx, dy) vector." |
|||
(dx, dy) = delta |
|||
return {(x+dx, y+dy) for (x, y) in cells} |
|||
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)</syntaxhighlight> |
|||
{{out}} |
|||
<pre style="height:30ex;overflow:scroll"> |
|||
GENERATION 0: |
|||
##................................... |
|||
##................................... |
|||
......#...........####............... |
|||
......#.............................. |
|||
......#.............................. |
|||
...............##........#........... |
|||
...............#.#.......##.......... |
|||
...............#.........##........## |
|||
..........................#........## |
|||
GENERATION 1: |
|||
##................................... |
|||
##.................##................ |
|||
...................##................ |
|||
.....###...........##................ |
|||
..................................... |
|||
...............##........##.......... |
|||
..............##........#............ |
|||
................#..........#.......## |
|||
.........................##........## |
|||
GENERATION 2: |
|||
##................................... |
|||
##.................##................ |
|||
......#...........#..#............... |
|||
......#............##................ |
|||
......#.............................. |
|||
..............###........#........... |
|||
..............#..........##.......... |
|||
...............#.........##........## |
|||
..........................#........## |
|||
GENERATION 3: |
|||
##................................... |
|||
##.................##................ |
|||
..................#..#............... |
|||
.....###...........##................ |
|||
...............#..................... |
|||
..............##.........##.......... |
|||
..............#.#.......#............ |
|||
...........................#.......## |
|||
.........................##........## |
|||
GENERATION 4: |
|||
##................................... |
|||
##.................##................ |
|||
......#...........#..#............... |
|||
......#............##................ |
|||
......#.......##..................... |
|||
..............#.#........#........... |
|||
..............#..........##.......... |
|||
.........................##........## |
|||
..........................#........## |
|||
GENERATION 5: |
|||
##................................... |
|||
##.................##................ |
|||
..................#..#............... |
|||
.....###...........##................ |
|||
..............##..................... |
|||
.............##..........##.......... |
|||
...............#........#............ |
|||
...........................#.......## |
|||
.........................##........## |
|||
</pre> |
|||
===Using an array to define the world=== |
|||
<syntaxhighlight lang="python">import numpy as np |
|||
from pandas import DataFrame |
|||
import matplotlib.pyplot as plt |
|||
#import time |
|||
def conway_life(len=10, wid=10, gen=5): |
|||
curr_gen = DataFrame(np.random.randint(0, 2, (len+2, wid+2)), |
|||
index = range(len+2), |
|||
columns = range(wid+2)) |
|||
curr_gen[0] = 0 |
|||
curr_gen[wid+1] = 0 |
|||
curr_gen[0: 1] = 0 |
|||
curr_gen[len+1: len+2] = 0 |
|||
for i in range(gen): |
|||
fig, ax = plt.subplots() |
|||
draw = curr_gen[1:len+1].drop([0, wid+1], axis=1) |
|||
# 画图 |
|||
image = draw |
|||
ax.imshow(image, cmap=plt.cm.cool, interpolation='nearest') |
|||
ax.set_title("Conway's game of life.") |
|||
# Move left and bottom spines outward by 10 points |
|||
ax.spines['left'].set_position(('outward', 10)) |
|||
ax.spines['bottom'].set_position(('outward', 10)) |
|||
# Hide the right and top spines |
|||
ax.spines['right'].set_visible(False) |
|||
ax.spines['top'].set_visible(False) |
|||
# Only show ticks on the left and bottom spines |
|||
ax.yaxis.set_ticks_position('left') |
|||
ax.xaxis.set_ticks_position('bottom') |
|||
plt.show() |
|||
# time.sleep(1) |
|||
# 初始化空表 |
|||
next_gen = DataFrame(np.random.randint(0, 1, (len+2, wid+2)), |
|||
index = range(len+2), |
|||
columns = range(wid+2)) |
|||
# 生成下一代 |
|||
for x in range(1, wid+1): |
|||
for y in range(1, len+1): |
|||
env = (curr_gen[x-1][y-1] + curr_gen[x][y-1] + |
|||
curr_gen[x+1][y-1]+ curr_gen[x-1][y] + |
|||
curr_gen[x+1][y] + curr_gen[x-1][y+1] + |
|||
curr_gen[x][y+1] + curr_gen[x+1][y+1]) |
|||
if (not curr_gen[x][y] and env == 3): |
|||
next_gen[x][y] = 1 |
|||
if (curr_gen[x][y] and env in (2, 3)): |
|||
next_gen[x][y] = 1 |
|||
curr_gen = next_gen |
|||
conway_life()</syntaxhighlight> |
|||
=={{header|Quackery}}== |
|||
Uses a Tortoise and Hare algorithm to detect when Life becomes static or repetitive. The tortoise version is displayed, along with five victory laps of the cyclic part once a cycle is detected. |
|||
<syntaxhighlight lang="Quackery"> |
|||
[ $ "turtleduck.qky" loadfile ] now! |
|||
[ $ \ |
|||
import time |
|||
now = time.time() |
|||
sleepy_time = 5/7-(now%(5/7)) |
|||
time.sleep(sleepy_time) |
|||
\ python ] is wait ( --> ) |
|||
[ dup 1 |
|||
[ 2dup > while + 1 >> 2dup / again ] |
|||
drop nip ] is sqrt ( n --> n ) |
|||
[ stack ] is width ( --> s ) |
|||
[ tuck witheach |
|||
[ over i^ peek + rot i^ poke swap ] |
|||
drop ] is add ( [ [ --> [ ) |
|||
[ 1 split swap join ] is left ( [ --> [ ) |
|||
[ -1 split swap join ] is right ( [ --> [ ) |
|||
[ width share split swap join ] is up ( [ --> [ ) |
|||
[ width share negate split swap join ] is down ( [ --> [ ) |
|||
[ left dup up tuck add |
|||
swap right tuck add |
|||
swap right tuck add |
|||
swap down tuck add |
|||
swap down tuck add |
|||
swap left tuck add |
|||
swap left add ] is neighbours ( [ --> [ ) |
|||
[ [] swap |
|||
width share times |
|||
[ width share split |
|||
dip [ 0 swap 0 join join join ] ] |
|||
drop |
|||
2 width tally |
|||
0 width share of tuck join join ] is makeborder ( [ --> [ ) |
|||
[ dup size times |
|||
[ 0 swap i^ poke |
|||
0 swap i^ width share + 1 - poke |
|||
width share step ] |
|||
width share times |
|||
[ 0 swap i^ poke |
|||
0 swap i^ 1 + negate poke ] ] is clearborder ( [ --> [ ) |
|||
[ dup neighbours |
|||
witheach |
|||
[ over i^ peek iff |
|||
[ 1 | 3 != if |
|||
[ 0 swap i^ poke ] ] |
|||
done |
|||
3 = if [ 1 swap i^ poke ] ] |
|||
clearborder ] is nextgen ( [ --> [ ) |
|||
[ 6 random |
|||
[ table |
|||
[ 200 200 255 ] [ 200 255 200 ] |
|||
[ 255 200 200 ] [ 200 255 255 ] |
|||
[ 255 200 255 ] [ 255 255 200 ] ] |
|||
fill |
|||
[ 4 times |
|||
[ 20 1 walk -1 4 turn ] ] ] is block ( --> ) |
|||
[ clear |
|||
width share 10 * |
|||
dup negate |
|||
4 - 1 fly |
|||
-1 4 turn |
|||
16 - 1 fly |
|||
1 4 turn |
|||
20 1 fly |
|||
' [ 200 200 200 ] colour |
|||
4 times |
|||
[ width share 2 - 20 * 1 walk |
|||
1 4 turn ] |
|||
-20 1 fly |
|||
' [ 63 63 63 ] colour |
|||
width share times |
|||
[ width share split |
|||
swap witheach |
|||
[ if block |
|||
20 1 fly ] |
|||
width share -20 * 1 fly |
|||
1 4 turn |
|||
20 1 fly |
|||
-1 4 turn ] |
|||
drop wait frame ] is draw ( [ --> ) |
|||
[ turtle 0 frames 2 wide |
|||
dup size sqrt width put |
|||
makeborder |
|||
dup |
|||
[ dup draw nextgen |
|||
dip [ nextgen nextgen ] |
|||
2dup = until ] |
|||
5 times |
|||
[ dup draw nextgen 2dup = until ] |
|||
2drop width release ] is life ( [ --> ) |
|||
' [ 0 0 0 |
|||
1 1 1 |
|||
0 0 0 ] life ( task requirement ) |
|||
' [ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 |
|||
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 |
|||
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 |
|||
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 |
|||
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 |
|||
0 0 0 0 0 0 0 0 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 1 1 0 1 1 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 0 0 0 0 0 0 0 0 0 0 0 0 ] life ( more interesting ) |
|||
randomise |
|||
[ [] |
|||
1089 times |
|||
[ 2 random join ] |
|||
life |
|||
again ] ( ... runs forever )</syntaxhighlight> |
|||
{{out}} |
|||
<code>( task requirement )</code> |
|||
[[File:Quackery Life Task.png|thumb|center]] |
|||
<code>( more interesting )</code> |
|||
[[File:Quackery Life.gif|thumb|center]] |
|||
<code>( ... runs forever )</code> |
|||
https://youtu.be/U5owgEpxzwg |
|||
(The code runs forever, not the video. The accompanying music is also generative, and comes from the Kolakoski Sequence.) |
|||
=={{header|R}}== |
=={{header|R}}== |
||
< |
<syntaxhighlight lang="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) |
gen.board <- function(type="random", nrow=3, ncol=3, seeds=NULL) |
||
{ |
{ |
||
Line 1,971: | Line 13,516: | ||
game.of.life(gen.board("blinker")) |
game.of.life(gen.board("blinker")) |
||
game.of.life(gen.board("glider", 18, 20)) |
game.of.life(gen.board("glider", 18, 20)) |
||
game.of.life(gen.board(, 50, 50))</ |
game.of.life(gen.board(, 50, 50))</syntaxhighlight> |
||
=={{header|Racket}}== |
|||
<syntaxhighlight lang="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) |
|||
</syntaxhighlight> |
|||
Output for an 80x80 cross: |
|||
[[File:game_of_life_cross.gif]] |
|||
=={{header|Raku}}== |
|||
(formerly Perl 6) |
|||
<syntaxhighlight lang="raku" line>class Automaton { |
|||
subset World of Str where { |
|||
.lines>>.chars.unique == 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.cache; |
|||
} |
|||
method gist { join "\n", map { .join }, @!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: q:to/EOF/; |
|||
............ |
|||
............ |
|||
............ |
|||
.......###.. |
|||
.###...#.... |
|||
........#... |
|||
............ |
|||
EOF |
|||
for ^10 { |
|||
say $glider++; |
|||
say '--'; |
|||
}</syntaxhighlight> |
|||
=={{header|Red}}== |
|||
<syntaxhighlight lang="red">Red [ |
|||
Purpose: "Conway's Game of Life" |
|||
Author: "Joe Smith" |
|||
] |
|||
neigh: [[0 1] [0 -1] [1 0] [-1 0] [1 1] [1 -1] [-1 1] [-1 -1]] |
|||
conway: function [petri] [ |
|||
new-petri: copy/deep petri |
|||
repeat row length? petri [ |
|||
repeat col length? petri [ |
|||
live-neigh: 0 |
|||
foreach cell neigh [ |
|||
try [ |
|||
if petri/(:row + cell/1)/(:col + cell/2) = 1 [live-neigh: live-neigh + 1] |
|||
] |
|||
] |
|||
switch petri/:row/:col [ |
|||
1 [if any [live-neigh < 2 live-neigh > 3] |
|||
[new-petri/:row/:col: 0] |
|||
] |
|||
0 [if live-neigh = 3 [new-petri/:row/:col: 1]] |
|||
]]] |
|||
clear insert petri new-petri |
|||
] |
|||
*3-3: [ |
|||
[0 1 0] |
|||
[0 1 0] |
|||
[0 1 0] |
|||
] |
|||
*8-8: [ |
|||
[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] |
|||
] |
|||
display: function [table] [ |
|||
foreach row table [ |
|||
print replace/all (replace/all to-string row "0" "_") "1" "#" |
|||
] print "" |
|||
] |
|||
loop 5 [ |
|||
display *3-3 |
|||
conway *3-3 |
|||
] |
|||
loop 23 [ |
|||
display *8-8 |
|||
conway *8-8 |
|||
] |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre style="height:50ex;overflow:scroll"> |
|||
_#_ |
|||
_#_ |
|||
_#_ |
|||
___ |
|||
### |
|||
___ |
|||
_#_ |
|||
_#_ |
|||
_#_ |
|||
___ |
|||
### |
|||
___ |
|||
_#_ |
|||
_#_ |
|||
_#_ |
|||
__#_____ |
|||
___#____ |
|||
_###____ |
|||
________ |
|||
________ |
|||
________ |
|||
________ |
|||
________ |
|||
________ |
|||
_#_#____ |
|||
__##____ |
|||
__#_____ |
|||
________ |
|||
________ |
|||
________ |
|||
________ |
|||
________ |
|||
___#____ |
|||
_#_#____ |
|||
__##____ |
|||
________ |
|||
________ |
|||
________ |
|||
________ |
|||
________ |
|||
__#_____ |
|||
___##___ |
|||
__##____ |
|||
________ |
|||
________ |
|||
________ |
|||
________ |
|||
________ |
|||
___#____ |
|||
____#___ |
|||
__###___ |
|||
________ |
|||
________ |
|||
________ |
|||
________ |
|||
________ |
|||
________ |
|||
__#_#___ |
|||
___##___ |
|||
___#____ |
|||
________ |
|||
________ |
|||
________ |
|||
________ |
|||
________ |
|||
____#___ |
|||
__#_#___ |
|||
___##___ |
|||
________ |
|||
________ |
|||
________ |
|||
________ |
|||
________ |
|||
___#____ |
|||
____##__ |
|||
___##___ |
|||
________ |
|||
________ |
|||
________ |
|||
________ |
|||
________ |
|||
____#___ |
|||
_____#__ |
|||
___###__ |
|||
________ |
|||
________ |
|||
________ |
|||
________ |
|||
________ |
|||
________ |
|||
___#_#__ |
|||
____##__ |
|||
____#___ |
|||
________ |
|||
________ |
|||
________ |
|||
________ |
|||
________ |
|||
_____#__ |
|||
___#_#__ |
|||
____##__ |
|||
________ |
|||
________ |
|||
________ |
|||
________ |
|||
________ |
|||
____#___ |
|||
_____##_ |
|||
____##__ |
|||
________ |
|||
________ |
|||
________ |
|||
________ |
|||
________ |
|||
_____#__ |
|||
______#_ |
|||
____###_ |
|||
________ |
|||
________ |
|||
________ |
|||
________ |
|||
________ |
|||
________ |
|||
____#_#_ |
|||
_____##_ |
|||
_____#__ |
|||
________ |
|||
________ |
|||
________ |
|||
________ |
|||
________ |
|||
______#_ |
|||
____#_#_ |
|||
_____##_ |
|||
________ |
|||
________ |
|||
________ |
|||
________ |
|||
________ |
|||
_____#__ |
|||
______## |
|||
_____##_ |
|||
________ |
|||
________ |
|||
________ |
|||
________ |
|||
________ |
|||
______#_ |
|||
_______# |
|||
_____### |
|||
________ |
|||
________ |
|||
________ |
|||
________ |
|||
________ |
|||
________ |
|||
_____#_# |
|||
______## |
|||
______#_ |
|||
________ |
|||
________ |
|||
________ |
|||
________ |
|||
________ |
|||
_______# |
|||
_____#_# |
|||
______## |
|||
________ |
|||
________ |
|||
________ |
|||
________ |
|||
________ |
|||
______#_ |
|||
_______# |
|||
______## |
|||
________ |
|||
________ |
|||
________ |
|||
________ |
|||
________ |
|||
________ |
|||
_______# |
|||
______## |
|||
________ |
|||
________ |
|||
________ |
|||
________ |
|||
________ |
|||
________ |
|||
______## |
|||
______## |
|||
________ |
|||
________ |
|||
________ |
|||
________ |
|||
________ |
|||
________ |
|||
______## |
|||
______## |
|||
</pre> |
|||
=={{header|Retro}}== |
|||
<syntaxhighlight lang="retro">:w/l [ $. eq? [ #0 ] [ #1 ] choose , ] s:for-each ; |
|||
'World d:create |
|||
'.................... w/l |
|||
'.................... w/l |
|||
'.................... w/l |
|||
'..ooo............... w/l |
|||
'....o............... w/l |
|||
'...o................ w/l |
|||
'.................... w/l |
|||
'.................... w/l |
|||
'.................... w/l |
|||
'.................... w/l |
|||
'.................... w/l |
|||
'....ooo............. w/l |
|||
'.................... w/l |
|||
'.................... w/l |
|||
'.................... w/l |
|||
'........ooo......... w/l |
|||
'.......ooo.......... w/l |
|||
'.................... w/l |
|||
'.................... w/l |
|||
'.................... w/l |
|||
'Next d:create |
|||
#20 #20 * allot |
|||
{{ |
|||
'Surrounding var |
|||
:get (rc-v) |
|||
dup-pair [ #0 #19 n:between? |
|||
] bi@ and |
|||
[ &World + [ #20 * ] dip + fetch ] [ drop-pair #0 ] choose ; |
|||
:neighbor? (rc-) get &Surrounding v:inc-by ; |
|||
:NW (rc-rc) dup-pair [ n:dec ] bi@ neighbor? ; |
|||
:NN (rc-rc) dup-pair [ n:dec ] dip neighbor? ; |
|||
:NE (rc-rc) dup-pair [ n:dec ] dip n:inc neighbor? ; |
|||
:WW (rc-rc) dup-pair n:dec neighbor? ; |
|||
:EE (rc-rc) dup-pair n:inc neighbor? ; |
|||
:SW (rc-rc) dup-pair [ n:inc ] dip n:dec neighbor? ; |
|||
:SS (rc-rc) dup-pair [ n:inc ] dip neighbor? ; |
|||
:SE (rc-rc) dup-pair [ n:inc ] bi@ neighbor? ; |
|||
:count (rc-rcn) |
|||
#0 !Surrounding NW NN NE |
|||
WW EE |
|||
SW SS SE @Surrounding ; |
|||
:alive (rc-n) |
|||
count #2 #3 n:between? [ #1 ] if; #0 ; |
|||
:dead (rc-n) |
|||
count #3 eq? [ #1 ] if; #0 ; |
|||
:new-state (rc-n) |
|||
dup-pair get #1 eq? &alive &dead choose ; |
|||
:set (nrc-) &Next + [ #20 * ] dip + store ; |
|||
:cols (r-) |
|||
#20 [ I over swap new-state rot rot set ] times<with-index> drop ; |
|||
:output (n-) n:-zero? [ $o ] [ $. ] choose c:put sp ; |
|||
---reveal--- |
|||
:display (-) |
|||
nl &World #20 [ #20 [ fetch-next output ] times nl ] times drop ; |
|||
:gen (-) |
|||
#20 [ I cols ] times<with-index> &Next &World #20 #20 * copy ; |
|||
}} |
|||
{{ |
|||
:divide #20 [ $- c:put ] times sp 'Gen:_ s:put dup n:put nl ; |
|||
---reveal--- |
|||
:gens (n-) #0 swap [ display divide n:inc gen ] times drop ; |
|||
}} |
|||
#12 gens</syntaxhighlight> |
|||
=={{header|REXX}}== |
|||
===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) |
|||
<br>would probably be on the large side for general viewing, and maybe a wee bit complex to demonstrate how to program for this task. |
|||
<syntaxhighlight lang="rexx">/*REXX program runs and displays the Conway's game of life, it stops after N repeats. */ |
|||
signal on halt /*handle a cell growth interruptus. */ |
|||
parse arg peeps '(' rows cols empty life! clearScreen repeats generations . |
|||
rows = p(rows 3) /*the maximum number of cell rows. */ |
|||
cols = p(cols 3) /* " " " " " columns. */ |
|||
emp = pickChar(empty 'blank') /*an empty cell character (glyph). */ |
|||
clearScr = p(clearScreen 0) /* "1" indicates to clear the screen.*/ |
|||
life! = pickChar(life! '☼') /*the gylph kinda looks like an amoeba.*/ |
|||
reps = p(repeats 2) /*stop pgm if there are two repeats.*/ |
|||
generations = p(generations 100) /*the number of generations allowed. */ |
|||
sw= max(linesize() - 1, cols) /*usable screen width for the display. */ |
|||
#reps= 0; $.= emp /*the universe is new, ··· and barren.*/ |
|||
gens=abs(generations) /*used for a programming convenience.*/ |
|||
x= space(peeps); upper x /*elide superfluous spaces; uppercase. */ |
|||
if x=='' then x= "BLINKER" /*if nothing specified, use BLINKER. */ |
|||
if x=='BLINKER' then x= "2,1 2,2 2,3" |
|||
if x=='OCTAGON' then x= "1,5 1,6 2,4 2,7 3,3 3,8 4,2 4,9 5,2 5,9 6,3 6,8 7,4 7,7 8,5 8,6" |
|||
call assign. /*assign the initial state of all cells*/ |
|||
call showCells /*show the initial state of the cells.*/ |
|||
do life=1 for gens; call assign@ /*construct next generation of cells.*/ |
|||
if generations>0 | life==gens then call showCells /*should cells be displayed? */ |
|||
end /*life*/ /* [↑] cell colony grows, lives, dies.*/ |
|||
exit /*stick a fork in it, we're all done. */ |
|||
/*──────────────────────────────────────────────────────────────────────────────────────*/ |
|||
showCells: if clearScr then 'CLS' /* ◄─── change 'command' for your OS.*/ |
|||
call showRows /*show the rows in the proper order. */ |
|||
say right(copies('▒', sw) life, sw) /*show a fence between the generations.*/ |
|||
if _=='' then exit /*if there's no life, then stop the run*/ |
|||
if !._ then #reps= #reps + 1 /*we detected a repeated cell pattern. */ |
|||
!._= 1 /*existence state and compare later. */ |
|||
if reps\==0 & #reps<=reps then return /*so far, so good, regarding repeats.*/ |
|||
say |
|||
say center('"Life" repeated itself' reps "times, simulation has ended.",sw,'▒') |
|||
exit /*stick a fork in it, we're all done. */ |
|||
/*───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────*/ |
|||
$: 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 |
|||
assign.: do while x\==''; parse var x r "," c x; $.r.c=life!; rows=max(rows,r); cols=max(cols,c); end; life=0; !.=0; return |
|||
assign?: ?=$.r.c; n=neighbors(); if ?==emp then do;if n==3 then ?=life!; end; else if n<2 | n>3 then ?=emp; @.r.c=?; return |
|||
assign@: @.=emp; do r=1 for rows; do c=1 for cols; call assign?; end; end; call assign$; return |
|||
halt: say; say "REXX program (Conway's Life) halted."; say; exit 0 |
|||
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)); arg u .; if u=='BLANK' then _=" "; L=length(_); if L==3 then _=d2c(_); if L==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',emp); say z; _=_||z; end; return</syntaxhighlight> |
|||
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); not all REXXes have this BIF. |
|||
The '''LINESIZE.REX''' REXX program is included here ──► [[LINESIZE.REX]]. <br> |
|||
{{out|output|text= when using the default inputs:}} |
|||
<pre> |
|||
☼☼☼ |
|||
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ 0 |
|||
☼ |
|||
☼ |
|||
☼ |
|||
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ 1 |
|||
☼☼☼ |
|||
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ 2 |
|||
☼ |
|||
☼ |
|||
☼ |
|||
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ 3 |
|||
☼☼☼ |
|||
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ 4 |
|||
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ "Life" repeated itself 2 times, simulation has ended. ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ |
|||
</pre> |
|||
===version 2=== |
|||
<syntaxhighlight lang="rexx">/* 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</syntaxhighlight> |
|||
{{out}} |
|||
<pre> blinker |
|||
+--1--+--2--+--3--+ |
|||
| | * | | |
|||
| *** | * | *** | |
|||
| | * | | |
|||
+-----+-----+-----+ </pre> |
|||
<pre>oktagon |
|||
*--1-------*--2-------*--3-------*--4-------*--5-------*--6-------* |
|||
| ** | ** | * * | | | ** | |
|||
| * * | **** | * * | * * | **** | * * | |
|||
| * * | * * | ** ** ** | * ** * | * ** * | * * | |
|||
| * * | ** ** | * * | * * | ** ** | * * | |
|||
| * * | ** ** | * * | * * | ** ** | * * | |
|||
| * * | * * | ** ** ** | * ** * | * ** * | * * | |
|||
| * * | **** | * * | * * | **** | * * | |
|||
| ** | ** | * * | | | ** | |
|||
*----------*----------*----------*----------*----------*----------*</pre> |
|||
=={{header|Ruby}}== |
=={{header|Ruby}}== |
||
< |
<syntaxhighlight lang="ruby">def game_of_life(name, size, generations, initial_life=nil) |
||
board = new_board size |
board = new_board size |
||
seed board, size, initial_life |
seed board, size, initial_life |
||
print_board board, |
print_board board, name, 0 |
||
reason = generations.times do |gen| |
reason = generations.times do |gen| |
||
new = evolve board, size |
new = evolve board, size |
||
print_board new, gen+1 |
print_board new, name, gen+1 |
||
break :all_dead if barren? new, size |
break :all_dead if barren? new, size |
||
break :static if board == new |
break :static if board == new |
||
Line 1,987: | Line 14,375: | ||
if reason == :all_dead then puts "no more life." |
if reason == :all_dead then puts "no more life." |
||
elsif reason == :static then puts "no movement" |
elsif reason == :static then puts "no movement" |
||
else puts "specified lifetime ended" |
else puts "specified lifetime ended" |
||
end |
end |
||
puts |
|||
end |
end |
||
Line 1,998: | Line 14,387: | ||
if points.nil? |
if points.nil? |
||
# randomly seed board |
# randomly seed board |
||
srand |
|||
indices = [] |
indices = [] |
||
n.times {|x| n.times {|y| indices << [x,y] }} |
n.times {|x| n.times {|y| indices << [x,y] }} |
||
Line 2,030: | Line 14,418: | ||
end |
end |
||
def print_board(m, |
def print_board(m, name, generation) |
||
puts "#{name}: generation #{generation}" |
puts "#{name}: generation #{generation}" |
||
m.each {|row| row.each {|val| print "#{val == 1 ? '#' : '.'} "}; puts} |
m.each {|row| row.each {|val| print "#{val == 1 ? '#' : '.'} "}; puts} |
||
puts |
|||
end |
end |
||
game_of_life "blinker", 3, 2, [[1,0],[1,1],[1,2]] |
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</syntaxhighlight> |
|||
{{out}} |
|||
<pre style="height: 64ex; overflow: scroll"> |
|||
blinker: generation 0 |
|||
. # . |
|||
. # . |
|||
. # . |
|||
blinker: generation 1 |
|||
. . . |
|||
# # # |
|||
. . . |
|||
blinker: generation 2 |
|||
. # . |
|||
. # . |
|||
. # . |
|||
specified lifetime ended |
|||
glider: generation 0 |
|||
. # . . |
|||
. . # . |
|||
# # # . |
|||
. . . . |
|||
glider: generation 1 |
|||
. . . . |
|||
# . # . |
|||
. # # . |
|||
. # . . |
|||
glider: generation 2 |
|||
. . . . |
|||
. . # . |
|||
# . # . |
|||
. # # . |
|||
glider: generation 3 |
|||
. . . . |
|||
. # . . |
|||
. . # # |
|||
. # # . |
|||
glider: generation 4 |
|||
. . . . |
|||
. . # . |
|||
. . . # |
|||
. # # # |
|||
specified lifetime ended |
|||
random: generation 0 |
|||
. . . # # |
|||
. . . # . |
|||
. . . # . |
|||
# . . . # |
|||
# . # # # |
|||
random: generation 1 |
|||
. . . # # |
|||
. . # # . |
|||
. . . # # |
|||
. # # . # |
|||
. # . # # |
|||
random: generation 2 |
|||
. . # # # |
|||
. . # . . |
|||
. # . . # |
|||
. # . . . |
|||
. # . # # |
|||
random: generation 3 |
|||
. . # # . |
|||
. # # . # |
|||
. # # . . |
|||
# # . # # |
|||
. . # . . |
|||
random: generation 4 |
|||
. # # # . |
|||
. . . . . |
|||
. . . . # |
|||
# . . # . |
|||
. # # # . |
|||
random: generation 5 |
|||
. . # . . |
|||
. . # # . |
|||
. . . . . |
|||
. # . # # |
|||
. # # # . |
|||
random: generation 6 |
|||
. . # # . |
|||
. . # # . |
|||
. . . . # |
|||
. # . # # |
|||
. # . # # |
|||
random: generation 7 |
|||
. . # # . |
|||
. . # . # |
|||
. . . . # |
|||
. . . . . |
|||
. . . # # |
|||
random: generation 8 |
|||
. . # # . |
|||
. . # . # |
|||
. . . # . |
|||
. . . # # |
|||
. . . . . |
|||
random: generation 9 |
|||
. . # # . |
|||
. . # . # |
|||
. . # . . |
|||
. . . # # |
|||
. . . . . |
|||
random: generation 10 |
|||
. . # # . |
|||
. # # . . |
|||
. . # . # |
|||
. . . # . |
|||
. . . . . |
|||
specified lifetime ended |
|||
</pre> |
|||
===Class version=== |
|||
The above implementation uses only methods. Below is one that is object-oriented and feels perhaps a bit more Ruby-ish. |
|||
<syntaxhighlight lang="ruby">class Game |
|||
def initialize(name, size, generations, initial_life=nil) |
|||
@size = size |
|||
@board = GameBoard.new size, initial_life |
|||
@board.display name, 0 |
|||
reason = generations.times do |gen| |
|||
new_board = evolve |
|||
new_board.display name, gen+1 |
|||
break :all_dead if new_board.barren? |
|||
break :static if @board == new_board |
|||
@board = new_board |
|||
end |
|||
case reason |
|||
when :all_dead then puts "No more life." |
|||
when :static then puts "No movement." |
|||
else puts "Specified lifetime ended." |
|||
end |
|||
puts |
|||
end |
|||
def evolve |
|||
life = @board.each_index.select {|i,j| cell_fate(i,j)} |
|||
GameBoard.new @size, life |
|||
end |
|||
def cell_fate(i, j) |
|||
left_right = [0, i-1].max .. [i+1, @size-1].min |
|||
top_bottom = [0, j-1].max .. [j+1, @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 |
|||
include Enumerable |
|||
def initialize(size, initial_life=nil) |
|||
@size = size |
|||
@board = Array.new(size) {Array.new(size) {Cell.new false}} |
|||
seed_board initial_life |
|||
end |
|||
def seed_board(life) |
|||
if life.nil? |
|||
# randomly seed board |
|||
each_index.to_a.sample(10).each {|x,y| @board[y][x].live} |
|||
else |
|||
life.each {|x,y| @board[y][x].live} |
|||
end |
|||
end |
|||
def each |
|||
@size.times {|x| @size.times {|y| yield @board[y][x] }} |
|||
end |
|||
def each_index |
|||
return to_enum(__method__) unless block_given? |
|||
@size.times {|x| @size.times {|y| yield x,y }} |
|||
end |
|||
def [](x, y) |
|||
@board[y][x] |
|||
end |
|||
def ==(board) |
|||
self.life == board.life |
|||
end |
|||
def barren? |
|||
none? {|cell| cell.alive?} |
|||
end |
|||
def life |
|||
each_index.select {|x,y| @board[y][x].alive?} |
|||
end |
|||
def display(name, generation) |
|||
puts "#{name}: generation #{generation}" |
|||
puts @board.map {|row| row.map {|cell| cell.alive? ? '#' : '.'}.join(' ')} |
|||
end |
|||
def apocalypse |
|||
# utility function to entirely clear the game board |
|||
each {|cell| cell.die} |
|||
end |
|||
end |
|||
class Cell |
|||
def initialize(alive) @alive = alive end |
|||
def alive?; @alive end |
|||
def value; @alive ? 1 : 0 end |
|||
def live; @alive = true end |
|||
def die; @alive = false end |
|||
end |
|||
Game.new "blinker", 3, 2, [[1,0],[1,1],[1,2]] |
|||
Game.new "glider", 4, 4, [[1,0],[2,1],[0,2],[1,2],[2,2]] |
|||
Game.new "random", 5, 10</syntaxhighlight> |
|||
=={{header|Rust}}== |
|||
<syntaxhighlight lang="rust"> |
|||
use std::collections::HashMap; |
|||
use std::collections::HashSet; |
|||
type Cell = (i32, i32); |
|||
type Colony = HashSet<Cell>; |
|||
fn print_colony(col: &Colony, width: i32, height: i32) { |
|||
for y in 0..height { |
|||
for x in 0..width { |
|||
print!("{} ", |
|||
if col.contains(&(x, y)) {"O"} |
|||
else {"."} |
|||
); |
|||
} |
|||
println!(); |
|||
} |
|||
} |
|||
fn neighbours(&(x,y): &Cell) -> Vec<Cell> { |
|||
vec![ |
|||
(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), |
|||
] |
|||
} |
|||
fn neighbour_counts(col: &Colony) -> HashMap<Cell, i32> { |
|||
let mut ncnts = HashMap::new(); |
|||
for cell in col.iter().flat_map(neighbours) { |
|||
*ncnts.entry(cell).or_insert(0) += 1; |
|||
} |
|||
ncnts |
|||
} |
|||
fn generation(col: Colony) -> Colony { |
|||
neighbour_counts(&col) |
|||
.into_iter() |
|||
.filter_map(|(cell, cnt)| |
|||
match (cnt, col.contains(&cell)) { |
|||
(2, true) | |
|||
(3, ..) => Some(cell), |
|||
_ => None |
|||
}) |
|||
.collect() |
|||
} |
|||
fn life(init: Vec<Cell>, iters: i32, width: i32, height: i32) { |
|||
let mut col: Colony = init.into_iter().collect(); |
|||
for i in 0..iters+1 |
|||
{ |
|||
println!("({})", &i); |
|||
if i != 0 { |
|||
col = generation(col); |
|||
} |
|||
print_colony(&col, width, height); |
|||
} |
|||
} |
|||
fn main() { |
|||
let blinker = vec![ |
|||
(1,0), |
|||
(1,1), |
|||
(1,2)]; |
|||
life(blinker, 3, 3, 3); |
|||
let glider = vec![ |
|||
(1,0), |
|||
(2,1), |
|||
(0,2), (1,2), (2,2)]; |
|||
life(glider, 20, 8, 8); |
|||
} |
|||
</syntaxhighlight> |
|||
=={{header|Scala}}== |
=={{header|Scala}}== |
||
See [[Conway's Game of Life/Scala]] |
See [[Conway's Game of Life/Scala]], [https://github.com/kostaskougios/spi/tree/master/gameOfLife Scala/Spark distributed] |
||
=={{header|Scheme}}== |
=={{header|Scheme}}== |
||
{{works with|Scheme|implementing R6RS (tested with PLT Scheme, Petite Chez Scheme)}} |
{{works with|Scheme|implementing R6RS (tested with PLT Scheme, Petite Chez Scheme)}} |
||
<syntaxhighlight lang="scheme"> |
|||
<lang Scheme> |
|||
;;An R6RS Scheme implementation of Conway's Game of Life --- assumes |
;;An R6RS Scheme implementation of Conway's Game of Life --- assumes |
||
;;all cells outside the defined grid are dead |
;;all cells outside the defined grid are dead |
||
Line 2,137: | Line 14,821: | ||
(0 0 0 0 0 0 0 0) |
(0 0 0 0 0 0 0 0) |
||
(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) |
(0 0 0 0 0 0 0 0)) 30)</syntaxhighlight> |
||
</lang> |
|||
{{out}} |
|||
=== Sample output: === |
|||
<pre style="height:30ex;overflow:scroll"> |
<pre style="height:30ex;overflow:scroll"> |
||
-#- |
-#- |
||
Line 2,432: | Line 15,115: | ||
------## |
------## |
||
</pre> |
</pre> |
||
=={{header|Scilab}}== |
|||
The game's initial state can be input manually by setting the values of <code>Init_state</code>, or using a bitmap image (or other supported formats). The output can be either printed on Scilab's console, or on a graphic window. For both image input and graphic output, either SIVP or IPCV modules are required, and <code>console_output</code> should be set to false. |
|||
<syntaxhighlight lang="text">Init_state=[0 0 0;... |
|||
1 1 1;... |
|||
0 0 0]; |
|||
console_output=%T; |
|||
if (atomsIsLoaded('IPCV') | atomsIsLoaded('SIVP')) & ~console_output then |
|||
Input=imread('initial_state.bmp'); //Comment this three lines in case |
|||
Init_state=~im2bw(Input,0.1); //there is no input image but |
|||
Init_state=1.0.*Init_state; //you still want the graphic window |
|||
scf(0); clf(); |
|||
imshow(~Init_state); |
|||
set(gca(),"isoview","on"); |
|||
end |
|||
Curr_state=1.0.*Init_state; |
|||
Grid_size=size(Init_state); |
|||
Gens=4; |
|||
function varargout=neighbourhood(A,i,j) |
|||
R_top=i-1; |
|||
if i==1 then |
|||
R_top=1; |
|||
end |
|||
R_bottom=i+1; |
|||
if i==Grid_size(1) then |
|||
R_bottom=Grid_size(1); |
|||
end |
|||
R_left=j-1; |
|||
if j==1 then |
|||
R_left=1; |
|||
end |
|||
C_right=j+1; |
|||
if j==Grid_size(2) then |
|||
C_right=Grid_size(2); |
|||
end |
|||
varargout=list(A(R_top:R_bottom,R_left:C_right)); |
|||
endfunction |
|||
function []=console_print(Grid) |
|||
String_grid=string(Grid); |
|||
for i=1:size(Grid,'r') |
|||
for j=1:size(Grid,'c') |
|||
if Grid(i,j) then |
|||
String_grid(i,j)="#"; |
|||
else |
|||
String_grid(i,j)=" "; |
|||
end |
|||
end |
|||
end |
|||
disp(String_grid); |
|||
endfunction |
|||
neighbours=[]; |
|||
Next_state=[]; |
|||
for gen=1:Gens |
|||
Next_state=zeros(Init_state); |
|||
for i=1:Grid_size(1) |
|||
for j=1:Grid_size(2) |
|||
neighbours=zeros(3,3); |
|||
neighbours=neighbourhood(Curr_state,i,j); |
|||
Sum_neighbours=sum(neighbours)-1*Curr_state(i,j); |
|||
Alive=Curr_state(i,j); |
|||
if Alive then |
|||
if Sum_neighbours<2 then |
|||
Next_state(i,j)=0; |
|||
elseif Sum_neighbours==2 | Sum_neighbours==3 then |
|||
Next_state(i,j)=1; |
|||
elseif Sum_neighbours>3 then |
|||
Next_state(i,j)=0; |
|||
end |
|||
else |
|||
if Sum_neighbours==3 then |
|||
Next_state(i,j)=1; |
|||
end |
|||
end |
|||
end |
|||
end |
|||
if (atomsIsLoaded('IPCV') | atomsIsLoaded('SIVP')) & ~console_output then |
|||
imshow(~Next_state); |
|||
sleep(50); |
|||
else |
|||
sleep(50); |
|||
disp("Generation "+string(gen)+":") |
|||
console_print(Next_state); |
|||
end |
|||
if sum(Next_state)==0 | Curr_state==Next_state then |
|||
disp('ALL CELLS HAVE DIED OR BECAME INERT'); |
|||
disp('No. of Generations: '+string(gen)) |
|||
break |
|||
end |
|||
Curr_state=Next_state; |
|||
end</syntaxhighlight> |
|||
{{out}} |
|||
Output when <code>console_output=%T</code>. |
|||
<pre> Generation 1: |
|||
! # ! |
|||
! ! |
|||
! # ! |
|||
! ! |
|||
! # ! |
|||
Generation 2: |
|||
! ! |
|||
! ! |
|||
!# # # ! |
|||
! ! |
|||
! ! |
|||
Generation 3: |
|||
! # ! |
|||
! ! |
|||
! # ! |
|||
! ! |
|||
! # ! |
|||
Generation 4: |
|||
! ! |
|||
! ! |
|||
!# # # ! |
|||
! ! |
|||
! ! |
|||
</pre> |
|||
=={{header|SenseTalk}}== |
|||
<syntaxhighlight lang="sensetalk">set starting_condition to ((0, 0), (1, 0), (2, 0), (-1, -1), (0, -1), (1, -1)) |
|||
RunGameOfLife starting_condition |
|||
to printColony colonies |
|||
set xCoords to the first item of each item of colonies |
|||
set yCoords to the second item of each item of colonies |
|||
set min_x to the min of xCoords |
|||
set max_x to the max of xCoords |
|||
set min_y to the min of yCoords |
|||
set max_y to the max of yCoords |
|||
repeat for y in min_y..max_y |
|||
set row to () |
|||
repeat for x in min_x..max_x |
|||
if (x, y) is in colonies |
|||
push "#" into row |
|||
else |
|||
push "-" into row |
|||
end if |
|||
end repeat |
|||
join row using "" |
|||
put row |
|||
end repeat |
|||
end printColony |
|||
to neighboursOf coordinate |
|||
return ( \ |
|||
coordinate + (-1, 1), \ |
|||
coordinate + (0, 1), \ |
|||
coordinate + (1, 1), \ |
|||
coordinate + (-1, 0), \ |
|||
coordinate + (1, 0), \ |
|||
coordinate + (-1, -1), \ |
|||
coordinate + (0, -1), \ |
|||
coordinate + (1, -1), \ |
|||
) |
|||
end neighboursOf |
|||
to WillNextGenHaveCell colony, coordinate |
|||
set neighbour_count to the number of items in (each item of neighboursOf(coordinate) where each is in colony) |
|||
if coordinate is in colony |
|||
return neighbour_count is in (2, 3) |
|||
else |
|||
return neighbour_count equals 3 |
|||
end if |
|||
end WillNextGenHaveCell |
|||
to RunGameOfLife colony |
|||
printColony colony |
|||
set the listInsertionMode to "nested" |
|||
repeat 10 times |
|||
set new_colony to () |
|||
set xCoords to the first item of each item of colony |
|||
set yCoords to the second item of each item of colony |
|||
set min_x to (the min of xCoords) - 1 |
|||
set max_x to (the max of xCoords) + 1 |
|||
set min_y to (the min of yCoords) - 1 |
|||
set max_y to (the max of yCoords) + 1 |
|||
repeat for y in min_y..max_y |
|||
repeat for x in min_x..max_x |
|||
if WillNextGenHaveCell(colony, (x, y)) |
|||
insert (x, y) into new_colony |
|||
end if |
|||
end repeat |
|||
end repeat |
|||
set colony to new_colony |
|||
printColony colony |
|||
wait 1 second |
|||
end repeat |
|||
end RunGameOfLife</syntaxhighlight> |
|||
=={{header|SequenceL}}== |
|||
{{libheader|CImg}} |
|||
'''SequenceL Code:''' |
|||
<syntaxhighlight lang="sequencel">life(Cells(2))[I, J] := |
|||
let |
|||
numNeighbors := Cells[I-1,J-1] + Cells[I-1,J] + Cells[I-1,J+1] + |
|||
Cells[I,J-1] +/*current cell*/Cells[I,J+1] + |
|||
Cells[I+1,J-1] + Cells[I+1,J] + Cells[I+1,J+1]; |
|||
in |
|||
0 when I=1 or J=1 or I=size(Cells) or J=size(Cells[I]) //On Border |
|||
else |
|||
0 when numNeighbors < 2 or numNeighbors > 3 //Cell Dies |
|||
else |
|||
1 when Cells[I,J] = 1 and numNeighbors = 2 or numNeighbors = 3 //Cell lives on or is born. |
|||
else |
|||
Cells[I,J]; //No Change |
|||
stressTestInput(n(0))[y,x] := |
|||
0 when not y = n / 2 |
|||
else |
|||
1 |
|||
foreach y within 1 ... n, |
|||
x within 1 ... n;</syntaxhighlight> |
|||
'''C++ Driver Code:''' |
|||
<syntaxhighlight lang="c">#include <iostream> |
|||
#include <string> |
|||
#include <vector> |
|||
#include <fstream> |
|||
#include <cerrno> |
|||
#include "Cimg.h" |
|||
#include "SL_Generated.h" |
|||
using namespace std; |
|||
using namespace cimg_library; |
|||
char titleBuffer[200]; |
|||
std::string get_file_contents(const char*); |
|||
int main(int argc, char ** argv) |
|||
{ |
|||
int cores = 0; |
|||
int maxSize = 700; |
|||
int drawSkip = 1; |
|||
int drawWait = 0; |
|||
string inputFile = "test"; |
|||
if(argc > 1) |
|||
{ |
|||
inputFile = argv[1]; |
|||
} |
|||
if(argc > 2) |
|||
{ |
|||
cores = atoi(argv[2]); |
|||
} |
|||
if(argc > 3) |
|||
{ |
|||
maxSize = atoi(argv[3]); |
|||
} |
|||
if(argc > 4) |
|||
{ |
|||
drawSkip = atoi(argv[4]); |
|||
if(drawSkip < 0) |
|||
{ |
|||
drawWait = abs(drawSkip); |
|||
drawSkip = 1; |
|||
} |
|||
} |
|||
sl_init(cores); |
|||
SLTimer drawTimer; |
|||
sprintf(titleBuffer, "Conway's Game of Life in SequenceL: %d Cores", cores); |
|||
int width = 0; |
|||
int height = 0; |
|||
//Read input file-------------- |
|||
Sequence<Sequence<int>> initialGrid; |
|||
if(inputFile != "test") |
|||
{ |
|||
stringstream ss(get_file_contents(inputFile.c_str())); |
|||
string stringItem; |
|||
vector<int> elems; |
|||
char delim = ','; |
|||
getline(ss, stringItem, delim); |
|||
width = atoi(stringItem.c_str()); |
|||
getline(ss, stringItem, delim); |
|||
height = atoi(stringItem.c_str()); |
|||
while(getline(ss, stringItem, delim)) |
|||
{ |
|||
elems.push_back(atoi(stringItem.c_str())); |
|||
} |
|||
//Sequence Pointer Constructor---- |
|||
int gridDims[] = {height, width, 0}; |
|||
Sequence<Sequence<int>> tempGrid( (void*)&elems[0], gridDims); |
|||
initialGrid.hard_copy(tempGrid, 0); |
|||
//----------------------------- |
|||
} |
|||
else |
|||
{ |
|||
width = maxSize; |
|||
height = maxSize; |
|||
sl_stressTestInput(maxSize, cores, initialGrid); |
|||
} |
|||
//-------- |
|||
Sequence<Sequence<int>> result; |
|||
const unsigned char black[] = {0}; |
|||
CImg<unsigned char> visu(width * 4, height * 4, 1, 1, 0); |
|||
CImgDisplay draw_disp(visu, titleBuffer); |
|||
cout << "Conway's Game of Life in SequenceL" << endl << "Cores: " << cores << endl; |
|||
int generations = 0; |
|||
double compTime = 0; |
|||
double drawTime = 0; |
|||
SLTimer t; |
|||
while(!draw_disp.is_closed()) |
|||
{ |
|||
t.start(); |
|||
sl_life(initialGrid, cores, result); |
|||
generations++; |
|||
initialGrid.hard_copy(result, 0); |
|||
t.stop(); |
|||
compTime += t.getTime(); |
|||
drawTimer.start(); |
|||
if(drawSkip > 0 && generations % drawSkip == 0) |
|||
{ |
|||
visu.fill(255); |
|||
for(int i = 1; i <= result.size(); i++) |
|||
{ |
|||
for(int j = 1; j <= result[i].size(); j++) |
|||
{ |
|||
if(result[i][j] == 1) visu.draw_circle((j-1) * 4, (i - 1) * 4, 2, black, 1); |
|||
} |
|||
} |
|||
visu.display(draw_disp); |
|||
int drawWidth = maxSize; |
|||
int drawHeight = maxSize; |
|||
if(width < height) |
|||
{ |
|||
drawWidth = ((double)drawHeight * ((double)width / (double)height)); |
|||
} |
|||
else |
|||
{ |
|||
drawHeight = ((double)drawWidth * ((double)height / (double)width)); |
|||
} |
|||
draw_disp.resize(drawWidth, drawHeight, false); |
|||
draw_disp.wait(drawWait); |
|||
drawTimer.stop(); |
|||
drawTime += drawTimer.getTime(); |
|||
} |
|||
} |
|||
cout << "Total Generations: " << generations << endl; |
|||
cout << "Average Compute Time: " << compTime / generations << " seconds" << endl; |
|||
cout << "Average Draw Time: " << drawTime / generations << " seconds" << endl; |
|||
sl_done(); |
|||
return 0; |
|||
} |
|||
std::string get_file_contents(const char *filename) |
|||
{ |
|||
std::ifstream in(filename, std::ios::in | std::ios::binary); |
|||
if (in) |
|||
{ |
|||
std::string contents; |
|||
in.seekg(0, std::ios::end); |
|||
contents.resize(in.tellg()); |
|||
in.seekg(0, std::ios::beg); |
|||
in.read(&contents[0], contents.size()); |
|||
in.close(); |
|||
return(contents); |
|||
} |
|||
throw(errno); |
|||
}</syntaxhighlight> |
|||
'''Usage:''' |
|||
SequenceL Intended Use: |
|||
life(int(2)); stressTestInput(int(0)) |
|||
Running the program: |
|||
*If no inputs are provided, a 700 element stress test is performed. |
|||
*Argument 1 is the input file ("test" to run stress test). |
|||
*Argument 2 is the optional number of cores which defaults to 0(all cores). |
|||
*Argument 3 is the size to make the largest dimension of the display window. |
|||
*Argument 4 is the number of frames to skip when drawing. |
|||
'''Sample Input:''' |
|||
Blinker: |
|||
<pre>5,5,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</pre> |
|||
Output: http://i.imgur.com/9ZtKw1X.gifv |
|||
'''Stress Test Output:''' |
|||
This is the output for the 700 element stress test, which is an input of a 700X700 grid with a single line of live cells horizontally in the center. |
|||
Output: http://i.imgur.com/yKTrszV.gifv |
|||
=={{header|SETL}}== |
=={{header|SETL}}== |
||
Line 2,438: | Line 15,574: | ||
This version uses a live cell set representation (set of coordinate pairs.) |
This version uses a live cell set representation (set of coordinate pairs.) |
||
This example first appeared [http://www.setl-lang.org/wiki/index.php/Conway%27s_Game_of_Life here]. |
This example first appeared [http://www.setl-lang.org/wiki/index.php/Conway%27s_Game_of_Life here]. |
||
< |
<syntaxhighlight lang="setl">program life; |
||
const |
const |
||
Line 2,472: | Line 15,608: | ||
end proc; |
end proc; |
||
end program;</ |
end program;</syntaxhighlight> |
||
=={{header|Shen}}== |
|||
Somewhat verbose but functional and type-checked implementation (tested with chibi-scheme and Shen/SBCL). Running this shows ten iterations of a toad. |
|||
<syntaxhighlight lang="shen">(tc +) |
|||
(datatype subtype |
|||
(subtype B A); X : B; |
|||
_____________________ |
|||
X : A;) |
|||
(datatype integer |
|||
if (integer? X) |
|||
___________ |
|||
X: integer; |
|||
________________________ |
|||
(subtype integer number);) |
|||
(datatype bit |
|||
if (< X 2) |
|||
_______ |
|||
X: bit; |
|||
_____________________ |
|||
(subtype bit integer);) |
|||
(datatype row |
|||
_________ |
|||
[] : row; |
|||
C : bit; Row : row; |
|||
=================== |
|||
[C | Row] : row;) |
|||
(datatype universe |
|||
______________ |
|||
[] : universe; |
|||
R : row; Uni : universe; |
|||
======================== |
|||
[R | Uni] : universe;) |
|||
(define conway-nth |
|||
\\ returns value of x from row if it exists, else 0 |
|||
{ number --> row --> bit } |
|||
_ [] -> 0 |
|||
N _ -> 0 where (< N 0) |
|||
0 [A|B] -> A |
|||
N [A|B] -> (conway-nth (- N 1) B)) |
|||
(define row-retrieve |
|||
{ number --> universe --> row } |
|||
_ [] -> [] |
|||
0 [] -> [] |
|||
0 [A|B] -> A |
|||
N [A|B] -> (row-retrieve (- N 1) B)) |
|||
(define cell-retrieve |
|||
{ number --> number --> universe --> bit } |
|||
X Y Universe -> (conway-nth X (row-retrieve Y Universe))) |
|||
(define neighbors |
|||
\\ takes an X and Y, retrieves the number of neighbors |
|||
{ number --> number --> universe --> number } |
|||
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 |
|||
{ number --> number --> universe --> bit } |
|||
X Y Universe -> (if (or (= (neighbors X Y Universe) 2) |
|||
(= (neighbors X Y Universe) 3)) |
|||
1 0)) |
|||
(define handle-dead |
|||
{ number --> number --> universe --> bit } |
|||
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 universe |
|||
{ row --> number --> number --> universe --> row } |
|||
[] _ _ _ -> [] |
|||
[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 |
|||
{ universe --> number --> universe --> universe } |
|||
[] _ _ -> [] |
|||
[Row|Rest] Y Universe -> (cons (next-row Row 0 Y Universe) |
|||
(next-universe Rest (+ Y 1) Universe))) |
|||
(define display-row |
|||
{ row --> number } |
|||
[] -> (nl) |
|||
[1|Rest] -> (do (output "* ") |
|||
(display-row Rest)) |
|||
[_|Rest] -> (do (output " ") |
|||
(display-row Rest))) |
|||
(define display-universe |
|||
{ universe --> number } |
|||
[] -> (nl 2) |
|||
[Row|Rest] -> (do (display-row Row) |
|||
(display-universe Rest))) |
|||
(define iterate-universe |
|||
{ number --> universe --> number } |
|||
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]])</syntaxhighlight> |
|||
=={{header|Sidef}}== |
|||
{{trans|Perl}} |
|||
<syntaxhighlight lang="ruby">var w = Num(`tput cols`) |
|||
var h = Num(`tput lines`) |
|||
var r = "\033[H" |
|||
var dirs = [[-1,-1], [-1, 0], [-1, 1], [ 0,-1], |
|||
[ 0, 1], [ 1,-1], [ 1, 0], [ 1, 1]] |
|||
var universe = h.of { w.of {1.rand < 0.1} } |
|||
func iterate { |
|||
var new = h.of { w.of(false) } |
|||
static rx = (^h ~X ^w) |
|||
for i,j in rx { |
|||
var neighbor = 0 |
|||
for y,x in (dirs.map {|dir| dir »+« [i, j] }) { |
|||
universe[y % h][x % w] && ++neighbor |
|||
neighbor > 3 && break |
|||
} |
|||
new[i][j] = (universe[i][j] |
|||
? (neighbor==2 || neighbor==3) |
|||
: (neighbor==3)) |
|||
} |
|||
universe = new |
|||
} |
|||
STDOUT.autoflush(true) |
|||
loop { |
|||
print r |
|||
say universe.map{|row| row.map{|cell| cell ? '#' : ' '}.join }.join("\n") |
|||
iterate() |
|||
}</syntaxhighlight> |
|||
=={{header|Simula}}== |
|||
{{trans|Scheme}} |
|||
<syntaxhighlight lang="simula">COMMENT A PORT OF AN R6RS SCHEME IMPLEMENTATION OF CONWAY'S GAME OF LIFE TO SIMULA --- ASSUMES ; |
|||
COMMENT ALL CELLS OUTSIDE THE DEFINED GRID ARE DEAD ; |
|||
BEGIN |
|||
COMMENT FIRST WE NEED CONS, CAR, CDR, LENGTH AND MAP TO SIMULATE SCHEME ; |
|||
CLASS ATOM;; |
|||
ATOM CLASS CELL(ALIVE); INTEGER ALIVE;; |
|||
ATOM CLASS LIST(A1, A2); REF(ATOM) A1, A2;; |
|||
REF(LIST) PROCEDURE CONS(A1, A2); REF(ATOM) A1, A2; |
|||
CONS :- NEW LIST(A1, A2); |
|||
REF(ATOM) PROCEDURE CAR(LST); REF(LIST) LST; |
|||
CAR :- LST.A1; |
|||
REF(ATOM) PROCEDURE CDR(LST); REF(LIST) LST; |
|||
CDR :- LST.A2; |
|||
INTEGER PROCEDURE LENGTH(LST); REF(LIST) LST; |
|||
LENGTH := IF LST == NONE THEN 0 ELSE 1 + LENGTH(CDR(LST)); |
|||
REF(LIST) PROCEDURE MAP(F, LST); PROCEDURE F IS REF(ATOM) PROCEDURE F(ARG); REF(ATOM) ARG;; REF(LIST) LST; |
|||
MAP :- IF LST == NONE THEN NONE ELSE CONS(F(CAR(LST)), MAP(F, CDR(LST))); |
|||
COMMENT NOW FOLLOWS THE PROBLEM SPECIFIC PART ; |
|||
COMMENT IF N IS OUTSIDE BOUNDS OF LIST, RETURN 0 ELSE VALUE AT N ; |
|||
REF(ATOM) PROCEDURE NTH(N, LST); INTEGER N; REF(LIST) LST; |
|||
NTH :- IF N > LENGTH(LST) THEN NEW CELL(0) ELSE |
|||
IF N < 1 THEN NEW CELL(0) ELSE |
|||
IF N = 1 THEN CAR(LST) ELSE NTH(N - 1, CDR(LST)); |
|||
COMMENT RETURN THE NEXT STATE OF THE SUPPLIED UNIVERSE ; |
|||
REF(LIST) PROCEDURE NEXTUNIVERSE(UNIVERSE); REF(LIST) UNIVERSE; |
|||
BEGIN |
|||
COMMENT VALUE AT (X, Y) ; |
|||
INTEGER PROCEDURE VALUEAT(X, Y); INTEGER X, Y; |
|||
BEGIN |
|||
REF(ATOM) A; |
|||
A :- NTH(Y, UNIVERSE); |
|||
INSPECT A |
|||
WHEN LIST DO |
|||
VALUEAT := NTH(X, THIS LIST) QUA CELL.ALIVE |
|||
OTHERWISE |
|||
VALUEAT := 0; |
|||
END VALUEAT; |
|||
COMMENT SUM OF THE VALUES OF THE CELLS SURROUNDING (X, Y) ; |
|||
INTEGER PROCEDURE NEIGHBORSUM(X, Y); INTEGER X, Y; |
|||
NEIGHBORSUM := |
|||
VALUEAT(X - 1, Y - 1) |
|||
+ VALUEAT(X - 1, Y ) |
|||
+ VALUEAT(X - 1, Y + 1) |
|||
+ VALUEAT(X, Y - 1) |
|||
+ VALUEAT(X, Y + 1) |
|||
+ VALUEAT(X + 1, Y - 1) |
|||
+ VALUEAT(X + 1, Y ) |
|||
+ VALUEAT(X + 1, Y + 1); |
|||
COMMENT NEXT STATE OF THE CELL AT (X, Y) ; |
|||
INTEGER PROCEDURE NEXTCELL(X, Y); INTEGER X, Y; |
|||
BEGIN |
|||
INTEGER CUR, NS; |
|||
CUR := VALUEAT(X, Y); |
|||
NS := NEIGHBORSUM(X, Y); |
|||
NEXTCELL := IF CUR = 1 AND (NS < 2 OR NS > 3) THEN 0 ELSE |
|||
IF CUR = 0 AND NS = 3 THEN 1 ELSE CUR; |
|||
END NEXTCELL; |
|||
COMMENT NEXT STATE OF ROW N ; |
|||
REF(LIST) PROCEDURE ROW(N, OUT); INTEGER N; REF(LIST) OUT; |
|||
BEGIN |
|||
INTEGER W, O; |
|||
W := LENGTH(CAR(UNIVERSE)); |
|||
O := LENGTH(OUT); |
|||
ROW :- IF W = O THEN OUT ELSE ROW(N, CONS(NEW CELL(NEXTCELL(W - O, N)), OUT)); |
|||
END ROW; |
|||
COMMENT A RANGE OF INTS FROM BOT TO TOP ; |
|||
REF(LIST) PROCEDURE INTRANGE(BOT, TOP); INTEGER BOT, TOP; |
|||
INTRANGE :- IF BOT > TOP THEN NONE ELSE CONS(NEW CELL(BOT), INTRANGE(BOT + 1, TOP)); |
|||
REF(ATOM) PROCEDURE LAMBDA(N); REF(ATOM) N; |
|||
LAMBDA :- ROW(N QUA CELL.ALIVE, NONE); |
|||
NEXTUNIVERSE :- MAP(LAMBDA, INTRANGE(1, LENGTH(UNIVERSE))); |
|||
END NEXTUNIVERSE; |
|||
COMMENT DISPLAY THE UNIVERSE ; |
|||
PROCEDURE DISPLAY(LST); REF(LIST) LST; |
|||
BEGIN |
|||
WHILE LST =/= NONE DO |
|||
BEGIN |
|||
REF(LIST) LI; |
|||
LI :- CAR(LST); |
|||
WHILE LI =/= NONE DO |
|||
BEGIN |
|||
REF(CELL) CE; |
|||
CE :- CAR(LI); |
|||
OUTCHAR(IF CE.ALIVE = 1 THEN '#' ELSE '-'); |
|||
LI :- CDR(LI); |
|||
END; |
|||
OUTIMAGE; |
|||
LST :- CDR(LST); |
|||
END; |
|||
END DISPLAY; |
|||
COMMENT STARTING WITH SEED, SHOW REPS STATES OF THE UNIVERSE ; |
|||
PROCEDURE CONWAY(SEED, REPS); REF(LIST) SEED; INTEGER REPS; |
|||
BEGIN |
|||
IF REPS > 0 THEN |
|||
BEGIN |
|||
DISPLAY(SEED); |
|||
OUTIMAGE; |
|||
CONWAY(NEXTUNIVERSE(SEED), REPS - 1); |
|||
END; |
|||
END CONWAY; |
|||
REF(CELL) O, L; |
|||
O :- NEW CELL(0); |
|||
L :- NEW CELL(1); |
|||
COMMENT BLINKER IN A 3X3 UNIVERSE ; |
|||
! (CONWAY '((0 1 0) |
|||
(0 1 0) |
|||
(0 1 0)) 5) ; |
|||
CONWAY(CONS(CONS(O, CONS(L, CONS(O, NONE))), |
|||
CONS(CONS(O, CONS(L, CONS(O, NONE))), |
|||
CONS(CONS(O, CONS(L, CONS(O, NONE))), |
|||
NONE))), 5); |
|||
COMMENT 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) ; |
|||
OUTIMAGE; |
|||
CONWAY(CONS(CONS(O, CONS(O, CONS(L, CONS(O, CONS(O, CONS(O, CONS(O, CONS(O, NONE)))))))), |
|||
CONS(CONS(O, CONS(O, CONS(O, CONS(L, CONS(O, CONS(O, CONS(O, CONS(O, NONE)))))))), |
|||
CONS(CONS(O, CONS(L, CONS(L, CONS(L, CONS(O, CONS(O, CONS(O, CONS(O, NONE)))))))), |
|||
CONS(CONS(O, CONS(O, CONS(O, CONS(O, CONS(O, CONS(O, CONS(O, CONS(O, NONE)))))))), |
|||
CONS(CONS(O, CONS(O, CONS(O, CONS(O, CONS(O, CONS(O, CONS(O, CONS(O, NONE)))))))), |
|||
CONS(CONS(O, CONS(O, CONS(O, CONS(O, CONS(O, CONS(O, CONS(O, CONS(O, NONE)))))))), |
|||
CONS(CONS(O, CONS(O, CONS(O, CONS(O, CONS(O, CONS(O, CONS(O, CONS(O, NONE)))))))), |
|||
CONS(CONS(O, CONS(O, CONS(O, CONS(O, CONS(O, CONS(O, CONS(O, CONS(O, NONE)))))))), |
|||
NONE)))))))), 30); |
|||
END. |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre style="height:30ex;overflow:scroll"> |
|||
-#- |
|||
-#- |
|||
-#- |
|||
--- |
|||
### |
|||
--- |
|||
-#- |
|||
-#- |
|||
-#- |
|||
--- |
|||
### |
|||
--- |
|||
-#- |
|||
-#- |
|||
-#- |
|||
--#----- |
|||
---#---- |
|||
-###---- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-#-#---- |
|||
--##---- |
|||
--#----- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
---#---- |
|||
-#-#---- |
|||
--##---- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
--#----- |
|||
---##--- |
|||
--##---- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
---#---- |
|||
----#--- |
|||
--###--- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
--#-#--- |
|||
---##--- |
|||
---#---- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
----#--- |
|||
--#-#--- |
|||
---##--- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
---#---- |
|||
----##-- |
|||
---##--- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
----#--- |
|||
-----#-- |
|||
---###-- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
---#-#-- |
|||
----##-- |
|||
----#--- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-----#-- |
|||
---#-#-- |
|||
----##-- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
----#--- |
|||
-----##- |
|||
----##-- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-----#-- |
|||
------#- |
|||
----###- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
----#-#- |
|||
-----##- |
|||
-----#-- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
------#- |
|||
----#-#- |
|||
-----##- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-----#-- |
|||
------## |
|||
-----##- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
------#- |
|||
-------# |
|||
-----### |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-----#-# |
|||
------## |
|||
------#- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------# |
|||
-----#-# |
|||
------## |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
------#- |
|||
-------# |
|||
------## |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------# |
|||
------## |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
------## |
|||
------## |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
------## |
|||
------## |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
------## |
|||
------## |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
------## |
|||
------## |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
------## |
|||
------## |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
------## |
|||
------## |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
------## |
|||
------## |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
------## |
|||
------## |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
-------- |
|||
------## |
|||
------## |
|||
180 garbage collection(s) in 0.1 seconds. |
|||
</pre> |
|||
=={{header|Smalltalk}}== |
|||
This implementation has been developed using '''Cuis Smalltalk''' [https://github.com/Cuis-Smalltalk/Cuis-Smalltalk-Dev]. |
|||
Here are different configurations: |
|||
<syntaxhighlight lang="smalltalk"> |
|||
"Blinker" |
|||
GameOfLife withAliveCells: { 4@2. 4@3. 4@4. 3@3. 4@3. 5@3 } ofSize: 10@10. |
|||
"Toad" |
|||
GameOfLife withAliveCells: { 2@4. 3@4. 4@4. 3@3. 4@3. 5@3 } ofSize: 10@10 |
|||
</syntaxhighlight> |
|||
This is the implementation: |
|||
<syntaxhighlight lang="smalltalk"> |
|||
Object subclass: #GameOfLife |
|||
instanceVariableNames: 'aliveCells boardSize' |
|||
classVariableNames: '' |
|||
poolDictionaries: '' |
|||
category: 'GameOfLife' |
|||
GameOfLife class>>withAliveCells: aCollectionOfCells ofSize: aBoardSize |
|||
self assertAll: aCollectionOfCells areInside: aBoardSize. |
|||
^self new initializeWithAliveCells: aCollectionOfCells ofSize: aBoardSize |
|||
GameOfLife class>>assertAll: aCollectionOfAliveCells areInside: aSize |
|||
| origin | |
|||
origin := 0@0. |
|||
aCollectionOfAliveCells do: [:aCell | |
|||
(aCell between: origin and: aSize) ifFalse: [ self error: 'Cell ', aCell printString,' out of range' ]] |
|||
initializeWithAliveCells: aCollectionOfCells ofSize: aBoardSize |
|||
aliveCells := aCollectionOfCells asSet. |
|||
boardSize := aBoardSize |
|||
calculateNextGeneration |
|||
aliveCells := self boardCellsSelect: [ :aCell | self shouldBeAliveOnNextGeneration: aCell ] |
|||
shouldBeAliveOnNextGeneration: aCell |
|||
| numberOfAliveNeighbors | |
|||
numberOfAliveNeighbors := self numberOfAliveNeighborsOf: aCell. |
|||
^numberOfAliveNeighbors = 3 or: [ self shouldSurvive: aCell with: numberOfAliveNeighbors] |
|||
shouldSurvive: aCell with: numberOfAliveNeighbors |
|||
^ (self isAlive: aCell) and: [ numberOfAliveNeighbors = 2 ] |
|||
isAlive: aCell |
|||
^aliveCells includes: aCell |
|||
isDead: aCell |
|||
^(self isAlive: aCell) not |
|||
boardCellsDo: aClosure |
|||
0 to: boardSize x do: [ :x | |
|||
0 to: boardSize y do: [ :y | aClosure value: x@y ]] |
|||
boardCellsSelect: aCondition |
|||
| selectedCells | |
|||
selectedCells := Set new. |
|||
self boardCellsDo: [ :aCell | (aCondition value: aCell) ifTrue: [ selectedCells add: aCell ]]. |
|||
^selectedCells |
|||
numberOfAliveNeighborsOf: aCell |
|||
^aCell eightNeighbors count: [ :aNeighbor | self isAlive: aNeighbor ] |
|||
</syntaxhighlight> |
|||
The implementation was developed using TDD. This are the tests used to implement it. |
|||
<syntaxhighlight lang="smalltalk"> |
|||
TestCase subclass: #GameOfLifeTest |
|||
instanceVariableNames: '' |
|||
classVariableNames: '' |
|||
poolDictionaries: '' |
|||
category: 'GameOfLife' |
|||
test01AliveCellWithLessThatTwoAliveCellsDies |
|||
| gameOfLife | |
|||
gameOfLife := GameOfLife withAliveCells: {1@1} ofSize: 3@3. |
|||
gameOfLife calculateNextGeneration. |
|||
self assert: (gameOfLife isDead: 1@1) |
|||
test02AliveCellWithTwoAliveNeighborsSurvives |
|||
| gameOfLife | |
|||
gameOfLife := GameOfLife withAliveCells: {1@1. 1@2. 2@1} ofSize: 3@3. |
|||
gameOfLife calculateNextGeneration. |
|||
self assert: (gameOfLife isAlive: 1@1). |
|||
test03AliveCellWithThreeAliveNeighborsSurvives |
|||
| gameOfLife | |
|||
gameOfLife := GameOfLife withAliveCells: {1@1. 1@2. 2@1. 2@2} ofSize: 3@3. |
|||
gameOfLife calculateNextGeneration. |
|||
self assert: (gameOfLife isAlive: 1@1). |
|||
test04DeadCellWithThreeAliveNeighborsBecomesAlive |
|||
| gameOfLife | |
|||
gameOfLife := GameOfLife withAliveCells: {1@1. 1@2. 2@1} ofSize: 3@3. |
|||
gameOfLife calculateNextGeneration. |
|||
self assert: (gameOfLife isAlive: 2@2). |
|||
test05DeadCellWithAliveNeighborsDifferentToThreeKeepsDead |
|||
| gameOfLife | |
|||
gameOfLife := GameOfLife withAliveCells: {1@2. 2@2. 3@2. 1@3} ofSize: 3@3. |
|||
gameOfLife calculateNextGeneration. |
|||
self assert: (gameOfLife isDead: 1@1). |
|||
self assert: (gameOfLife isDead: 2@3). |
|||
test06CanNotCreateGameWithCellOutsideXOrigin |
|||
self |
|||
should: [ GameOfLife withAliveCells: { 2@0. 1@0. -1@1 } ofSize: 3@3 ] |
|||
raise: Error - MessageNotUnderstood |
|||
withMessageText: 'Cell -1@1 out of range' |
|||
test07CanNotCreateGameWithCellOutsideYOrigin |
|||
self |
|||
should: [ GameOfLife withAliveCells: { 2@0. 1@0. 1@-1 } ofSize: 3@3 ] |
|||
raise: Error - MessageNotUnderstood |
|||
withMessageText: 'Cell 1@-1 out of range' |
|||
test08CanNotCreateGameWithCellOutsideXLimit |
|||
self |
|||
should: [ GameOfLife withAliveCells: { 2@0. 1@0. 4@1 } ofSize: 3@3 ] |
|||
raise: Error - MessageNotUnderstood |
|||
withMessageText: 'Cell 4@1 out of range' |
|||
test09CanNotCreateGameWithCellOutsideYLimit |
|||
self |
|||
should: [ GameOfLife withAliveCells: { 2@0. 1@0. 1@4 } ofSize: 3@3 ] |
|||
raise: Error - MessageNotUnderstood |
|||
withMessageText: 'Cell 1@4 out of range' |
|||
</syntaxhighlight> |
|||
If you want to have a visual representation, here is a view for it: |
|||
<syntaxhighlight lang="smalltalk"> |
|||
ImageMorph subclass: #GameOfLifeView |
|||
instanceVariableNames: 'game' |
|||
classVariableNames: '' |
|||
poolDictionaries: '' |
|||
category: 'GameOfLife' |
|||
GameOfLifeView class>>for: aGameOfLife |
|||
^self new initializeFor: aGameOfLife |
|||
GameOfLifeView class>>openFor: aGameOfLife |
|||
^(self for: aGameOfLife) open |
|||
GameOfLifeView class>>blinker |
|||
^GameOfLife withAliveCells: { 4@2. 4@3. 4@4. 3@3. 4@3. 5@3 } ofSize: 10@10 |
|||
GameOfLifeView class>>toad |
|||
^GameOfLife withAliveCells: { 2@4. 3@4. 4@4. 3@3. 4@3. 5@3 } ofSize: 10@10 |
|||
GameOfLifeView class>>openBlinker |
|||
^self openFor: self blinker |
|||
GameOfLifeView class>>openToad |
|||
^self openFor: self toad |
|||
initializeFor: aGameOfLife |
|||
game := aGameOfLife. |
|||
self image: (Form extent: game boardSize * 5 depth: 2). |
|||
open |
|||
self showBoard. |
|||
self openInWorld. |
|||
self startSteppingStepTime: 500 |
|||
showBoard |
|||
game boardCellsDo: [ :aPoint | |
|||
(game isAlive: aPoint) |
|||
ifTrue: [ self form fillBlack: (aPoint*5 extent: 5@5) ] |
|||
ifFalse: [ self form fillWhite: (aPoint*5 extent: 5@5) ]]. |
|||
step |
|||
game calculateNextGeneration. |
|||
self showBoard. |
|||
self redrawNeeded. |
|||
</syntaxhighlight> |
|||
=={{header|SQL}}== |
|||
Microsoft SQL here: http://www.sqlservercentral.com/articles/CTE/130407/ |
|||
{{works with|Oracle}} |
|||
<syntaxhighlight lang="sql"> |
|||
-- save these lines in a file called |
|||
-- setupworld.sql |
|||
-- turn off feedback for cleaner display |
|||
set feedback off |
|||
-- 3 x 3 world |
|||
-- alive has coordinates of living cells |
|||
drop table alive; |
|||
create table alive (x number,y number); |
|||
-- three alive up the middle |
|||
-- * |
|||
-- * |
|||
-- * |
|||
insert into alive values (2,1); |
|||
insert into alive values (2,2); |
|||
insert into alive values (2,3); |
|||
commit; |
|||
-- save these lines in a file called |
|||
newgeneration.sql |
|||
-- adjact contains one row for each pair of |
|||
-- coordinates that is adjacent to a living cell |
|||
drop table adjacent; |
|||
create table adjacent (x number,y number); |
|||
-- add row for each of the 8 adjacent squares |
|||
insert into adjacent select x-1,y-1 from alive; |
|||
insert into adjacent select x-1,y from alive; |
|||
insert into adjacent select x-1,y+1 from alive; |
|||
insert into adjacent select x,y-1 from alive; |
|||
insert into adjacent select x,y+1 from alive; |
|||
insert into adjacent select x+1,y-1 from alive; |
|||
insert into adjacent select x+1,y from alive; |
|||
insert into adjacent select x+1,y+1 from alive; |
|||
commit; |
|||
-- delete rows for squares that are outside the world |
|||
delete from adjacent where x<1 or y<1 or x>3 or y>3; |
|||
commit; |
|||
-- table counts is the number of live cells |
|||
-- adjacent to that point |
|||
drop table counts; |
|||
create table counts as |
|||
select x,y,count(*) n |
|||
from adjacent a |
|||
group by x,y; |
|||
-- 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 |
|||
-- delete the ones who die |
|||
delete from alive a |
|||
where |
|||
((a.x,a.y) not in (select x,y from counts)) or |
|||
((select c.n from counts c where a.x=c.x and a.y=c.y) in |
|||
(1,4,5,6,7,8)); |
|||
-- insert the ones that are born |
|||
insert into alive a |
|||
select x,y from counts c |
|||
where c.n=3 and |
|||
((c.x,c.y) not in (select x,y from alive)); |
|||
commit; |
|||
-- create output table |
|||
drop table output; |
|||
create table output as |
|||
select rownum y,' ' x1,' ' x2,' ' x3 |
|||
from dba_tables where rownum < 4; |
|||
update output set x1='*' |
|||
where (1,y) in |
|||
(select x,y from alive); |
|||
update output set x2='*' |
|||
where (2,y) in |
|||
(select x,y from alive); |
|||
update output set x3='*' |
|||
where (3,y) in |
|||
(select x,y from alive); |
|||
commit |
|||
-- output configuration |
|||
select x1||x2||x3 WLD |
|||
from output |
|||
order by y desc; |
|||
</syntaxhighlight> |
|||
<pre> |
|||
Running in sqlplus: |
|||
SQL> @setupworld |
|||
SQL> @newgeneration |
|||
WLD |
|||
--- |
|||
*** |
|||
SQL> @newgeneration |
|||
WLD |
|||
--- |
|||
* |
|||
* |
|||
* |
|||
SQL> @newgeneration |
|||
WLD |
|||
--- |
|||
*** |
|||
</pre> |
|||
=={{header|Swift}}== |
|||
{{trans|Rust}} |
|||
<syntaxhighlight lang="swift">struct Cell: Hashable { |
|||
var x: Int |
|||
var y: Int |
|||
} |
|||
struct Colony { |
|||
private var height: Int |
|||
private var width: Int |
|||
private var cells: Set<Cell> |
|||
init(cells: Set<Cell>, height: Int, width: Int) { |
|||
self.cells = cells |
|||
self.height = height |
|||
self.width = width |
|||
} |
|||
private func neighborCounts() -> [Cell: Int] { |
|||
var counts = [Cell: Int]() |
|||
for cell in cells.flatMap(Colony.neighbors(for:)) { |
|||
counts[cell, default: 0] += 1 |
|||
} |
|||
return counts |
|||
} |
|||
private static func neighbors(for cell: Cell) -> [Cell] { |
|||
return [ |
|||
Cell(x: cell.x - 1, y: cell.y - 1), |
|||
Cell(x: cell.x, y: cell.y - 1), |
|||
Cell(x: cell.x + 1, y: cell.y - 1), |
|||
Cell(x: cell.x - 1, y: cell.y), |
|||
Cell(x: cell.x + 1, y: cell.y), |
|||
Cell(x: cell.x - 1, y: cell.y + 1), |
|||
Cell(x: cell.x, y: cell.y + 1), |
|||
Cell(x: cell.x + 1, y: cell.y + 1), |
|||
] |
|||
} |
|||
func printColony() { |
|||
for y in 0..<height { |
|||
for x in 0..<width { |
|||
let char = cells.contains(Cell(x: x, y: y)) ? "0" : "." |
|||
print("\(char) ", terminator: "") |
|||
} |
|||
print() |
|||
} |
|||
} |
|||
mutating func run(iterations: Int) { |
|||
print("(0)") |
|||
printColony() |
|||
print() |
|||
for i in 1...iterations { |
|||
print("(\(i))") |
|||
runGeneration() |
|||
printColony() |
|||
print() |
|||
} |
|||
} |
|||
private mutating func runGeneration() { |
|||
cells = Set(neighborCounts().compactMap({keyValue in |
|||
switch (keyValue.value, cells.contains(keyValue.key)) { |
|||
case (2, true), (3, _): |
|||
return keyValue.key |
|||
case _: |
|||
return nil |
|||
} |
|||
})) |
|||
} |
|||
} |
|||
let blinker = [Cell(x: 1, y: 0), Cell(x: 1, y: 1), Cell(x: 1, y: 2)] as Set |
|||
var col = Colony(cells: blinker, height: 3, width: 3) |
|||
print("Blinker: ") |
|||
col.run(iterations: 3) |
|||
let glider = [ |
|||
Cell(x: 1, y: 0), |
|||
Cell(x: 2, y: 1), |
|||
Cell(x: 0, y: 2), |
|||
Cell(x: 1, y: 2), |
|||
Cell(x: 2, y: 2) |
|||
] as Set |
|||
col = Colony(cells: glider, height: 8, width: 8) |
|||
print("Glider: ") |
|||
col.run(iterations: 20)</syntaxhighlight> |
|||
{{out}} |
|||
<pre style="height: 20em; overflow: scroll">Blinker: |
|||
(0) |
|||
. 0 . |
|||
. 0 . |
|||
. 0 . |
|||
(1) |
|||
. . . |
|||
0 0 0 |
|||
. . . |
|||
(2) |
|||
. 0 . |
|||
. 0 . |
|||
. 0 . |
|||
(3) |
|||
. . . |
|||
0 0 0 |
|||
. . . |
|||
Glider: |
|||
(0) |
|||
. 0 . . . . . . |
|||
. . 0 . . . . . |
|||
0 0 0 . . . . . |
|||
. . . . . . . . |
|||
. . . . . . . . |
|||
. . . . . . . . |
|||
. . . . . . . . |
|||
. . . . . . . . |
|||
(1) |
|||
. . . . . . . . |
|||
0 . 0 . . . . . |
|||
. 0 0 . . . . . |
|||
. 0 . . . . . . |
|||
. . . . . . . . |
|||
. . . . . . . . |
|||
. . . . . . . . |
|||
. . . . . . . . |
|||
(2) |
|||
. . . . . . . . |
|||
. . 0 . . . . . |
|||
0 . 0 . . . . . |
|||
. 0 0 . . . . . |
|||
. . . . . . . . |
|||
. . . . . . . . |
|||
. . . . . . . . |
|||
. . . . . . . . |
|||
(3) |
|||
. . . . . . . . |
|||
. 0 . . . . . . |
|||
. . 0 0 . . . . |
|||
. 0 0 . . . . . |
|||
. . . . . . . . |
|||
. . . . . . . . |
|||
. . . . . . . . |
|||
. . . . . . . . |
|||
(4) |
|||
. . . . . . . . |
|||
. . 0 . . . . . |
|||
. . . 0 . . . . |
|||
. 0 0 0 . . . . |
|||
. . . . . . . . |
|||
. . . . . . . . |
|||
. . . . . . . . |
|||
. . . . . . . . |
|||
(5) |
|||
. . . . . . . . |
|||
. . . . . . . . |
|||
. 0 . 0 . . . . |
|||
. . 0 0 . . . . |
|||
. . 0 . . . . . |
|||
. . . . . . . . |
|||
. . . . . . . . |
|||
. . . . . . . . |
|||
(6) |
|||
. . . . . . . . |
|||
. . . . . . . . |
|||
. . . 0 . . . . |
|||
. 0 . 0 . . . . |
|||
. . 0 0 . . . . |
|||
. . . . . . . . |
|||
. . . . . . . . |
|||
. . . . . . . . |
|||
(7) |
|||
. . . . . . . . |
|||
. . . . . . . . |
|||
. . 0 . . . . . |
|||
. . . 0 0 . . . |
|||
. . 0 0 . . . . |
|||
. . . . . . . . |
|||
. . . . . . . . |
|||
. . . . . . . . |
|||
(8) |
|||
. . . . . . . . |
|||
. . . . . . . . |
|||
. . . 0 . . . . |
|||
. . . . 0 . . . |
|||
. . 0 0 0 . . . |
|||
. . . . . . . . |
|||
. . . . . . . . |
|||
. . . . . . . . |
|||
(9) |
|||
. . . . . . . . |
|||
. . . . . . . . |
|||
. . . . . . . . |
|||
. . 0 . 0 . . . |
|||
. . . 0 0 . . . |
|||
. . . 0 . . . . |
|||
. . . . . . . . |
|||
. . . . . . . . |
|||
(10) |
|||
. . . . . . . . |
|||
. . . . . . . . |
|||
. . . . . . . . |
|||
. . . . 0 . . . |
|||
. . 0 . 0 . . . |
|||
. . . 0 0 . . . |
|||
. . . . . . . . |
|||
. . . . . . . . |
|||
(11) |
|||
. . . . . . . . |
|||
. . . . . . . . |
|||
. . . . . . . . |
|||
. . . 0 . . . . |
|||
. . . . 0 0 . . |
|||
. . . 0 0 . . . |
|||
. . . . . . . . |
|||
. . . . . . . . |
|||
(12) |
|||
. . . . . . . . |
|||
. . . . . . . . |
|||
. . . . . . . . |
|||
. . . . 0 . . . |
|||
. . . . . 0 . . |
|||
. . . 0 0 0 . . |
|||
. . . . . . . . |
|||
. . . . . . . . |
|||
(13) |
|||
. . . . . . . . |
|||
. . . . . . . . |
|||
. . . . . . . . |
|||
. . . . . . . . |
|||
. . . 0 . 0 . . |
|||
. . . . 0 0 . . |
|||
. . . . 0 . . . |
|||
. . . . . . . . |
|||
(14) |
|||
. . . . . . . . |
|||
. . . . . . . . |
|||
. . . . . . . . |
|||
. . . . . . . . |
|||
. . . . . 0 . . |
|||
. . . 0 . 0 . . |
|||
. . . . 0 0 . . |
|||
. . . . . . . . |
|||
(15) |
|||
. . . . . . . . |
|||
. . . . . . . . |
|||
. . . . . . . . |
|||
. . . . . . . . |
|||
. . . . 0 . . . |
|||
. . . . . 0 0 . |
|||
. . . . 0 0 . . |
|||
. . . . . . . . |
|||
(16) |
|||
. . . . . . . . |
|||
. . . . . . . . |
|||
. . . . . . . . |
|||
. . . . . . . . |
|||
. . . . . 0 . . |
|||
. . . . . . 0 . |
|||
. . . . 0 0 0 . |
|||
. . . . . . . . |
|||
(17) |
|||
. . . . . . . . |
|||
. . . . . . . . |
|||
. . . . . . . . |
|||
. . . . . . . . |
|||
. . . . . . . . |
|||
. . . . 0 . 0 . |
|||
. . . . . 0 0 . |
|||
. . . . . 0 . . |
|||
(18) |
|||
. . . . . . . . |
|||
. . . . . . . . |
|||
. . . . . . . . |
|||
. . . . . . . . |
|||
. . . . . . . . |
|||
. . . . . . 0 . |
|||
. . . . 0 . 0 . |
|||
. . . . . 0 0 . |
|||
(19) |
|||
. . . . . . . . |
|||
. . . . . . . . |
|||
. . . . . . . . |
|||
. . . . . . . . |
|||
. . . . . . . . |
|||
. . . . . 0 . . |
|||
. . . . . . 0 0 |
|||
. . . . . 0 0 . |
|||
(20) |
|||
. . . . . . . . |
|||
. . . . . . . . |
|||
. . . . . . . . |
|||
. . . . . . . . |
|||
. . . . . . . . |
|||
. . . . . . 0 . |
|||
. . . . . . . 0 |
|||
. . . . . 0 0 0</pre> |
|||
=={{header|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). |
|||
<syntaxhighlight lang="systemverilog">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</syntaxhighlight> |
|||
=={{header|Tcl}}== |
=={{header|Tcl}}== |
||
{{works with|Tcl|8.5}} |
{{works with|Tcl|8.5}} |
||
< |
<syntaxhighlight lang="tcl">package require Tcl 8.5 |
||
proc main {} { |
proc main {} { |
||
Line 2,554: | Line 17,013: | ||
} |
} |
||
main</ |
main</syntaxhighlight> |
||
<pre style='height:30ex; overflow:scroll'>blinker generation 1: |
<pre style='height:30ex; overflow:scroll'>blinker generation 1: |
||
. # . |
. # . |
||
Line 2,594: | Line 17,053: | ||
. # # #</pre> |
. # # #</pre> |
||
=={{header| |
=={{header|Uiua}}== |
||
Canonical solution from Uiua homepage. [https://www.uiua.org/pad?src=0_12_0-dev_1__IyBMaWZlIGluIFVpdWEKTGlmZSDihpAg4oal4oqZ4oan4oipPTMsMi0sLyviiaHihrvimIcxLTHih6EzXzPCpC4K4oGFw5cwLjYg4oi14ouF4pqCIOKGr-KfnOKKmiA1MCAjIEluaXQK4oeM4peM4o2lKOKfnOKKgkxpZmUpMzAw4p-cwqQgIyBSdW4K4omhKOKWveKfnOKJoeKWvTQpICAgICAgICAgIyBVcHNjYWxlCg== Run it in Uiua Pad] to see it in all its glory. |
|||
<syntaxhighlight lang="Uiua"> |
|||
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. <!-- I wrote this program, but I am not the inventor of this technique; I saw something like it in the WireWorld system mentioned in http://kpreid.livejournal.com/4424.html . --> |
|||
Life ← ↥⊙↧∩=3,2-,/+≡↻☇1-1⇡3_3¤. |
|||
⁅×0.6 ∵⋅⚂ ↯⟜⊚ 50 # Init |
|||
⇌◌⍥(⟜⊂Life)300⟜¤ # Run |
|||
≡(▽⟜≡▽4) # Upscale |
|||
</syntaxhighlight> |
|||
=={{header|UNIX Shell}}== |
|||
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. |
|||
{{works with|Bourne Again SHell}} |
|||
{{works with|Korn Shell}} |
|||
{{works with|Z Shell}} |
|||
<syntaxhighlight lang="bash"># Shells only have 1-dimensional arrays, so each row is one string |
|||
function main { |
|||
typeset blinker=(000 111 000) |
|||
run list 4 "${blinker[@]}" |
|||
} |
|||
function run { |
|||
<lang ti89b>Define life(pattern) = Prgm |
|||
typeset mode=$1 # screen or list |
|||
Local x,y,nt,count,save,xl,yl,xh,yh |
|||
shift |
|||
Define nt(y,x) = when(pxlTest(y,x), 1, 0) |
|||
typeset -i limit=0 |
|||
if (( $1 > 1 )); then |
|||
{}→save |
|||
limit=$1 |
|||
setGraph("Axes", "Off")→save[1] |
|||
shift |
|||
setGraph("Grid", "Off")→save[2] |
|||
fi |
|||
setGraph("Labels", "Off")→save[3] |
|||
if [[ mode == screen ]]; then |
|||
FnOff |
|||
clear |
|||
fi |
|||
typeset universe=("$@") |
|||
typeset -i generation=0 |
|||
while (( !limit || generation < limit )); do |
|||
if [[ mode == screen ]]; then |
|||
printf '\e[H' |
|||
else |
|||
printf '\n' |
|||
fi |
|||
printf 'Generation %d\n' "$generation" |
|||
disp "${universe[@]}" | tr '01' '.@' |
|||
universe=($(next_generation "${universe[@]}")) |
|||
(( generation += 1 )) |
|||
sleep 0.125 |
|||
done |
|||
} |
|||
# display a matrix |
|||
If pattern = "blinker" Then |
|||
function disp { |
|||
36→yl |
|||
# remove sed for more compact display |
|||
40→yh |
|||
printf '%s\n' "$@" | sed 's/./ &/g' |
|||
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 |
|||
# center a matrix within a given size by padding with 0s |
|||
While getKey() = 0 |
|||
function center { |
|||
© Expand upper-left corner to whole cell |
|||
typeset height=$1 width=$2 |
|||
For y,yl,yh,2 |
|||
shift 2 |
|||
For x,xl,xh,2 |
|||
typeset -i offset_i offset_j i j |
|||
If pxlTest(y,x) Then |
|||
if (( $# > height || ${#1} > width )); then |
|||
printf >&2 'Source larger than target.\n' |
|||
PxlOn y+1,x+1 |
|||
return 1 |
|||
fi |
|||
Else |
|||
(( offset_i = (height - $#) / 2 )) |
|||
(( offset_j = (width - ${#1}) / 2 )) |
|||
PxlOff y+1,x+1 |
|||
typeset prefix zeroes suffix |
|||
PxlOff y, x+1 |
|||
for (( j=0; j<width; ++j )); do |
|||
zeroes+=0 |
|||
if (( j < offset_j )); then |
|||
EndFor |
|||
prefix+=0 |
|||
elif (( j >= offset_j+${#1} )); then |
|||
suffix+=0 |
|||
fi |
|||
done |
|||
for (( i=0; i<offset_i; ++i )); do |
|||
printf '%s\n' "$zeroes" |
|||
done |
|||
while (( $# )); do |
|||
printf '%s%s%s\n' "$prefix" "$1" "$suffix" |
|||
shift |
|||
(( i++ )) |
|||
done |
|||
while (( i++ < height )); do |
|||
printf '%s\n' "$zeroes" |
|||
done |
|||
} |
|||
# compute the next generation |
|||
# the grid is finite; pass -t to treat as torus (wraparound), otherwise it's |
|||
For y,yl,yh,2 |
|||
# bounded by always-dead cells |
|||
For x,xl,xh,2 |
|||
next_generation() { |
|||
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 |
|||
typeset -i torus=0 |
|||
If count = 3 Then |
|||
if [[ $1 == -t ]]; then |
|||
torus=1 |
|||
ElseIf count ≠ 2 Then |
|||
shift |
|||
PxlOff y,x |
|||
fi |
|||
EndIf |
|||
# cache cells in an associative array |
|||
EndFor |
|||
# (for fast lookup when counting neighbors) |
|||
EndFor |
|||
typeset -A cells |
|||
EndWhile |
|||
typeset -i i=0 j=0 height=$# width=${#1} |
|||
while (( $# )); do |
|||
row=$1 |
|||
shift |
|||
for (( j=0; j<${#row}; ++j )); do |
|||
cells[$i,$j]=${row:$j:1} |
|||
done |
|||
(( i+=1 )) |
|||
done |
|||
typeset -i di dj ni nj n cell |
|||
for (( i=0; i<height; ++i )); do |
|||
for (( j=0; j<width; ++j )); do |
|||
n=0 |
|||
for (( di=-1; di<2; ++di )); do |
|||
(( ni = i + di )) |
|||
if (( torus )); then |
|||
(( ni = (ni + height) % height )) |
|||
fi |
|||
for (( dj=-1; dj<2; ++dj )); do |
|||
(( nj = j + dj )) |
|||
if (( torus )); then |
|||
(( nj = (nj + width) % width )) |
|||
fi |
|||
if (( (di || dj) && ni >= 0 && nj >= 0 && ni < height && nj < width )); then |
|||
(( n += ${cells[$ni,$nj]} )) |
|||
fi |
|||
done |
|||
done |
|||
printf '%d' "$(( ( n == 3 ) || ( ${cells[$i,$j]} && n==2 ) ))" |
|||
done |
|||
printf '\n' |
|||
done |
|||
} |
|||
main "$@"</syntaxhighlight> |
|||
© Restore changed options |
|||
setGraph("Axes", save[1]) |
|||
{{Out}} |
|||
setGraph("Grid", save[2]) |
|||
<pre>Generation 0 |
|||
setGraph("Labels", save[3]) |
|||
. . . |
|||
EndPrgm</lang> |
|||
@ @ @ |
|||
. . . |
|||
Generation 1 |
|||
. @ . |
|||
. @ . |
|||
. @ . |
|||
Generation 2 |
|||
. . . |
|||
@ @ @ |
|||
. . . |
|||
Generation 3 |
|||
. @ . |
|||
. @ . |
|||
. @ .</pre> |
|||
=={{header|Ursala}}== |
=={{header|Ursala}}== |
||
Three functions are defined: |
Three functions are defined: |
||
rule takes a pair (c,<n..>) representing a cell and its list of neighboring cells to the new cell, |
rule takes a pair (c,<n..>) representing a cell and its list of neighboring cells to the new cell, |
||
Line 2,685: | Line 17,215: | ||
sequence of n boards evolving from it. |
sequence of n boards evolving from it. |
||
< |
<syntaxhighlight lang="ursala">#import std |
||
#import nat |
#import nat |
||
Line 2,692: | Line 17,222: | ||
neighborhoods = ~&thth3hthhttPCPthPTPTX**K7S+ swin3**+ swin3@hNSPiCihNCT+ --<0>*+ 0-* |
neighborhoods = ~&thth3hthhttPCPthPTPTX**K7S+ swin3**+ swin3@hNSPiCihNCT+ --<0>*+ 0-* |
||
evolve "n" = next"n" rule**+ neighborhoods</ |
evolve "n" = next"n" rule**+ neighborhoods</syntaxhighlight> |
||
test program: |
test program: |
||
< |
<syntaxhighlight lang="ursala">blinker = |
||
(==`O)**t -[ |
(==`O)**t -[ |
||
Line 2,712: | Line 17,242: | ||
#show+ |
#show+ |
||
examples = mat0 ~&?(`O!,`+!)*** evolve3(blinker)-- evolve5(glider)</ |
examples = mat0 ~&?(`O!,`+!)*** evolve3(blinker)-- evolve5(glider)</syntaxhighlight> |
||
{{out}} |
|||
output: |
|||
<pre style="height:30ex;overflow:scroll">+++ |
<pre style="height:30ex;overflow:scroll">+++ |
||
OOO |
OOO |
||
Line 2,757: | Line 17,287: | ||
=={{header|Vedit macro language}}== |
=={{header|Vedit macro language}}== |
||
This implementation uses an edit buffer for data storage and to show results. |
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. |
For purpose of this task, the macro writes the initial pattern in the buffer. |
||
Line 2,769: | Line 17,298: | ||
Two <tt>Replace</tt> commands are then used to change characters into '.' or 'O' to represent dead and living cells in the new generation. |
Two <tt>Replace</tt> commands are then used to change characters into '.' or 'O' to represent dead and living cells in the new generation. |
||
< |
<syntaxhighlight lang="vedit">IT("Generation 0 ") IN |
||
IT(".O.") IN |
IT(".O.") IN |
||
IT(".O.") IN |
IT(".O.") IN |
||
Line 2,826: | Line 17,355: | ||
Ins_Char(Cur_Char+1,OVERWRITE) |
Ins_Char(Cur_Char+1,OVERWRITE) |
||
} |
} |
||
Return</ |
Return</syntaxhighlight> |
||
{{out}} |
|||
Output: |
|||
< |
<pre>Generation 0 |
||
.O. |
.O. |
||
.O. |
.O. |
||
Line 2,842: | Line 17,371: | ||
.O. |
.O. |
||
.O. |
.O. |
||
.O.</ |
.O.</pre> |
||
=={{header|Wortel}}== |
|||
Mapping over a matrix. |
|||
<syntaxhighlight lang="wortel">@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 |
|||
]] |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
<pre>[ |
|||
[[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]] |
|||
]</pre> |
|||
Different solution by using functions that operate on matrices. |
|||
<syntaxhighlight lang="wortel">@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 |
|||
]] |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
<pre>[ |
|||
[[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]] |
|||
]</pre> |
|||
=={{header|VBScript}}== |
|||
I have used ANSI escape codes, so the program will not whork from W2000 to W8.1, where Microsoft did see fit to remove support to these codes. The game wraps around at borders, so the gliders are always alive. Must be invoked from cscript. |
|||
<syntaxhighlight lang="vb"> |
|||
option explicit |
|||
const tlcr=3, tlcc=3 |
|||
const maxdim=15 |
|||
dim a(15,15) |
|||
dim b(15,15) |
|||
dim ans0:ans0=chr(27)&"[" |
|||
dim gen,n |
|||
erase a |
|||
'glider |
|||
a(0,1)=true:a(1,2)=true:a(2,0)=true:a(2,1)=true:a(2,2)=true |
|||
'blinker |
|||
a(3,13)=true:a(4,13)=true:a(5,13)=true |
|||
'beacon |
|||
a(11,1)=true:a(11,2)=true: a(12,1)=true:a(12,2)=true |
|||
a(13,3)=true:a(13,4)=true: a(14,3)=true:a(14,4)=true |
|||
gen=0 |
|||
doframe |
|||
do |
|||
display |
|||
wscript.sleep(100) |
|||
n=newgen |
|||
loop until n=0 |
|||
display |
|||
sub cls() wscript.StdOut.Write ans0 &"2J"&ans0 &"?25l":end sub 'hide cursor too |
|||
sub toxy(r,c,s) wscript.StdOut.Write ans0 & r & ";" & c & "f" & s :end sub |
|||
function iif(a,b,c) if a then iif=b else iif=c end if :end function |
|||
sub doframe |
|||
dim r,c,s |
|||
const frame="@" |
|||
cls |
|||
toxy 1,tlcc-1, "Conway's Game of Life" |
|||
toxy tlcr-1,tlcc-1,string(maxdim+3,frame) |
|||
s=frame&space(maxdim+1)&frame |
|||
for r=0 to maxdim |
|||
toxy tlcr+r,tlcc-1,s |
|||
next |
|||
toxy tlcr+maxdim+1,tlcc-1,string(maxdim+3,frame) |
|||
end sub |
|||
sub display |
|||
dim r,c |
|||
const pix="#" |
|||
for r=0 to maxdim |
|||
for c=0 to maxdim |
|||
toxy tlcr+r,tlcc+c,iif (a(r,c),pix," ") |
|||
next |
|||
next |
|||
toxy tlcr+maxdim+2,tlcc-1,gen & " " & n |
|||
toxy tlcr+maxdim+3,tlcc-1,"" |
|||
gen=gen+1 |
|||
end sub |
|||
function newgen |
|||
dim r,c,cnt,cp,cm,rp,rm |
|||
for r=0 to maxdim |
|||
for c=0 to maxdim |
|||
cp=(c+1) and maxdim 'wrap around |
|||
cm=(c-1) and maxdim |
|||
rp=(r+1) and maxdim |
|||
rm=(r-1) and maxdim |
|||
cnt=0 |
|||
cnt=- a(rp,cp) - a(rp,c) - a(rp,cm) 'true =-1 |
|||
cnt=cnt- a(r,cp)- a(r,cm) |
|||
cnt=cnt- a(rm,cp)- a(rm,c) - a(rm,cm) |
|||
if a(r,c) then |
|||
b(r,c)=iif (cnt=2 or cnt=3,true,false) |
|||
else |
|||
b(r,c)=iif (cnt=3,true,false) |
|||
end if |
|||
next |
|||
next |
|||
for r=0 to maxdim |
|||
for c=0 to maxdim |
|||
a(r,c)=b(r,c) |
|||
newgen=newgen- b(r,c) |
|||
next |
|||
next |
|||
end function |
|||
</syntaxhighlight> |
|||
<pre> |
|||
Conway's Game of Life |
|||
@@@@@@@@@@@@@@@@@@ |
|||
@ @ |
|||
@ @ |
|||
@ @ |
|||
@ # # @ |
|||
@ # # # @ |
|||
@ ## # @ |
|||
@ @ |
|||
@ @ |
|||
@ @ |
|||
@ @ |
|||
@ @ |
|||
@ ## @ |
|||
@ # @ |
|||
@ # @ |
|||
@ ## @ |
|||
@ @ |
|||
@@@@@@@@@@@@@@@@@@ |
|||
9 14 |
|||
</pre> |
|||
A fast version using a thechnique from Abrash's Black Book. Set your console to 34 rows minimum, as it uses a 32x32 cells world |
|||
<syntaxhighlight lang="vb"> |
|||
option explicit |
|||
const pix="##" |
|||
const cl_on=16 |
|||
const cl_mnum=15 |
|||
randomize timer |
|||
const tlcr=2, tlcc=2 |
|||
const maxdim =31 |
|||
settitle "Conway's game of life" |
|||
dim a(31,31) |
|||
dim ans0:ans0=chr(27)&"[" |
|||
dim gen,n |
|||
gen=0 |
|||
paintframe |
|||
'init1 'objects |
|||
init2 400 'random |
|||
do |
|||
n=nextgen |
|||
toxy tlcr-1,tlcc+maxdim*2+4,gen & " " & n |
|||
toxy tlcr,tlcc+maxdim*2+3,"" |
|||
gen=gen+1 |
|||
loop until n=0 |
|||
sub pause() wscript.stdout.write vbcrlf & "Press Enter":wscript.stdin.readline: end sub |
|||
sub settitle(s) wscript.StdOut.Write chr(27)&"]0;"& s &chr(7):end sub |
|||
sub cls() wscript.StdOut.Write ans0 &"2J"&ans0 &"?25l":end sub |
|||
sub toxy(r,c,s) wscript.StdOut.Write ans0 & r & ";" & c & "f" & s :end sub |
|||
function iif(a,b,c) if a then iif=b else iif=c end if :end function |
|||
sub init1 |
|||
dim x |
|||
update 0,1,1,x:update 1,2,1,x:update 2,0,1,x:update 2,1,1,x:update 2,2,1,x |
|||
update 3,13,1,x:update 4,13,1,x:update 5,13,1,x |
|||
update 11,1,1,x:update 11,2,1,x: update 12,1,1,x:update 12,2,1,x |
|||
update 13,3,1,x:update 13,4,1,x: update 14,3,1,x:update 14,4,1,x |
|||
end sub |
|||
sub init2 (n) |
|||
dim i,r,c,x |
|||
for i=1 to n |
|||
do |
|||
r=cint(rnd*maxdim) |
|||
c=cint(rnd*maxdim) |
|||
loop until (a(r,c) and cl_on)=0 |
|||
update r,c,1,x |
|||
next |
|||
end sub |
|||
sub paintframe |
|||
dim r,c,s |
|||
const frame="@" |
|||
cls |
|||
'toxy 1,tlcc-1, "Conway's Game of Life" |
|||
toxy tlcr-1,tlcc-1,string(maxdim*2+4,frame) |
|||
s=frame&space(maxdim*2+2)&frame |
|||
for r=0 to maxdim |
|||
toxy tlcr+r,tlcc-1,s |
|||
next |
|||
toxy tlcr+maxdim+1,tlcc-1,string(maxdim*2+4,frame) |
|||
end sub |
|||
function nextgen() |
|||
dim r,c,pre,cnt,a1,onoff,chg |
|||
nextgen=0 |
|||
a1=a 'does a hard a copy of a |
|||
for r=0 to maxdim |
|||
for c=0 to maxdim |
|||
if a1(r,c)then 'check only cells alive or having neighbors |
|||
pre=a1(r,c) and cl_on |
|||
cnt=a1(r,c) and cl_mnum |
|||
'check life condition and update cell |
|||
if pre<>0 and (cnt<2 or cnt>3) then |
|||
update r,c,0,nextgen |
|||
elseif pre=0 and cnt=3 then |
|||
update r,c,1,nextgen |
|||
end if |
|||
end if |
|||
next |
|||
next |
|||
end function |
|||
sub update(r,c,onoff,nextgen) |
|||
'displays cell and update neighbors count in neighbors |
|||
dim cp,cm,rp,rm,inc,mask |
|||
mask=iif(onoff,cl_on,0) |
|||
a(r,c)= (a(r,c) and cl_mnum) or mask 'update cell |
|||
toxy tlcr+r,tlcc+c*2,iif (onoff,pix," ") 'display cell |
|||
cp=(c+1) and maxdim 'wrap around |
|||
cm=(c-1) and maxdim |
|||
rp=(r+1) and maxdim |
|||
rm=(r-1) and maxdim |
|||
if onoff then inc=1 else inc=-1 |
|||
'update count in neighbors |
|||
a(rp,cp)=(a(rp,cp) and cl_on) or ((a(rp,cp) and cl_mnum)+inc) |
|||
a(rp,c)=(a(rp,c) and cl_on) or ((a(rp,c) and cl_mnum)+inc) |
|||
a(rp,cm)=(a(rp,cm) and cl_on) or ((a(rp,cm) and cl_mnum)+inc) |
|||
a(r,cp)=(a(r,cp) and cl_on) or ((a(r,cp) and cl_mnum)+inc) |
|||
a(r,cm)=(a(r,cm) and cl_on) or ((a(r,cm) and cl_mnum)+inc) |
|||
a(rm,cp)=(a(rm,cp) and cl_on) or ((a(rm,cp) and cl_mnum)+inc) |
|||
a(rm,c)=(a(rm,c) and cl_on) or ((a(rm,c) and cl_mnum)+inc) |
|||
a(rm,cm)=(a(rm,cm) and cl_on) or ((a(rm,cm) and cl_mnum)+inc) |
|||
nextgen=nextgen+1 |
|||
end sub |
|||
</syntaxhighlight> |
|||
=={{header|V (Vlang)}}== |
|||
{{trans|Go}} |
|||
<syntaxhighlight lang="v (vlang)">import rand |
|||
import strings |
|||
import time |
|||
struct Field { |
|||
mut: |
|||
s [][]bool |
|||
w int |
|||
h int |
|||
} |
|||
fn new_field(w int, h int) Field { |
|||
s := [][]bool{len: h, init: []bool{len: w}} |
|||
return Field{s, w, h} |
|||
} |
|||
fn (mut f Field) set(x int, y int, b bool) { |
|||
f.s[y][x] = b |
|||
} |
|||
fn (f Field) next(x int, y int) bool { |
|||
mut 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)) |
|||
} |
|||
fn (f Field) state(xx int, yy int) bool { |
|||
mut x, mut y := xx, yy |
|||
for y < 0 { |
|||
y += f.h |
|||
} |
|||
for x < 0 { |
|||
x += f.w |
|||
} |
|||
return f.s[y%f.h][x%f.w] |
|||
} |
|||
struct Life { |
|||
mut: |
|||
w int |
|||
h int |
|||
a Field |
|||
b Field |
|||
} |
|||
fn new_life(w int, h int) Life { |
|||
mut a := new_field(w, h) |
|||
for _ in 0..(w * h / 2) { |
|||
a.set(rand.intn(w) or {0}, rand.intn(h) or {0}, true) |
|||
} |
|||
return Life{ |
|||
a: a, |
|||
b: new_field(w, h), |
|||
w: w |
|||
h: h, |
|||
} |
|||
} |
|||
fn (mut l Life) step() { |
|||
for y in 0..l.h { |
|||
for x in 0.. l.w { |
|||
l.b.set(x, y, l.a.next(x, y)) |
|||
} |
|||
} |
|||
l.a, l.b = l.b, l.a |
|||
} |
|||
fn (l Life) str() string { |
|||
mut buf := strings.new_builder(128) |
|||
for y in 0..l.h { |
|||
for x in 0..l.w { |
|||
mut b := ' ' |
|||
if l.a.state(x, y) { |
|||
b = '*' |
|||
} |
|||
buf.write_string(b) |
|||
} |
|||
buf.write_string('\n') |
|||
} |
|||
return buf.str() |
|||
} |
|||
fn main() { |
|||
mut l := new_life(80, 15) |
|||
for i := 0; i < 300; i++ { |
|||
l.step() |
|||
//println("------------------------------------------------") |
|||
print('\x0c') |
|||
println(l) |
|||
time.sleep(time.second / 30) |
|||
} |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
Generation 300: |
|||
<pre> ** *** * |
|||
* ** |
|||
* * ** |
|||
** ** ** * |
|||
** ** |
|||
** *** * * |
|||
****** * |
|||
* * ** |
|||
* * ** * * |
|||
** * * ** |
|||
** * * * * ** |
|||
* * ** ** |
|||
* * ** * |
|||
** * </pre> |
|||
=={{header|Wren}}== |
|||
{{trans|Kotlin}} |
|||
<syntaxhighlight lang="wren">import "random" for Random |
|||
import "timer" for Timer |
|||
var Rand = Random.new(0) // using a constant seed to produce same output on each run |
|||
// patterns |
|||
var BLINKER = 0 |
|||
var GLIDER = 1 |
|||
var RANDOM = 2 |
|||
class Field { |
|||
construct new(w, h) { |
|||
_w = w |
|||
_h = h |
|||
_s = List.filled(h, null) |
|||
for (i in 0...h) _s[i] = List.filled(w, false) |
|||
} |
|||
[x, y]=(b) { _s[y][x] = b } |
|||
state(x, y) { |
|||
if (!(0..._w).contains(x) || !(0..._h).contains(y)) return false |
|||
return _s[y][x] |
|||
} |
|||
next(x, y) { |
|||
var on = 0 |
|||
for (i in -1..1) { |
|||
for (j in -1..1) { |
|||
if (state(x + i, y + j) && !(j == 0 && i == 0)) on = on + 1 |
|||
} |
|||
} |
|||
return on == 3 || (on == 2 && state(x, y)) |
|||
} |
|||
} |
|||
class Life { |
|||
construct new(pattern) { |
|||
if (pattern == BLINKER) { |
|||
_w = 3 |
|||
_h = 3 |
|||
_a = Field.new(_w, _h) |
|||
_b = Field.new(_w, _h) |
|||
_a[0, 1] = true |
|||
_a[1, 1] = true |
|||
_a[2, 1] = true |
|||
} else if (pattern == GLIDER) { |
|||
_w = 4 |
|||
_h = 4 |
|||
_a = Field.new(_w, _h) |
|||
_b = Field.new(_w, _h) |
|||
_a[1, 0] = true |
|||
_a[2, 1] = true |
|||
for (i in 0..2) _a[i, 2] = true |
|||
} else if(pattern == RANDOM) { |
|||
_w = 80 |
|||
_h = 15 |
|||
_a = Field.new(_w, _h) |
|||
_b = Field.new(_w, _h) |
|||
for (i in 0...(_w * _h).floor / 2) { |
|||
_a[Rand.int(_w), Rand.int(_h)] = true |
|||
} |
|||
} |
|||
} |
|||
step() { |
|||
for (y in 0..._h) { |
|||
for (x in 0..._w) _b[x, y] = _a.next(x, y) |
|||
} |
|||
var t = _a |
|||
_a = _b |
|||
_b = t |
|||
} |
|||
toString { |
|||
var sb = "" |
|||
for (y in 0..._h) { |
|||
for (x in 0..._w) { |
|||
var c = _a.state(x, y) ? "#" : "." |
|||
sb = sb + c |
|||
} |
|||
sb = sb + "\n" |
|||
} |
|||
return sb |
|||
} |
|||
} |
|||
var lives = [ |
|||
[Life.new(BLINKER), 3, "BLINKER"], |
|||
[Life.new(GLIDER), 4, "GLIDER" ], |
|||
[Life.new(RANDOM), 100, "RANDOM" ] |
|||
] |
|||
for (t in lives) { |
|||
var game = t[0] |
|||
var gens = t[1] |
|||
var title = t[2] |
|||
System.print("%(title):\n") |
|||
for (i in 0..gens) { |
|||
System.print("Generation: %(i)\n%(game)") |
|||
Timer.sleep(30) |
|||
game.step() |
|||
} |
|||
System.print() |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
Just shows generations 0 and 100 for RANDOM pattern: |
|||
<pre> |
|||
BLINKER: |
|||
Generation: 0 |
|||
... |
|||
### |
|||
... |
|||
Generation: 1 |
|||
.#. |
|||
.#. |
|||
.#. |
|||
Generation: 2 |
|||
... |
|||
### |
|||
... |
|||
Generation: 3 |
|||
.#. |
|||
.#. |
|||
.#. |
|||
GLIDER: |
|||
Generation: 0 |
|||
.#.. |
|||
..#. |
|||
###. |
|||
.... |
|||
Generation: 1 |
|||
.... |
|||
#.#. |
|||
.##. |
|||
.#.. |
|||
Generation: 2 |
|||
.... |
|||
..#. |
|||
#.#. |
|||
.##. |
|||
Generation: 3 |
|||
.... |
|||
.#.. |
|||
..## |
|||
.##. |
|||
Generation: 4 |
|||
.... |
|||
..#. |
|||
...# |
|||
.### |
|||
RANDOM: |
|||
Generation: 0 |
|||
#.#.###.#...#...#..##.....#......#....#...#.....#...#..#..#.....##.##....#..#### |
|||
..#...#....#.##..#..#...##.#.....#....#..##.#.###..#.#.#.#..#.##..#.#.......#.#. |
|||
###.###..##...#......#.#.#............#.......##.#..#...###.##..#...###...#..#.. |
|||
.#.....#.#.#.#.#..###.#.#.######...##..##.###.####....#....#####..##...####.#..# |
|||
..##.###.#..###...#..#..##....##...#..##.##.#..###.#..#..#.#...##..#..###....... |
|||
.####..###.##..#..#...#..#..#.##..#..........##.#..#.....#.###.#...######.#..#.. |
|||
...##...#.....##.....##.##..#..#####...##.###.#.###..#..#.#.....##.#..##....##.# |
|||
##...##.#...#.#...##.##..#.#..##.#....###.##.#.....#.##..#..#.##..#....##......# |
|||
....#.#..##.#..####..#..##....#####.#....#.#...#.#.###.#.#.#.#.#.....#...#.....# |
|||
..#..####..###..###..##.####.#....#.....#.###.#.#.###.####..#...#...#...#.##.... |
|||
#.........##..#..##...###...#..#....#.###...#.##.....###..###..##....###.#..#... |
|||
.#..#..#......#...##.###.#...#.#...##.#.....#...#......##..#.....###...##..#.#.. |
|||
.#.#.....##..#.##.....#.#.#..#.#..######.#....#..##...##.##.....#..##...#.##.##. |
|||
.#......###..#...###..........................##......#....####.#...#..#...#..#. |
|||
#..#.#......#...#...#...#..#..##...#.##...#.####.##......#....#..#.##.....#####. |
|||
Generation: 100 |
|||
...............####......#...................................................... |
|||
...............#..#.....#.#...................#................................. |
|||
........................#.#.................##.................................. |
|||
.........................#..................##............##.................... |
|||
............................................#.....#.......##.................... |
|||
.............................................#.###.............................. |
|||
...................................##...###.....#####........................... |
|||
........................##........#..#..###........#............................ |
|||
.......................#..#...........#....##................................... |
|||
.......................#....#.......#....###.................................... |
|||
........................######.....##....##..................................... |
|||
...........................#.##.....#..#..........................##.##......... |
|||
...........................#.#.......##...........................##.##......... |
|||
......##...................##................................................... |
|||
......##........................................................................ |
|||
</pre> |
|||
=={{header|XPL0}}== |
|||
<syntaxhighlight lang="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 |
|||
]; |
|||
]</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
# # # |
|||
# |
|||
# |
|||
# |
|||
# # # |
|||
</pre> |
|||
=={{header|XSLT}}== |
|||
So when the following templates |
|||
<syntaxhighlight lang="xml"><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></syntaxhighlight> |
|||
are applied against the document |
|||
<syntaxhighlight lang="html"><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></syntaxhighlight> |
|||
then the transformed XML document contains the new universe evolved by one tick: |
|||
<syntaxhighlight lang="html"><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></syntaxhighlight> |
|||
=={{header|Z80 Assembly}}== |
|||
{{works with|zmac|5nov2016}} |
|||
This code will run on an MSX computer if progammed on a cartride |
|||
or on an emulator (like openMSX) if loaded as a .rom file |
|||
The code below will show the Gosper glider gun, but |
|||
if INITLINES is set to 0 the code will show the blinker |
|||
<syntaxhighlight lang="z80">;MSX Bios calls |
|||
CHPUT equ 000A2H ;print character A |
|||
CHGET equ 0009FH ;wait for keyboard input |
|||
INITXT equ 0006CH ;init TEXT1 mode (40 x 24), based on TXTNAM,TXTCGP and LINL40 |
|||
LDIRVM equ 0005CH ;write BC times MEM(HL++) to VRAM(DE++) |
|||
;MSX system variables |
|||
TXTNAM equ 0F3B3H ;pattern name table |
|||
TXTCGP equ 0F3B7H ;pattern generator table |
|||
LINL40 equ 0F3AEH ;line length |
|||
;defines |
|||
LIFE_CHAR equ 0xDB ;MSX all-pixels-on character |
|||
;variables |
|||
screen equ 0E000H ;40x24 bytes |
|||
scratch equ screen+(40*24) ;table for calculations |
|||
org 04000H |
|||
;********************************************************************* |
|||
HEADER ;first 16 bytes of the rom. (as specified by MSX standard) |
|||
;********************************************************************* |
|||
db "AB" ;id |
|||
dw Main ;init |
|||
dw 0 ;statement |
|||
dw 0 ;device |
|||
dw 0 ;text |
|||
dw 0 ;reserved |
|||
dw 0 ;reserved |
|||
dw 0 ;reserved |
|||
init_tab1 db 0x18 |
|||
dc 7, 0x17 |
|||
db " Conway's Game Of Life " |
|||
dc 8, 0x17 |
|||
db 0x19 |
|||
db 0x16 |
|||
dc 38, " " |
|||
db 0x16, 0x1A |
|||
dc 38, 0x17 |
|||
db 0x1B |
|||
init_tab2 dc 41, 0x80 |
|||
dc 38, 0 |
|||
dc 41, 0x80 |
|||
INITLINES equ 10 |
|||
init_tab3 db " " |
|||
db " X " |
|||
db " X X " |
|||
db " XX XX XX " |
|||
db " X X XX XX " |
|||
db " XX X X XX " |
|||
db " XX X X XX X X " |
|||
db " X X X " |
|||
db " X X " |
|||
db " XX " |
|||
Expand ld bc, 40 |
|||
ldir ;copy top line |
|||
push de |
|||
ld bc, 40 |
|||
ldir ;copy first middle line |
|||
ex (sp), hl |
|||
ld bc, 40*21 |
|||
ldir ;repeat middle line |
|||
pop hl |
|||
ld bc, 40 ;copy bottom line |
|||
ldir |
|||
ret |
|||
InitGOL ;create empty screen |
|||
ld hl, init_tab1 |
|||
ld de, screen |
|||
call Expand |
|||
;Now add intial pattern |
|||
if INITLINES |
|||
ld hl, init_tab3 ;glider gun start pattern |
|||
ld de, init_tab3 ;de=hl, so ldi does not change RAM |
|||
ld bc, 40*INITLINES |
|||
ld ix, screen+40 |
|||
InitGOLl ld a, (hl) |
|||
cp 'X' |
|||
jr nz, InitGOLe |
|||
ld (ix), LIFE_CHAR |
|||
InitGOLe inc ix |
|||
ldi ;hl++ and bc-- (with flag!) |
|||
jp pe, InitGOLl |
|||
else |
|||
ld hl, screen+(4*40)+18 |
|||
ld a, LIFE_CHAR ;blinker start pattern |
|||
ld (hl), a |
|||
inc hl |
|||
ld (hl), a |
|||
inc hl |
|||
ld (hl), a |
|||
endif |
|||
ret |
|||
DoGOL ld hl, init_tab2 ;initialize scratchpad (sums to zero, borders marked) |
|||
ld de, scratch |
|||
call Expand |
|||
;first loop: create sums in scratchpad |
|||
ld bc, 40*24 |
|||
ld hl, screen |
|||
ld de, screen ;de=hl, so ldi does not change RAM |
|||
ld ix, scratch |
|||
DoGOLsuml ld a, (hl) |
|||
cp LIFE_CHAR ;if cell is alive increase sums of all surrounding cells |
|||
jr nz, DoGOLsume |
|||
inc (ix-41) |
|||
inc (ix-40) |
|||
inc (ix-39) |
|||
inc (ix-1) |
|||
inc (ix+1) |
|||
inc (ix+39) |
|||
inc (ix+40) |
|||
inc (ix+41) |
|||
DoGOLsume inc ix |
|||
ldi ;hl++, bc-- (with flag!) |
|||
jp pe, DoGOLsuml |
|||
;second loop: update cell based on current state and sum |
|||
ld bc, 40*24 |
|||
ld hl, screen |
|||
ld de, screen ;de=hl, so ldi does not change RAM |
|||
ld ix, scratch |
|||
DoGOLupdl ld a, (ix) |
|||
cp 0x7f ;border -> keep the same |
|||
jr nc, DoGOLupde |
|||
cp 3 ;3 neighbors-> always live |
|||
jr z, DoGOLlive |
|||
cp 2 |
|||
jr z, DoGOLupde ;2 -> stay the same |
|||
DoGOLdie ld (hl), 0x20 ;1,4,5,6,7,8 -> always die |
|||
jr DoGoLupde |
|||
DoGOLlive ld (hl), LIFE_CHAR |
|||
DoGOLupde inc ix |
|||
ldi ;hl++, bc-- (with flag!) |
|||
jp pe, DoGOLupdl |
|||
ret |
|||
;********************************************************************* |
|||
Main |
|||
;********************************************************************* |
|||
call INITXT |
|||
call InitGOL |
|||
Mainloop ld hl, screen |
|||
ld de, (TXTNAM) |
|||
ld bc, 40*24 |
|||
call LDIRVM |
|||
;call CHGET |
|||
call DoGOL |
|||
jr Mainloop |
|||
;force 16k ROM size binary output |
|||
EndOfCode dc 0x8000 - EndOfCode, 0xFF |
|||
end</syntaxhighlight> |
|||
=={{header|Yabasic}}== |
|||
{{output?|Yabasic}} |
|||
{{trans|BASIC256}} |
|||
<syntaxhighlight lang="yabasic">// Conway's_Game_of_Life |
|||
X = 59 : Y = 35 : H = 4 |
|||
open window X*H,Y*H |
|||
backcolor 0, 0, 0 |
|||
dim c(X,Y) : dim cn(X,Y) : dim cl(X,Y) |
|||
// Thunderbird methuselah |
|||
c(X/2-1,Y/3+1) = 1 : c(X/2,Y/3+1) = 1 : c(X/2+1,Y/3+1) = 1 |
|||
c(X/2,Y/3+3) = 1 : c(X/2,Y/3+4) = 1 : c(X/2,Y/3+5) = 1 |
|||
s = 0 |
|||
repeat |
|||
clear window |
|||
alive = 0 : stable = 1 |
|||
s = s + 1 |
|||
for y = 0 to Y-1 |
|||
for x = 0 to X-1 |
|||
xm1 = mod(x-1+X, X) : xp1 = mod(x+1+X, X) |
|||
ym1 = mod(y-1+Y, Y) : yp1 = mod(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) color 0, 0, 255 // adult |
|||
if not cl(x,y) color 0, 255, 0 // newborn |
|||
else |
|||
if cl(x,y) color 255, 0, 0 // old |
|||
if not cl(x,y) color 255, 255, 0 // shortlived |
|||
end if |
|||
fill rect x*H,y*H,x*H+H,y*H+H |
|||
end if |
|||
next x |
|||
next y |
|||
pause 0.06 |
|||
// Copy arrays |
|||
for i = 0 to X-1 |
|||
for j = 0 to Y-1 |
|||
if cl(i,j)<>cn(i,j) 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" |
|||
clear window |
|||
else |
|||
print "Stabilized in ", s-2, " iterations" |
|||
end if</syntaxhighlight> |
|||
=={{header|zkl}}== |
|||
<syntaxhighlight lang="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(); } } |
|||
}</syntaxhighlight> |
|||
The data structure is a Data, which is a linear block of bytes. |
|||
<syntaxhighlight lang="zkl">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);</syntaxhighlight> |
|||
{{out}} |
|||
Just the glider (reformatted), if you have an ANSI terminal (eg xterm), you'll see the glider moving down the screen. |
|||
<pre> |
|||
-*-- ---- -*-- |
|||
-*-- ***- -*-- |
|||
-*-- ---- -*-- |
|||
---- ---- ---- |
|||
==== ==== ==== |
|||
</pre> |
|||
=={{header|ZPL}}== |
=={{header|ZPL}}== |
||
<syntaxhighlight lang="zpl">program Life; |
|||
<lang ZPL> |
|||
program Life; |
|||
config var |
config var |
||
Line 2,873: | Line 18,466: | ||
TW := (TW & NN = 2) | ( NN = 3); |
TW := (TW & NN = 2) | ( NN = 3); |
||
until !(|<< TW); |
until !(|<< TW); |
||
end;</syntaxhighlight> |
|||
end; |
|||
</lang> |
|||
=={{header|ZX Spectrum Basic}}== |
|||
Play on emulator at full speed for reasonable performance. |
|||
The ZX Spectrum was shipped with a demo tape called ''Horizons'', which included an implementation of Life; however, it relied on machine code. |
|||
<syntaxhighlight lang="zxbasic">10 REM Initialize |
|||
20 LET w=32*22 |
|||
30 DIM w$(w): DIM n$(w) |
|||
40 FOR n=1 TO 100 |
|||
50 LET w$(RND*w+1)="O" |
|||
60 NEXT n |
|||
70 REM Loop |
|||
80 FOR i=34 TO w-34 |
|||
90 LET p$="": LET d=0 |
|||
100 LET p$=p$+w$(i-1)+w$(i+1)+w$(i-33)+w$(i-32)+w$(i-31)+w$(i+31)+w$(i+32)+w$(i+33) |
|||
110 LET n$(i)=w$(i) |
|||
120 FOR n=1 TO LEN p$ |
|||
130 IF p$(n)="O" THEN LET d=d+1 |
|||
140 NEXT n |
|||
150 IF (w$(i)=" ") AND (d=3) THEN LET n$(i)="O": GO TO 180 |
|||
160 IF (w$(i)="O") AND (d<2) THEN LET n$(i)=" ": GO TO 180 |
|||
170 IF (w$(i)="O") AND (d>3) THEN LET n$(i)=" " |
|||
180 NEXT i |
|||
190 PRINT AT 0,0;w$ |
|||
200 LET w$=n$ |
|||
210 GO TO 80</syntaxhighlight> |
Latest revision as of 19:42, 22 July 2024
![Task](http://static.miraheze.org/rosettacodewiki/thumb/b/ba/Rcode-button-task-crushed.png/64px-Rcode-button-task-crushed.png)
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 (or m×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.
- Task
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
- Its creator John Conway, explains the game of life. Video from numberphile on youtube.
- John Conway Inventing Game of Life - Numberphile video.
- Related task
- Langton's ant - another well known cellular automaton.
11l
V cellcountx = 6
V cellcounty = 5
V celltable = [(1, 2) = 1,
(1, 3) = 1,
(0, 3) = 1]
DefaultDict[(Int, Int), Int] universe
universe[(1, 1)] = 1
universe[(2, 1)] = 1
universe[(3, 1)] = 1
universe[(1, 4)] = 1
universe[(2, 4)] = 1
universe[(3, 4)] = 1
L(i) 4
print("\nGeneration "i‘:’)
L(row) 0 .< cellcounty
print(‘ ’(0 .< cellcountx).map(col -> [‘. ’, ‘O ’][:universe[(@row, col)]]).join(‘’))
DefaultDict[(Int, Int), Int] nextgeneration
L(row) 0 .< cellcounty
L(col) 0 .< cellcountx
nextgeneration[(row, col)] = celltable.get(
(universe[(row, col)],
-universe[(row, col)] + sum(multiloop(row-1..row+1,
col-1..col+1, (r, c) -> :universe[(r, c)]))
), 0)
universe = nextgeneration
More optimal solution
V cellcountx = 6
V cellcounty = 5
V universe = [[0B] * cellcountx] * cellcounty
universe[1][1] = 1B
universe[2][1] = 1B
universe[3][1] = 1B
universe[1][4] = 1B
universe[2][4] = 1B
universe[3][4] = 1B
V nextgeneration = [[0B] * cellcountx] * cellcounty
L(i) 4
print("\nGeneration "i‘:’)
L(row) 0 .< cellcounty
print(‘ ’, end' ‘’)
L(col) 0 .< cellcountx
print(I universe[row][col] {‘O ’} E ‘. ’, end' ‘’)
print()
L(row) 0 .< cellcounty
L(col) 0 .< cellcountx
V s = 0
I row > 0
s = universe[row-1][col]
I col > 0
s += universe[row-1][col-1]
I col < cellcountx-1
s += universe[row-1][col+1]
I col > 0
s += universe[row][col-1]
I col < cellcountx-1
s += universe[row][col+1]
I row < cellcounty-1
s += universe[row+1][col]
I col > 0
s += universe[row+1][col-1]
I col < cellcountx-1
s += universe[row+1][col+1]
nextgeneration[row][col] = I universe[row][col] {s C 2..3} E s == 3
universe = nextgeneration
- Output:
Generation 0: . . . . . . . O . . O . . O . . O . . O . . O . . . . . . . Generation 1: . . . . . . . . . . . . O O O O O O . . . . . . . . . . . . Generation 2: . . . . . . . O O O O . . O O O O . . O O O O . . . . . . . Generation 3: . . O O . . . O . . O . O . . . . O . O . . O . . . O O . .
6502 Assembly
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
68000 Assembly
I went a little further and created a 40x30 grid, but this implementation is accurate and does have a blinker in it. As always, thanks to Keith of Chibiakumas for the cartridge header and hardware routines. This is the source code for a Sega Genesis game that you can compile with VASM. (It was tested in the Fusion emulator but it should work anywhere.)
Explanation of the implementation:
- I'm using the Genesis's foreground tilemap to create the graphics.
- Grid boundaries are handled by using the classic trick of padding all sides with a value that's not used in the "real" grid, and always counts as a dead cell.
- Every iteration of the main loop does the same thing: check neighbors of each cell in the grid, write the next generation to a buffer, copy that buffer over the original, and display the output. This way, updates don't impact the outcome of the rest of the cells down the line (which would go against the rules of the game, as all cell births/deaths happen simultaneously.)
Explanation of macros:
- pushRegs = MOVEM.L ___,-(SP)
- popRegs = MOVEM.L (SP)+,___
- pushLong = MOVE.L ___,-(SP)
- popLong = MOVE.L (SP)+,___
include "\SrcALL\68000_Macros.asm"
;Ram Variables
Cursor_X equ $00FF0000 ;Ram for Cursor Xpos
Cursor_Y equ $00FF0000+1 ;Ram for Cursor Ypos
conwayTilemapRam equ $00FF1000
conwayBackupTilemapRam equ $00FF2000
;Video Ports
VDP_data EQU $C00000 ; VDP data, R/W word or longword access only
VDP_ctrl EQU $C00004 ; VDP control, word or longword writes only
;constants
ROWLENGTH equ 40
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Traps
DC.L $FFFFFE00 ;SP register value
DC.L ProgramStart ;Start of Program Code
DS.L 7,IntReturn ; bus err,addr err,illegal inst,divzero,CHK,TRAPV,priv viol
DC.L IntReturn ; TRACE
DC.L IntReturn ; Line A (1010) emulator
DC.L IntReturn ; Line F (1111) emulator
DS.L 4,IntReturn ; Reserverd /Coprocessor/Format err/ Uninit Interrupt
DS.L 8,IntReturn ; Reserved
DC.L IntReturn ; spurious interrupt
DC.L IntReturn ; IRQ level 1
DC.L IntReturn ; IRQ level 2 EXT
DC.L IntReturn ; IRQ level 3
DC.L IntReturn ; IRQ level 4 Hsync
DC.L IntReturn ; IRQ level 5
DC.L IntReturn ; IRQ level 6 Vsync
DC.L IntReturn ; IRQ level 7
DS.L 16,IntReturn ; TRAPs
DS.L 16,IntReturn ; Misc (FP/MMU)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Header
DC.B "SEGA GENESIS " ;System Name
DC.B "(C)CHBI " ;Copyright
DC.B "2019.JAN" ;Date
DC.B "ChibiAkumas.com " ; Cart Name
DC.B "ChibiAkumas.com " ; Cart Name (Alt)
DC.B "GM CHIBI001-00" ;TT NNNNNNNN-RR T=Type (GM=Game) N=game Num R=Revision
DC.W $0000 ;16-bit Checksum (Address $000200+)
DC.B "J " ;Control Data (J=3button K=Keyboard 6=6button C=cdrom)
DC.L $00000000 ;ROM Start
DC.L $003FFFFF ;ROM Length
DC.L $00FF0000,$00FFFFFF ;RAM start/end (fixed)
DC.B " " ;External RAM Data
DC.B " " ;Modem Data
DC.B " " ;MEMO
DC.B "JUE " ;Regions Allowed
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Generic Interrupt Handler
IntReturn:
rte
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Program Start
ProgramStart:
;initialize TMSS (TradeMark Security System)
move.b ($A10001),D0 ;A10001 test the hardware version
and.b #$0F,D0
beq NoTmss ;branch if no TMSS chip
move.l #'SEGA',($A14000);A14000 disable TMSS
NoTmss:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Set Up Graphics
lea VDPSettings,A5 ;Initialize Screen Registers
move.l #VDPSettingsEnd-VDPSettings,D1 ;length of Settings
move.w (VDP_ctrl),D0 ;C00004 read VDP status (interrupt acknowledge?)
move.l #$00008000,d5 ;VDP Reg command (%8rvv)
NextInitByte:
move.b (A5)+,D5 ;get next video control byte
move.w D5,(VDP_ctrl) ;C00004 send write register command to VDP
; 8RVV - R=Reg V=Value
add.w #$0100,D5 ;point to next VDP register
dbra D1,NextInitByte ;loop for rest of block
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Set up palette and graphics
move.l #$C0000000,d0 ;Color 0
move.l d0,VDP_Ctrl
MOVE.W #$0A00,VDP_Data ;BLUE
move.l #$C01E0000,d0 ;Color 0
move.l d0,VDP_Ctrl
MOVE.W #$00EE,VDP_Data ;YELLOW
lea Graphics,a0 ;background tiles
move.w #(GraphicsEnd-Graphics)-1,d1 ;data size
CLR.L D2 ;start loading at tile 0 of VRAM
jsr DefineTiles
clr.b Cursor_X ;Clear Cursor XY
clr.b Cursor_Y
;Turn on screen
move.w #$8144,(VDP_Ctrl);C00004 reg 1 = 0x44 unblank display
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; TESTING AREA
;load the tilemaps
LEA Conway_Tilemap,a3
LEA ConwayTilemapRam,A2
move.l #(Conway_Backup_Tilemap-Conway_Tilemap),d1
JSR LDIR
LEA Conway_Backup_Tilemap,a3
LEA conwayBackupTilemapRam,A2
move.l #(Conway_Backup_Tilemap-Conway_Tilemap),d1
JSR LDIR
CLR.B Cursor_X
CLR.B Cursor_Y
;print the tilemap once
LEA ConwayTilemapRam,A3
JSR Conway_Print
main:
;do the behavior
jsr Conway_CheckTilemap
;copy the tilemap
lea conwayBackupTilemapRam,A3
LEA ConwayTilemapRam,A2
move.l #(Conway_Backup_Tilemap-Conway_Tilemap),d1
jsr LDIR
CLR.B Cursor_X
CLR.B Cursor_Y
;print the tilemap
LEA ConwayTilemapRam,A3
JSR Conway_Print
;repeat
JMP main
Conway_CheckTilemap:
;since we don't want the partial results of the checks to mess with later ones, we'll copy the changes to a buffer, and then
;have that buffer clobber the original. That way, all changes to the ecosystem are effectively atomic.
LEA ConwayTilemapRam,A0
LEA conwayBackupTilemapRam,A6
LEA (ROWLENGTH+3,A0),A0 ;actual data starts here
LEA (ROWLENGTH+3,A6),A6 ;actual data starts here
.loop:
MOVE.B (A0),D2
CMP.B #255,D2
BEQ .done
CMP.B #2,D2
BEQ .overhead
JSR Conway_CheckNeighbors
pushLong D0
JSR popcnt_8
MOVE.B D0,D3
popLong D0
;now implement the table here.
;neighbor bits in D0
;current state in D2
;popcnt in D3
;pointer to where we are in A0
;the only time the current state is relevant to the output, is when popcnt = 2. Otherwise, it's entirely determined by popcnt.
CMP.B #4,D3
BCC .DeadCell
CMP.B #1,D3
BLS .DeadCell
CMP.B #3,D3
BEQ .LiveCell
;else, must be 2.
MOVE.B D2,(A6) ;store current value into backup table.
bra .overhead
.LiveCell:
MOVE.B #1,(A6)
BRA .overhead
.DeadCell:
MOVE.B #0,(A6) ;store in backup table.
.overhead:
ADDA.L #1,A0
ADDA.L #1,A6
JMP .loop
.done:
RTS
popcnt_8:
pushRegs D2-D5
MOVEQ #8-1,D5
MOVEQ #0,D3
.loop:
ROR.B #1,D0
BCC .overhead
ADDQ.B #1,D3
.overhead:
dbra d5,.loop
MOVE.B D3,D0
popRegs D2-D5
RTS
Conway_CheckNeighbors:
;A0 = pointer to where we are in the tilemap.
;returns: D0.B = uUrRdDlL
;u = top left
;U = top
;r = top Right
;R = Right
;d = bottom right
;D = bottom
;l = bottom Left
;L = Left
pushRegs D2-D5/A0
MOVEQ #0,D0
MOVE.L A0,A1
MOVE.L A1,A2
SUBA.L #ROWLENGTH+2,A1 ;POINTS ABOVE
ADDA.L #ROWLENGTH+2,A2 ;POINTS BELOW
MOVE.B (A1),D1
MOVE.B (A2),D2
CMP.B #1,D1
BNE .noUpper
BSET #6,D0
bra .next
.noUpper:
BCLR #6,D0
.next:
CheckLower:
CMP.B #1,D2
BNE .noLower
BSET #2,D0
bra .next
.noLower:
BCLR #2,D0
.next:
SUBA.L #1,A0 ;left
SUBA.L #1,A1 ;upper-left
SUBA.L #1,A2 ;lower-left
MOVE.B (A1),D1
MOVE.B (A2),D2
MOVE.B (A0),D3
CheckUpperLeft:
CMP.B #1,D1
BNE .noUpperLeft
BSET #7,D0
bra .next
.noUpperLeft:
BCLR #7,D0
.next:
CheckLowerLeft:
CMP.B #1,D2
BNE .noLowerLeft
BSET #1,D0
bra .next
.noLowerLeft:
BCLR #1,D0
.next:
CheckLeft:
CMP.B #1,D3
BNE .noLeft
BSET #0,D0
bra .next
.noLeft:
BCLR #0,D0
.next:
addA.L #2,A0
addA.L #2,A1
addA.L #2,A2
MOVE.B (A1),D1
MOVE.B (A2),D2
MOVE.B (A0),D3
CheckUpperRight:
CMP.B #1,D1
BNE .noUpperRight
BSET #5,D0
bra .next
.noUpperRight:
BCLR #5,D0
.next:
CheckLowerRight:
CMP.B #1,D2
BNE .noLowerRight
BSET #3,D0
bra .next
.noLowerRight:
BCLR #3,D0
.next:
CheckRight:
CMP.B #1,D3
BNE .noRight
BSET #4,D0
bra .next
.noRight:
BCLR #4,D0
.next:
popRegs D2-D5/A0
rts
Conway_Print:
;input: A3 = base address of tilemap
MOVE.B (A3)+,D0
CMP.B #255,D0
BEQ .done
CMP.B #2,D0
BEQ .skip
;else, print
JSR Conway_PrintChar
.skip
BRA Conway_Print
.done
rts
Conway_PrintChar: ;Show D0 to screen
moveM.l d0-d7/a0-a7,-(sp)
and.l #$FF,d0 ;Keep only 1 byte
Move.L #$40000003,d5 ;top 4=write, bottom $3=Cxxx range
MOVEQ #0,D4 ;Tilemap at $C000+
Move.B (Cursor_Y),D4
rol.L #8,D4 ;move $-FFF to $-FFF----
rol.L #8,D4
rol.L #7,D4 ;2 bytes per tile * 64 tiles per line
add.L D4,D5 ;add $4------3
Move.B (Cursor_X),D4
rol.L #8,D4 ;move $-FFF to $-FFF----
rol.L #8,D4
rol.L #1,D4 ;2 bytes per tile
add.L D4,D5 ;add $4------3
MOVE.L D5,(VDP_ctrl) ; C00004 write next character to VDP
MOVE.W D0,(VDP_data) ; C00000 store next word of name data
addq.b #1,(Cursor_X) ;INC Xpos
move.b (Cursor_X),d0
cmp.b #39,d0
bls .nextpixel_Xok
jsr NewLine ;If we're at end of line, start newline
.nextpixel_Xok:
moveM.l (sp)+,d0-d7/a0-a7
rts
;don't forget, these are in ROM so we can't write to them directly.
;instead, they will be copied to ram and worked with from there.
Conway_Tilemap: ;(screenwidth + 2) by (screenheight+2)
DC.B $02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02
DC.B $02,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$02
DC.B $02,$00,$00,$01,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$01,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$02
DC.B $02,$00,$00,$00,$01,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$01,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$02
DC.B $02,$00,$01,$01,$01,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$01,$01,$01,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$02
DC.B $02,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$02
DC.B $02,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$02
DC.B $02,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$02
DC.B $02,$00,$00,$00,$00,$00,$00,$00,$00,$00,$01,$01,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$02
DC.B $02,$00,$00,$00,$00,$00,$00,$00,$00,$01,$00,$00,$01,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$02
DC.B $02,$00,$01,$01,$00,$00,$00,$00,$00,$00,$01,$01,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$02
DC.B $02,$00,$01,$01,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$02
DC.B $02,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$02
DC.B $02,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$02
DC.B $02,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$02
DC.B $02,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$02
DC.B $02,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$02
DC.B $02,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$02
DC.B $02,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$02
DC.B $02,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$02
DC.B $02,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$02
DC.B $02,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$02
DC.B $02,$00,$00,$00,$00,$01,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$02
DC.B $02,$00,$00,$00,$00,$01,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$02
DC.B $02,$00,$00,$00,$00,$01,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$02
DC.B $02,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$02
DC.B $02,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$02
DC.B $02,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$02
DC.B $02,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$02
DC.B $02,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$02
DC.B $02,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$02
DC.B $02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02
DC.B 255
EVEN
Conway_Backup_Tilemap: ;(screenwidth + 2) by (screenheight+2)
DC.B $02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02
rept 30
DC.B $02,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$02
endr
DC.B $02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02
DC.B 255
EVEN
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
NewLine:
addq.b #1,(Cursor_Y) ;INC Y
clr.b (Cursor_X) ;Zero X
rts
LDIR: ;COPY D1 BYTES FROM A3 TO A2
move.b (a3)+,(a2)+
DBRA D1,LDIR
rts
DefineTiles: ;Copy D1 bytes of data from A0 to VDP memory D2
jsr prepareVram ;Calculate the memory location we want to write
.again: ; the tile pattern definitions to
move.l (a0)+,(VDP_data)
dbra d1,.again
rts
prepareVram: ;To select a memory location D2 we need to calculate
;the command byte... depending on the memory location
pushall ;$7FFF0003 = Vram $FFFF.... $40000000=Vram $0000
move.l d2,d0
and.w #%1100000000000000,d0 ;Shift the top two bits to the far right
rol.w #2,d0
and.l #%0011111111111111,d2 ; shift all the other bits left two bytes
rol.l #8,d2
rol.l #8,d2
or.l d0,d2
or.l #$40000000,d2 ;Set the second bit from the top to 1
;#%01000000 00000000 00000000 00000000
move.l d2,(VDP_ctrl)
popallRTS:
popall
rts
Graphics:
DC.L $0000000F,$0000000F,$0000000F,$0000000F,$0000000F,$0000000F,$0000000F,$FFFFFFFF
DC.L $FFFFFFFF,$FFFFFFFF,$FFFFFFFF,$FFFFFFFF,$FFFFFFFF,$FFFFFFFF,$FFFFFFFF,$FFFFFFFF
GraphicsEnd:
VDPSettings:
DC.B $04 ; 0 mode register 1 ---H-1M-
DC.B $04 ; 1 mode register 2 -DVdP---
DC.B $30 ; 2 name table base for scroll A (A=top 3 bits) --AAA--- = $C000
DC.B $3C ; 3 name table base for window (A=top 4 bits / 5 in H40 Mode) --AAAAA- = $F000
DC.B $07 ; 4 name table base for scroll B (A=top 3 bits) -----AAA = $E000
DC.B $6C ; 5 sprite attribute table base (A=top 7 bits / 6 in H40) -AAAAAAA = $D800
DC.B $00 ; 6 unused register --------
DC.B $00 ; 7 background color (P=Palette C=Color) --PPCCCC
DC.B $00 ; 8 unused register --------
DC.B $00 ; 9 unused register --------
DC.B $FF ;10 H interrupt register (L=Number of lines) LLLLLLLL
DC.B $00 ;11 mode register 3 ----IVHL
DC.B $81 ;12 mode register 4 (C bits both1 = H40 Cell) C---SIIC
DC.B $37 ;13 H scroll table base (A=Top 6 bits) --AAAAAA = $FC00
DC.B $00 ;14 unused register --------
DC.B $02 ;15 auto increment (After each Read/Write) NNNNNNNN
DC.B $01 ;16 scroll size (Horiz & Vert size of ScrollA & B) --VV--HH = 64x32 tiles
DC.B $00 ;17 window H position (D=Direction C=Cells) D--CCCCC
DC.B $00 ;18 window V position (D=Direction C=Cells) D--CCCCC
DC.B $FF ;19 DMA length count low LLLLLLLL
DC.B $FF ;20 DMA length count high HHHHHHHH
DC.B $00 ;21 DMA source address low LLLLLLLL
DC.B $00 ;22 DMA source address mid MMMMMMMM
DC.B $80 ;23 DMA source address high (C=CMD) CCHHHHHH
VDPSettingsEnd:
even
- Output:
ABAP
For the proposed task use 10,9;10,10;10,11 on screen field P_POS. Can be left blank for random filling
*&---------------------------------------------------------------------*
*& Report ZCONWAYS_GAME_OF_LIFE
*&---------------------------------------------------------------------*
*& by Marcelo Bovo
*&---------------------------------------------------------------------*
report zconways_game_of_life line-size 174 no standard page heading
line-count 40.
parameters: p_char type c default '#',
p_pos type string.
class lcl_game_of_life definition.
public section.
data: x_max type i value 174,
y_max type i value 40,
character type c value abap_true,
x type i,
y type i,
pos type string,
offlimits type i value 9998,
grid(9999) type c. " every X_MAX characters on grid equals one line on screen
data: o_random_y type ref to cl_abap_random_int,
o_random_x type ref to cl_abap_random_int.
methods: constructor,
play,
initial_position,
initial_grid,
write,
change_grid.
endclass.
class lcl_game_of_life implementation.
method constructor.
o_random_y = cl_abap_random_int=>create( seed = cl_abap_random=>create( conv i( sy-uzeit ) )->intinrange( low = 1 high = 999999 )
min = 1
max = y_max ).
o_random_x = cl_abap_random_int=>create( seed = cl_abap_random=>create( conv i( |{ sy-datum(4) }{ sy-uzeit }| ) )->intinrange( low = 1 high = 999999 )
min = 1
max = x_max ).
endmethod.
method play.
"fill initial data ramdonly if user hasnt typed any coordenates
if pos is initial.
initial_position( ).
endif.
initial_grid( ).
write( ).
endmethod.
method initial_position.
do cl_abap_random_int=>create( "seed = conv i( sy-uzeit )
min = 50
max = 800 )->get_next( ) times.
data(lv_index) = sy-index.
x = o_random_x->get_next( ).
y = o_random_y->get_next( ).
p_pos = |{ p_pos }{ switch char1( lv_index when '1' then space else ';' ) }{ y },{ x }|.
enddo.
endmethod.
method initial_grid.
"Split coordenates
split p_pos at ';' into table data(lt_pos_inicial) .
"Sort By Line(Easy to read)
sort lt_pos_inicial.
loop at lt_pos_inicial assigning field-symbol(<pos_inicial>).
split <pos_inicial> at ',' into data(y_char) data(x_char).
x = x_char.
y = y_char.
"Ensure maximum coordenates are not surpassed
x = cond #( when x <= x_max then x
else o_random_x->get_next( ) ).
y = cond #( when y <= y_max then y
else o_random_y->get_next( ) ).
"Write on string grid
"Every x_max lines represent one line(Y) on the grid
data(grid_xy) = ( x_max * y ) + x - x_max - 1.
grid+grid_xy(1) = character.
endloop.
endmethod.
method write.
skip to line 1.
"Write every line on screen
do y_max times.
data(lv_index) = sy-index - 1.
"Write whole line(current line plus number of maximum X characters)
data(grid_xy) = ( lv_index * x_max ).
write / grid+grid_xy(x_max) no-gap.
if grid+grid_xy(x_max) is initial.
skip.
endif.
enddo.
change_grid( ).
endmethod.
method change_grid.
data(grid_aux) = grid.
clear grid_aux.
data(grid_size) = strlen( grid ).
"Validate neighbours based on previous written grid
"ABC
"D.F
"GHI
do grid_size + x_max times.
"Doens't write anything beyond borders
check sy-index <= x_max * y_max.
data(grid_xy) = sy-index - 1.
data(neighbours) = 0.
"Current Line neighbours
data(d) = grid_xy - 1.
data(f) = grid_xy + 1.
"Line above neighbours
data(b) = grid_xy - x_max.
data(a) = b - 1.
data(c) = b + 1.
"Line Bellow neighbours
data(h) = grid_xy + x_max.
data(g) = h - 1.
data(i) = h + 1.
"Previous Neighbours
neighbours += cond #( when a < 0 then 0 else cond #( when grid+a(1) is not initial then 1 else 0 ) ).
neighbours += cond #( when b < 0 then 0 else cond #( when grid+b(1) is not initial then 1 else 0 ) ).
neighbours += cond #( when c < 0 then 0 else cond #( when grid+c(1) is not initial then 1 else 0 ) ).
neighbours += cond #( when d < 0 then 0 else cond #( when grid+d(1) is not initial then 1 else 0 ) ).
"Next Neighbours
neighbours += cond #( when f > grid_size then 0 else cond #( when grid+f(1) is not initial then 1 else 0 ) ).
neighbours += cond #( when g > grid_size then 0 else cond #( when grid+g(1) is not initial then 1 else 0 ) ).
neighbours += cond #( when h > grid_size then 0 else cond #( when grid+h(1) is not initial then 1 else 0 ) ).
neighbours += cond #( when i > grid_size then 0 else cond #( when grid+i(1) is not initial then 1 else 0 ) ).
grid_aux+grid_xy(1) = cond #( when neighbours = 3 or ( neighbours = 2 and grid+grid_xy(1) = character )
then character
else space ).
enddo.
grid = grid_aux.
endmethod.
endclass.
start-of-selection.
set pf-status 'STANDARD_FULLSCREEN'. "Use &REFRESH button with F8 Shortcut to go to next generation
data(lo_prog) = new lcl_game_of_life( ).
lo_prog->character = p_char.
lo_prog->pos = p_pos.
lo_prog->play( ).
at user-command.
case sy-ucomm.
when 'REFR' or '&REFRESH'.
sy-lsind = 1. "Prevents LIST_TOO_MANY_LEVELS DUMP
lo_prog->write( ).
when others.
leave list-processing.
endcase.
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:
+------+ | [] | | [] | | [] | +------+ +------+ | | |[][][]| | | +------+ +------+ | [] | | [] | | [] | +------+
Ada
WITH Ada.Text_IO; USE Ada.Text_IO;
PROCEDURE Life IS
SUBTYPE Cell IS Natural RANGE 0 .. 1;
TYPE Petri_Dish IS ARRAY (Positive RANGE <>, Positive RANGE <>) OF Cell;
PROCEDURE Step (Gen : IN OUT Petri_Dish) IS
Above : ARRAY (Gen'Range (2)) OF Cell := (OTHERS => 0);
Left, This : Cell;
BEGIN
FOR I IN Gen'First (1) + 1 .. Gen'Last (1) - 1 LOOP
Left := 0;
FOR J IN Gen'First (2) + 1 .. Gen'Last (2) - 1 LOOP
This := (CASE Above (J - 1) + Above (J) + Above (J + 1) +
Left + Gen (I, J + 1) +
Gen (I + 1, J - 1) + Gen (I + 1, J) + Gen (I + 1, J + 1) IS
WHEN 2 => Gen (I, J),
WHEN 3 => 1,
WHEN OTHERS => 0);
Above (J - 1):= Left;
Left := Gen (I, J);
Gen (I, J) := This;
END LOOP;
Above (Above'Last - 1) := Left;
END LOOP;
END Step;
PROCEDURE Put (Gen : Petri_Dish) IS
BEGIN
FOR I IN Gen'Range (1) LOOP
FOR J IN Gen'Range (2) LOOP
Put ( if Gen (I, J) = 0 then " " else "#");
END LOOP;
New_Line;
END LOOP;
END Put;
Blinker : Petri_Dish := (2 .. 4 => (0, 0, 1, 0, 0), 1 | 5 => (0, 0, 0, 0, 0));
Glider : Petri_Dish (1..6,1..11):= (2 => (3 => 1, others => 0),
3 => (4 => 1, others => 0),
4 => (2|3|4=>1, others => 0),
others => (others => 0));
PROCEDURE Put_And_Step_Generation (N : Positive; Name : String; P : IN OUT Petri_Dish) IS
BEGIN
FOR Generation IN 1 .. N LOOP
Put_Line (Name & Generation'Img);
Put (P);
Step (P);
END LOOP;
END Put_And_Step_Generation;
BEGIN
Put_And_Step_Generation (3, "Blinker", Blinker);
Put_And_Step_Generation (5, "Glider", Glider);
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:
- Output:
Blinker 1 # # # Blinker 2 ### Blinker 3 # # # Glider 1 # # ### Glider 2 # # ## # Glider 3 # # # ## Glider 4 # ## ## Glider 5 # # ###
ALGOL 68
See Conway's Game of Life/ALGOL 68
Amazing Hopper
El programa genera el algoritmo en modo texto.
Adaptado del programa "Conway's Game of Life" de Vishal Nagda, en Hithub
https://github.com/vishalnagda1/CONWAYs-Game-of-Life-C/blob/master/game_of_life.c
#include <jambo.h>
#define SIZER 90
#define SIZEC 120
Main
Dim (SIZER, SIZEC) as zeros 'grid, neighbour_count'
Dim (SIZER, SIZEC) as fill '" ",disp grid'
c=0, Let( c := Utf8(Chr(254)))
moi=0, moj=0, poi=0, poj=0
m={}
Set ' 0,1,1 ' Apnd row 'm'
Set ' 1,1,0 ' Apnd row 'm'
Set ' 0,1,0 ' Apnd row 'm'
[44:46, 59:61] Set 'm', Put 'grid'
Clr all marks
i=0, j=0
Tok sep '""'
Locate (1,1)
Loop
i=1
Iterator ( ++i, Less equal(i,SIZER),\
j=1, Iterator ( ++j, Less equal(j,SIZEC), \
Set 'i,j', Gosub 'Count NBR', [i,j], Put 'neighbour_count' ) )
i=1
Loop if( Less equal(i,SIZER) )
j=1
Loop if( Less equal(j,SIZEC) )
[i,j]
If ( Get 'grid' )
Get 'neighbour_count'
When ( Out of range including '1,4' ) {
Set '0," "', Put 'disp grid', Put 'grid'
}
Else
Get 'neighbour_count'
When ( Is equal to '3' ) {
Set '1,c', Put 'disp grid', Put 'grid'
}
End If
++j
Back
++i
Back
Clr all marks
Print table 'disp grid'
Break if ( Key pressed )
Back
Pause
End
Subrutines
Define 'Count NBR, i, j'
n_count = 0
When ( Must be( Minus one 'i' ---Backup to 'moi'---, Minus one 'j' ---Backup to 'moj'--- ) ) {
When ( [moi,moj]Get 'grid' ) {
++n_count
}
}
Plus one 'j', Move to 'poj'
When ( moi ) {
When ( [ moi, j ] Get 'grid' ) {
++n_count
}
When ( Less equal( poj, SIZEC )) {
When ( [ moi, poj] Get 'grid' ) {
++n_count
}
}
}
When ( moj ) {
When( [i, moj] Get 'grid' ) {
++n_count
}
}
When ( Less equal ( poj, SIZEC ) ){
When( [i, poj] Get 'grid' ) {
++n_count
}
}
When ( Less equal (Plus one 'i' ---Backup to 'poi'---, SIZER ) ) {
When ( [ poi, j] Get 'grid' ) {
++n_count
}
When ( Less equal ( poj, SIZEC) ) {
When ( [poi, poj] Get 'grid' ) {
++n_count
}
}
When ( moj ){
When ([poi, moj] Get 'grid' ){
++n_count
}
}
}
Return 'n_count'
- Output:
La llamada se realiza desde terminal, con el programa "rxvt" de Linux, para adaptar la terminal a los pixeles y dimensiones adecuados. Llamada: rxvt -g 270x100 -fn "xft:FantasqueSansMono-Regular:pixelsize=3" -e hopper jm/gamelife.jambo
Version 2
Me di cuenta de que la subrutina "Count NBR" era un poco lenta, y además, una función que cuente los vecinos en un radio dado, me es útil para hacer juegos, por lo que incluí esa función dentro de las funciones de HOPPER.
Además, reescribí el programa "a la Hopper", como debió ser desde un principio.
#include <jambo.h>
#define SIZER 90
#define SIZEC 120
Main
Cls
Hide cursor
Dim (SIZER, SIZEC), as zeros 'grid, neighbour_count'
Dim (SIZER, SIZEC), as fill '" ",disp grid'
c=0 , Let( c := Utf8(Chr(254)))
m={}
Set ' 0,1,1 ' Apend row to 'm'
Set ' 1,1,0 ' Apend row to 'm'
Set ' 0,1,0 ' Apend row to 'm'
[44:46, 59:61] Set 'm', Put 'grid'
Clr all marks
radio=1, r=0
Tok sep '""'
Locate (1,1)
Loop
i=1
Iterator ( ++i, Less equal(i,SIZER),\
j=1, Iterator ( ++j, Less equal(j,SIZEC), \
[i,j], Neighbour count (grid,radio), Put 'neighbour_count' ) )
Cartesian ( Greater equal(grid, 1)---Back up to 'r'--- Mul by 'neighbour_count';\
Out of range including '1,4' )
Get range, Set '0," "', Put 'disp grid', Put 'grid', Forget
Cartesian ( Not( r ); Mul by 'neighbour_count'; Is equal to '3' )
Get range, Set '1,c', Put 'disp grid', Put 'grid', Forget
Clr range
Clr all marks
Print table 'disp grid'
Break if ( Key pressed )
Back
Pause
Show cursor
End
- Output:
La salida es la misma, pero ahora va mucho más rápido... parecen estrellitas explotando :D
APL
GNU APL (from Wikipedia: https://aplwiki.com/wiki/John_Scholes%27_Conway%27s_Game_of_Life#Translations)
Life←{↑↑1 ⍵∨.∧3 4=+/,¯1 0 1∘.⊖¯1 0 1∘.⌽⊂⍵}
m ← 5 5⍴(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)
m
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 m
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
Life Life m
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
APL2 (Dyalog) Example in one line
APL \ 1130 example (very old APL dialect via simulator)
https://web.archive.org/web/20160313204013/http://www.farnik.com/wiki/uploads/lifeAPL1130.jpg
From: APL \ 1130 Samples
The following APL \ 1130 code will need APL385 font installed to display correctly.
See Download APL TT Font
∇LIFE[⎕]∇
[0] NG←LIFE CG;W
[1] W←CG+(¯1⊖CG)+(1⊖CG)+(¯1⌽CG)+(1⌽CG)
[2] W←W+(1⊖1⌽CG)+(¯1⊖1⌽CG)+(1⊖¯1⌽CG)+(¯1⊖¯1⌽CG)
[3] NG←(3=W)+(CG∧4=W)
∇
RP←5 5⍴0 0 0 0 0 0 0 1 1 0 0 1 1 0 0 0 0 1 0 0
RP
0 0 0 0 0
0 0 1 1 0
0 1 1 0 0
0 0 1 0 0
0 0 0 0 0
LIFE RP
0 0 0 0 0
0 1 1 1 0
0 1 0 0 0
0 1 1 0 0
0 0 0 0 0
LIFE LIFE RP
0 0 1 0 0
0 1 1 0 0
1 0 0 1 0
0 1 1 0 0
0 0 0 0 0
AppleScript
This handler creates and returns a "universe" script object initialised with given "seed" text and dimensions. For convenience, the seed text's visible characters can be anything, the set-up code itself replacing them with "■" characters. The use of the returned universe is demo'd later.
use AppleScript version "2.4" -- OS X 10.10 (Yosemite) or later
use framework "Foundation" -- For the regex at the top of newUniverse()
use scripting additions
-- The characters to represent the live and dead cells.
property live : "■" -- character id 9632 (U+25A0).
property dead : space
-- Infinite universes are expensive to maintain, so only a local region of universe is represented here.
-- Its invisible border is a wall of "dead" cells one cell deep, lined with a two-cell buffer layer into which
-- objects nominally leaving the region can disappear without being seen to hit the wall or bouncing back.
property borderThickness : 3
on newUniverse(seed, {w, h})
-- Replace every visible character in the seed text with "■" and every horizontal space with a space.
set seed to current application's class "NSMutableString"'s stringWithString:(seed)
set regex to current application's NSRegularExpressionSearch
tell seed to replaceOccurrencesOfString:("\\S") withString:(live) options:(regex) range:({0, its |length|()})
tell seed to replaceOccurrencesOfString:("\\h") withString:(dead) options:(regex) range:({0, its |length|()})
-- Ensure the universe dimensions are at least equal to the number of lines and the length of the longest.
set seedLines to paragraphs of (seed as text)
set lineCount to (count seedLines)
if (lineCount > h) then set h to lineCount
set seedWidth to 0
repeat with thisLine in seedLines
set lineLength to (count thisLine)
if (lineLength > seedWidth) then set seedWidth to lineLength
end repeat
if (seedWidth > w) then set w to seedWidth
-- Get a new universe.
script universe
-- State lists. These will contain or be lists of 0s and 1s and will include the border cells.
property newState : {}
property previousState : {}
property currentRow : {}
property rowAbove : {}
property rowBelow : {}
property replacementRow : {}
-- Equivalent text lists. These will only cover what's in the bounded region.
property lineList : {}
property characterGrid : {}
property currentLineCharacters : {}
-- Precalculated border cell indices.
property rightInnerBuffer : borderThickness + w + 1
property rightOuterBuffer : rightInnerBuffer + borderThickness - 2
property bottomInnerBuffer : borderThickness + h + 1
property bottomOuterBuffer : bottomInnerBuffer + borderThickness - 2
-- Generation counter.
property counter : 0
-- Temporary lists used in the set-up.
property rowTemplate : {}
property lineCharacterTemplate : {}
-- Built-in handlers. Both return text representing a universe state and
-- a boolean indicating whether or not the state's the same as the previous one.
on nextState()
set astid to AppleScript's text item delimiters
set AppleScript's text item delimiters to ""
copy newState to previousState
set currentRow to beginning of my previousState
set rowBelow to item 2 of my previousState
-- Check each occupiable cell in each occupiable row of the 'previousState' grid, including the buffer cells.
-- If warranted by the number of live neighbours, edit the equivalent cell in 'newState' and,
-- if within the region's bounds, change the corresponding text character too.
repeat with r from 2 to bottomOuterBuffer
set rowAbove to currentRow
set currentRow to rowBelow
set rowBelow to item (r + 1) of my previousState
set replacementRow to item r of my newState
set rowCrossesRegion to ((r comes after borderThickness) and (r comes before bottomInnerBuffer))
if (rowCrossesRegion) then set currentLineCharacters to item (r - borderThickness) of my characterGrid
set lineChanged to false
repeat with c from 2 to rightOuterBuffer
set liveNeighbours to ¬
(item (c - 1) of my rowAbove) + (item c of my rowAbove) + (item (c + 1) of my rowAbove) + ¬
(item (c - 1) of my currentRow) + (item (c + 1) of my currentRow) + ¬
(item (c - 1) of my rowBelow) + (item c of my rowBelow) + (item (c + 1) of my rowBelow)
if (item c of my currentRow is 1) then
if ((liveNeighbours < 2) or (liveNeighbours > 3)) then
set item c of my replacementRow to 0
if ((c comes after borderThickness) and (c comes before rightInnerBuffer) and (rowCrossesRegion)) then
set item (c - borderThickness) of my currentLineCharacters to dead
set lineChanged to true
else if ((c is 3) or (c is rightOuterBuffer) or (r is 3) or (r is bottomOuterBuffer)) then
-- This is a fudge to dissolve "bombers" entering the buffer zone.
set item (c - 1) of my replacementRow to -1
set item c of item (r - 1) of my newState to -1
end if
end if
else if (liveNeighbours is 3) then
set item c of my replacementRow to 1
if ((c comes after borderThickness) and (c comes before rightInnerBuffer) and (rowCrossesRegion)) then
set item (c - borderThickness) of my currentLineCharacters to live
set lineChanged to true
end if
end if
end repeat
if (lineChanged) then set item (r - borderThickness) of my lineList to currentLineCharacters as text
end repeat
set AppleScript's text item delimiters to astid
set counter to counter + 1
set last item of my lineList to "Generation " & counter
return currentState()
end nextState
on currentState()
set noChanges to (newState = previousState)
if (noChanges) then ¬
set last item of my lineList to (last item of my lineList) & " (all dead, still lifes, or left the universe)"
set astid to AppleScript's text item delimiters
set AppleScript's text item delimiters to return
set stateText to lineList as text
set AppleScript's text item delimiters to astid
return {stateText, noChanges}
end currentState
end script
-- Set the universe's start conditions.
-- Build a row template list containing w + 2 * borderThickness zeros
-- and a line character template list containing w 'dead' characters.
repeat (borderThickness * 2) times
set end of universe's rowTemplate to 0
end repeat
repeat w times
set end of universe's rowTemplate to 0
set end of universe's lineCharacterTemplate to dead
end repeat
set astid to AppleScript's text item delimiters
set AppleScript's text item delimiters to ""
set blankLine to universe's lineCharacterTemplate as text
-- Use the templates to populate lists representing the universe's conditions.
-- Firstly the top border rows ('newState' list only).
repeat borderThickness times
copy universe's rowTemplate to end of universe's newState
end repeat
-- Then enough rows and text lines to centre the input roughly halfway down the grid.
set headroom to (h - lineCount) div 2
repeat headroom times
copy universe's rowTemplate to end of universe's newState
copy universe's lineCharacterTemplate to end of universe's characterGrid
set end of universe's lineList to blankLine
end repeat
-- Then the rows and lines representing the input itself, centring it roughly halfway across the grid.
set textInset to (w - seedWidth) div 2
set stateInset to textInset + borderThickness
repeat with thisLine in seedLines
copy universe's rowTemplate to universe's currentRow
copy universe's lineCharacterTemplate to universe's currentLineCharacters
repeat with c from 1 to (count thisLine)
set thisCharacter to character c of thisLine
set item (textInset + c) of universe's currentLineCharacters to thisCharacter
set item (stateInset + c) of universe's currentRow to (thisCharacter is live) as integer
end repeat
set end of universe's newState to universe's currentRow
set end of universe's characterGrid to universe's currentLineCharacters
set end of universe's lineList to universe's currentLineCharacters as text
end repeat
set AppleScript's text item delimiters to astid
-- Then the rows and lines beneath and the bottom border.
repeat (h - (headroom + lineCount)) times
copy universe's rowTemplate to end of universe's newState
copy universe's lineCharacterTemplate to end of universe's characterGrid
set end of universe's lineList to blankLine
end repeat
repeat borderThickness times
copy universe's rowTemplate to end of universe's newState
end repeat
-- Add a generation counter display line to the end of the line list.
set end of universe's lineList to "Generation 0"
-- Lose the no-longer-needed template lists.
set universe's rowTemplate to missing value
set universe's lineCharacterTemplate to universe's rowTemplate
return universe
end newUniverse
In conjunction with the above, this fulfills the task as set:
on RCTask(seed, dimensions, maxGenerations)
-- Create a universe and start a list with its initial state.
set universe to newUniverse(seed, dimensions)
set {stateText} to universe's currentState()
set output to {stateText}
-- Add successive states to the list.
repeat maxGenerations times
set {stateText, noChanges} to universe's nextState()
set end of output to stateText
if (noChanges) then exit repeat
end repeat
-- Coerce the states to a single text, each followed by a short line of dashes.
set astid to AppleScript's text item delimiters
set AppleScript's text item delimiters to linefeed & "-----" & linefeed & linefeed
set output to (output as text) & linefeed & "-----"
set AppleScript's text item delimiters to astid
return output
end RCTask
-- Return text containing the original and three generations of a "blinker" in a 3 x 3 grid.
return RCTask("***", {3, 3}, 3)
- Output:
"
■■■
Generation 0
-----
■
■
■
Generation 1
-----
■■■
Generation 2
-----
■
■
■
Generation 3
-----"
This alternative to the task code runs an animation of a "Gosper glider gun" in TextEdit, the AppleScriptable text editor included with macOS. The animation's achieved by replacing the entire text of a document with successive universe states. It's faster than it sounds, but the universe size specified shouldn't be much greater than 150 x 150 with current machines.
on runGame(seed, dimensions, maxGenerations)
-- Create an RTF file set up for Menlo-Regular 12pt, half spacing, and a reasonable window size.
set fontName to "Menlo-Regular"
set fontSize to 12
set viewScale to fontSize * 12.4 -- Seems to work well.
set {w, h} to dimensions
set RTFHeaders to "{\\rtf1\\ansi\\ansicpg1252\\cocoartf1671\\cocoasubrtf600
{\\fonttbl\\f0\\fnil\\fcharset0 " & fontName & ";}
{\\colortbl;\\red255\\green255\\blue255;}
{\\*\\expandedcolortbl;;}
\\margl1440\\margr1440\\vieww" & (w * viewScale as integer) & "\\viewh" & ((h + 1) * viewScale as integer) & "\\viewkind0
\\pard\\sl120\\slmult1\\pardirnatural\\partightenfactor0
\\f0\\fs" & (fontSize * 2) & " \\cf0 }" -- Contains a space as body text for TextEdit to see as an 'attribute run'.
set RTFFile to ((path to temporary items as text) & "Conway's Game of Life.rtf") as «class furl»
set fRef to (open for access RTFFile with write permission)
try
set eof fRef to 0
write RTFHeaders as «class utf8» to fRef
close access fRef
on error errMsg number errNum
close access fRef
error errMsg number errNum
end try
-- Open the file as a document in TextEdit.
tell application "TextEdit"
activate
tell document "Conway's Game of Life.rtf" to if (it exists) then close saving no
set CGoLDoc to (open RTFFile)
end tell
-- Create a universe and display its initial state in the document window.
set universe to newUniverse(seed, dimensions)
set {stateText} to universe's currentState()
tell application "TextEdit" to set CGoLDoc's first attribute run to stateText
-- Get and display successive states.
repeat maxGenerations times
set {stateText, noChanges} to universe's nextState()
tell application "TextEdit" to set CGoLDoc's first attribute run to stateText
if (noChanges) then exit repeat
end repeat
end runGame
set GosperGliderGun to " *
* *
** ** **
* * ** **
** * * **
** * * ** * *
* * *
* *
**"
-- Run for 500 generations in a 100 x 100 universe.
runGame(GosperGliderGun, {100, 100}, 500)
ARM Assembly
.string "PRG"
lcd_ptr .req r4
active_fb .req r5
inactive_fb .req r6
offset_r .req r7
backup_fb .req r8
@ start
push {r4-r10, r12, lr}
ldr lcd_ptr, =0xC0000000 @ address of the LCD controller
adr offset_r, offsets
ldrh r0, [offset_r, #6] @ 0xffff is already in memory because -1 is in the offsets table
str r0, [lcd_ptr, #0x200] @ set up paletted colors: 1 is black, 0 is white
ldr r2, [lcd_ptr, #0x18] @ load lcd configuration
bic r2, #14
orr r2, #6 @ Set color mode to 8 bpp, paletted
str r2, [lcd_ptr, #0x18]
ldr backup_fb, [lcd_ptr, #0x10] @ Save address of OS framebuffer
@ allocate a buffer for game state / framebuffer
ldr r0, =153600 @ 320 * 240 * 2
add r0, #8
svc #5 @ malloc
push {r0}
orr inactive_fb, r0, #7
add inactive_fb, #1
add active_fb, inactive_fb, #76800
@ fill buffer with random ones and zeroes
ldr r10, =76800
mov r9, #0
1: subs r10, r10, #1
strb r9, [active_fb, r10] @ zero other framebuffer
svc #206 @ rand syscall
and r0, r0, #1
strb r0, [inactive_fb, r10]
bne 1b
@ set first and last rows to zero
mov r2, #320
mov r1, #0
mov r0, inactive_fb
push {r1,r2}
svc #7 @ memset
pop {r1,r2}
ldr r3, =76480
add r0, r0, r3
svc #7
@ beginning of main loop, swap framebuffers
3: ldr r0, =76480 @ 320 * 239
str inactive_fb, [lcd_ptr, #0x10]
mov inactive_fb, active_fb
ldr active_fb, [lcd_ptr, #0x10]
@ per-pixel loop
2: mov r1, #16 @ 8 * 2
mov r2, #0
sub r0, #1
@ loop to count up neighboring living cells
1: subs r1, #2
ldrsh r3, [offset_r, r1] @ cant use lsl #1
add r3, r3, r0
ldrb r3, [active_fb, r3]
add r2, r2, r3
bne 1b @ at end of loop, r1 and r3 can be discarded
@ decides whether the cell should live or die based on neighbors
ldrb r1, [active_fb, r0]
add r2, r2, r1
teq r2, #3
moveq r1, #1
teqne r2, #4
movne r1, #0
strb r1, [inactive_fb, r0]
teq r0, #320
bne 2b
@ checks if the escape key is pressed
ldr r0, =0x900E001C
ldr r1, [r0]
tst r1, #0x80
beq 3b
str backup_fb, [lcd_ptr, #0x10] @ restores OS framebuffer
pop {r0}
svc #6 @ free buffer
pop {r4-r10, r12, pc}
offsets:
.hword -321, -320, -319, -1, 1, 319, 320, 321
http://i.imgur.com/kV9RirP.gif
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
AWK
50x20 grid (hardcoded) with empty border, filled with random cells, running for 220 generations, using ANSI escape-codes for output to terminal:
BEGIN {
c=220; d=619; i=10000;
printf("\033[2J"); # Clear screen
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"); # Home cursor
for(i=1;i<=1000;i++) # gridsize 50x20
{
if(n[i]) printf("O"); else printf(".");
m[i]=n[i];
if(!(i%50)) printf("\n");
}
printf("%3d\n",c); # Countdown
x=30000; while(x--) ; # Delay
}
}
- Output:
Finally
.................................................. ..........................................OO...... ..........................................O.O..... ...........................................O...... ......................................OOO.......OO ................................................OO ....................................O.....O....... ....................................O.....O....... ....................................O.....O....... .................................................. ......................................OOO......... .................................................. .................................................. .................................................. ..O............................................... .O.O.............................................. O.O...........OO...........OO..................... .O............OO.........O..O..................... .........................OOO...................... .................................................. 0
Axe
This implementation uses the full screen buffer instead of a 3x3 grid. This naive, unoptimized version gets less than 1 FPS.
Full
While getKey(0)
End
ClrDraw
.BLINKER
Pxl-On(45,45)
Pxl-On(46,45)
Pxl-On(47,45)
.GLIDER
Pxl-On(1,1)
Pxl-On(2,2)
Pxl-On(2,3)
Pxl-On(3,1)
Pxl-On(3,2)
Repeat getKey(0)
DispGraph
EVOLVE()
RecallPic
ClrDrawʳ
End
Return
Lbl EVOLVE
For(Y,0,63)
For(X,0,95)
0→N
For(B,Y-1,Y+1)
For(A,X-1,X+1)
pxl-Test(A,B)?N++
End
End
pxl_Test(X,Y)?N--
If N=3??(N=2?pxl-Test(X,Y))
Pxl-On(X,Y)ʳ
Else
Pxl-Off(X,Y)ʳ
End
End
End
Return
BASIC
BASIC256
![](http://static.miraheze.org/rosettacodewiki/9/90/Game_of_life_BASIC-256.gif)
Saving to PNG files function is omited. You can find it in the Galton box animation example.
# Conway's_Game_of_Life
X = 59 : Y = 35 : H = 4
fastgraphics
graphsize X*H,Y*H
dim c(X,Y) : dim cn(X,Y) : dim cl(X,Y)
# Thunderbird methuselah
c[X/2-1,Y/3+1] = 1 : c[X/2,Y/3+1] = 1 : c[X/2+1,Y/3+1] = 1
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
- Output:
Stabilized in 243 iterations
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:
CASIO BASIC
Filename:JG VIDA
Cls
20→D
D+2→F
1→E
{F,F}→Dim Mat A
{F,F}→Dim Mat B
{F,F}→Dim Mat C
Fill(0,Mat A)
Fill(0,Mat B)
Fill(0,Mat C)
"PONDERACION 1"?→G
"PONDERACION 2"?→H
For 1→I To D
For 1→J To D
RanInt#(1,G)→R
If R≦H:Then
0→Mat A[I+1,J+1]
Else
1→Mat A[I+1,J+1]
IfEnd
Next
Next
Goto 2
Lbl 1
Mat A→Mat C
Mat B→Mat A
Lbl 2
For 1→I To D
For 1→J To D
I+1→K
J+1→L
K→M
L+20→N
If Mat C[K,L]=0:Then
If Mat A[K,L]=1:Then For 0→R To 2
For 0→S To 2
PxlOn 3I+R-2,3J+S-2
Next
Next
IfEnd
IfEnd
If Mat C[K,L]=1:Then
If Mat A[K,L]=0:Then For 0→R To 2
For 0→S To 2
PxlOff 3I+R-2,3J+S-2
Next
Next
IfEnd
IfEnd
Next
Next
For 1→I To D
For 1→J To D
0→C
I+1→K
J+1→L
Mat A[I,J]=1⇨C+1→C
Mat A[I+1,J]=1⇨C+1→C
Mat A[I+2,J]=1⇨C+1→C
Mat A[I+2,J+1]=1⇨C+1→C
Mat A[I+2,J+2]=1⇨C+1→C
Mat A[I+1,J+2]=1⇨C+1→C
Mat A[I,J+2]=1⇨C+1→C
Mat A[I,J+1]=1⇨C+1→C
If Mat A[K,L]=1:Then
C<2⇨0→Mat B[K,L]
C>3⇨0→Mat B[K,L]
IfEnd
If Mat A[K,L]=0:Then
C=3⇨1→Mat B[K,L]
IfEnd
Next
Next
Goto 1
FreeBASIC
' FreeBASIC Conway's Game of Life
' May 2015
' 07-10-2016 cleanup/little changes
' moved test inkey outside the ScreenLock - ScreenUnLock block
' compile: fbc -s gui
Const As UInteger grid = 300 '480 by 480
Const As UInteger gridy = grid
Const As UInteger gridx = grid
Const As UInteger pointsize = 5 'pixels
Const As UInteger steps = 10
Dim As UInteger gen, n, neighbours, x, y, was
Dim As String press
Const As UByte red = 4 'red is color 6
Const As UByte white = 15 'color
Const As UByte black = 0 'color
'color 0 normaly is black
'color 1 normaly is dark blue
'color 2 normaly is green
Const As UInteger bot = 35 'this is 35 lines from the top of the page
Dim As UByte old( grid + 10, grid +10), new_( grid +10, grid +10)
'Set blinker:
' old( 160, 160) =1: old( 160, 170) =1 : old( 160, 180) =1
'Set blinker:
' old( 160, 20) =1: old( 160, 30) =1 : old( 160, 40) =1
'Set blinker:
' old( 20, 20) =1: old( 20, 30) =1 : old( 20, 40) =1
'Set glider:
' old( 50, 70) =1: old( 60, 70) =1: old( 70, 70) =1
' old( 70, 60) =1: old( 60, 50) =1
' http://en.wikipedia.org/wiki/Conway%27s_Game_of_Life
' Thunderbird methuselah
'X = 59 : Y = 35 : H = 4
'c[X/2-1,Y/3+1] = 1 : c[X/2,Y/3+1] = 1 : c[X/2+1,Y/3+1] = 1
'c[X/2,Y/3+3] = 1 : c[X/2,Y/3+4] = 1 : c[X/2,Y/3+5] = 1
'xb = 59 : yb = 35
' old( Xb/2-1,Yb/3+1) =1: old(Xb/2,Yb/3+1) =1: old(Xb/2+1,Yb/3+1) =1
' old( Xb/2,Yb/3+3) =1: old(Xb/2,Yb/3+4) =1 :old(Xb/2,Yb/3+5) = 1
'r-pentomino
' old( 150,140) =1: old( 160,140) =1
' old( 140,150) =1 :old( 150,150) =1
' old( 150,160) =1
'Die Hard around 150 generations
' old( 150,140) =1: old(160,140) =1 : old(160,150) =1
' old( 200,150) =1: old(210,150) =1 : old(210,130) = 1 : old(220,150) = 1
'Acorn around 450 generations
' it looks like this:
' 0X
' 000X
' XX00XXX
old( 180,200) =1
old( 200,210) =1
old( 170,220) =1 : old( 180,220) =1 : old( 210,220) =1 : old( 220,220) =1 : old( 230,220) =1
Screen 20 'Resolution 800x600 with at least 256 colors
Color white
Line (10, 10) - (gridx + 10, gridy + 10),,B 'box from top left to bottom right
Locate bot, 1 'Use a standard place on the bottom of the page
Color white
Print " Welcome to Conway's Game of Life"
Print " Using a constrained playing field (300x300), the Acorn seed runs"
Print " for about 450 generations before it becomes stable (or stale)."
Print " Enter any key to start"
Beep
Sleep
Do ' flush the key input buffer
press = Inkey
Loop Until press = ""
'Print " "
'Draw initial grid
For x = 10 To gridX Step steps
For y = 10 To gridY Step steps
Color white 'old(x,y)
If old(x,y) = 1 Then Circle (x + pointsize, y + pointsize), pointsize,,,,, F
Next y
Next x
'
Locate bot, 1
Color white
Print " Welcome to Conway's Game of Life"
Print " Using a constrained playing field, the Acorn seed runs for "
Print " about 450 generations before it becomes stable (or stale). "
Color red
Print " Enter spacebar to continue or pause, ESC to stop"
Sleep
'
Do ' flush the key input buffer
press = Inkey
Loop Until press = ""
Do
gen = gen + 1
Locate bot+5,1
Color white
Print " Gen = "; gen
ScreenLock
For x = 10 To gridX Step steps
For y = 10 To gridY Step steps
'find number of live neighbours
neighbours = old( x - steps, y - steps) +old( x , y - steps)
neighbours = neighbours + old( x + steps, y -steps)
neighbours = neighbours + old( x - steps, y) + old( x + steps, y)
neighbours = neighbours + old( x - steps, y + steps)
neighbours = neighbours + old( x, y + steps) +old( x + steps, y + steps)
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
If n = 2 Then Color white
If n = 1 Then Color red
If n = 0 Then Color black
Circle (x + pointsize, y + pointsize), pointsize,,,,, F
Next y
Next x
Color white
Line (10, 10) - (gridx + 10, gridy + 10),,B 'box from top left to bottom right
' Locate bot,1
'
't = timer
'do
'loop until timer > t + .2
ScreenUnlock
' might not be slow enough
Sleep 70, 1 ' ignore key press
press = Inkey
If press = " " Then
Do ' flush the key input buffer
press = Inkey
Loop Until press = ""
Do ' wait until a key is pressed
press = Inkey
Loop Until press <> ""
End If
If press = Chr(27) Then Exit Do
' mouse click on close window "X"
If press = Chr(255)+"k" Then End ' stop and close window
For x =10 To gridX Step steps
For y =10 To gridY Step steps
old( x, y) =new_( x, y)
Next y
Next x
Loop ' UNTIL press = CHR(27) 'return to do loop up top until "esc" key is pressed.
Color white
Locate bot+3,1
Print Space(55) 'clear instructions
Locate bot+6,1
Print " Press any key to exit "
Sleep
End
GFA Basic
'
' Conway's Game of Life
'
' 30x30 world held in an array size 32x32
' world is in indices 1->30, 0 and 31 are always false, for neighbourhoods
'
DIM world!(32,32)
DIM ns%(31,31) ! used to hold the neighbour counts
clock%=1
'
' run the world
'
@setup_world
@open_window
DO
@display_world
t$=INKEY$
EXIT IF t$="q" ! need to hold key down to exit
@update_world
DELAY 0.5 ! delay of 0.5s needed in compiled version
LOOP
@close_window
'
' Setup the world, with a blinker in one corner and a glider in the other
'
PROCEDURE setup_world
ARRAYFILL world!(),FALSE
' blinker in lower-right
world!(25,25)=TRUE
world!(26,25)=TRUE
world!(27,25)=TRUE
' glider in top-left
world!(2,2)=TRUE
world!(3,3)=TRUE
world!(3,4)=TRUE
world!(2,4)=TRUE
world!(1,4)=TRUE
RETURN
'
' Count the number of neighbours of the point i,j
' (Assume i/j +/- 1 will not fall out of world)
'
FUNCTION count_neighbours(i%,j%)
LOCAL count%,l%
count%=0
FOR l%=-1 TO 1
IF world!(i%+l%,j%-1)
count%=count%+1
ENDIF
IF world!(i%+l%,j%+1)
count%=count%+1
ENDIF
NEXT l%
IF world!(i%-1,j%)
count%=count%+1
ENDIF
IF world!(i%+1,j%)
count%=count%+1
ENDIF
RETURN count%
ENDFUNC
'
' Update the world one step
'
PROCEDURE update_world
LOCAL i%,j%
' compute neighbour counts and store
FOR i%=1 TO 30
FOR j%=1 TO 30
ns%(i%,j%)=@count_neighbours(i%,j%)
NEXT j%
NEXT i%
' update the world cells
FOR i%=1 TO 30
FOR j%=1 TO 30
IF world!(i%,j%)
SELECT ns%(i%,j%)
CASE 0,1
world!(i%,j%)=FALSE ! LONELY
CASE 2,3
world!(i%,j%)=TRUE ! LIVES
CASE 4,5,6,7,8
world!(i%,j%)=FALSE ! OVERCROWDED
ENDSELECT
ELSE
IF ns%(i%,j%)=3
world!(i%,j%)=TRUE ! BIRTH
ELSE
world!(i%,j%)=FALSE ! BARREN
ENDIF
ENDIF
NEXT j%
NEXT i%
' update the clock
clock%=clock%+1
RETURN
'
' Display the world in window
'
PROCEDURE display_world
LOCAL offsetx%,offsety%,i%,j%,x%,y%,scale%
@clear_window
' show clock
VSETCOLOR 2,0,0,0
DEFTEXT 2
PRINT AT(5,1);"Clock: ";clock%
' offset from top-left of display
offsetx%=10
offsety%=10
' colour to display active cell
VSETCOLOR 1,15,0,0
DEFFILL 1
' scale of display
scale%=9
' display each cell in world
FOR i%=1 TO 30
FOR j%=1 TO 30
IF world!(i%,j%)
' display active cell
x%=offsetx%+scale%*i%
y%=offsety%+scale%*j%
PBOX x%,y%,x%+scale%,y%+scale%
ENDIF
NEXT j%
NEXT i%
RETURN
'
' Manage window for display
'
PROCEDURE open_window
OPENW 1
CLEARW 1
RETURN
'
PROCEDURE clear_window
VSETCOLOR 0,15,15,15
DEFFILL 0
PBOX 0,0,300,300
RETURN
'
PROCEDURE close_window
CLOSEW 1
RETURN
GW-BASIC
Allows a blinker to be loaded. It also has a routine for randomising the grid; common objects like blocks, gliders, etc turn up semi-frequently so it won't take long to verify that these all work.
10 REM Conway's Game of Life
20 REM 30x30 grid, padded with zeroes as the boundary
30 DIM WORLD(31, 31, 1)
40 RANDOMIZE TIMER
50 CUR = 0 : BUF = 1
60 CLS
70 SCREEN 2
80 LOCATE 5,10: PRINT "Press space to perform one iteration"
90 LOCATE 6,10: PRINT "B to load a blinker"
100 LOCATE 7,10: PRINT "R to randomise the world"
110 LOCATE 8,10: PRINT "Q to quit"
120 K$ = INKEY$
130 IF K$=" " THEN GOSUB 250: GOSUB 180
140 IF K$="B" OR K$="b" THEN GOSUB 400: GOSUB 180
150 IF K$="R" OR K$="r" THEN GOSUB 480: GOSUB 180
160 IF K$="Q" OR K$="q" THEN SCREEN 0: END
170 GOTO 120
180 REM draw the world
190 FOR XX=1 TO 30
200 FOR YY=1 TO 30
210 PSET (XX, YY), 15*WORLD(XX, YY, CUR)
220 NEXT YY
230 NEXT XX
240 RETURN
250 REM perform one iteration
260 FOR XX=1 TO 30
270 FOR YY=1 TO 30
280 SM=0
290 SM = SM + WORLD(XX-1, YY-1, CUR) + WORLD(XX, YY-1, CUR) + WORLD(XX+1, YY-1, CUR)
300 SM = SM + WORLD(XX-1, YY, CUR) + WORLD(XX+1, YY, CUR)
310 SM = SM + WORLD(XX-1, YY+1, CUR) + WORLD(XX, YY+1, CUR) + WORLD(XX+1, YY+1, CUR)
320 IF SM<2 OR SM>3 THEN WORLD(XX, YY, BUF) = 0
330 IF SM=3 THEN WORLD(XX, YY, BUF) = 1
340 IF SM=2 THEN WORLD(XX,YY,BUF) = WORLD(XX,YY,CUR)
350 NEXT YY
360 NEXT XX
370 CUR = BUF : REM exchange identities of current and buffer
380 BUF = 1 - BUF
390 RETURN
400 REM produces a vertical blinker at the top left corner, and blanks the rest
410 FOR XX=1 TO 30
420 FOR YY=1 TO 30
430 WORLD(XX,YY,CUR) = 0
440 IF XX=2 AND YY<4 THEN WORLD(XX, YY, CUR) = 1
450 NEXT YY
460 NEXT XX
470 RETURN
480 REM randomizes the world with a density of 1/2
490 FOR XX = 1 TO 30
500 FOR YY = 1 TO 30
510 WORLD(XX, YY, CUR) = INT(RND*2)
520 NEXT YY
530 NEXT XX
540 RETURN
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 =0Tail Recursive
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
MSX Basic
10 DEFINT A-Z
20 DIM L(16,16), N(16,16)
30 M = 16
40 CLS
50 PRINT "PRESS A KEY TO START...";
60 W = RND(1)
70 IF INKEY$ = "" THEN 60
80 CLS
100 FOR I=1 TO M
110 FOR J=1 TO M
120 IF RND(1)>=.7 THEN N(I,J) = 1 ELSE N(I,J) = 0
130 NEXT J
140 NEXT I
1000 FOR I=0 TO M+1
1010 LOCATE I,0 : PRINT "+";
1020 LOCATE I,M+1 : PRINT "+";
1030 LOCATE 0,I : PRINT "+";
1040 LOCATE M+1,I : PRINT "+";
1050 NEXT I
1080 G=0
1090 LOCATE 1,M+3 : PRINT USING "#####";G
1100 FOR I=1 TO M
1110 FOR J=1 TO M
1115 W = N(I,J) : L(I,J) = W
1120 LOCATE I,J
1130 IF W=0 THEN PRINT " "; ELSE PRINT "*";
1160 NEXT J
1170 NEXT I
1180 FOR I=1 TO M
1190 FOR J=1 TO M
1200 NC=0
1210 FOR K=I-1 TO I+1
1215 IF K=0 OR K>M THEN 1260
1220 FOR W=J-1 TO J+1
1230 IF W=0 OR W>M OR (K=I AND W=J) THEN 1250
1240 NC = NC + L(K,W)
1250 NEXT W
1260 NEXT K
1270 IF NC=2 THEN N(I,J)=L(I,J) : GOTO 1300
1280 IF NC=3 THEN N(I,J)=1 ELSE N(I,J)=0
1300 NEXT J
1310 NEXT I
1350 G=G+1
1360 GOTO 1090
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() <> ""
QBasic
El código es de Ben Wagner (bwgames.org)
The code is from Ben Wagner (bwgames.org)
Yo solo lo transcrito y comento al español.
I just transcribed it and comment it in Spanish.
SCREEN 9, 0, 0, 1
RANDOMIZE TIMER
WINDOW (0, 0)-(80, 80)
'La matrizA es la actual, la matrizB es la siguiente iteración
'ArrayA is current, arrayB is next iteration
DIM matrizA(-1 TO 81, -1 TO 81)
DIM matrizB(-1 TO 81, -1 TO 81)
'Aleatorizar las celdas de matrizA,
'Randomize cells in arrayA,
'y establecer las de matrizB a 0
'and set those of matrixB to 0
y = 0
DO
x = 0
DO
x = x + 1
matrizA(x, y) = INT(RND + .5)
matrizB(x, y) = 0
LOOP UNTIL x > 80
y = y + 1
LOOP UNTIL y > 80
''--- Bucle Principal ---
'' --- Main Loop ---
DO
CLS
'Dibuja la matriz
'Draw the matrix
y = 0
DO
x = 0
DO
IF matrizA(x, y) = 1 THEN LINE (x, y)-(x + 1, y + 1), 1, BF
x = x + 1
LOOP UNTIL x > 80
y = y + 1
LOOP UNTIL y > 80
'Cuenta el recuento de la celda circundante
'Counts the count of the surrounding cell
'Luego aplica la operación a la celda
'Then apply the operation to the cell
y = 0
DO
x = 0
DO
'Cuenta las células circundantes
'Count the surrounding cells
cuenta = 0
IF matrizA(x - 1, y + 1) = 1 THEN cuenta = cuenta + 1
IF matrizA(x, y + 1) = 1 THEN cuenta = cuenta + 1
IF matrizA(x + 1, y + 1) = 1 THEN cuenta = cuenta + 1
IF matrizA(x - 1, y) = 1 THEN cuenta = cuenta + 1
IF matrizA(x + 1, y) = 1 THEN cuenta = cuenta + 1
IF matrizA(x - 1, y - 1) = 1 THEN cuenta = cuenta + 1
IF matrizA(x, y - 1) = 1 THEN cuenta = cuenta + 1
IF matrizA(x + 1, y - 1) = 1 THEN cuenta = cuenta + 1
'Aplica las operaciones
'Apply the operations
'Muerte
'Death
IF matrizA(x, y) = 1 THEN
IF cuenta = 2 OR cuenta = 3 THEN matrizB(x, y) = 1 ELSE matrizB(x, y) = 0
END IF
'Nacimiento
'Birth
IF matrizA(x, y) = 0 THEN
IF cuenta = 3 THEN matrizB(x, y) = 1 ELSE matrizB(x, y) = 0
END IF
x = x + 1
LOOP UNTIL x > 80
y = y + 1
LOOP UNTIL y > 80
'Actualiza la matriz con la nueva matriz que hemos calculado.
'Update the matrix with the new matrix that we have calculated.
y = 0
DO
x = 0
DO
x = x + 1
matrizA(x, y) = matrizB(x, y)
LOOP UNTIL x > 80
y = y + 1
LOOP UNTIL y > 80
PCOPY 0, 1
LOOP WHILE INKEY$ = ""
Sinclair ZX81 BASIC
Requires at least 2k of RAM. Expects to find a square array of "0" and "1" characters, L$(), giving the initial configuration. You can build this up by issuing commands in immediate mode and then run the program by entering GOTO 1000
, but it's probably easier—assuming you can spare some RAM—to write the setup into the program using line numbers below 1000.
The graphics character in lines 1030 to 1060 can be obtained by typing SHIFT
9
then SHIFT
H
, and the one in line 1130 by typing SHIFT
9
then SPACE
.
1000 LET M=LEN L$(1)
1010 DIM N$(M,M)
1020 FOR I=0 TO M+1
1030 PRINT AT I,0;"▩"
1040 PRINT AT I,M+1;"▩"
1050 PRINT AT 0,I;"▩"
1060 PRINT AT M+1,I;"▩"
1070 NEXT I
1080 LET G=0
1090 PRINT AT 1,M+3;G
1100 FOR I=1 TO M
1110 FOR J=1 TO M
1120 IF L$(I,J)="0" THEN GOTO 1150
1130 PRINT AT I,J;"■"
1140 GOTO 1160
1150 PRINT AT I,J;" "
1160 NEXT J
1170 NEXT I
1180 FOR I=1 TO M
1190 FOR J=1 TO M
1200 LET N=0
1210 FOR K=I-1 TO I+1
1220 FOR L=J-1 TO J+1
1230 IF K=0 OR K>M OR L=0 OR L>M OR (K=I AND L=J) THEN GOTO 1250
1240 LET N=N+VAL L$(K,L)
1250 NEXT L
1260 NEXT K
1270 LET N$(I,J)=L$(I,J)
1280 IF N<=1 OR N>=4 THEN LET N$(I,J)="0"
1290 IF N=3 THEN LET N$(I,J)="1"
1300 NEXT J
1310 NEXT I
1320 FOR I=1 TO M
1330 LET L$(I)=N$(I)
1340 NEXT I
1350 LET G=G+1
1360 GOTO 1090
To run the blinker, add this code:
10 DIM L$(3,3)
20 LET L$(1)="000"
30 LET L$(2)="111"
40 LET L$(3)="000"
A screenshot of it running can be found here.
To try a random starting configuration on a 16x16 grid, use this:
10 DIM L$(16,16)
20 FOR I=1 TO 16
30 FOR J=1 TO 16
40 LET L$(I,J)="0"
50 IF RND>=.7 THEN LET L$(I,J)="1"
60 NEXT J
70 NEXT I
A screenshot is here.
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
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
Batch File
This code takes three parameters: m chance iterations
Where,
m - The length and width of the array of cells
chance - The percent chance of any cell within the set array initially being alive. Full numbers only.
iterations - The amount of iterations of evolution the array goes through to display.
If no parameters are parsed, it defaults to 5 iterations of the blinking example.
@echo off
setlocal enabledelayedexpansion
if "%1"=="" (
call:_blinkerArray
) else (
call:_randomArray %*
)
for /l %%i in (1,1,%iterations%) do (
call:_setStatus
call:_display
for /l %%m in (1,1,%m%) do (
for /l %%n in (1,1,%m%) do (
call:_evolution %%m %%n
)
)
)
:_blinkerArray
for /l %%m in (0,1,4) do (
for /l %%n in (0,1,4) do (
set cell[%%m][%%n]=0
)
)
set cell[2][1]=1
set cell[2][2]=1
set cell[2][3]=1
set iterations=5
set m=3
set cellsaddone=4
exit /b
:_randomArray
set cellsaddone=%1+1
for /l %%m in (0,1,%cellsaddone%) do for /l %%n in (0,1,%cellsaddone%) do set cell[%%m][%%n]=0
for /l %%m in (1,1,%1) do (
for /l %%n in (1,1,%1) do (
set /a cellrandom=!random! %% 101
set cell[%%m][%%n]=0
if !cellrandom! leq %2 set cell[%%m][%%n]=1
)
)
set iterations=%3
set m=%1
exit /b
:_setStatus
for /l %%m in (0,1,%cellsaddone%) do (
for /l %%n in (0,1,%cellsaddone%) do (
if !cell[%%m][%%n]!==1 set cellstatus[%%m][%%n]=alive
if !cell[%%m][%%n]!==0 set cellstatus[%%m][%%n]=dead
)
)
exit /b
:_evolution
set /a lowerm=%1-1
set /a upperm=%1+1
set /a lowern=%2-1
set /a uppern=%2+1
set numm=%1
set numn=%2
set sum=0
for /l %%m in (%lowerm%,1,%upperm%) do (
for /l %%n in (%lowern%,1,%uppern%) do (
if %%m==%numm% (
if %%n==%numn% (
set /a sum=!sum!
) else (
if !cellstatus[%%m][%%n]!==alive set /a sum+=1
)
) else (
if !cellstatus[%%m][%%n]!==alive set /a sum+=1
)
)
)
goto:!cell[%numm%][%numn%]!
exit /b
:0
set alive=3
set death=0 1 2 4 5 6 7 8
for %%i in (%alive%) do if %sum%==%%i set cell[%numm%][%numn%]=1
for %%i in (%death%) do if %sum%==%%i set cell[%numm%][%numn%]=0
exit /b
:1
set alive=2 3
set death=0 1 4 5 6 7 8
for %%i in (%alive%) do if %sum%==%%i set cell[%1][%2]=1
for %%i in (%death%) do if %sum%==%%i set cell[%1][%2]=0
exit /b
:_display
echo.
for /l %%m in (1,1,%m%) do (
set m%%m=
for /l %%n in (1,1,%m%) do set m%%m=!m%%m! !cell[%%m][%%n]!
echo !m%%m!
)
exit /b
- Output:
Blinking example:
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 0 1 0 0 1 0 0 1 0 0 0 0 1 1 1 0 0 0
- Input:
10 35 5
- Output:
1 1 0 0 0 0 1 1 0 0 1 1 0 0 1 1 0 1 1 0 1 0 1 1 0 0 0 1 0 0 0 1 0 1 0 1 0 1 0 1 1 0 0 0 1 0 0 1 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 1 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 1 1 1 1 0 0 1 0 0 1 1 0 1 1 0 0 0 1 1 1 1 0 0 0 0 1 1 1 0 0 1 0 1 0 0 1 0 1 0 1 0 0 1 1 0 1 0 0 0 1 0 0 0 0 1 1 1 0 1 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 1 0 1 0 1 1 0 0 0 1 1 1 1 1 0 0 0 0 0 1 1 1 0 0 0 0 0 1 1 1 1 0 1 1 1 1 0 0 0 0 1 0 1 1 0 1 0 1 0 1 1 0 1 1 0 0 0 1 0 1 0 0 0 1 1 1 1 1 1 0 1 0 0 0 0 1 0 1 1 1 0 0 0 0 0 0 0 0 1 0 0 0 0 1 1 0 0 0 0 1 0 1 1 0 1 0 0 0 1 0 0 0 1 1 1 0 0 0 0 1 0 1 0 1 1 0 0 0 1 1 1 0 1 0 0 1 0 1 0 0 0 1 0 0 0 1 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 1 1 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 1 0 0 1 0 0 1 1 0 0 0 1 1 0 0 1 0 0 1 0 0 1 1 0 0 1 0 1 0 0 0 0 0 0 0 0 1 1 0 0 0 1 1 1 0 0 1 0 1 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 0 1 1 0 0 0 1 0 0 0 0 0 0 0 0 1 0 1 1 0 0 0 0 0 0 1 1 1 0 0 1 1 0 0 0 1 1 1 0 1 0 0 1 0 1 0 0 1 0 0 1 1 0 0 0 1 0 0 0 0 0 0 0 0
Befunge
Takes as input the width and height of the universe, followed by the pattern (which is terminated by the end of file). If your interpreter can't easily redirect the input from a file, or doesn't handle end-of-file detection, you can also type in the pattern manually and mark the end of input with a ~ character.
The pattern format itself is fairly lenient in what it accepts. You can use either space or . for dead cells, and o, O, * or # for live cells. This should make it fairly easy to cut and paste a number of existing formats, including the Life 1.05 format and the Plaintext .cells format used on the LifeWiki website (comments aren't supported though, so make sure to copy just the pattern itself).
In Befunge-93, the maximum value for the width and height of the universe is 127, but there is an additional constraint of 4080 cells in total, so the largest universe would really be something like 120x34 or 68x60. Befunge-98 has no real limit on the size, although in practice a much larger universe will probably be unbearably slow.
00p10p20p30p&>40p&>50p60p>$#v~>:55+-vv+`1:%3:+*g04p03< >3/"P"%\56v>p\56*8*/8+:v
v5\`\"~"::-*3p06!:!-+67:_^#!<*<!g06!<>1+70g*\:3/"P"%v^ ^::+*g04%<*0v`1:%3\gp08<
>6*`*#v_55+-#v_p10g1+10p>^pg08g07+gp08:+8/*8*65\p07:<^ >/10g-50g^87>+1+:01p/8/v
>%#74#<-!!70p 00g::1+00p:20g\-:0`*+20p10g::30g\-:0`*+^ ^2+2+g03*<*:v+g06p09:%2<
.v,:*93"[2J"0<>"H["39*,,,50g0v!:-1,+55$_:40g3*20g+2+2/\-40g%50g3^/%\ >:3-\3-90v
O>"l52?[">:#,_^v/3+2:*g05g04$_>:10p40g0^!:-1,g+4\0%2/+1+`1:%3\g+8<^: $v10!*-g<<
g+70g80gp:#v_$^>1-:::"P"%\"P"/8+:10v >/10g+1-50g+50g%40g*+::3/"P"^>!|>g*70g80g
:p00%g04:-1<<$_^#!:pg01%"P"\*8%8gp<< ^3\%g04+g04-1+g00%3:%9+4:-1p06\<90p01/g04
- Input:
Here's an example of what the input could look like for the Blinker pattern in a 5x5 universe:
5 5 OOO
And for a more complicated example, this is the Queen bee pattern in a 50x30 universe:
50 30 ...* ..*.* .*...* ..*** **...**
- Output:
In order to produce an animated view of the universe evolving, we use a few basic ANSI escape sequences to reset the cursor position between frames. Without ANSI support, you'll just see the individual frames scrolling past with a bit of junk inbetween. The output shown below is just an extract of the first three generations of the Blinker in a 5x5 universe.
..... ..... ..... ..... ..O.. ..... .OOO. ..O.. .OOO. ..... ..O.. ..... ..... ..... .....
Brainf***
A life-program written in Brainf***
With Example-Output.
Brat
width = 3
height = 3
rounds = 3
universe = [[0 1 0]
[0 1 0]
[0 1 0]]
next = height.of({width.of(0)})
cell = { x, y |
true? x < width && { x >= 0 && { y >= 0 && { y < height }}}
{
universe[y][x]
}
{ 0 }
}
neighbors = { x, y |
cell(x - 1, y - 1) +
cell(x, y - 1) +
cell(x + 1, y - 1) +
cell(x + 1, y) +
cell(x + 1, y + 1) +
cell(x, y + 1) +
cell(x - 1, y + 1) +
cell(x - 1, y)
}
set_next = { x, y, v |
next[y][x] = v
}
step = {
universe.each_with_index { row, y |
row.each_with_index { c, x |
n = neighbors(x, y)
when { n < 2 } { set_next x,y, 0 }
{ n > 3 } { set_next x, y, 0 }
{ n == 3 } { set_next x, y, 1 }
{ true } { set_next x, y, c }
}
}
u2 = universe
universe = next
next = u2
}
display = {
p universe.map({ r |
r.map({ n | true? n == 0, '-', "O" }).join
}).join("\n")
}
rounds.times {
display
p
step
}
- Output:
-O- -O- -O- --- OOO --- -O- -O- -O-
BQN
Life←{
r←¯1(⌽⎉1)¯1⌽(2+≢𝕩)↑𝕩
s←∨´ (1∾<r) ∧ 3‿4 = <+´⥊ ¯1‿0‿1 (⌽⎉1)⌜ ¯1‿0‿1 ⌽⌜ <r
1(↓⎉1) ¯1(↓⎉1) 1↓ ¯1↓s
}
blinker←>⟨0‿0‿0,1‿1‿1,0‿0‿0⟩
(<".#") ⊏¨˜ Life⍟(↕3) blinker
- Output:
┌─ · ┌─ ┌─ ┌─ ╵"... ╵".#. ╵"... ### .#. ### ..." .#." ..." ┘ ┘ ┘ ┘
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
C for Arduino
Play game of life on your arduino (using FastLED) - based on the C example.
#include <FastLED.h>
#define LED_PIN 3
#define LED_TYPE WS2812B
#define WIDTH 20
#define HEIGHT 15
#define NUM_LEDS (WIDTH*HEIGHT)
#define BRIGHTNESS 100
#define COLOR_ORDER GRB
#define SERPENTINE 1
#define FRAMERATE 1
#define SCALE 1
CRGB leds[NUM_LEDS];
#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
const int w = WIDTH, h = HEIGHT;
unsigned univ[h][w];
int getLEDpos(int x, int y){ // for a serpentine raster
return (y%2 || !SERPENTINE) ? y*WIDTH + x : y*WIDTH + (WIDTH - 1 - x);
}
void show(void *u, int w, int h)
{
int (*univ)[w] = u;
for_xy {
leds[getLEDpos(x, y)] = univ[y][x] ? CRGB::White: CRGB::Black;
}
FastLED.show();
}
void evolve(void *u, int w, int h)
{
unsigned (*univ)[w] = u;
unsigned newU[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--;
newU[y][x] = (n == 3 || (n == 2 && univ[y][x]));
}
for_y for_x univ[y][x] = newU[y][x];
}
void setup(){
//Initialize leds after safety period
FastLED.delay(500);
FastLED.addLeds<LED_TYPE, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection( TypicalLEDStrip );
FastLED.setBrightness( BRIGHTNESS );
//Seed random with analog noise
randomSeed(analogRead(0));
for_xy univ[y][x] = random() %10 <= 1 ? 1 : 0;
}
void loop(){
show(univ, w, h);
evolve(univ, w, h);
FastLED.delay(1000/FRAMERATE);
}
C for Arduino
Play game of life on your arduino (using two MAX7219 led 'screens') - based on the C example.
#include <MaxMatrix.h>
int DIN = 11; // DIN pin of MAX7219 module
int CS = 12; // CS pin of MAX7219 module
int CLK = 13; // CLK pin of MAX7219 module
int DIN2 = 8; // DIN pin of MAX7219 module
int CS2 = 9; // CS pin of MAX7219 module
int CLK2 = 10; // CLK pin of MAX7219 module
int maxInUse = 1;
//setup two screens
MaxMatrix m(DIN, CS, CLK, maxInUse);
MaxMatrix m2(DIN2, CS2, CLK2, maxInUse);
void setup() {
randomSeed(analogRead(0));
m.init(); // MAX7219 initialization
m.setIntensity(0); // initial led matrix intensity, 0-15
m.clear(); // Clears the display
m2.init(); // MAX7219 initialization
m2.setIntensity(0); // initial led matrix intensity, 0-15
m2.clear(); // Clears the display
}
void loop() {
game(16,8);//w,h
}
void setDot(int x,int y,bool isOn){
if(x<8){
m.setDot(x,y,isOn);
}else{
m2.setDot(x-8,y,isOn);
}
}
void show(void *u, int w, int h){
int (*univ)[w] = u;
for (int y = 0; y < h; y++){
for (int x = 0; x < w; x++){
bool sh=(univ[y][x]==1);
setDot(x,y,sh);
}
}
}
void evolve(void *u, int w, int h){
unsigned (*univ)[w] = u;
unsigned newar[h][w];
for (int y = 0; y < h; y++){
for (int x = 0; x < w; 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--;
newar[y][x] = (n == 3 || (n == 2 && univ[y][x]));
}
}
for (int y = 0; y < h; y++){
for (int x = 0; x < w; x++){
univ[y][x] = newar[y][x];
}
}
}
void game(int w, int h) {
unsigned univ[h][w];
for (int x = 0; x < w; x++){
for (int y = 0; y < h; y++){
univ[y][x] = random(0, 100)>65 ? 1 : 0;
}
}
int sc=0;
while (1) {
show(univ, w, h);
evolve(univ, w, h);
delay(150);
sc++;if(sc>150)break;
}
}
C#
using System;
using System.Text;
using System.Threading;
namespace ConwaysGameOfLife
{
// Plays Conway's Game of Life on the console with a random initial state.
class Program
{
// The delay in milliseconds between board updates.
private const int DELAY = 50;
// The cell colors.
private const ConsoleColor DEAD_COLOR = ConsoleColor.White;
private const ConsoleColor LIVE_COLOR = ConsoleColor.Black;
// The color of the cells that are off of the board.
private const ConsoleColor EXTRA_COLOR = ConsoleColor.Gray;
private const char EMPTY_BLOCK_CHAR = ' ';
private const char FULL_BLOCK_CHAR = '\u2588';
// Holds the current state of the board.
private static bool[,] board;
// The dimensions of the board in cells.
private static int width = 32;
private static int height = 32;
// True if cell rules can loop around edges.
private static bool loopEdges = true;
static void Main(string[] args)
{
// Use initializeRandomBoard for a larger, random board.
initializeDemoBoard();
initializeConsole();
// Run the game until the Escape key is pressed.
while (!Console.KeyAvailable || Console.ReadKey(true).Key != ConsoleKey.Escape) {
Program.drawBoard();
Program.updateBoard();
// Wait for a bit between updates.
Thread.Sleep(DELAY);
}
}
// Sets up the Console.
private static void initializeConsole()
{
Console.BackgroundColor = EXTRA_COLOR;
Console.Clear();
Console.CursorVisible = false;
// Each cell is two characters wide.
// Using an extra row on the bottom to prevent scrolling when drawing the board.
int width = Math.Max(Program.width, 8) * 2 + 1;
int height = Math.Max(Program.height, 8) + 1;
Console.SetWindowSize(width, height);
Console.SetBufferSize(width, height);
Console.BackgroundColor = DEAD_COLOR;
Console.ForegroundColor = LIVE_COLOR;
}
// Creates the initial board with a random state.
private static void initializeRandomBoard()
{
var random = new Random();
Program.board = new bool[Program.width, Program.height];
for (var y = 0; y < Program.height; y++) {
for (var x = 0; x < Program.width; x++) {
// Equal probability of being true or false.
Program.board[x, y] = random.Next(2) == 0;
}
}
}
// Creates a 3x3 board with a blinker.
private static void initializeDemoBoard()
{
Program.width = 3;
Program.height = 3;
Program.loopEdges = false;
Program.board = new bool[3, 3];
Program.board[1, 0] = true;
Program.board[1, 1] = true;
Program.board[1, 2] = true;
}
// Draws the board to the console.
private static void drawBoard()
{
// One Console.Write call is much faster than writing each cell individually.
var builder = new StringBuilder();
for (var y = 0; y < Program.height; y++) {
for (var x = 0; x < Program.width; x++) {
char c = Program.board[x, y] ? FULL_BLOCK_CHAR : EMPTY_BLOCK_CHAR;
// Each cell is two characters wide.
builder.Append(c);
builder.Append(c);
}
builder.Append('\n');
}
// Write the string to the console.
Console.SetCursorPosition(0, 0);
Console.Write (builder.ToString());
}
// Moves the board to the next state based on Conway's rules.
private static void updateBoard()
{
// A temp variable to hold the next state while it's being calculated.
bool[,] newBoard = new bool[Program.width, Program.height];
for (var y = 0; y < Program.height; y++) {
for (var x = 0; x < Program.width; x++) {
var n = countLiveNeighbors(x, y);
var c = Program.board[x, y];
// A live cell dies unless it has exactly 2 or 3 live neighbors.
// A dead cell remains dead unless it has exactly 3 live neighbors.
newBoard[x, y] = c && (n == 2 || n == 3) || !c && n == 3;
}
}
// Set the board to its new state.
Program.board = newBoard;
}
// Returns the number of live neighbors around the cell at position (x,y).
private static int countLiveNeighbors(int x, int y)
{
// The number of live neighbors.
int value = 0;
// This nested loop enumerates the 9 cells in the specified cells neighborhood.
for (var j = -1; j <= 1; j++) {
// If loopEdges is set to false and y+j is off the board, continue.
if (!Program.loopEdges && y + j < 0 || y + j >= Program.height) {
continue;
}
// Loop around the edges if y+j is off the board.
int k = (y + j + Program.height) % Program.height;
for (var i = -1; i <= 1; i++) {
// If loopEdges is set to false and x+i is off the board, continue.
if (!Program.loopEdges && x + i < 0 || x + i >= Program.width) {
continue;
}
// Loop around the edges if x+i is off the board.
int h = (x + i + Program.width) % Program.width;
// Count the neighbor cell at (h,k) if it is alive.
value += Program.board[h, k] ? 1 : 0;
}
}
// Subtract 1 if (x,y) is alive since we counted it as a neighbor.
return value - (Program.board[x, y] ? 1 : 0);
}
}
}
Output:
Frame 1: Frame 2: Frame 3:
██
██████ ██ ██████
██
C++
Because Nobody else included a version with Graphics, Here is a simple implementation using SFML for graphic rendering
#include <iostream>
#include <vector>
#include <SFML/Graphics.hpp>
#include <thread>
#include <chrono>
using namespace std;
class Life {
private:
int ticks;
bool pass;
int height;
int width;
bool **board;
bool **buffer;
vector<pair<float,float>> liveCoords;
void init();
void lives(int x, int y);
void dies(int x, int y);
bool isAlive(bool **curr, int x, int y);
int checkNeighbors(bool **curr, int x, int y);
void evaluatePosition(bool** curr, int x, int y);
public:
Life(int w = 100, int h = 50, int seed = 1337);
Life(const Life& life);
~Life();
vector<pair<float,float>> doTick();
Life& operator=(const Life& life);
};
void Life::init() {
board = new bool*[height];
buffer = new bool*[height];
for (int y = 0; y < height; y++) {
board[y] = new bool[width];
buffer[y] = new bool[width];
for (int x = 0; x < width; x++) {
board[y][x] = false;
buffer[y][x] = false;
}
}
}
Life::Life(int w, int h, int seed) {
width = w;
height = h;
init();
for (int i = 0; i < seed; i++) {
board[rand() % height][rand() % width] = true;
}
pass = true;
}
Life::Life(const Life& life) {
width = life.width;
height = life.height;
init();
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
board[y][x] = life.board[y][x];
buffer[y][x] = life.buffer[y][x];
}
}
}
Life& Life::operator=(const Life& life) {
width = life.width;
height = life.height;
init();
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
board[y][x] = life.board[y][x];
buffer[y][x] = life.buffer[y][x];
}
}
return *this;
}
Life::~Life() {
for (int i = 0; i < height; i++) {
delete [] board[i];
delete [] buffer[i];
}
delete [] board;
delete [] buffer;
}
vector<pair<float,float>> Life::doTick() {
liveCoords.clear();
bool **currentGeneration = pass ? board:buffer;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
evaluatePosition(currentGeneration, x, y);
}
}
pass = !pass;
ticks++;
return liveCoords;
}
bool Life::isAlive(bool **curr, int x, int y) {
return curr[y][x];
}
int Life::checkNeighbors(bool **curr, int x, int y) {
int lc = 0;
int dx[8] = {-1, 0, 1,1,1,-1, 0,-1};
int dy[8] = {-1,-1,-1,0,1, 1, 1, 0};
for (int i = 0; i < 8; i++) {
int nx = ((dx[i]+x)+width) % width;
int ny = ((dy[i]+y)+height) % height;
lc += isAlive(curr, nx, ny);
}
return lc;
}
void Life::lives(int x, int y) {
if (!pass) {
board[y][x] = true;
} else {
buffer[y][x] = true;
}
liveCoords.push_back(make_pair((float)x,(float)y));
}
void Life::dies(int x, int y) {
if (!pass) {
board[y][x] = false;
} else {
buffer[y][x] = false;
}
}
void Life::evaluatePosition(bool** generation, int x, int y) {
int lc = checkNeighbors(generation, x, y);
if (isAlive(generation, x, y)) {
if (lc == 2 || lc == 3) {
lives(x, y);
} else {
dies(x, y);
}
} else {
if (lc == 3) {
lives(x, y);
} else {
dies(x, y);
}
}
}
class App {
private:
void sleep();
void drawLiveCells();
void render();
void handleEvent(sf::Event& event);
void saveDisplay();
int width;
int height;
Life life;
bool isRecording;
int tick;
sf::RenderWindow* window;
sf::RenderTexture* texture;
public:
App(int w = 100, int h = 50);
void start();
};
App::App(int w, int h) {
height = h;
width = w;
life = Life(width, height);
isRecording = false;
tick = 0;
}
void App::start() {
sf::Event event;
window = new sf::RenderWindow(sf::VideoMode(width*10, height*10), "The Game of Life");
texture = new sf::RenderTexture();
texture->create(width*10, height*10);
window->setFramerateLimit(60);
while (window->isOpen()) {
while (window->pollEvent(event)) {
handleEvent(event);
}
render();
tick++;
}
delete window;
delete texture;
}
void App::handleEvent(sf::Event& event) {
if (event.type == sf::Event::Closed) {
window->close();
}
if (event.type == sf::Event::KeyPressed) {
switch (event.key.code) {
case sf::Keyboard::R:
life = Life(width, height);
break;
case sf::Keyboard::S:
isRecording = !isRecording;
break;
case sf::Keyboard::Q:
case sf::Keyboard::Escape:
window->close();
break;
default:
break;
}
}
}
void App::sleep() {
std::this_thread::sleep_for(350ms);
}
void App::drawLiveCells() {
float XSCALE = 10.0, YSCALE = 10.0;
sf::RectangleShape rect;
rect.setSize(sf::Vector2f(XSCALE, YSCALE));
rect.setFillColor(sf::Color::Green);
auto coords = life.doTick();
texture->clear(sf::Color::Black);
for (auto m : coords) {
rect.setPosition(m.first*XSCALE, m.second*YSCALE);
texture->draw(rect);
}
texture->display();
}
void App::render() {
drawLiveCells();
window->clear();
sf::Sprite sprite(texture->getTexture());
window->draw(sprite);
window->display();
if (isRecording) saveDisplay();
sleep();
}
void App::saveDisplay() {
string name = "tick" + to_string(tick) + ".png";
sf::Image image = texture->getTexture().copyToImage();
image.saveToFile(name);
}
int main(int argc, char* argv[]) {
srand(time(0));
App app;
app.start();
return 0;
}
And the output of the above program:
![](http://static.miraheze.org/rosettacodewiki/thumb/0/02/Game_of_Life.gif/300px-Game_of_Life.gif)
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);
}
- Output:
first a glider, then a blinker, over a few iterations
(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.. .... .... .... ==== ==== ====
Alternate version
Another aproach - a pretty simple one.
This version allows you to start the automata with different set of rules. Just for the fun of it.
#include <algorithm>
#include <vector>
#include <iostream>
#include <string>
typedef unsigned char byte;
class world {
public:
world( int x, int y ) : _wid( x ), _hei( y ) {
int s = _wid * _hei * sizeof( byte );
_cells = new byte[s];
memset( _cells, 0, s );
}
~world() {
delete [] _cells;
}
int wid() const {
return _wid;
}
int hei() const {
return _hei;
}
byte at( int x, int y ) const {
return _cells[x + y * _wid];
}
void set( int x, int y, byte c ) {
_cells[x + y * _wid] = c;
}
void swap( world* w ) {
memcpy( _cells, w->_cells, _wid * _hei * sizeof( byte ) );
}
private:
int _wid, _hei;
byte* _cells;
};
class rule {
public:
rule( world* w ) : wrd( w ) {
wid = wrd->wid();
hei = wrd->hei();
wrdT = new world( wid, hei );
}
~rule() {
if( wrdT ) delete wrdT;
}
bool hasLivingCells() {
for( int y = 0; y < hei; y++ )
for( int x = 0; x < wid; x++ )
if( wrd->at( x, y ) ) return true;
std::cout << "*** All cells are dead!!! ***\n\n";
return false;
}
void swapWrds() {
wrd->swap( wrdT );
}
void setRuleB( std::vector<int>& birth ) {
_birth = birth;
}
void setRuleS( std::vector<int>& stay ) {
_stay = stay;
}
void applyRules() {
int n;
for( int y = 0; y < hei; y++ ) {
for( int x = 0; x < wid; x++ ) {
n = neighbours( x, y );
if( wrd->at( x, y ) ) {
wrdT->set( x, y, inStay( n ) ? 1 : 0 );
} else {
wrdT->set( x, y, inBirth( n ) ? 1 : 0 );
}
}
}
}
private:
int neighbours( int xx, int yy ) {
int n = 0, nx, ny;
for( int y = -1; y < 2; y++ ) {
for( int x = -1; x < 2; x++ ) {
if( !x && !y ) continue;
nx = ( wid + xx + x ) % wid;
ny = ( hei + yy + y ) % hei;
n += wrd->at( nx, ny ) > 0 ? 1 : 0;
}
}
return n;
}
bool inStay( int n ) {
return( _stay.end() != find( _stay.begin(), _stay.end(), n ) );
}
bool inBirth( int n ) {
return( _birth.end() != find( _birth.begin(), _birth.end(), n ) );
}
int wid, hei;
world *wrd, *wrdT;
std::vector<int> _stay, _birth;
};
class cellular {
public:
cellular( int w, int h ) : rl( 0 ) {
wrd = new world( w, h );
}
~cellular() {
if( rl ) delete rl;
delete wrd;
}
void start( int r ) {
rl = new rule( wrd );
gen = 1;
std::vector<int> t;
switch( r ) {
case 1: // conway
t.push_back( 2 ); t.push_back( 3 ); rl->setRuleS( t );
t.clear(); t.push_back( 3 ); rl->setRuleB( t );
break;
case 2: // amoeba
t.push_back( 1 ); t.push_back( 3 ); t.push_back( 5 ); t.push_back( 8 ); rl->setRuleS( t );
t.clear(); t.push_back( 3 ); t.push_back( 5 ); t.push_back( 7 ); rl->setRuleB( t );
break;
case 3: // life34
t.push_back( 3 ); t.push_back( 4 ); rl->setRuleS( t );
rl->setRuleB( t );
break;
case 4: // maze
t.push_back( 1 ); t.push_back( 2 ); t.push_back( 3 ); t.push_back( 4 ); t.push_back( 5 ); rl->setRuleS( t );
t.clear(); t.push_back( 3 ); rl->setRuleB( t );
break;
}
/* just for test - shoud read from a file */
/* GLIDER */
wrd->set( 6, 1, 1 ); wrd->set( 7, 2, 1 );
wrd->set( 5, 3, 1 ); wrd->set( 6, 3, 1 );
wrd->set( 7, 3, 1 );
/* BLINKER */
wrd->set( 1, 3, 1 ); wrd->set( 2, 3, 1 );
wrd->set( 3, 3, 1 );
/******************************************/
generation();
}
private:
void display() {
system( "cls" );
int wid = wrd->wid(),
hei = wrd->hei();
std::cout << "+" << std::string( wid, '-' ) << "+\n";
for( int y = 0; y < hei; y++ ) {
std::cout << "|";
for( int x = 0; x < wid; x++ ) {
if( wrd->at( x, y ) ) std::cout << "#";
else std::cout << ".";
}
std::cout << "|\n";
}
std::cout << "+" << std::string( wid, '-' ) << "+\n";
std::cout << "Generation: " << gen << "\n\nPress [RETURN] for the next generation...";
std::cin.get();
}
void generation() {
do {
display();
rl->applyRules();
rl->swapWrds();
gen++;
}
while ( rl->hasLivingCells() );
}
rule* rl;
world* wrd;
int gen;
};
int main( int argc, char* argv[] ) {
cellular c( 20, 12 );
std::cout << "\n\t*** CELLULAR AUTOMATA ***" << "\n\n Which one you want to run?\n\n\n";
std::cout << " [1]\tConway's Life\n [2]\tAmoeba\n [3]\tLife 34\n [4]\tMaze\n\n > ";
int o;
do {
std::cin >> o;
}
while( o < 1 || o > 4 );
std::cin.ignore();
c.start( o );
return system( "pause" );
}
- Output:
+--------------------+ +--------------------+ +--------------------+ +--------------------+ |....................| |....................| |....................| |....................| |......#.............| |....................| |....................| |....................| |.......#............| |..#..#.#............| |.......#............| |..#...#.............| |.###.###............| |..#...##............| |.###.#.#............| |..#....##...........| |....................| |..#...#.............| |......##............| |..#...##............| |....................| |....................| |....................| |....................| |....................| |....................| |....................| |....................| |....................| |....................| |....................| |....................| +--------------------+ +--------------------+ +--------------------+ +--------------------+ Generation: 1 Generation: 2 Generation: 3 Generation: 4
Simple Without Classes
Shows a glider over 20 generations Board edges wrap around to simulate infinite board
#include <iostream>
#include <vector>
#include <numeric>
// ----------------------------------------------------------------------------
using Row = std::vector<int>;
using Cells = std::vector<Row>;
// ----------------------------------------------------------------------------
Cells board = {
{0,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,0,0,0},
{0,0,0,1,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,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
};
int numRows = 10;
int numCols = 20;
// ----------------------------------------------------------------------------
int getNeighbor(int row, int col, Cells& board) {
// use modulus to get wrapping effect at board edges
return board.at((row + numRows) % numRows).at((col + numCols) % numCols);
}
int getCount(int row, int col, Cells& board) {
int count = 0;
std::vector<int> deltas {-1, 0, 1};
for (int dc : deltas) {
for (int dr : deltas) {
if (dr || dc) {
count += getNeighbor(row + dr, col + dc, board);
}
}
}
return count;
}
void showCell(int cell) {
std::cout << (cell ? "*" : " ");
}
void showRow(const Row& row) {
std::cout << "|";
for (int cell : row) {showCell(cell);}
std::cout << "|\n";
}
void showCells(Cells board) {
for (const Row& row : board) { showRow(row); }
}
int tick(Cells& board, int row, int col) {
int count = getCount(row, col, board);
bool birth = !board.at(row).at(col) && count == 3;
bool survive = board.at(row).at(col) && (count == 2 || count == 3);
return birth || survive;
}
void updateCells(Cells& board) {
Cells original = board;
for (int row = 0; row < numRows; row++) {
for (int col = 0; col < numCols; col++) {
board.at(row).at(col) = tick(original, row, col);
}
}
}
int main () {
for (int gen = 0; gen < 20; gen++) {
std::cout << "\ngeneration " << gen << ":\n";
showCells(board);
updateCells(board);
}
}
- Output:
generation 0: | | | * | | * | | *** | | | | | | | | | | | | |
generation 1: | | | | | * * | | ** | | * | | | | | | | | | | |
generation 2: | | | | | * | | * * | | ** | | | | | | | | | | |
generation 3: | | | | | * | | ** | | ** | | | | | | | | | | | ...
Chapel
Compile and run with chpl gol.chpl; ./gol --gridWidth [x] --gridHeight [y]
.
config const gridHeight : int = 3;
config const gridWidth : int = 3;
enum state { dead = 0, alive = 1 };
class ConwaysGameofLife
{
var gridDomain : domain(2, int);
var computeDomain : subdomain(gridDomain);
var grid : [gridDomain] state;
proc init(height : int, width : int)
{
this.gridDomain = {0..#height+2, 0..#width+2};
this.computeDomain = this.gridDomain.expand(-1);
}
proc step() : void
{
var tempGrid: [this.computeDomain] state;
forall (i,j) in this.computeDomain
{
var isAlive = this.grid[i,j] == state.alive;
var numAlive = (+ reduce this.grid[i-1..i+1, j-1..j+1]:int) - if isAlive then 1 else 0;
tempGrid[i,j] = if ( (2 == numAlive && isAlive) || numAlive == 3 ) then state.alive else state.dead ;
}
this.grid[this.computeDomain] = tempGrid;
}
proc this(i : int, j : int) ref : state
{
return this.grid[i,j];
}
proc prettyPrint() : string
{
var str : string;
for i in this.gridDomain.dim(0)
{
if i == 0 || i == gridDomain.dim(0).last
{
for j in this.gridDomain.dim(1)
{
str += "-";
}
}
else
{
for j in this.gridDomain.dim(1)
{
if j == 0 || j == this.gridDomain.dim(1).last
{
str += "|";
}
else
{
str += if this.grid[i,j] == state.alive then "#" else " ";
}
}
}
str += "\n";
}
return str;
}
}
proc main()
{
var game = new ConwaysGameofLife(gridHeight, gridWidth);
game[gridHeight/2 + 1, gridWidth/2 ] = state.alive;
game[gridHeight/2 + 1, gridWidth/2 + 1 ] = state.alive;
game[gridHeight/2 + 1, gridWidth/2 + 2 ] = state.alive;
for i in 1..3
{
writeln(game.prettyPrint());
game.step();
}
}
Output:
----- | | |###| | | ----- ----- | # | | # | | # | ----- ----- | | |###| | | -----
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]})
COBOL
identification division.
program-id. game-of-life-program.
data division.
working-storage section.
01 grid.
05 cell-table.
10 row occurs 5 times.
15 cell pic x value space occurs 5 times.
05 next-gen-cell-table.
10 next-gen-row occurs 5 times.
15 next-gen-cell pic x occurs 5 times.
01 counters.
05 generation pic 9.
05 current-row pic 9.
05 current-cell pic 9.
05 living-neighbours pic 9.
05 neighbour-row pic 9.
05 neighbour-cell pic 9.
05 check-row pic s9.
05 check-cell pic s9.
procedure division.
control-paragraph.
perform blinker-paragraph varying current-cell from 2 by 1
until current-cell is greater than 4.
perform show-grid-paragraph through life-paragraph
varying generation from 0 by 1
until generation is greater than 2.
stop run.
blinker-paragraph.
move '#' to cell(3,current-cell).
show-grid-paragraph.
display 'GENERATION ' generation ':'.
display ' +---+'.
perform show-row-paragraph varying current-row from 2 by 1
until current-row is greater than 4.
display ' +---+'.
display ''.
life-paragraph.
perform update-row-paragraph varying current-row from 2 by 1
until current-row is greater than 4.
move next-gen-cell-table to cell-table.
show-row-paragraph.
display ' |' with no advancing.
perform show-cell-paragraph varying current-cell from 2 by 1
until current-cell is greater than 4.
display '|'.
show-cell-paragraph.
display cell(current-row,current-cell) with no advancing.
update-row-paragraph.
perform update-cell-paragraph varying current-cell from 2 by 1
until current-cell is greater than 4.
update-cell-paragraph.
move 0 to living-neighbours.
perform check-row-paragraph varying check-row from -1 by 1
until check-row is greater than 1.
evaluate living-neighbours,
when 2 move cell(current-row,current-cell) to next-gen-cell(current-row,current-cell),
when 3 move '#' to next-gen-cell(current-row,current-cell),
when other move space to next-gen-cell(current-row,current-cell),
end-evaluate.
check-row-paragraph.
add check-row to current-row giving neighbour-row.
perform check-cell-paragraph varying check-cell from -1 by 1
until check-cell is greater than 1.
check-cell-paragraph.
add check-cell to current-cell giving neighbour-cell.
if cell(neighbour-row,neighbour-cell) is equal to '#',
and check-cell is not equal to zero or check-row is not equal to zero,
then add 1 to living-neighbours.
end program game-of-life-program.
- Output:
GENERATION 0: +---+ | | |###| | | +---+ GENERATION 1: +---+ | # | | # | | # | +---+ GENERATION 2: +---+ | | |###| | | +---+
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)))
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:
------------------------------------------------------------- | | | | | # # # # | | # # # # # # # # | | ## ## ## ## # # | | # | | # # | | #### | | | | | | | | | | | | | | | | | | | | | | | | | -------------------------------------------------------------
Faster Version
Same output.
import std.stdio, std.string, std.algorithm, std.typetuple,
std.array, std.conv;
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; TypeTuple!(-1, 0, 1))
/*static*/ foreach (immutable j; TypeTuple!(-1, 0, 1))
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;
}
}
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:
# # # ### # # # ###
Delphi
Thanks Rudy Velthuis for the Velthuis.Console library.
program game_of_life;
{$APPTYPE CONSOLE}
uses
System.SysUtils,
Velthuis.Console; // CrlScr
type
TBoolMatrix = TArray<TArray<Boolean>>;
TField = record
s: TBoolMatrix;
w, h: Integer;
procedure SetValue(x, y: Integer; b: boolean);
function Next(x, y: Integer): boolean;
function State(x, y: Integer): boolean;
class function NewField(w1, h1: Integer): TField; static;
end;
TLife = record
a, b: TField;
w, h: Integer;
class function NewLife(w1, h1: Integer): TLife; static;
procedure Step;
function ToString: string;
end;
{ TField }
class function TField.NewField(w1, h1: Integer): TField;
var
s1: TBoolMatrix;
begin
SetLength(s1, h1);
for var i := 0 to High(s1) do
SetLength(s1[i], w1);
with Result do
begin
s := s1;
w := w1;
h := h1;
end;
end;
function TField.Next(x, y: Integer): boolean;
var
_on: Integer;
begin
_on := 0;
for var i := -1 to 1 do
for var j := -1 to 1 do
if self.State(x + i, y + j) and not ((j = 0) and (i = 0)) then
inc(_on);
Result := (_on = 3) or (_on = 2) and self.State(x, y);
end;
procedure TField.SetValue(x, y: Integer; b: boolean);
begin
self.s[y, x] := b;
end;
function TField.State(x, y: Integer): boolean;
begin
while y < 0 do
inc(y, self.h);
while x < 0 do
inc(x, self.w);
result := self.s[y mod self.h, x mod self.w]
end;
{ TLife }
class function TLife.NewLife(w1, h1: Integer): TLife;
var
a1: TField;
begin
a1 := TField.NewField(w1, h1);
for var i := 0 to (w1 * h1 div 2) do
a1.SetValue(Random(w1), Random(h1), True);
with Result do
begin
a := a1;
b := TField.NewField(w1, h1);
w := w1;
h := h1;
end;
end;
procedure TLife.Step;
var
tmp: TField;
begin
for var y := 0 to self.h - 1 do
for var x := 0 to self.w - 1 do
self.b.SetValue(x, y, self.a.Next(x, y));
tmp := self.a;
self.a := self.b;
self.b := tmp;
end;
function TLife.ToString: string;
begin
result := '';
for var y := 0 to self.h - 1 do
begin
for var x := 0 to self.w - 1 do
begin
var b: char := ' ';
if self.a.State(x, y) then
b := '*';
result := result + b;
end;
result := result + #10;
end;
end;
begin
Randomize;
var life := TLife.NewLife(80, 15);
for var i := 1 to 300 do
begin
life.Step;
ClrScr;
writeln(life.ToString);
sleep(30);
end;
readln;
end.
- Output:
* *** * * * * ** ** * **** * * * * *** * * ** * * *** ** * * * * * * ** ** **** ** * ****** ** * * * * * *** ** *** ** * * * * ** * * * * * * * * *** * * * ** * *** * * * ***
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)
}
EasyLang
n = 70
time = 0.1
#
nx = n + 1
subr init
for r = 1 to n
for c = 1 to n
i = r * nx + c
if randomf < 0.3
f[i] = 1
.
.
.
.
f = 100 / n
subr show
clear
for r = 1 to n
for c = 1 to n
if f[r * nx + c] = 1
move c * f - f r * f - f
rect f * 0.9 f * 0.9
.
.
.
.
subr update
swap f[] p[]
for r = 1 to n
sm = 0
i = r * nx + 1
sr = p[i - nx] + p[i] + p[i + nx]
for c = 1 to n
sl = sm
sm = sr
in = i + 1
sr = p[in - nx] + p[in] + p[in + nx]
s = sl + sm + sr
if s = 3 or s = 4 and p[i] = 1
f[i] = 1
else
f[i] = 0
.
i = in
.
.
.
on timer
update
show
timer time
.
on mouse_down
c = mouse_x div f + 1
r = mouse_y div f + 1
i = r * nx + c
f[i] = 1 - f[i]
show
timer 3
.
len f[] nx * nx + nx
len p[] nx * nx + nx
init
timer 0
eC
import "ecere"
define seed = 12345;
define popInit = 1000;
define width = 100;
define height = 100;
define cellWidth = 4;
define cellHeight = 4;
Array<byte> grid { size = width * height };
Array<byte> newState { size = width * height };
class GameOfLife : Window
{
caption = $"Conway's Game of Life";
background = lightBlue;
borderStyle = sizable;
hasMaximize = true;
hasMinimize = true;
hasClose = true;
clientSize = { width * cellWidth, height * cellHeight };
Timer tickTimer
{
delay = 0.05, started = true, userData = this;
bool DelayExpired()
{
int y, x, ix = 0;
for(y = 0; y < height; y++)
{
for(x = 0; x < width; x++, ix++)
{
int nCount = 0;
byte alive;
if(x > 0 && y > 0 && grid[ix - width - 1]) nCount++;
if( y > 0 && grid[ix - width ]) nCount++;
if(x < width-1 && y > 0 && grid[ix - width + 1]) nCount++;
if(x > 0 && grid[ix - 1]) nCount++;
if(x < width - 1 && grid[ix + 1]) nCount++;
if(x > 0 && y < height-1 && grid[ix + width - 1]) nCount++;
if( y < height-1 && grid[ix + width ]) nCount++;
if(x < width-1 && y < height-1 && grid[ix + width + 1]) nCount++;
if(grid[ix])
alive = nCount >= 2 && nCount <= 3; // Death
else
alive = nCount == 3; // Birth
newState[ix] = alive;
}
}
memcpy(grid.array, newState.array, width * height);
Update(null);
return true;
}
};
void OnRedraw(Surface surface)
{
int x, y;
int ix = 0;
surface.background = navy;
for(y = 0; y < height; y++)
{
for(x = 0; x < width; x++, ix++)
{
if(grid[ix])
{
int sy = y * cellHeight;
int sx = x * cellWidth;
surface.Area(sx, sy, sx + cellWidth-1, sy + cellHeight-1);
}
}
}
}
bool OnCreate()
{
int i;
RandomSeed(seed);
for(i = 0; i < popInit; i++)
{
int x = GetRandom(0, width-1);
int y = GetRandom(0, height-1);
grid[y * width + x] = 1;
}
return true;
}
}
GameOfLife life {};
Egel
import "prelude.eg"
import "io.ego"
using System
using List
using IO
def boardsize = 5
def empty = [ X Y -> 0 ]
def insert =
[ X Y BOARD ->
[ X0 Y0 -> if and (X0 == X) (Y0 == Y) then 1
else BOARD X0 Y0 ] ]
def coords =
let R = fromto 0 (boardsize - 1) in
[ XX YY -> map (\X -> map (\Y -> X Y) YY) XX ] R R
def printcell =
[ 0 -> print ". "
| _ -> print "* " ]
def printboard =
[ BOARD ->
let M = map [XX -> let _ = map [(X Y) -> printcell (BOARD X Y)] XX in print "\n" ] coords in
nop ]
def count =
[ BOARD, X, Y ->
(BOARD (X - 1) (Y - 1)) + (BOARD (X) (Y - 1)) + (BOARD (X+1) (Y - 1)) +
(BOARD (X - 1) Y) + (BOARD (X+1) Y) +
(BOARD (X - 1) (Y+1)) + (BOARD (X) (Y+1)) + (BOARD (X+1) (Y+1)) ]
def next =
[ 0 N -> if N == 3 then 1 else 0
| _ N -> if or (N == 2) (N == 3) then 1 else 0 ]
def updateboard =
[ BOARD ->
let XX = map (\(X Y) -> X Y (BOARD X Y) (count BOARD X Y)) (flatten coords) in
let YY = map (\(X Y C N) -> X Y (next C N)) XX in
foldr [(X Y 0) BOARD -> BOARD | (X Y _) BOARD -> insert X Y BOARD ] empty YY ]
def blinker =
(insert 1 2) @ (insert 2 2) @ (insert 3 2)
def main =
let GEN0 = blinker empty in
let GEN1 = updateboard GEN0 in
let GEN2 = updateboard GEN1 in
let _ = map [ G -> let _ = print "generation:\n" in printboard G ] {GEN0, GEN1, GEN2} in
nop
Elena
ELENA 6.x, using cellular library
import extensions;
import system'threading;
import system'text;
import cellular;
const int maxX = 48;
const int maxY = 28;
const int DELAY = 50;
sealed class Model
{
Space _space;
RuleSet _ruleSet;
bool _started;
Func<Space, object> OnUpdate : event;
constructor newRandomset(RuleSet transformSet)
{
_space := IntMatrixSpace.allocate(maxY, maxX, randomSet);
_ruleSet := transformSet;
_started := false
}
private onUpdate()
{
OnUpdate.?(_space)
}
run()
{
if (_started)
{
_space.update(_ruleSet)
}
else
{
_started := true
};
self.onUpdate()
}
}
singleton gameOfLifeRuleSet : RuleSet
{
int proceed(Space s, int x, int y)
{
int cell := s.at(x, y);
int number := s.LiveCell(x, y, 1); // NOTE : number of living cells around the self includes the cell itself
if (cell == 0 && number == 3)
{
^ 1
}
else if (cell == 1 && (number == 4 || number == 3))
{
^ 1
}
else
{
^ 0
}
}
}
public extension presenterOp : Space
{
print()
{
console.setCursorPosition(0, 0);
int columns := self.Columns;
int rows := self.Rows;
auto line := new TextBuilder();
for(int i := 0; i < rows; i += 1)
{
line.clear();
for(int j := 0; j < columns; j += 1)
{
int cell := self.at(i, j);
line.write((cell == 0).iif(" ","o"));
};
console.writeLine(line.Value)
}
}
}
public program()
{
auto model := Model.newRandomset(gameOfLifeRuleSet);
console.clear();
model.OnUpdate := (Space sp){ sp.print() };
until (console.KeyAvailable)
{
model.run();
threadControl.sleep(DELAY)
};
console.readChar()
}
Elixir
defmodule Conway do
def game_of_life(name, size, generations, initial_life\\nil) do
board = seed(size, initial_life)
print_board(board, name, size, 0)
reason = generate(name, size, generations, board, 1)
case reason do
:all_dead -> "no more life."
:static -> "no movement"
_ -> "specified lifetime ended"
end
|> IO.puts
IO.puts ""
end
defp new_board(n) do
for x <- 1..n, y <- 1..n, into: %{}, do: {{x,y}, 0}
end
defp seed(n, points) do
if points do
points
else # randomly seed board
(for x <- 1..n, y <- 1..n, do: {x,y}) |> Enum.take_random(10)
end
|> Enum.reduce(new_board(n), fn pos,acc -> %{acc | pos => 1} end)
end
defp generate(_, _, generations, _, gen) when generations < gen, do: :ok
defp generate(name, size, generations, board, gen) do
new = evolve(board, size)
print_board(new, name, size, gen)
cond do
barren?(new) -> :all_dead
board == new -> :static
true -> generate(name, size, generations, new, gen+1)
end
end
defp evolve(board, n) do
for x <- 1..n, y <- 1..n, into: %{}, do: {{x,y}, fate(board, x, y, n)}
end
defp fate(board, x, y, n) do
irange = max(1, x-1) .. min(x+1, n)
jrange = max(1, y-1) .. min(y+1, n)
sum = ((for i <- irange, j <- jrange, do: board[{i,j}]) |> Enum.sum) - board[{x,y}]
cond do
sum == 3 -> 1
sum == 2 and board[{x,y}] == 1 -> 1
true -> 0
end
end
defp barren?(board) do
Enum.all?(board, fn {_,v} -> v == 0 end)
end
defp print_board(board, name, n, generation) do
IO.puts "#{name}: generation #{generation}"
Enum.each(1..n, fn y ->
Enum.map(1..n, fn x -> if board[{x,y}]==1, do: "#", else: "." end)
|> IO.puts
end)
end
end
Conway.game_of_life("blinker", 3, 2, [{2,1},{2,2},{2,3}])
Conway.game_of_life("glider", 4, 4, [{2,1},{3,2},{1,3},{2,3},{3,3}])
Conway.game_of_life("random", 5, 10)
- Output:
blinker: generation 0 .#. .#. .#. blinker: generation 1 ... ### ... blinker: generation 2 .#. .#. .#. specified lifetime ended glider: generation 0 .#.. ..#. ###. .... glider: generation 1 .... #.#. .##. .#.. glider: generation 2 .... ..#. #.#. .##. glider: generation 3 .... .#.. ..## .##. glider: generation 4 .... ..#. ...# .### specified lifetime ended random: generation 0 .#... #.#.. #...# ###.# ..#.. random: generation 1 .#... #.... #.#.. #.#.. ..##. random: generation 2 ..... #.... #.... ..#.. .###. random: generation 3 ..... ..... .#... ..##. .###. random: generation 4 ..... ..... ..#.. ...#. .#.#. random: generation 5 ..... ..... ..... ...#. ..#.. random: generation 6 ..... ..... ..... ..... ..... no more life.
Emacs Lisp
#!/usr/bin/env emacs -script
;; -*- lexical-binding: t -*-
;; run: ./conways-life conways-life.config
(require 'cl-lib)
(defconst blinker '("***"))
(defconst toad '(".***" "***."))
(defconst pentomino-p '(".**" ".**" ".*."))
(defconst pi-heptomino '("***" "*.*" "*.*"))
(defconst glider '(".*." "..*" "***"))
(defconst pre-pulsar '("***...***" "*.*...*.*" "***...***"))
(defconst ship '("**." "*.*" ".**"))
(defconst pentadecathalon '("**********"))
(defconst clock '("..*." "*.*." ".*.*" ".*.."))
(defmacro swap (a b)
`(setq ,b (prog1 ,a (setq ,a ,b))))
(cl-defstruct world rows cols data)
(defun new-world (rows cols)
(make-world :rows rows :cols cols :data (make-vector (* rows cols) nil)))
(defmacro world-pt (w r c)
`(+ (* (mod ,r (world-rows ,w)) (world-cols ,w))
(mod ,c (world-cols ,w))))
(defmacro world-ref (w r c)
`(aref (world-data ,w) (world-pt ,w ,r ,c)))
(defun print-world (world)
(dotimes (r (world-rows world))
(dotimes (c (world-cols world))
(princ (format "%c" (if (world-ref world r c) ?* ?.))))
(terpri)))
(defun insert-pattern (world row col shape)
(let ((r row)
(c col))
(unless (listp shape)
(setq shape (symbol-value shape)))
(dolist (row-data shape)
(dolist (col-data (mapcar 'identity row-data))
(setf (world-ref world r c) (not (or (eq col-data ?.))))
(setq c (1+ c)))
(setq r (1+ r))
(setq c col))))
(defun neighbors (world row col)
(let ((n 0))
(dolist (offset '((1 . 1) (1 . 0) (1 . -1) (0 . 1) (0 . -1) (-1 . 1) (-1 . 0) (-1 . -1)))
(when (world-ref world (+ row (car offset)) (+ col (cdr offset)))
(setq n (1+ n))))
n))
(defun advance-generation (old new)
(dotimes (r (world-rows old))
(dotimes (c (world-cols old))
(let ((n (neighbors old r c)))
(setf (world-ref new r c)
(if (world-ref old r c)
(or (= n 2) (= n 3))
(= n 3)))))))
(defun read-config (file-name)
(with-temp-buffer
(insert-file-contents-literally file-name)
(read (current-buffer))))
(defun get-config (key config)
(let ((val (assoc key config)))
(if (null val)
(error (format "missing value for %s" key))
(cdr val))))
(defun insert-patterns (world patterns)
(dolist (p patterns)
(apply 'insert-pattern (cons world p))))
(defun simulate-life (file-name)
(let* ((config (read-config file-name))
(rows (get-config 'rows config))
(cols (get-config 'cols config))
(generations (get-config 'generations config))
(a (new-world rows cols))
(b (new-world rows cols)))
(insert-patterns a (get-config 'patterns config))
(dotimes (g generations)
(princ (format "generation %d\n" g))
(print-world a)
(advance-generation a b)
(swap a b))))
(simulate-life (elt command-line-args-left 0))
Configuration file, which defines the size starting patterns and how long the simulation will run.
((rows . 8)
(cols . 10)
(generations . 3)
(patterns
;; Blinker is defined in the script.
(1 1 blinker)
;; This is a custom pattern.
(4 4 (".***"
"***."))))
- Output:
generation 0 .......... .***...... .......... .......... .....***.. ....***... .......... .......... generation 1 ..*....... ..*....... ..*....... ......*... ....*..*.. ....*..*.. .....*.... .......... generation 2 .......... .***...... .......... .......... .....***.. ....***... .......... ..........
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)).
ERRE
This is a simple implementation of Conway's game of Life with an endless world. Test pattern configuration is 'glider'.
PROGRAM LIFE
!$INTEGER
!$KEY
!for C-64 compatibility
CONST Xmax=38,Ymax=20
DIM x,y,N
DIM WORLD[39,21],NextWORLD[39,21]
BEGIN
! 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
!------------------------------------------
PRINT(CHR$(12);"Press any key to interrupt")
LOOP
PRINT(CHR$(11);) PRINT
PRINT(STRING$(Xmax+2,"-"))
!---------- endless world ---------
FOR y=1 TO Ymax DO
WORLD[0,y]=WORLD[Xmax,y]
WORLD[Xmax+1,y]=WORLD[1,y]
END FOR
FOR x=1 TO Xmax DO
WORLD[x,0]=WORLD[x,Ymax]
WORLD[x,Ymax+1]=WORLD[x,1]
END FOR
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 DO
PRINT("|";)
FOR x=1 TO Xmax DO
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=N+WORLD[x,y+1]+WORLD[x+1,y-1]+WORLD[x+1,y]+WORLD[x+1,y+1]
IF (WORLD[x,y]<>0 AND (N=2 OR N=3)) OR (WORLD[x,y]=0 AND N=3) THEN
NextWORLD[x,y]=1
ELSE
NextWORLD[x,y]=0
END IF
END FOR
PRINT("|")
END FOR
PRINT(STRING$(Xmax+2,"-"))
PAUSE(0.1)
FOR x=0 TO Xmax+1 DO
FOR y=0 TO Ymax+1 DO
WORLD[x,y]=NextWORLD[x,y]
NextWORLD[x,y]=0
END FOR
END FOR
REPEAT
GET(A$)
UNTIL A$<>""
EXIT IF A$=CHR$(27)
END LOOP
PRINT("Press any key to exit")
REPEAT
UNTIL GETKEY$<>""
END PROGRAM
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
Fermat
;{Conway's Game of Life in Fermat}
;{square grid with wrap-around boundaries}
size:=50; {how big a grid do you want? This fits my screen OK, change this for your own screen}
Array w1[size,size], w2[size,size]; {set up an active world and a 'scratchpad' world}
act:=1;
buf:=2;
%[1]:=[w1]; {Fermat doesn't have 3D arrays in the normal sense--}
%[2]:=[w2]; {we need to use the somewhat odd "array of arrays" functionality}
Func Cls = for i = 1 to size do !!; od.; {"clear screen" by printing a bunch of newlines}
Func Draw = {draw the active screen}
for i = 1 to size do
for j = 1 to size do
if %[act][i, j] = 1 then !('# ') else !('. ') fi;
od;
!;
od;
.;
Func Rnd = {randomize the grid with a density of 40% live cells}
for i = 1 to size do
for j = 1 to size do
if Rand|5<2 then %[act][i, j] := 1 else %[act][i, j] := 0 fi;
od;
od;
Cls;
Draw;
.;
Func Blinker = {clears the screen except for a blinker in the top left corner}
for i = 1 to size do
for j = 1 to size do
%[act][i, j] := 0;
od;
od;
%[act][1,2] := 1;
%[act][2,2] := 1;
%[act][3,2] := 1;
Cls;
Draw;
.;
Func Iter = {do one iteration}
for i = 1 to size do
if i = 1 then im := size else im := i - 1 fi; {handle wrap around}
if i = size then ip := 1 else ip := i + 1 fi;
for j = 1 to size do
if j = 1 then jm := size else jm := j - 1 fi;
if j = size then jp := 1 else jp := j + 1 fi;
neigh := %[act][im, jm]; {count neigbours}
neigh :+ (%[act][im, j ]);
neigh :+ (%[act][im, jp]);
neigh :+ (%[act][i , jm]);
neigh :+ (%[act][i , jp]);
neigh :+ (%[act][ip, jm]);
neigh :+ (%[act][ip, j ]);
neigh :+ (%[act][ip, jp]);
if neigh < 2 or neigh > 3 then %[buf][i, j] := 0 fi; {alive and dead rules}
if neigh = 2 then %[buf][i, j] := %[act][i, j] fi;
if neigh = 3 then %[buf][i, j] := 1 fi;
od;
od;
Swap(act, buf); {rather than copying the scratch over into the active, just exchange their identities}
Cls;
Draw;
.;
choice := 9;
while choice <> 0 do {really rough menu, not the point of this exercise}
?choice;
if choice=4 then Cls fi;
if choice=3 then Blinker fi;
if choice=2 then Rnd fi;
if choice=1 then Iter fi;
od;
!!'John Horton Conway (26 December 1937 – 11 April 2020)';
- Output:
One iteration starting from random soup, showing a few well-known objects (blinker, block, glider, beehive, loaf)
# . . . . . . . . . # . . # . . . . . . . . # . . . . . . . . . . . . . . . . . . . . . . # # . . . . . . . . . . # . . # . . . # . . . . # # . # # . . # # # . . . . . . . . . . . . . . . # # . # . # . . . . . . # . # # . . . # . . . . . # # . # . . . # . . # . . . . . . . . . . . . . . # # . . # . . . . . . . # . # . # . . # . . . . . # . # # . . # . . . # . . . . # # . . . . . . . . . . . . . . . . . . . . . # . # # . . # . . . # # # # . . . . # . . # . . . . # . . # . . . . . . . . . . . . . . . . . . . . . . . . . # . . . . # . . . . . . . . . . . . . . . . # . . # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . # # # . . . . . . # # . . . . . . # . . # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . # . . . . . . . . . . . . . . . # . # . . . . . . . . . . . . . . . . . # # # . . . . . . . . . . . # . . . . # # . . . . . . . . . . # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . # # # # . . # . # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . # # . . # . . # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . # # # . . . . . . . # # . . . . . # . . . . . . . . . . # . . . . . . . . . . . . . . . . . . . . . # # # . . . . . . . . . . . # # . . . . . . . . . . . # . # . . . . . . . . . . . . . . . . . . . # . . # . # # . . . . . . # # . # . . . . . . . . . . . # . . # . . . . . . . . . . . . . . . . . . . # # . # # . . . . . . . . # # . . . . . . . . . . . . . # # . . . . . . . . . . . . . . . . . . . . # # # # # . . . . . . . . # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . # . . # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . # # . . . . . . . . . . . . . . . . # # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . # # . . . . . . . . . . . . . . . # # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . # # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . # # # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . # # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . # . # # . . . . . . # # # # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . # # . . . . . . # . . . # . . . . . . # # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . # # . # . . . . . . # . . # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . # # . . . . . . . . . . . . . . . . . . . . . . . . . . . . # # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . # . # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . # . . . . . . . . . . . # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . # # # # # . . . . . . . # # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . # . # # . # # . . . . . . # # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . # # . . # . # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . # . # . # # . . . . . . . # # . . . # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . # . # . # . . . . . . . . . # # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . # . . . . # . # # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . # . . . # # # # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . # # # . . # # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . # # # . . . . . . . . . . . . . . . . . . . . . . . . # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . # # # . . . . . . . . . . # . . . . . . . . . . . . . . . . . . . . # . . . . . . . . . . . . . . # . # . # # . # . . . . . . # . . . . . . . . . . . . . . . . . . . . # . . . . . . . . . . . . . # . . . . . . . . # . . . . . . . . . . . . . . . . . . . . . . . . . . # . . . . . . . . . . . . # . . # # # # . . . # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . # # # . . . . . . # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . # # . . . . . . . . . . . . . . . . . . # # . . . . . . . . . . . . . . . . . . . . . . . . . . . # . # . . . . . . . . . . . . . # # . . # . . . . . . . . . . . . . . . . . . . . . . . . . . . . # . . # . . . . . . . . . . . . . # . # . . # . . . . . . . . . . . . . . # . . . . . . . . . . . . # . . . . . . . . . . . . . . . . # . . . . . . # . . . . . . . . . . . # . . # . . . . . . . . . # # . # . . . . . . . . . . . . . . . . . . . . # . . . . . . . . . . . . . # # . . . . . . . . . . . . . . # # . . . . . . . . . . . # . # . . . . # . . # . . . . . . . . . . . . . . . . . . . . . . . # # # . #
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
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 NextgenV2(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 j = 1, SIZE(cells, 2)-2
DO i = 1, SIZE(cells, 1)-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
!###########################################################################
! In this version instead of cycling through all points an integer array
! is used the sum the live neighbors of all points. The sum is done with
! the entire array cycling through the eight positions of the neighbors.
! Executing a grid size of 10000 in 500 generations this version gave a
! speedup of almost 4 times.
!###########################################################################
PURE SUBROUTINE NextgenV2(cells)
LOGICAL, INTENT(IN OUT) :: cells(:,:)
INTEGER(KIND=1) :: buffer(1:SIZE(cells, 1)-2,1:SIZE(cells, 2)-2)
INTEGER :: gridsize, i, j
gridsize=SIZE(cells, 1)
buffer=0
DO j=-1, 1
DO i=-1,1
IF(i==0 .AND. j==0) CYCLE
WHERE(cells(i+2:gridsize-i-1,j+2:gridsize-j-1)) buffer=buffer+1
END DO
END DO
WHERE(buffer<2 .or. buffer>3) cells(2:gridsize-1,2:gridsize-1) = .FALSE.
WHERE(buffer==3) cells(2:gridsize-1,2:gridsize-1) = .TRUE.
END SUBROUTINE NextgenV2
!###########################################################################
END PROGRAM LIFE_2D
- 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 ### ### ### ### ### ###
Frink
Simple solution using two dictionaries (a display and a toggle) to store grid and track changes. The program outputs an animation of the game.
start = now[]
// Generate a random 10x10 grid with "1" being on and "0" being off
instructions = ["1000100110","0001100010","1000111101","1001111110","0000110011","1111000001","0100001110","1011101001","1001011000","1101110111"]
// Create dictionary of starting positions.
rowCounter = 0
display = new dict
for instructionStr = instructions
{
rowCounter = rowCounter + 1
columnCounter = 0
instructionArr = charList[instructionStr]
for instruction = instructionArr
{
columnCounter = columnCounter + 1
arr = [rowCounter,columnCounter]
if instruction == "1"
display@arr = 1
else
display@arr = 0
}
}
// Create toggle dictionary to track changes. It starts off with everything off.
toggle = new dict
multifor[x,y] = [new range[1,10],new range[1,10]]
{
arr = [x,y]
toggle@arr = 0
}
// Animate the game of life
a = new Animation[3/s]
win = undef
// Loop through 10 changes to the grid. The starting points will tick down to two stable unchanging shapes in 10 steps.
for i = 1 to 12 // 12 steps so animation will pause on final state.
{
// Graphics item for this frame of the animation.
g = new graphics
g.backgroundColor[1,1,1]
// Add in a transparent shape to prevent the image from jiggle to automatic scaling.
g.color[0,0,0,0] // Transparent black
g.fillRectSides[-1, -1, 12, 12] // Set minimum size
g.clipRectSides[-1, -1, 12, 12] // Set maximum size
g.color[0,0,0] // Color back to default black
multifor[x1,y1] = [new range[1,10],new range[1,10]]
{
tval = [x1,y1]
// This is programmed with a hard edge. Points beyond the border aren't considered.
xmax = min[x1+1,10]
xmin = max[x1-1,1]
ymax = min[y1+1,10]
ymin = max[y1-1,1]
// Range will be 8 surrounding cells or cells up to border.
pointx = new range[xmin,xmax]
pointy = new range[ymin,ymax]
pointsum = 0
status = 0
// Process each surrounded point
multifor[x2,y2] = [pointx,pointy]
{
// Assign the array to a variable so it can be used as a dictionary reference.
point = [x2,y2]
if x2 == x1 && y2 == y1
{
status = display@point
} else // Calculate the total of surrounding points
{
pointsum = pointsum + display@point
}
}
// Animate if the point is on.
if status == 1
{
g.color[0,0,0]
g.fillEllipseCenter[x1,y1,1,1]
}
toggle@tval = status // This will be overwritten if needed by neighbor check conditions below.
// Check if off point has 3 on point neighbors
if status == 0 && pointsum == 3
{
toggle@tval = 1
}
// Check if on point has between 2 and 3 on point neighbors
if status == 1 && (pointsum < 2 || pointsum > 3)
{
toggle@tval = 0
}
}
// Add the current frame to the animation
a.add[g]
// Replace the current display with the toggle values.
for toggleKeys = keys[toggle]
{
val = toggle@toggleKeys
display@toggleKeys = val
}
}
// Write the animation file
a.write["FrinkAnimationGoL.gif",400,400]
end = now[]
println["Program run time: " + ((end - start)*1.0 -> "seconds")]
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:
···· ***· ···· ···· ----- ·*·· ·*·· ·*·· ···· ----- ···· ***· ···· ···· ----- ·*·· ··*· ***· ···· ----- ···· *·*· ·**· ·*·· ----- ···· ··*· *·*· ·**· ----- ···· ·*·· ··** ·**· ----- ···· ··*· ···* ·*** -----
Furor
// Life simulator (game). Console (CLI) version.
// It is a 'cellular automaton', and was invented by Cambridge mathematician John Conway.
// The Rules
// For a space that is 'populated':
// Each cell with one or no neighbors dies, as if by solitude.
// Each cell with four or more neighbors dies, as if by overpopulation.
// Each cell with two or three neighbors survives.
// For a space that is 'empty' or 'unpopulated'
// Each cell with three neighbors becomes populated.
// -----------------------------------------------------
#g
// Get the terminal-resolution:
terminallines -- sto tlin
terminalcolumns sto tcol
// .............................
// Verify the commandline parameters:
argc 3 < { #s ."Usage: " 0 argv print SPACE 1 argv print ." lifeshape-file.txt\n" end }
2 argv 'f !istrue { #s ."The given file ( " 2 argv print ." ) doesn't exist!\n" end }
startingshape 2 argv filetolist // read the file into the list
startingshape maxlinelength sto maxlinlen
neighbour @tlin @tcol createlist // Generate the stringarray for the neighbour-calculations
livingspace @tlin @tcol createlist // Generate the stringarray for the actual generations
cellscreen @tlin @tcol createscreen // Generate the stringarray for the visible livingspace
// Calculate offset for the shape ( it must be put to the centre):
@tlin startingshape~ - 2 / sto originlin
@tcol @maxlinlen - 2 / sto origincol
startingshape {{|
{{}} {{|}} {{-}} [[]] 32 > { 1 }{ 0 } sto emblem
livingspace @originlin {{|}} + @origincol {{-}} + @emblem [[^]]
|}}
cursoroff
// ==================================================================
{... // infinite loop starts
sbr §renderingsbr
topleft cellscreen !printlist
."Generation: " {...} print fflush // print the number of the generations.
neighbour 0 filluplist // fill up the neighbour list with zero value
// Calculate neighbourhoods
neighbour {{|
{{|}} {{-}} sbr §neighbors
{{}} {{|}} {{-}} @n [[^]] // store the neighbournumber
|}}
// Now, kill everybody if the neighbors are less than 2 or more than 3:
neighbour {{|
{{|}} {{-}} sbr §killsbr
|}}
// Generate the newborn cells:
neighbour {{|
{{}} {{|}} {{-}} [[]] 3 == { livingspace {{|}} {{-}} 1 [[^]] }
|}}
50000 usleep
//2 sleep
...} // infinite loop ends
// ==================================================================
end
killsbr:
sto innerindex sto outerindex
neighbour @outerindex @innerindex [[]] 2 < then §kill
neighbour @outerindex @innerindex [[]] 3 > then §kill
rts
kill: livingspace @outerindex @innerindex 0 [[^]] rts
// ==========================================================
neighbors: // This subroutine calculates the quantity of neighborhood
sto y sto x zero n
livingspace @x ? @tlin -- @y ? @tcol -- [[]] sum n // upleft corner
livingspace @x ? @tlin -- @y [[]] sum n // upmid corner
livingspace @x ? @tlin -- @y ++ dup @tcol == { drop 0 } [[]] sum n // upright corner
livingspace @x @y ? @tcol -- [[]] sum n // midleft corner
livingspace @x @y ++ dup @tcol == { drop 0 } [[]] sum n // midright corner
livingspace @x ++ dup @tlin == { drop 0 } @y ? @tcol -- [[]] sum n // downleft corner
livingspace @x ++ dup @tlin == { drop 0 } @y [[]] sum n // downmid corner
livingspace @x ++ dup @tlin == { drop 0 } @y ++ dup @tcol == { drop 0 } [[]] sum n // downright corner
rts
// ==========================================================
renderingsbr:
livingspace {{|
cellscreen {{|}} {{-}}
{{}} {{|}} {{-}} [[]] { '* }{ 32 } [[^]]
|}}
rts
{ „startingshape” }
{ „livingspace” }
{ „cellscreen” }
{ „innerindex” }
{ „outerindex” }
{ „maxlinlen” }
{ „neighbour” }
{ „originlin” }
{ „origincol” }
{ „emblem” }
{ „tlin” }
{ „tcol” }
{ „x” } { „y” } { „n” }
Peri
###sysinclude standard.uh
###sysinclude args.uh
###sysinclude str.uh
###sysinclude system.uh
// Life simulator (game).
// It is a 'cellular automaton', and was invented by Cambridge mathematician John Conway.
// The Rules
// For a space that is 'populated':
// Each cell with one or no neighbors dies, as if by solitude.
// Each cell with four or more neighbors dies, as if by overpopulation.
// Each cell with two or three neighbors survives.
// For a space that is 'empty' or 'unpopulated'
// Each cell with three neighbors becomes populated.
// --------------------------------------------------------------------------------------------
#g
// Get the terminal-resolution:
terminallines -- sto tlin
terminalcolumns sto tcol
// .............................
// Verify the commandline parameters:
argc 3 < { #s ."Usage: " 0 argv print SPACE 1 argv print ." lifeshape-file.txt\n" end }
2 argv 'f inv istrue { #s ."The given file ( " 2 argv print ." ) doesn't exist!\n" end }
2 argv filetolist sto startingshape // read the file into the list
[[startingshape]]~~~ sto maxlinlen
@tlin @tcol [[mem]] sto neighbour // Generate the stringarray for the neighbour-calculations
@tlin @tcol [[mem]] sto livingspace // Generate the stringarray for the actual generations
@tlin @tcol [[mem]]! sto cellscreen // Generate the stringarray for the visible livingspace
// Calculate offset for the shape ( it must be put to the centre):
@tlin startingshape~ - 2 / sto originlin
@tcol @maxlinlen - 2 / sto origincol
startingshape~ shaperead: {{ {{}} [[startingshape]]~ {{ {{}}§shaperead {{}} [[startingshape]][]
32 > { 1 }{ 0 } sto emblem
@originlin {{}}§shaperead + @origincol {{}} + @emblem inv [[livingspace]][]
}}
}}
cursoroff
// ==================================================================
{.. // infinite loop starts
sbr §renderingsbr
topleft @cellscreen ![[print]]
."Generation: " {..} print fflush // print the number of the generations.
0 [[neighbour]][^] // fill up the neighbour list with zero value
// Calculate neighbourhoods
@tlin linloop: {{ // loop for every lines
@tcol {{ // loop for every columns
{{}}§linloop {{}} sbr §neighbors
{{}}§linloop {{}} @nn inv [[neighbour]][] // store the neighbournumber
}} }}
// Now, kill everybody if the neighbors are less than 2 or more than 3:
@tlin linloop2: {{ // loop for every lines
@tcol {{ // loop for every columns
{{}}§linloop2 {{}} [[neighbour]][] 2 < then §kill
{{}}§linloop2 {{}} [[neighbour]][] 3 > then §kill
{{<}} // Continue the inner loop
kill: {{}}§linloop2 {{}} 0 inv [[livingspace]][]
}} }}
@tlin linloop3: {{ // loop for every lines
@tcol {{ // loop for every columns
// Generate the newborn cells:
{{}}§linloop3 {{}} [[neighbour]][] 3 == { {{}}§linloop3 {{}} 1 inv [[livingspace]][] }
}} }}
50000 inv sleep
..} // infinite loop ends
// ==================================================================
end
// ==========================================================
neighbors: // This subroutine calculates the quantity of neighborhood
sto xx sto yy zero nn
@yy ? @tlin -- @xx ? @tcol -- [[livingspace]][] sum nn // upleft corner
@yy @xx ? @tcol -- [[livingspace]][] sum nn // upmid corner
@yy ++? tlin @xx ? @tcol -- [[livingspace]][] sum nn // upright corner
@yy ? @tlin -- @xx [[livingspace]][] sum nn // midleft corner
@yy ++? tlin @xx [[livingspace]][] sum nn // midright corner
@yy ? @tlin -- @xx ++? tcol [[livingspace]][] sum nn // downleft corner
@yy @xx ++? tcol [[livingspace]][] sum nn // downmid corner
@yy ++? tlin @xx ++? tcol [[livingspace]][] sum nn // downright corner
rts
// ==========================================================
renderingsbr:
@tlin livingspaceloop: {{ @tcol {{
{{}}§livingspaceloop {{}}
{{}}§livingspaceloop {{}} [[livingspace]][] { '* }{ 32 } inv [[cellscreen]][]
}}
}}
rts
{ „tlin” }
{ „tcol” }
{ „startingshape” }
{ „maxlinlen” }
{ „livingspace” }
{ „neighbour” }
{ „originlin” }
{ „origincol” }
{ „emblem” }
{ „cellscreen” }
{ „xx” } { „yy” } { „nn” }
Futhark
fun bint(b: bool): int = if b then 1 else 0
fun intb(x: int): bool = if x == 0 then False else True
fun to_bool_board(board: [][]int): [][]bool =
map (fn (r: []int): []bool => map intb r) board
fun to_int_board(board: [][]bool): [][]int =
map (fn (r: []bool): []int => map bint r) board
fun cell_neighbors(i: int, j: int, board: [n][m]bool): int =
unsafe
let above = (i - 1) % n
let below = (i + 1) % n
let right = (j + 1) % m
let left = (j - 1) % m in
bint board[above,left] + bint board[above,j] + bint board[above,right] +
bint board[i,left] + bint board[i,right] +
bint board[below,left] + bint board[below,j] + bint board[below,right]
fun all_neighbours(board: [n][m]bool): [n][m]int =
map (fn (i: int): []int =>
map (fn (j: int): int => cell_neighbors(i,j,board)) (iota m))
(iota n)
fun iteration(board: [n][m]bool): [n][m]bool =
let lives = all_neighbours(board) in
zipWith (fn (lives_r: []int) (board_r: []bool): []bool =>
zipWith (fn (neighbors: int) (alive: bool): bool =>
if neighbors < 2
then False
else if neighbors == 3 then True
else if alive && neighbors < 4 then True
else False)
lives_r board_r)
lives board
fun main(int_board: [][]int, iterations: int): [][]int =
-- We accept the board as integers for convenience, and then we
-- convert to booleans here.
let board = to_bool_board int_board in
loop (board) = for i < iterations do
iteration board in
to_int_board board
FutureBasic
This is just the basic central routine.
Short a(4, 4), c(4, 4) // Initialize arrays of cells and a working copy
void local fn seed
Short x, y
// Blinker
a(1, 2) = 1 : a(2, 2) = 1 : a(3, 2) = 1
for y = 1 to 3 : for x = 1 to 3
print a(x, y); // Draw array
next : print : next : print
end fn
void local fn nextGen
Short x, y, dx, dy, n
// Calculate next generation on temporary board
for y = 1 to 3 : for x = 1 to 3
c(x, y) = 0 // Initialize
n = -a(x, y) // Don't count center cell
for dy = -1 to 1 : for dx = -1 to 1
n += a(x + dx, y + dy) // Count the neighbours
next : next
c(x, y) = ( n == 3 ) or ( n == 2 and a(x, y) ) // Conway’s rule
next : next
// Copy temp array to actual array and draw
for y = 1 to 3 : for x = 1 to 3
a(x, y) = c(x, y) // Copy
print a(x, y); // Draw
next : print : next : print
end fn
fn seed
fn nextGen
fn nextGen
handleevents // Go into Mac event loop
Just three generations, as requested:
000 111 000 010 010 010 000 111 000
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:
** **** * * ** * ** * * * * ** **** ** * * ** * ** * ** * ** * **** * * ** * *** ** * ** **** * * ** * ** * ** * ** ** * *** *** * ** ** ** ** ** * * * *
Groovy
class GameOfLife {
int generations
int dimensions
def board
GameOfLife(generations = 5, dimensions = 5) {
this.generations = generations
this.dimensions = dimensions
this.board = createBlinkerBoard()
}
static def createBlinkerBoard() {
[
[].withDefault{0},
[0,0,1].withDefault{0},
[0,0,1].withDefault{0},
[0,0,1].withDefault{0}
].withDefault{[]}
}
static def createGliderBoard() {
[
[].withDefault{0},
[0,0,1].withDefault{0},
[0,0,0,1].withDefault{0},
[0,1,1,1].withDefault{0}
].withDefault{[]}
}
static def getValue(board, point) {
def x,y
(x,y) = point
if(x < 0 || y < 0) {
return 0
}
board[x][y] ? 1 : 0
}
static def countNeighbors(board, point) {
def x,y
(x,y) = point
def neighbors = 0
neighbors += getValue(board, [x-1,y-1])
neighbors += getValue(board, [x-1,y])
neighbors += getValue(board, [x-1,y+1])
neighbors += getValue(board, [x,y-1])
neighbors += getValue(board, [x,y+1])
neighbors += getValue(board, [x+1,y-1])
neighbors += getValue(board, [x+1,y])
neighbors += getValue(board, [x+1,y+1])
neighbors
}
static def conwaysRule(currentValue, neighbors) {
def newValue = 0
if(neighbors == 3 || (currentValue && neighbors == 2)) {
newValue = 1
}
newValue
}
static def createNextGeneration(currentBoard, dimensions) {
def newBoard = [].withDefault{[].withDefault{0}}
(0..(dimensions-1)).each { row ->
(0..(dimensions-1)).each { column ->
def point = [row, column]
def currentValue = getValue(currentBoard, point)
def neighbors = countNeighbors(currentBoard, point)
newBoard[row][column] = conwaysRule(currentValue, neighbors)
}
}
newBoard
}
static def printBoard(generationCount, board, dimensions) {
println "Generation ${generationCount}"
println '*' * 80
(0..(dimensions-1)).each { row ->
(0..(dimensions-1)).each { column ->
print board[row][column] ? 'X' : '.'
}
print System.getProperty('line.separator')
}
println ''
}
def start() {
(1..generations).each { generation ->
printBoard(generation, this.board, this.dimensions)
this.board = createNextGeneration(this.board, this.dimensions)
}
}
}
// Blinker
def game = new GameOfLife()
game.start()
// Glider
game = new GameOfLife(10, 10)
game.board = game.createGliderBoard()
game.start()
The output of this program:
Generation 1 ******************************************************************************** ..... ..X.. ..X.. ..X.. ..... Generation 2 ******************************************************************************** ..... ..... .XXX. ..... ..... Generation 3 ******************************************************************************** ..... ..X.. ..X.. ..X.. ..... Generation 4 ******************************************************************************** ..... ..... .XXX. ..... ..... Generation 5 ******************************************************************************** ..... ..X.. ..X.. ..X.. ..... Generation 1 ******************************************************************************** .......... ..X....... ...X...... .XXX...... .......... .......... .......... .......... .......... .......... Generation 2 ******************************************************************************** .......... .......... .X.X...... ..XX...... ..X....... .......... .......... .......... .......... .......... Generation 3 ******************************************************************************** .......... .......... ...X...... .X.X...... ..XX...... .......... .......... .......... .......... .......... Generation 4 ******************************************************************************** .......... .......... ..X....... ...XX..... ..XX...... .......... .......... .......... .......... .......... Generation 5 ******************************************************************************** .......... .......... ...X...... ....X..... ..XXX..... .......... .......... .......... .......... .......... Generation 6 ******************************************************************************** .......... .......... .......... ..X.X..... ...XX..... ...X...... .......... .......... .......... .......... Generation 7 ******************************************************************************** .......... .......... .......... ....X..... ..X.X..... ...XX..... .......... .......... .......... .......... Generation 8 ******************************************************************************** .......... .......... .......... ...X...... ....XX.... ...XX..... .......... .......... .......... .......... Generation 9 ******************************************************************************** .......... .......... .......... ....X..... .....X.... ...XXX.... .......... .......... .......... .......... Generation 10 ******************************************************************************** .......... .......... .......... .......... ...X.X.... ....XX.... ....X..... .......... .......... ..........
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
HolyC
Conway's Game of Life in HolyC for TempleOS ported from Conway's_Game_of_Life#C.
Also see the TempleOS implementation of Conway's Game of Life which makes use of the graphics engine.
Icon and Unicon
A sample run:
->life 3 3 Enter the starting pattern, end with EOF *** --- *** --- --- * * * --- --- *** --- ->
INTERCAL
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:
- . adding extra empty cells, surrounding the life instance,
- . tessellating the result, finding every overlapping 3 by 3 subinstance,
- . totaling the number of live cells in each subinstance,
- . 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
Example (showing start and six following generations of a glider)
blocks=: (2 2$2) ((7 u:' ▗▖▄▝▐▞▟▘▚▌▙▀▜▛█') {~ #.@,);._3 >.&.-:@$ {. ]
blocks"2 life^:(i.7) 4 5{.#:1 5 3 ▖▌ ▝▘ ▝▄ ▝▘ ▚ ▝▀ ▗▗ ▛ ▗ ▝▟ ▖ ▟▘ ▗ ▄▌
JAMES II/Rule-based Cellular Automata
@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:
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: _#_ _#_ _#_
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.
____________________________________________________________ _________________________*__________________________________ _______________________*_*__________________________________ _____________**______**____________**_______________________ ____________*___*____**____________**_______________________ _**________*_____*___**_____________________________________ _**________*___*_**____*_*__________________________________ ___________*_____*_______*__________________________________ ____________*___*___________________________________________ _____________**_____________________________________________ ____________________________________________________________ ____________________________________________________________ ____________________________________________________________ ____________________________________________________________ ____________________________________________________________ ____________________________________________________________ ____________________________________________________________ ____________________________________________________________ ____________________________________________________________ ____________________________________________________________
Swing
See Conway's Game of Life/Java/Swing
Java 10
import static java.util.List.of;
class GameOfLife {
boolean[][] board = new boolean[3][3];
GameOfLife() {}
GameOfLife(String[] board) {
set((i, j, s) -> board[i].charAt(j * 2) == '■');
}
void set(Setter setter) {
for (int i = 0; i < board.length; i++) {
for (int j = 0; j < board[i].length; j++) {
board[i][j] = setter.set(i, j, board[i][j]);
}
}
}
void get(Getter getter) {
set((i, j, s) -> {
getter.get(i, j, s);
return s;
});
}
int countNeighbors(int i, int j) {
var counter = new Getter() {
int count;
@Override
public void get(int li, int lj, boolean state) {
if (distance(i, j, li, lj) == 1 && board[li][lj])
count++;
}
};
get(counter);
return counter.count;
}
int distance(int i, int j, int li, int lj) {
return Math.max(
Math.abs(i - li),
Math.abs(j - lj));
}
GameOfLife makeNextGeneration() {
var n = new GameOfLife();
n.set((i, j, s) -> {
var alive = board[i][j];
int c = countNeighbors(i, j);
if (alive) {
return c == 2 || c == 3;
} else {
return c == 3;
}
});
return n;
}
void print() {
get((i, j, s) -> {
if (j == 0)
System.out.println();
System.out.print(s ? "■ " : "□ ");
});
}
interface Setter {
boolean set(int i, int j, boolean state);
}
interface Getter {
void get(int i, int j, boolean state);
}
public static void main(String[] args) {
String[] board = {
"□ ■ □ ",
"□ ■ □ ",
"□ ■ □ ",
};
var gol = new GameOfLife(board);
for (var generation : of(0, 1, 2)) {
gol.print();
System.out.println("\n");
gol = gol.makeNextGeneration();
}
}
}
Outputs:
□ ■ □ □ ■ □ □ ■ □ □ □ □ ■ ■ ■ □ □ □ □ ■ □ □ ■ □ □ ■ □
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() {
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
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
More functional style:
const _ = require('lodash');
///////////////////
// LODASH IMPORT //
///////////////////
// import all lodash functions to the main namespace, but isNaN not to cause conflicts
_.each(_.keys(_), k => global[k === 'isNaN' ? '_isNaN' : k] = _[k]);
///////////////
// FUNCTIONS //
///////////////
const WORLD_WIDTH = 3,
WORLD_HEIGHT = 3,
displayWorld = (world) => console.log(map(world, x => x.join(' ')).join('\n') + '\n'),
aliveNeighbours = (world, x, y) => chain(range(-1, 2))
.reduce((acc, i) => acc.concat(map(range(-1, 2), ii => [i, ii])), [])
.reject(partial(isEqual, [0, 0]))
.map(i => {
try {
return world[x + i[0]][y + i[1]];
} catch (err) {
return null;
}
})
.compact()
.value()
.length,
isAlive = (cell, numAliveNeighbours) => (cell === 1 && inRange(numAliveNeighbours, 2, 4)) || (cell === 0 && numAliveNeighbours === 3) ? 1 : 0,
updateWorld = (world) => map(world, (row, rowidx) => map(row, (cell, colidx) => isAlive(cell, aliveNeighbours(world, rowidx, colidx))));
// let world = map(range(WORLD_WIDTH), partial(ary(map, 2), range(WORLD_HEIGHT), partial(random, 0, 1, false)));
let world = [[0, 0, 0], [1, 1, 1], [0, 0, 0]];
setInterval(() => {
world = updateWorld(world)
displayWorld(world);
}, 1000);
ES6 + :
const alive = 1;
const dead = 0;
const conwaysGameOfLife = (game) => {
const newGame = []
for (let y = 0; y < game.length; y += 1) {
const newRow = []
for (let x = 0; x < game[y].length; x += 1) {
const cell = game[y][x];
const prevX = x > 0 ? x - 1 : x;
const nextX = x < game[y].length - 1 ? x + 2 : x + 1;
const counter =
(game[y - 1] ? game[y - 1].slice(prevX, nextX).reduce((acc, v) => acc + v) : 0) +
(game[y][x - 1] || 0) + (game[y][x + 1] || 0) +
(game[y + 1] ? game[y + 1].slice(prevX, nextX).reduce((acc, v) => acc + v) : 0)
cell === alive
? counter > 1 && counter <= 3
? newRow.push(alive)
: newRow.push(dead)
: counter === 3
? newRow.push(alive)
: newRow.push(dead)
}
newGame.push(newRow)
}
return newGame
}
const generateGame = (height, width) => {
return Array.from({ length: height }, (v, k) => (
Array.from({ length: width}, (v, k) => {
return (Math.random() * 100 | 0) < 50 ? dead : alive
})
))
}
const output = (game) =>{
process.stdout.write('\033c');
let screen = '';
for (let i = 0; i < game.length; i += 1) {
screen += game[i].join('')
screen += '\n'
}
console.log(screen)
}
const setup = ((game) => {
return () => {
setInterval(() => {
output(game)
const newGame = conwaysGameOfLife(game)
game = newGame
}, 1000)
}
})
// for random game
// const game = generateGame(10, 10)
// glider test
const game = [
[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]
];
const run = setup(game);
run()
jq
In this implementation, a "world" is simply a suitably constructed string as illustrated by world3 and world11 below. The "game" can be played either by creating separate frames (using frames(n)) or by calling animation(n; sleep) with sleep approximately equal to the number of milliseconds between refreshes.
# Notes on the implementation:
# 1. For efficiency, the implementation requires that the world
# has boundaries, as illustrated in the examples.
# 2. For speed, the simulation uses the exploded string.
# 3. The ASCII values of the "alive" and "empty" symbols are
# hardcoded: "." => 46; " " => 32
# 4. To adjust the refresh rate, adjust the input to "spin".
def lines: split("\n")|length;
def cols: split("\n")[0]|length + 1; # allow for the newline
# Is there a "." (46) at [x,y] relative to position i,
# assuming the width is w?
# Input is an array; result is 0 or 1 so we can easily count the total.
def isAlive(x; y; i; w): if .[i+ w*y + x] == 46 then 1 else 0 end;
def neighborhood(i;w):
isAlive(-1; -1; i; w) + isAlive(0; -1; i; w) + isAlive(1; -1; i; w) +
isAlive(-1; 0; i; w) + isAlive(1; 0; i; w) +
isAlive(-1; 1; i; w) + isAlive(0; 1; i; w) + isAlive(1; 1; i; w) ;
# The basic rules:
def evolve(cell; sum) :
if cell == 46 then if sum == 2 or sum == 3 then 46 else 32 end
elif cell == 32 then if sum == 3 then 46 else 32 end
else cell
end ;
# [world, lines, cols] | next(w) => [world, lines, cols]
def next:
.[0] as $world | .[1] as $lines | .[2] as $w
| reduce range(0; $world|length) as $i
($world;
.[$i] as $c
| if $c == 32 or $c == 46 then
# updates are "simultaneous" i.e. relative to $world, not "."
($world | neighborhood($i; $w)) as $sum
| evolve($c; $sum) as $next
| if $c == $next then . else .[$i] = $next end
else .
end )
| [., $lines, $w] ;
Animation:
# "clear screen":
def cls: "\u001b[2J";
# Input: an integer; 1000 ~ 1 sec
def spin:
reduce range(1; 500 * .) as $i
(0; . + ($i|cos)*($i|cos) + ($i|sin)*($i|sin) )
| "" ;
# Animate n steps;
# if "sleep" is non-negative then cls and
# sleep about "sleep" ms between frames.
def animate(n; sleep):
if n == 0 then empty
else (if sleep >= 0 then cls else "" end),
(.[0]|implode), n, "\n",
(sleep|spin),
( next|animate(n-1; sleep) )
end ;
# Input: a string representing the initial state
def animation(n; sleep):
[ explode, lines, cols] | animate(n; sleep) ;
# Input: a string representing the initial state
def frames(n): animation(n; -1);
Examples:
def world3:
"+---+\n" +
"| |\n" +
"|...|\n" +
"| |\n" +
"+---+\n" ;
def world11:
"+-----------+\n" +
"| |\n" +
"| .. |\n" +
"| ... |\n" +
"| .. |\n" +
"| |\n" +
"+-----------+\n" ;
Task:
world3 | frames(3)
- Output:
$ jq -n -r -f Game_of_life.jq
+---+
| |
|...|
| |
+---+
3
+---+
| . |
| . |
| . |
+---+
2
+---+
| |
|...|
| |
+---+
1
Animation example
# Animation of 100 frames with approximately 1 second between each update:
world11 | animation(100; 1000)
Jsish
From Javascript, SpiderMonkey entry.
/* Conway's game of life, in Jsish */
function GameOfLife () {
this.title = "Conway's Game of Life";
this.cls = "\u001B[H\u001B[2J";
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(0);
};
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 += " ";
}
puts(l);
}
};
this.start = function() {
for (var t = 0; t < this.turns; t++) {
sleep(500);
printf(this.cls);
puts(this.title + "\n---\nTurn "+(t+1));
this.print();
this.nextGen();
}
};
}
var game = new GameOfLife();
if (Interp.conf('unitTest')) {
game.init(3,3,3);
game.title="---\n3x3 Blinker over three turns.";
game.board = [
[0,0,0],
[1,1,1],
[0,0,0]];
game.cls="";
game.start();
} else {
game.init(3,3,3);
game.title="---\n3x3 Blinker over three turns.";
game.board = [
[0,0,0],
[1,1,1],
[0,0,0]];
game.start();
game.init(5,10,6);
game.title="---\n10x6 Glider over five turns.";
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();
var steps = (console.args[0]) ? parseInt(console.args[0]) || 1 : 50;
game.init(steps, 32,16);
game.title="---\nRandom 32x16, " + steps + " step" + ((steps === 1) ? "" : "s");
game.start();
}
/*
=!EXPECTSTART!=
---
3x3 Blinker over three turns.
---
Turn 1
XXX
---
3x3 Blinker over three turns.
---
Turn 2
X
X
X
---
3x3 Blinker over three turns.
---
Turn 3
XXX
=!EXPECTEND!=
*/
- Output:
prompt$ jsish -u conwaysGame.jsi [PASS] conwaysGame.jsi
Julia
Using the CellularAutomata package: https://github.com/natj/CellularAutomata.jl
julia> Pkg.add("CellularAutomata")
INFO: Installing CellularAutomata v0.1.2
INFO: Package database updated
julia> using CellularAutomata
julia> gameOfLife{T<:Int}(n::T, m::T, gen::T) = CA2d([3], [2,3], int(randbool(n, m)), gen)
gameOfLife (generic function with 1 method)
julia> gameOfLife(15, 30, 5)
30x15x5 Cellular Automaton
# ## # ###### ### ### ## # #### # # # ## ##### ## # # ## ### ## # # ## # # ##### # # # ## # # # ## ## # # ## ### # #### ## ## ### # # # # # # # ## ##### # ##### # ## ### # ### # ## #### ## # #### # ## ## ### ### ### ## #### ####### # ## # # ## ##### ## #### # ##### ## ## ##### # # # # # # # # # ## ## ## ## ##### # ## # # # # ############# # ## # # ### ## ##
# ### # # # ## # # # # ### # # # ## # # # # # # ## ## #### ### # # # ## # # # ## # # # # #### # ## # # ## # ## # # # # # # ## # ## # # # # # # ## # ## # # ## # # # # ##
# ## ## # # # # ## # # # ## # # ##### # # ## # # ## ## ## # # ## ### # # ## ##### # # # ## ## # ## # # # # # # # ##### ## # # ## # # # # ## # # ### # ### # ### ####
## ### #### # # # ## ## ## # ### # ## ## # # ### ## ## # ## ### # ## # # ##### #### #### ## ## # ##### # #### # # # # ## ## # # ## # # ## ## ## # # # # ##### # ## # # # # # # #
## # ## # ## # # ### ## # # ## # ### # # ## ## # ## # #### # ## ## # # ## # # ## ## # ### # #### # #### ## # # # ### ## ## # ## ## ## # ## # # ## # # # # ####### #### ### ## #
GPU calculation based version
Requires a CUDA compatible graphics card and uses the ArrayFire library.
using ArrayFire
using Images
using LinearAlgebra
const blinker = [0 0 0; 1 1 1; 0 0 0]
const glider = [0 0 1; 1 0 1; 0 1 1]
const lwss = [0 1 1 1 1; 1 0 0 0 1; 0 0 0 0 1; 1 0 0 1 0]
const glidergun = [
0 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 0 0 0 0 0 0 0 0 0 0 0 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 0 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 1 1 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0;
0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0;
0 1 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0;
0 1 1 0 0 0 0 0 0 0 0 1 0 0 0 1 0 1 1 0 0 0 0 1 0 1 0 0 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 1 0 0 0 0 0 0 0 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 1 0 0 0 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 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]
function lifegame(fname, initializer, mapsize=30, imgsteps=50, upscaleratio=20)
kernel = convert(Array{Float32}, [1 1 1; 1 0 1; 1 1 1]) |> AFArray
initialstate = zeros(Bool, mapsize, mapsize)
mid = div(mapsize, 2)
(xlen, ylen), (halfx, halfy) = size(initializer), div.(size(initializer), 2)
x1, x2 = mid - halfx, isodd(xlen) ? mid + halfx : mid + halfx - 1
y1, y2 = mid - halfy, isodd(ylen) ? mid + halfy : mid + halfy - 1
initialstate[x1:x2, y1:y2] .= initializer
state = initialstate .+ Float32(.0) |> AFArray
img = zeros(Float32, mapsize * upscaleratio, mapsize * upscaleratio, imgsteps)
for i in 1:imgsteps
nb = convolve2(state, kernel, UInt32(0), UInt32(0))
a = (nb == 2)
b = (nb == 3)
state = ((state .* a .+ b) > 0) + Float32(0)
frame = imresize(state, ratio=upscaleratio)
img[:, :, i] .= frame
end
save(fname, img)
end
lifegame("blinker.gif", blinker)
lifegame("glider.gif", glider)
lifegame("lwss.gif", lwss)
lifegame("glidergun.gif", glidergun, 90, 200)
Kotlin
This is based on the Go entry but has been altered in several respects.
In particular, it now allows for the blinker and glider patterns as well as an initially random pattern.
Also any cells beyond the boundary are now treated as dead as per the current task description.
The particular random pattern used now needs only 99 generations to reach stability.
// version 1.2.0
import java.util.Random
val rand = Random(0) // using a seed to produce same output on each run
enum class Pattern { BLINKER, GLIDER, RANDOM }
class Field(val w: Int, val h: Int) {
val s = List(h) { BooleanArray(w) }
operator fun set(x: Int, y: Int, b: Boolean) {
s[y][x] = b
}
fun next(x: Int, y: Int): Boolean {
var on = 0
for (i in -1..1) {
for (j in -1..1) {
if (state(x + i, y + j) && !(j == 0 && i == 0)) on++
}
}
return on == 3 || (on == 2 && state(x, y))
}
fun state(x: Int, y: Int): Boolean {
if ((x !in 0 until w) || (y !in 0 until h)) return false
return s[y][x]
}
}
class Life(val pattern: Pattern) {
val w: Int
val h: Int
var a: Field
var b: Field
init {
when (pattern) {
Pattern.BLINKER -> {
w = 3
h = 3
a = Field(w, h)
b = Field(w, h)
a[0, 1] = true
a[1, 1] = true
a[2, 1] = true
}
Pattern.GLIDER -> {
w = 4
h = 4
a = Field(w, h)
b = Field(w, h)
a[1, 0] = true
a[2, 1] = true
for (i in 0..2) a[i, 2] = true
}
Pattern.RANDOM -> {
w = 80
h = 15
a = Field(w, h)
b = Field(w, h)
for (i in 0 until w * h / 2) {
a[rand.nextInt(w), rand.nextInt(h)] = true
}
}
}
}
fun step() {
for (y in 0 until h) {
for (x in 0 until w) {
b[x, y] = a.next(x, y)
}
}
val t = a
a = b
b = t
}
override fun toString(): String {
val sb = StringBuilder()
for (y in 0 until h) {
for (x in 0 until w) {
val c = if (a.state(x, y)) '#' else '.'
sb.append(c)
}
sb.append('\n')
}
return sb.toString()
}
}
fun main(args: Array<String>) {
val lives = listOf(
Triple(Life(Pattern.BLINKER), 3, "BLINKER"),
Triple(Life(Pattern.GLIDER), 4, "GLIDER"),
Triple(Life(Pattern.RANDOM), 100, "RANDOM")
)
for ((game, gens, title) in lives) {
println("$title:\n")
repeat(gens + 1) {
println("Generation: $it\n$game")
Thread.sleep(30)
game.step()
}
println()
}
}
- Output:
In the interests of brevity, only generations 0 and 100 are shown for the 'random' pattern:
BLINKER: Generation: 0 ... ### ... Generation: 1 .#. .#. .#. Generation: 2 ... ### ... Generation: 3 .#. .#. .#. GLIDER: Generation: 0 .#.. ..#. ###. .... Generation: 1 .... #.#. .##. .#.. Generation: 2 .... ..#. #.#. .##. Generation: 3 .... .#.. ..## .##. Generation: 4 .... ..#. ...# .### RANDOM: Generation: 0 ....###....#.#....###...#.#..#.###...#..#......#.#..#####......######...##.#.##. ####..###.#....#.#####.##.....####.##..####.####.........#.#.###...#.##.#.#..... ..##.##.#.##...#..#.#..#.#.#.#..####.#...#..##....#..##.........#.#..#....#...#. ..##..#..##.#.#.....#.##.##...#####...##.##.....##...#.....##......###..##.#..## ......##..........#.#.#.......#..#.##.#.##....#.#...#.#.#.#.....#..#.......#.#.# .#.#.....#####..#..##............#.#.#...###..#...##.....#..##...#.#.##.#..##... .##.#.#.##.#..#####.....##..#.####..#.#..#...#.#..#...#.#.#.#....#.#..#.#.#..##. .#..#..#.....#...###..###.....####..........##.##.####.....#..##..####..#...##.. ..##.#...#.#..#.#....#..#...##.#..##.......#.#..##..##..##.#.....##.#......#.#.# #.######..#.#.#.###.#....###.....#.....#..#......####.#.#..#....#...#.......#.#. #...#..###.##....#.#..##..#..#.#.#...#..#....#....##...#..#..#.#....#...##....#. #....##.......#.####.##..#....#.#....#....#######.#..####.#..#.#.##....#.#####.. ...#.......##..##.##......##....#..#.####.......#..#.#..##.###.#.#.#.#..##.....# #.##..####....#..#..##..#.#..#..#....#.###.##.....#.....##....#.#..#.##.....##.. ...#.#..#.#.....###...#..##.#.....#......#...........###...#.#....#..#.#..##.#.# Generation: 100 ................................................................................ ................................................................................ .........................................#...................................... ......................#.................#.#..................................... .##..................#.#................#.#..................................... .##..................#.#.................#...#.................................. ......................#.....................#.#................................. ...........................................#..#................................. ............................................##.................................. .............##................................................................. ............#..#................................................................ .##..........#.#.......................................##..........#............ .##...........#........................................##.........#.#........... ..........##.......................................................##........... ..........##....................................................................
Lambdatalk
Lambdatalk works in any web browsers coming with javascript and canvas.
{center
{input {@ type="button" value="start random"
onclick="LIFE.startstop(this,'random')"}}
{input {@ type="button" value="start cross"
onclick="LIFE.startstop(this,'cross')"}}
{canvas {@ id="life" width="330" height="330"
style="box-shadow:0 0 8px #000"}}
}
calling the following javascript code
{script
var LIFE = (function() {
var board = [],
xmax = 8, ymax = 8,
interval = null,
delay = 1000;
var isdefined = function(x,y) {
return typeof board[x] !== 'undefined'
&& typeof board[x][y] !== 'undefined'
&& board[x][y];
};
var alive = function(c,n) {
if (n < 2 || n > 3) c = 0;
if (n === 3) c = 1;
return c
};
var neighbours = function(x,y) {
var n = 0;
if (isdefined(x-1,y-1)) n++;
if (isdefined(x ,y-1)) n++;
if (isdefined(x+1,y-1)) n++;
if (isdefined(x-1,y )) n++;
if (isdefined(x+1,y )) n++;
if (isdefined(x-1,y+1)) n++;
if (isdefined(x ,y+1)) n++;
if (isdefined(x+1,y+1)) n++;
return n
};
var nextGen = function() {
var next = [];
for (var x = 0; x < xmax; x++) {
next[x] = [];
for (var y = 0; y < ymax; y++)
next[x][y] = alive( board[x][y], neighbours(x,y) );
}
board = next
};
var print = function(ctx,dw,dh) {
for (var x = 0; x < xmax; x++) {
for (var y = 0; y < ymax; y++) {
ctx.fillStyle = (board[x][y])? "#fff" : "#888";
ctx.fillRect(y*dh+1,x*dw+1,dh-1,dw-1)
}
}
};
var init = function (type,w,h) {
xmax = w;
ymax = h;
delay = (type === "random")? 100 : 1000;
for (var x = 0; x < xmax; x++) {
board[x] = [];
for (var y = 0; y < ymax; y++) {
if (type === "random") {
board[x][y] = Math.round(Math.random());
} else {
board[x][y] = 0;
if (x === Math.floor(w/2)) board[x][y] = 1;
if (y === Math.floor(h/2)) board[x][y] = 1;
}
}
}
};
var run = function(ctx,dw,dh) {
print(ctx,dw,dh);
nextGen()
};
var startstop = function(id,type) {
var can = document.getElementById('life').getContext('2d');
if (interval === null) {
id.value = "stop";
init(type,33,33);
interval = window.setInterval( run, delay, can, 10, 10 );
} else {
id.value = (type === "random")? "start_random" : "start_cross";
window.clearInterval( interval );
interval = null;
}
};
return { startstop }
}) (); // end LIFE
}
Results can be seen in http://lambdaway.free.fr/lambdaspeech/?view=life and in http://lambdaway.free.fr/lambdawalks/?view=conway
Lua
A slight modernization of my original "life.lua" for Lua 3.x circa 2000 -- heck, we didn't even have for loops back then! :D (a copy can be found in the 4.0 source distro if interested in comparing syntax)
local function T2D(w,h) local t={} for y=1,h do t[y]={} for x=1,w do t[y][x]=0 end end return t end
local Life = {
new = function(self,w,h)
return setmetatable({ w=w, h=h, gen=1, curr=T2D(w,h), next=T2D(w,h)}, {__index=self})
end,
set = function(self, coords)
for i = 1, #coords, 2 do
self.curr[coords[i+1]][coords[i]] = 1
end
end,
evolve = function(self)
local curr, next = self.curr, self.next
local ym1, y, yp1 = self.h-1, self.h, 1
for i = 1, self.h do
local xm1, x, xp1 = self.w-1, self.w, 1
for j = 1, self.w do
local sum = curr[ym1][xm1] + curr[ym1][x] + curr[ym1][xp1] +
curr[y][xm1] + curr[y][xp1] +
curr[yp1][xm1] + curr[yp1][x] + curr[yp1][xp1]
next[y][x] = ((sum==2) and curr[y][x]) or ((sum==3) and 1) or 0
xm1, x, xp1 = x, xp1, xp1+1
end
ym1, y, yp1 = y, yp1, yp1+1
end
self.curr, self.next, self.gen = self.next, self.curr, self.gen+1
end,
render = function(self)
print("Generation "..self.gen..":")
for y = 1, self.h do
for x = 1, self.w do
io.write(self.curr[y][x]==0 and "□ " or "■ ")
end
print()
end
end
}
Example usage. Coordinates wrap to simulate an infinite universe, so here a glider/lwss are evolved through one complete period, then advanced forward until returning to starting conditions.
print("GLIDER:")
local life = Life:new(5,5)
life:set({ 2,1, 3,2, 1,3, 2,3, 3,3 })
for i = 1, 5 do
life:render()
life:evolve()
end
for i = 6,20 do life:evolve() end
life:render()
print()
print("LWSS:")
life = Life:new(10,7)
life:set({ 2,2, 5,2, 6,3, 2,4, 6,4, 3,5, 4,5, 5,5, 6,5 })
for i = 1, 5 do
life:render()
life:evolve()
end
for i = 6,20 do life:evolve() end
life:render()
- Output:
GLIDER: Generation 1: □ ■ □ □ □ □ □ ■ □ □ ■ ■ ■ □ □ □ □ □ □ □ □ □ □ □ □ Generation 2: □ □ □ □ □ ■ □ ■ □ □ □ ■ ■ □ □ □ ■ □ □ □ □ □ □ □ □ Generation 3: □ □ □ □ □ □ □ ■ □ □ ■ □ ■ □ □ □ ■ ■ □ □ □ □ □ □ □ Generation 4: □ □ □ □ □ □ ■ □ □ □ □ □ ■ ■ □ □ ■ ■ □ □ □ □ □ □ □ Generation 5: □ □ □ □ □ □ □ ■ □ □ □ □ □ ■ □ □ ■ ■ ■ □ □ □ □ □ □ Generation 21: □ ■ □ □ □ □ □ ■ □ □ ■ ■ ■ □ □ □ □ □ □ □ □ □ □ □ □ LWSS: Generation 1: □ □ □ □ □ □ □ □ □ □ □ ■ □ □ ■ □ □ □ □ □ □ □ □ □ □ ■ □ □ □ □ □ ■ □ □ □ ■ □ □ □ □ □ □ ■ ■ ■ ■ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ Generation 2: □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ ■ ■ □ □ □ □ □ □ ■ ■ □ ■ ■ □ □ □ □ □ ■ ■ ■ ■ □ □ □ □ □ □ □ ■ ■ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ Generation 3: □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ ■ ■ ■ ■ □ □ □ □ □ ■ □ □ □ ■ □ □ □ □ □ □ □ □ □ ■ □ □ □ □ □ ■ □ □ ■ □ □ □ □ □ □ □ □ □ □ □ □ □ □ Generation 4: □ □ □ □ □ □ □ □ □ □ □ □ □ □ ■ ■ □ □ □ □ □ □ □ ■ ■ ■ ■ □ □ □ □ □ □ ■ ■ □ ■ ■ □ □ □ □ □ □ □ ■ ■ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ Generation 5: □ □ □ □ □ □ □ □ □ □ □ □ □ ■ □ □ ■ □ □ □ □ □ □ □ □ □ □ ■ □ □ □ □ □ ■ □ □ □ ■ □ □ □ □ □ □ ■ ■ ■ ■ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ Generation 21: □ □ □ □ □ □ □ □ □ □ □ ■ □ □ ■ □ □ □ □ □ □ □ □ □ □ ■ □ □ □ □ □ ■ □ □ □ ■ □ □ □ □ □ □ ■ ■ ■ ■ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □ □
ksh
#!/bin/ksh
# # Version AJM 93u+ 2012-08-01
# Conway's Game of Life
# # Variables:
#
integer RAND_MAX=32767
LIFE="�[07m �[m"
NULL=" "
typeset -a char=( "$NULL" "$LIFE" )
# # Input x y or default to 30x30, positive integers only
#
integer h=${1:-30} ; h=$(( h<=0 ? 30 : h )) # Height (y)
integer w=${2:-30} ; w=$(( w<=0 ? 30 : w )) # Width (x)
# # Functions:
#
# # Function _display(map, h, w)
#
function _display {
typeset _dmap ; nameref _dmap="$1"
typeset _h _w ; integer _h=$2 _w=$3
typeset _x _y ; integer _x _y
printf %s "�[H"
for (( _y=0; _y<_h; _y++ )); do
for (( _x=0; _x<_w; _x++ )); do
printf %s "${char[_dmap[_y][_x]]}"
done
printf "%s\n"
done
}
# # Function _evolve(h, w)
#
_evolve() {
typeset _h _w ; integer _h=$1 _w=$2
typeset _x _y _n _y1 _x1 ; integer _x _y _n _y1 _x1
typeset _new _newdef ; typeset -a _new
for (( _y=0; _y<_h; _y++ )); do
for (( _x=0; _x<_w; _x++ )); do
_n=0
for (( _y1=_y-1; _y1<=_y+1; _y1++ )); do
for (( _x1=_x-1; _x1<=_x+1; _x1++ )); do
(( _map[$(( (_y1 + _h) % _h ))][$(( (_x1 + _w) % _w ))] )) && (( _n++ ))
done
done
(( _map[_y][_x] )) && (( _n-- ))
_new[_y][_x]=$(( (_n==3) || (_n==2 && _map[_y][_x]) ))
done
done
for (( _y=0; _y<_h; _y++ )); do
for (( _x=0; _x<_w; _x++ )); do
_map[_y][_x]=${_new[_y][_x]}
done
done
}
# # Function _game(h, w)
#
function _game {
typeset _h ; integer _h=$1
typeset _w ; integer _w=$2
typeset _x _y ; integer _x _y
typeset -a _map
for (( _y=0 ; _y<_h ; _y++ )); do
for (( _x=0 ; _x<_h ; _x++ )); do
_map[_x][_y]=$(( RANDOM < RAND_MAX / 10 ? 1 : 0 )) # seed map
done
done
while : ; do
_display _map ${_h} ${_w}
_evolve ${_h} ${_w}
sleep 0.2
done
}
######
# main #
######
_game ${h} ${w}
M2000 Interpreter
Module Life {
Font "courier new"
Form 40, 18
Cls 3,0
Double
Report 2, "Game of Life"
Normal
Cls 5, 2
Const Mx=20, My=10
Dim A(0 to Mx+1, 0 to My+1)=0
Flush
REM Data (2,2),(2,3),(3,3),(4,3),(5,4),(,3,4),(5,3),(6,2),(8,5),(5,8)
Data (5,3)
Data (5,4)
Data (5,5)
generation=1
While not empty
(A, B)=Array
A(A,B)=1
End While
Display()
Do
k$=Key$
If k$=chr$(13) Then exit
A()=@GetNext()
refresh 500
Display()
Until A()#Sum()=0
K$=Key$
Cls, 0
End
Function GetNext()
Local B()
B()=A() ' copy array
Local B=B() ' get a pointer
B*=0 ' make all element zero
Local i, j, tot
For j=1 to My
For i=1 to Mx
tot=-A(i,j)
For k=j-1 to j+1
For m=i-1 to i+1
tot+=A(m, k)
Next
Next
If A(i,j)=1 Then
If tot=2 or tot=3 Then B(i,j)=1
Else.if tot=3 Then
B(i,j)=1
End If
Next
Next
=B()
End Function
Sub Display()
Cursor 0,2
move ! ' move graphic to character cursor
Fill scale.x, scale.y-pos.Y, 1, 5
Print "Generation:";Generation
Generation++
Local i, j
For j=1 to My
Print @(width div 2-Mx div 2);
For i=1 to Mx
Print If$(A(I,J)=1->"■", "□");
Next
Print
Next
Print
Report 2, "Press enter to exit or any other key for Next generation"
End Sub
}
Life
MANOOL
Straightforward implementation useful for benchmarking:
{ {extern "manool.org.18/std/0.3/all"} in
: let { N = 40; M = 80 } in
: let { G = 99 } in
: let
{ Display =
{ proc { B } as
: for { I = Range[N]$ } do
: do Out.WriteLine[] after
: for { J = Range[M]$ } do
Out.Write[{if B[I; J] <> 0 then "*" else " "}]
}
}
in
: var { B = {array N of: array M of 0} } in
-- initialization
B[19; 41] = 1
B[20; 40] = 1
B[21; 40] = 1
B[22; 40] = 1
B[22; 41] = 1
B[22; 42] = 1
B[22; 43] = 1
B[19; 44] = 1
-- end of initialization
Out.WriteLine["Before:"]; Display[B]
{ var { NextB = B } in
: repeat G do
: do {assign B = NextB; NextB = B} after
: for { I = Range[N]$ } do
: var { Up = (I - 1).Mod[N]; Down = (I + 1).Mod[N] } in
: for { J = Range[M]$ } do
: var { Left = (J - 1).Mod[M]; Right = (J + 1).Mod[M] } in
: var
{ Count =
B[Up ; Left ] +
B[Up ; J ] +
B[Up ; Right] +
B[I ; Right] +
B[Down; Right] +
B[Down; J ] +
B[Down; Left ] +
B[I ; Left ]
}
in
NextB[I; J] =
{ if Count == 2 then B[I; J] else
: if Count == 3 then 1 else
0
}
}
Out.WriteLine["After " G " generations:"]; Display[B]
}
Using comprehension notation:
{ {extern "manool.org.18/std/0.3/all"} in
: let { N = 40; M = 80 } in
: let { G = 99 } in
: let
{ Display =
{ proc { B } as
: for { I = Range[N]$ } do
: do Out.WriteLine[] after
: for { J = Range[M]$ } do
Out.Write[{if B[I; J] <> 0 then "*" else " "}]
}
}
in
: var { B = {array N of: array M of 0} } in
-- initialization
B[19; 41] = 1
B[20; 40] = 1
B[21; 40] = 1
B[22; 40] = 1
B[22; 41] = 1
B[22; 42] = 1
B[22; 43] = 1
B[19; 44] = 1
-- end of initialization
Out.WriteLine["Before:"]; Display[B]
{ repeat G do
B =
{ array for { I = Range[N]$ } of
: var { Up = (I - 1).Mod[N]; Down = (I + 1).Mod[N] } in
: array for { J = Range[M]$ } of
: var { Left = (J - 1).Mod[M]; Right = (J + 1).Mod[M] } in
: var
{ Count =
B[Up ; Left ] +
B[Up ; J ] +
B[Up ; Right] +
B[I ; Right] +
B[Down; Right] +
B[Down; J ] +
B[Down; Left ] +
B[I ; Left ]
}
in
: if Count == 2 then B[I; J] else
: if Count == 3 then 1 else
0
}
}
Out.WriteLine["After " G " generations:"]; Display[B]
}
Mathematica / Wolfram Language
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 ..... ..... ...#. ....# ..###
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
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])
Blinker over three generations
three_all_alive: matrix([0,1,0],[0,1,0],[0,1,0])$
with_slider_draw(n,makelist(j,j,0,2),image(gen(three_all_alive,n),0,0,30,30));
MiniScript
This GUI implementation is for use with Mini Micro.
// Conway's Game of Life
clear
rows = 64; rowRange = range(0, rows-1)
cols = 96; colRange = range(0, cols-1)
// prepare two tile displays, in display layers 4 and 5
img = Image.create(2, 1); img.setPixel 1, 0, color.white
grids = []
for dispIdx in [4,5]
display(dispIdx).mode = displayMode.tile
td = display(dispIdx)
td.cellSize = 9 // size of cells on screen
td.extent = [cols, rows]
td.overlap = -1 // adds a small gap between cells
td.tileSet = img; td.tileSetTileSize = 1
td.clear 0
grids.push td
end for
// initialize to a random state
for x in colRange
for y in rowRange
td.setCell x, y, rnd > 0.5
end for
end for
curIdx = 5
// main loop
while true
yield
td = grids[curIdx-4]
newIdx = 4 + (curIdx == 4)
newTd = grids[newIdx-4]
for x in colRange
for y in rowRange
// get sum of neighboring states
sum = 0
for i in [x-1, x, x+1]
if i < 0 or i >= cols then continue
for j in [y-1, y, y+1]
if j < 0 or j >= rows then continue
if i==x and j==y then continue
sum = sum + td.cell(i,j)
end for
end for
// Now, update the cell based on current state
// and neighboring sum.
if td.cell(x,y) then
newTd.setCell x, y, (sum == 2 or sum == 3)
else
newTd.setCell x, y, sum == 3
end if
end for
end for
display(newIdx).mode = displayMode.tile
display(curIdx).mode = displayMode.off
curIdx = newIdx
end while
Nim
import os, strutils, random
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 rand(9) < 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
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 = ()
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:
opam install graphics && ocamlopt -o life -I $(ocamlfind query graphics) 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.
Octave
1st order two variable recurrence relation m-file, will also run under MATLAB.
clear all
x=55; % Size of the Lattice (same as LAWE)
z(1:1:x^2)=0; % Initialise the binary lattice
z_prime=z; % prepare the z prime
idx=7*x+2; % Origin
z(idx+4)=1; % Populate the binary lattice with the Gosper Glider
z(idx+x+4)=1;
z(idx+1+4)=1;
z(idx+x+1+4)=1;
z(idx+14)=1;
z(idx+14+x)=1;
z(idx+14+x+x)=1;
z(idx+15+x+x+x)=1;
z(idx+15-x)=1;
z(idx+16-x-x)=1;
z(idx+16+x+x+x+x)=1;
z(idx+17-x-x)=1;
z(idx+17+x+x+x+x)=1;
z(idx+18+x)=1;
z(idx+19-x)=1;
z(idx+19+x+x+x)=1;
z(idx+20)=1;
z(idx+20+x)=1;
z(idx+20+x+x)=1;
z(idx+21+x)=1;
z(idx+24)=1;
z(idx+25)=1;
z(idx+24-x)=1;
z(idx+25-x)=1;
z(idx+24-x-x)=1;
z(idx+25-x-x)=1;
z(idx+26-x-x-x)=1;
z(idx+26+x)=1;
z(idx+28-x-x-x)=1;
z(idx+28+x)=1;
z(idx+28-x-x-x-x)=1;
z(idx+28+x+x)=1;
z(idx+38)=1;
z(idx+38-x)=1;
z(idx+39)=1;
z(idx-x+39)=1;
for k=1:1:80; % Number of frames
for n=x+2:1:x^2-x-1; % one time pass
theta=0; % Sum the surrounding area (top equation)
for c=n-1:1:n+1;
for m=-1:1:1;
theta=theta+z(m*x+c);
endfor
endfor
z_prime(n)= round(1/5*(z(n)*acos(sin(pi/4*(theta-z(n)-9/2))) + (1-z(n)) * acos(sin(pi/4*(theta-z(n) + 3))))); % bottom equation
endfor
figure(2)
title('GOL','FontWeight','bold');
xlabel("X");
ylabel("Y");
imagesc(reshape(z,x,x)')
z=z_prime;
pause % each press advances the algorithm one step
endfor
Ol
#!/usr/bin/ol
(import (otus random!))
(define MAX 65536) ; should be power of two
; size of game board (should be less than MAX)
(define WIDTH 170)
(define HEIGHT 96)
; helper function
(define (hash x y)
(let ((x (mod (+ x WIDTH) WIDTH))
(y (mod (+ y HEIGHT) HEIGHT)))
(+ (* y MAX) x)))
; helper function
(define neighbors '(
(-1 . -1) ( 0 . -1) ( 1 . -1)
(-1 . 0) ( 1 . 0)
(-1 . 1) ( 0 . 1) ( 1 . 1)
))
; dead-or-alive cell test
(define (alive gen x y)
(case (fold (lambda (f xy)
(+ f (get gen (hash (+ x (car xy)) (+ y (cdr xy))) 0)))
0 neighbors)
(2
(get gen (hash x y) #false))
(3
#true)))
; ---------------
(import (lib gl2))
(gl:set-window-title "Convey's The game of Life")
(glShadeModel GL_SMOOTH)
(glClearColor 0.11 0.11 0.11 1)
(glOrtho 0 WIDTH 0 HEIGHT 0 1)
(glPointSize (/ 854 WIDTH))
; generate random field
(gl:set-userdata
(list->ff (map (lambda (i) (let ((x (rand! WIDTH)) (y (rand! HEIGHT)))
(cons (hash x y) 1))) (iota 2000))))
; main game loop
(gl:set-renderer (lambda (mouse)
(let ((generation (gl:get-userdata)))
(glClear GL_COLOR_BUFFER_BIT)
; draw the cells
(glColor3f 0.2 0.5 0.2)
(glBegin GL_POINTS)
(ff-fold (lambda (st key value)
(glVertex2f (mod key MAX)
(div key MAX))
) #f generation)
(glEnd)
(gl:set-userdata
; next cells generation
(ff-fold (lambda (st key value)
(let ((x (mod key MAX))
(y (div key MAX)))
(fold (lambda (st key)
(let ((x (+ x (car key)))
(y (+ y (cdr key))))
(if (alive generation x y) (put st (hash x y) 1) st)))
(if (alive generation x y) (put st (hash x y) 1) st) ; the cell
neighbors)))
#empty generation)))))
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
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
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))
Pascal
Uses crt for console output. Can make use of a "torus"-world. Optimized for speed on a Haswell CPU (without PrintGen ~ 8.5 Cpu-cyles/coordinate )
program Gol;
// Game of life
{$IFDEF FPC}
//save as gol.pp/gol.pas
{$Mode delphi}
{$ELSE}
//for Delphi save as gol.dpr
{$Apptype Console}
{$ENDIF}
uses
crt;
const
colMax = 76;
rowMax = 22;
dr = colMax+2; // element count of one row
cDelay = 20; // delay in ms
(*
expand field by one row/column before and after
for easier access no special treatment of torus
*)
type
tFldElem = byte;//0..1
tpFldElem = ^tFldElem;
tRow = array[0..colMax+1] of tFldElem;
tpRow = ^tRow;
tBoard = array[0..rowMax+1] of tRow;
tpBoard = ^tBoard;
tpBoards = array[0..1] of tpBoard;
type
tIntArr = array[0..2*dr+2] of tFldElem;
tpIntArr = ^tIntArr;
var
aBoard,bBoard : tBoard;
pBoards :tpBoards;
gblActBoard : byte;
gblUseTorus :boolean;
gblGenCnt : integer;
procedure PrintGen;
const
cChar: array[0..1] of char = (' ','#');
var
p0 : tpIntArr;
col,row: integer;
s : string[colMax];
begin
setlength(s,colmax);
gotoxy(1,1);
writeln(gblGenCnt:10);
For row := 1 to rowMax do
begin
p0 := @pBoards[gblActBoard]^[row,0];;
For col := 1 to colMax do
s[col] := cChar[p0[col]];
writeln(s);
end;
delay(cDelay);
end;
procedure Init0(useTorus:boolean);
begin
gblUseTorus := useTorus;
gblGenCnt := 0;
fillchar(aBoard,SizeOf(aBoard),#0);
pBoards[0] := @aBoard;
pBoards[1] := @bBoard;
gblActBoard := 0;
clrscr;
end;
procedure InitRandom(useTorus:boolean);
var
col,row : integer;
begin
Init0(useTorus);
For row := 1 to rowMax do
For col := 1 to colMax do
aBoard[row,col]:= tFldElem(random>0.9);
end;
procedure InitBlinker(useTorus:boolean);
var
col,row : integer;
begin
Init0(useTorus);
For col := 1 to colMax do
begin
IF (col+2) mod 4 = 0 then
begin
For row := 1 to rowmax do
IF row mod 4 <> 0 then
aBoard[row,col]:= 1;
end;
end;
end;
procedure Torus;
var
p0 : tpIntArr;
row: integer;
begin
//copy column 1-> colMax+1 and colMax-> 0
p0 := @pBoards[gblActBoard]^[1,0];
For row := 1 to rowMax do
begin
p0^[0] := p0^[colMax];
p0^[colmax+1] := p0^[1];
//next row
p0 := Pointer(PtrUint(p0)+SizeOf(tRow));
end;
//copy row 1-> rowMax+1
move(pBoards[gblActBoard]^[1,0],pBoards[gblActBoard]^[rowMax+1,0],sizeof(trow));
//copy row rowMax-> 0
move(pBoards[gblActBoard]^[rowMax,0],pBoards[gblActBoard]^[0,0],sizeof(trow));
end;
function Survive(p: tpIntArr):tFldElem;
//p points to actual_board [row-1,col-1]
//calculates the sum of alive around [row,col] aka p^[dr+1]
//really fast using fpc 2.6.4 no element on stack
const
cSurvives : array[boolean,0..8] of byte =
//0,1,2,3,4,5,6,7,8 sum of alive neighbours
((0,0,0,1,0,0,0,0,0), {alive =false 1->born}
(0,0,1,1,0,0,0,0,0)); {alive =true 0->die }
var
sum : integer;
begin
// row above
// sum := byte(aBoard[row-1,col-1])+byte(aBoard[row-1,col])+byte(aBoard[row-1,col+1]);
sum := integer(p^[ 0])+integer(p^[ 1])+integer(p^[ 2]);
sum := sum+integer(p^[ dr+0]) +integer(p^[ dr+2]);
sum := sum+integer(p^[2*dr+0])+integer(p^[2*dr+1])+integer(p^[2*dr+2]);
survive := cSurvives[boolean(p^[dr+1]),sum];
end;
procedure NextGen;
var
p0,p1 : tpFldElem;
row: NativeInt;
col :NativeInt;
begin
if gblUseTorus then
Torus;
p1 := @pBoards[1-gblActBoard]^[1,1];
//One row above and one column before because of survive
p0 := @pBoards[ gblActBoard]^[0,0];
For row := rowMax-1 downto 0 do
begin
For col := colMax-1 downto 0 do
begin
p1^ := survive(tpIntArr(p0));
inc(p0);
inc(p1);
end;
// jump over the borders
inc(p1,2);
inc(p0,2);
end;
//aBoard := bBoard;
gblActBoard :=1-gblActBoard;
inc(gblGenCnt);
end;
begin
InitBlinker(false);
repeat
PrintGen;
NextGen;
until keypressed;
PrintGen;
end.
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;
}
Next Generation in a Single Substitution Operator
#!/usr/bin/perl
print $_ = <<'' =~ tr/./ /r; # test case, dots only for layout
.....
.....
.###.
.....
.....
my $gap = /..\n/ ? qr/.{$-[0]}/s : die "too small";
my $neighborhood = qr/(?<=(...) $gap (.)) . (?=(.) $gap (...))/x;
for my $generation ( 2 .. 4 )
{
s/$neighborhood/ substr " $&# ", "$1$2$3$4" =~ tr|#||, 1 /ge;
print;
}
- Output:
### # # # ### # # #
Phix
You can run this online here.
-- -- demo\rosetta\Conways_Game_of_Life.exw -- ===================================== -- with javascript_semantics include pGUI.e constant title = "Conway's Game of Life" Ihandle dlg, canvas Ihandln hTimer = NULL cdCanvas cddbuffer sequence c = {}, -- cells cn, -- new cells cl -- last cells procedure draw(integer what) integer x = floor(length(c)/2)+1, y = floor(length(c[1])/2) switch what do case ' ': c = sq_mul(c,0) -- Clear case '+': c[x][y-1..y+1] = repeat(1,3) -- Blinker case 'G': { c[x+1,y+4], c[x+2,y+2], c[x+2,y+4], c[x+3,y+3], c[x+3,y+4]} = repeat(1,5) -- Glider case 'T': {c[x-1,y+1],c[x,y+1],c[x+1,y+1], c[x ,y+3],c[x,y+4],c[x ,y+5]} = repeat(1,6) -- Thunderbird case 'X': for i=0 to min(x,y)-1 do {c[x-i,y+i],c[x+i,y+i], c[x-i,y-i],c[x+i,y-i]} = repeat(1,4) -- Cross end for end switch end procedure atom t1 = time()+1 integer fps = 0 function redraw_cb(Ihandle /*ih*/) integer {width, height} = IupGetIntInt(canvas, "DRAWSIZE"), -- limit to 10K cells (performance target of 10fps) -- n here is the cell size in pixels (min of 1x1) n = ceil(sqrt(width*height/10000)), w = floor(width/n)+2, -- (see cx below) h = floor(height/n)+2, alive = 0 -- keep w, h odd for symmetry (plus I suspect the -- "Cross" code above may now crash without this) w -= even(w) h -= even(h) if length(c)!=w or length(c[1])!=h then c = repeat(repeat(0,h),w) cn = deep_copy(c) cl = deep_copy(c) draw('X') if hTimer!=NULL then IupStoreAttribute(hTimer, "RUN", "YES") end if end if cdCanvasActivate(cddbuffer) -- -- There is a "dead border" of 1 cell all around the edge of c (etc) which -- we never display or update. If we have got this right/an exact fit, then -- w*n should be exactly 2n too wide, whereas in the worst case there is an -- (2n-1) pixel border, which we split between left and right, ditto cy. -- integer cx = n+floor((width-w*n)/2) for x=2 to w-1 do integer cy = n+floor((height-h*n)/2) for y=2 to h-1 do integer cxy = c[x,y], clxy = cl[x,y], cnxy = -cxy for i=x-1 to x+1 do for j=y-1 to y+1 do cnxy += c[i,j] end for end for integer live = iff(cxy?cnxy>=2 and cnxy<=3:cnxy==3), colour = iff(live?iff(cxy?iff(clxy?CD_PURPLE -- adult :CD_GREEN) -- newborn :iff(clxy?CD_RED -- old :CD_YELLOW)) -- shortlived :CD_BLACK) cn[x,y] = live alive += live cdCanvasSetForeground(cddbuffer,colour) cdCanvasBox(cddbuffer, cx, cx+n-1, cy, cy+n-1) cy += n end for cx += n end for cdCanvasFlush(cddbuffer) if not alive then IupStoreAttribute(hTimer, "RUN", "NO") IupStoreAttribute(dlg, "TITLE", "died") elsif c=cn then IupStoreAttribute(hTimer, "RUN", "NO") IupStoreAttribute(dlg, "TITLE", "stable") else cl = c c = deep_copy(cn) fps += 1 if time()>=t1 then IupSetStrAttribute(dlg,"TITLE","%s (%d FPS)",{title,fps}) t1 = time()+1 fps = 0 end if end if return IUP_DEFAULT end function function map_cb(Ihandle ih) cdCanvas cdcanvas = cdCreateCanvas(CD_IUP, ih) cddbuffer = cdCreateCanvas(CD_DBUFFER, cdcanvas) cdCanvasSetBackground(cddbuffer, CD_WHITE) cdCanvasSetForeground(cddbuffer, CD_RED) return IUP_DEFAULT end function function key_cb(Ihandle /*ih*/, atom c) if c=K_ESC then return IUP_CLOSE end if -- (standard practice for me) if c=K_F5 then return IUP_DEFAULT end if -- (let browser reload work) if find(c," ") then draw(' ') -- Clear elsif find(c,"+bB") then draw('+') -- Blinker elsif find(c,"gG") then draw('G') -- Glider elsif find(c,"tT") then draw('T') -- Thunderbird elsif find(c,"xX") then draw('X') -- Cross end if IupStoreAttribute(hTimer, "RUN", "YES") return IUP_CONTINUE end function function timer_cb(Ihandle /*ih*/) IupUpdate(canvas) return IUP_IGNORE end function procedure main() IupOpen() canvas = IupCanvas("RASTERSIZE=390x390") IupSetCallbacks(canvas, {"MAP_CB", Icallback("map_cb"), "ACTION", Icallback("redraw_cb")}) dlg = IupDialog(canvas,`TITLE="%s", MINSIZE=350x55`, {title}) -- (above MINSIZE prevents the title from getting squished) IupSetCallback(dlg, "KEY_CB", Icallback("key_cb")) IupShow(dlg) IupSetAttribute(canvas,"RASTERSIZE",NULL) -- hTimer = IupTimer(Icallback("timer_cb"),40) -- 25fps (maybe) hTimer = IupTimer(Icallback("timer_cb"),100) -- 10fps if platform()!=JS then IupMainLoop() IupClose() end if end procedure main()
Picat
Blinker
go =>
Rows = 3,
Cols = 3,
println(blinker),
pattern(blinker, Pattern,I,J),
life(fill(Rows,Cols,Pattern,I,J)),
nl.
fill(Rows, Cols, Obj) = fill(Rows, Cols, Obj,1,1).
fill(Rows, Cols, Obj,OffsetI,OffsetJ) = Grid =>
Grid = new_array(Rows,Cols), bind_vars(Grid,0),
foreach(I in 1..Obj.length, J in 1..Obj[1].length)
Grid[I+OffsetI-1,J+OffsetJ-1] := Obj[I,J]
end.
% We stop whenever a fixpoint/cycle is detected
life(S) =>
Rows = S.length,
Cols = S[1].length,
println([rows=Rows, cols=Cols]),
print_grid(S),
Seen = new_map(), % detect fixpoint and cycle
Count = 0,
while (not Seen.has_key(S))
Seen.put(S,1),
T = new_array(Rows,Cols),
foreach(I in 1..Rows, J in 1..Cols)
Sum = sum([S[I+A,J+B] : A in -1..1,B in -1..1,
I+A > 0, J+B > 0,
I+A =< Rows, J+B =< Cols]) - S[I,J],
C = rules(S[I,J], Sum),
T[I,J] := C
end,
print_grid(T),
S := T,
Count := Count + 1
end,
printf("%d generations\n", Count).
print_grid(G) =>
foreach(I in 1..G.length)
foreach(J in 1..G[1].length)
if G[I,J] == 1 then print("#") else print(".") end
end,
nl
end,
nl.
% The Rules of Life
rules(This,Sum) = 1, (This == 1, member(Sum,[2,3]); This == 0, Sum == 3) => true.
rules(_This, _Sum) = 0 => true.
%
% The patterns
% pattern(Name, Pattern, OffsetI, OffsetJ)
% where Offset* is the recommended offsets in a grid.
%
% For the task
pattern(blinker, Pattern,I,J) ?=>
Pattern = [[0,0,0],
[1,1,1],
[0,0,0]],
I=1,J=1.
- Output:
blinker [rows = 3,cols = 3] ... ### ... .#. .#. .#. ... ### ... 2 generations
Testing some more forms
go2 =>
Rows = 20,
Cols = 20,
Names = [blinker2, p4, p5, glider, figure_eight],
foreach(Name in Names )
pattern(Name, Pattern,I,J),
println(Name),
life(fill(Rows,Cols,Pattern,I,J)),
nl
end,
nl.
% Increase the recommended offset
pattern(blinker2, Pattern,I,J) ?=>
Pattern = [[0,0,0],
[1,1,1],
[0,0,0]],
I=5,J=5.
pattern(p4, Pattern,I,J) ?=>
Pattern = [[0,0,0,0],
[1,1,1,1],
[1,1,1,1],
[0,0,0,0]],
I=10,J=10.
pattern(p5, Pattern,I,J) ?=>
Pattern = [[0,0,0,0,0],
[1,1,1,1,1],
[1,1,1,1,1],
[1,1,1,1,1],
[0,0,0,0,0]],
I=10,J=10.
pattern(glider, Pattern,I,J) ?=>
Pattern = [[0,0,1],
[1,0,1],
[0,1,1]],
I=5,J=5.
pattern(figure_eight, Pattern,I,J) =>
Pattern = [[1,1,1,0,0,0],
[1,1,1,0,0,0],
[1,1,1,0,0,0],
[0,0,0,1,1,1],
[0,0,0,1,1,1],
[0,0,0,1,1,1]],
I=10,J=10.
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
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
Pointless
-----------------------------------------------------------
-- Print 100 simulated states of conway's game of life
-- for a glider starting pattern on a wrapping grid
-- Print generation number along with cells
output =
initCells
|> iterate(updateCells)
|> take(130)
|> enumerate
|> map(showPair)
|> printFrames
initCells =
[[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 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 0 0 0 0 1 0 0 0 0 0 0 0]
[0 0 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 0 0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]]
width = length(initCells[0])
height = length(initCells)
positions =
for y in range(0, height - 1)
for x in range(0, width - 1)
yield (x, y)
-----------------------------------------------------------
-- Update each cell in the grid according to its position,
-- and convert the resulting list back to a 2D array
updateCells(cells) =
positions
|> map(tick(cells))
|> toNDArray((height, width))
-----------------------------------------------------------
-- Get the new value for a cell at a give position
-- based on the current cell values in the grid
tick(cells, pos) = toInt(survive or birth) where {
survive = cells[y][x] == 1 and count in {2, 3}
birth = cells[y][x] == 0 and count == 3
count = getCount(x, y, cells)
(x, y) = pos
}
-----------------------------------------------------------
-- Get the number of live neighbors of a given position
getCount(x, y, cells) = sum(
for dx in [-1, 0, 1]
for dy in [-1, 0, 1]
when (dx != 0 or dy != 0)
yield getNeighbor(x + dx, y + dy, cells)
)
getNeighbor(x, y, cells) = cells[y % height][x % width]
-----------------------------------------------------------
-- Print the board and generation number given pairs
-- of (gen, cells) from the enumerate function
showPair(pair) =
format("{}\ngeneration: {}", [showCells(cells), gen])
where (gen, cells) = pair
showCells(cells) =
toList(cells)
|> map(showRow)
|> join("\n")
showRow(row) =
format("|{}|", [map(showCell, toList(row)) |> join("")])
showCell(cell) =
if cell == 1 then "*" else " "
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
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(_,[]) :- !.
Processing
Inspired by Daniel Shiffman's book The Nature of Code (http://natureofcode.com)
boolean play = true;
int cellSize = 10;
int cols, rows;
int lastCell = 0;
int sample = 10;
// Game of life board
int[][] grid;
void setup() {
size(800, 500);
noStroke();
// Calculate cols, rows and init array
cols = width/cellSize;
rows = height/cellSize;
grid = new int[cols][rows];
init(-1); // randomized start
println("Press 'space' to start/stop");
println("'e' to clear all cells");
println("'b' demonstrate 'blinker'");
println("'g' demonstrate glider");
println("'r' to randomize grid");
println("'+' and '-' to change speed");
}
void draw() {
background(0);
for ( int i = 0; i < cols; i++) {
for ( int j = 0; j < rows; j++) {
if ((grid[i][j] == 1)) fill(255);
else fill(0);
rect(i*cellSize, j*cellSize, cellSize, cellSize);
}
}
if (play && frameCount%sample==0 && !mousePressed) {
generate();
}
}
void generate() {
int[][] nextGrid = new int[cols][rows];
for (int x = 0; x < cols; x++) {
for (int y = 0; y < rows; y++) {
int ngbs = countNgbs(x, y);
// the classic Conway rules
if ((grid[x][y] == 1) && (ngbs < 2)) nextGrid[x][y] = 0; // solitude
else if ((grid[x][y] == 1) && (ngbs > 3)) nextGrid[x][y] = 0; // crowded
else if ((grid[x][y] == 0) && (ngbs == 3)) nextGrid[x][y] = 1; // cell born
else nextGrid[x][y] = grid[x][y]; // keep
}
}
grid = nextGrid;
}
int countNgbs(int x, int y) {
int ngbCount = 0;
for (int i = -1; i <= 1; i++) {
for (int j = -1; j <= 1; j++) {
// 'united' borders
ngbCount += grid[(x+i+cols)%cols][(y+j+rows)%rows];
}
}
// cell taken out of count
ngbCount -= grid[x][y];
return ngbCount;
}
void init(int option) {
int state;
for (int x = 0; x < cols; x++) {
for (int y = 0; y < rows; y++) {
if (option == -1) {
state = int(random(2));
} else {
state = option;
}
grid[x][y] = state;
}
}
}
void keyReleased() {
if (key == 'r') {
init(-1); // randomize grid
}
if (key == 'e') {
init(0); // empty grid
}
if (key == 'g') {
int glider[][] = {
{0, 1, 0},
{0, 0, 1},
{1, 1, 1}};
setNine(10, 10, glider);
}
if (key == 'b') {
int blinker[][] = {
{0, 1, 0},
{0, 1, 0},
{0, 1, 0}};
setNine(10, 10, blinker);
}
if (key == ' ') {
play = !play;
}
if (key == '+' || key == '=') {
sample=max(sample-1, 1);
}
if (key == '-') {
sample++;
}
}
void setNine(int x, int y, int nine[][]) {
for (int i = 0; i <= 2; i++) {
for (int j = 0; j <= 2; j++) {
grid[(x+i+cols)%cols][(y+j+rows)%rows] = nine[i][j];
}
}
}
void mousePressed() {
paint();
}
void mouseDragged() {
paint();
}
void paint() {
int x = mouseX/cellSize;
int y = mouseY/cellSize;
int p = y*cols + x;
if (p!=lastCell) {
lastCell=p;
int states[] = {1, 0};
grid[x][y] = states[grid[x][y]]; // invert
}
}
Processing Python mode
cell_size = 10
sample = 10
play = False # simulation is running
last_cell = 0
def setup():
global grid, next_grid, rows, cols
size(800, 500)
rows = height / cell_size
cols = width / cell_size
grid = empty_grid()
next_grid = empty_grid()
randomize_grid()
println("Press 'space' to start/stop")
println("'e' to clear all cells")
println("'b' demonstrate 'blinker'")
println("'g' demonstrate glider")
println("'r' to randomize grid")
println("'+' and '-' to change speed")
def draw():
background(0)
for i in range(cols):
x = i * cell_size
for j in range(rows):
y = j * cell_size
current_state = grid[i][j]
fill(255)
noStroke()
if current_state:
rect(x, y, cell_size, cell_size)
if play:
ngbs_alive = calc_ngbs_alive(i, j)
result = rule(current_state, ngbs_alive)
next_grid[i][j] = result
if play and frameCount % sample == 0 and not mousePressed:
step()
def rule(current, ngbs):
""" classic Conway's Game of Life rule """
if ngbs < 2 or ngbs > 3:
return 0 # dies / dead
elif ngbs == 3:
return 1 # born / alive
else:
return current # stays the same (ngbs == 2)
def calc_ngbs_alive(i, j):
NEIGHBOURS = ((-1, 00), (01, 00), # a tuple describing the neighbourhood of a cell
(-1, -1), (00, -1),
(01, -1), (-1, 01),
(00, 01), (01, 01))
alive = 0
for iv, jv in NEIGHBOURS:
alive += grid[(i + iv) % cols][(j + jv) % rows]
return alive
def empty_grid():
grid = []
for _ in range(cols):
grid.append([0] * rows)
return grid
def randomize_grid():
from random import choice
for i in range(cols):
for j in range(rows):
grid[i][j] = choice((0, 1))
def step():
global grid, next_grid
grid = next_grid
next_grid = empty_grid()
def keyReleased():
global grid, play, sample
if key == "e":
grid = empty_grid()
if key == "r":
randomize_grid()
if key == "g":
grid[10][10:13] = [0, 1, 0]
grid[11][10:13] = [0, 0, 1]
grid[12][10:13] = [1, 1, 1]
if key == "b":
grid[10][10:13] = [0, 1, 0]
grid[11][10:13] = [0, 1, 0]
grid[12][10:13] = [0, 1, 0]
if key == " ":
play = not play
if str(key) in '+=':
sample = max(sample - 1, 1);
if key == '-':
sample += 1
def mousePressed():
paint()
def mouseDragged():
paint()
def paint():
global last_cell
i, j = mouseX // cell_size, mouseY // cell_size
p = j * cols + i
if p != last_cell:
last_cell = p
grid[i][j] = (1, 0)[grid[i][j]]
Python
Using defaultdict
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: --- ### ---
Boardless approach
A version using the boardless approach. A world is represented as a set of (x, y) coordinates of all the alive cells.
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 = {c for c in counts
if counts[c] == 3 or (counts[c] == 2 and c in world)}
neighboring_cells = [(-1, -1), (-1, 0), (-1, 1),
( 0, -1), ( 0, 1),
( 1, -1), ( 1, 0), ( 1, 1)]
def offset(cells, delta):
"Slide/offset all the cells by delta, a (dx, dy) vector."
(dx, dy) = delta
return {(x+dx, y+dy) for (x, y) in cells}
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: ##................................... ##.................##................ ..................#..#............... .....###...........##................ ..............##..................... .............##..........##.......... ...............#........#............ ...........................#.......## .........................##........##
Using an array to define the world
import numpy as np
from pandas import DataFrame
import matplotlib.pyplot as plt
#import time
def conway_life(len=10, wid=10, gen=5):
curr_gen = DataFrame(np.random.randint(0, 2, (len+2, wid+2)),
index = range(len+2),
columns = range(wid+2))
curr_gen[0] = 0
curr_gen[wid+1] = 0
curr_gen[0: 1] = 0
curr_gen[len+1: len+2] = 0
for i in range(gen):
fig, ax = plt.subplots()
draw = curr_gen[1:len+1].drop([0, wid+1], axis=1)
# 画图
image = draw
ax.imshow(image, cmap=plt.cm.cool, interpolation='nearest')
ax.set_title("Conway's game of life.")
# Move left and bottom spines outward by 10 points
ax.spines['left'].set_position(('outward', 10))
ax.spines['bottom'].set_position(('outward', 10))
# Hide the right and top spines
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)
# Only show ticks on the left and bottom spines
ax.yaxis.set_ticks_position('left')
ax.xaxis.set_ticks_position('bottom')
plt.show()
# time.sleep(1)
# 初始化空表
next_gen = DataFrame(np.random.randint(0, 1, (len+2, wid+2)),
index = range(len+2),
columns = range(wid+2))
# 生成下一代
for x in range(1, wid+1):
for y in range(1, len+1):
env = (curr_gen[x-1][y-1] + curr_gen[x][y-1] +
curr_gen[x+1][y-1]+ curr_gen[x-1][y] +
curr_gen[x+1][y] + curr_gen[x-1][y+1] +
curr_gen[x][y+1] + curr_gen[x+1][y+1])
if (not curr_gen[x][y] and env == 3):
next_gen[x][y] = 1
if (curr_gen[x][y] and env in (2, 3)):
next_gen[x][y] = 1
curr_gen = next_gen
conway_life()
Quackery
Uses a Tortoise and Hare algorithm to detect when Life becomes static or repetitive. The tortoise version is displayed, along with five victory laps of the cyclic part once a cycle is detected.
[ $ "turtleduck.qky" loadfile ] now!
[ $ \
import time
now = time.time()
sleepy_time = 5/7-(now%(5/7))
time.sleep(sleepy_time)
\ python ] is wait ( --> )
[ dup 1
[ 2dup > while + 1 >> 2dup / again ]
drop nip ] is sqrt ( n --> n )
[ stack ] is width ( --> s )
[ tuck witheach
[ over i^ peek + rot i^ poke swap ]
drop ] is add ( [ [ --> [ )
[ 1 split swap join ] is left ( [ --> [ )
[ -1 split swap join ] is right ( [ --> [ )
[ width share split swap join ] is up ( [ --> [ )
[ width share negate split swap join ] is down ( [ --> [ )
[ left dup up tuck add
swap right tuck add
swap right tuck add
swap down tuck add
swap down tuck add
swap left tuck add
swap left add ] is neighbours ( [ --> [ )
[ [] swap
width share times
[ width share split
dip [ 0 swap 0 join join join ] ]
drop
2 width tally
0 width share of tuck join join ] is makeborder ( [ --> [ )
[ dup size times
[ 0 swap i^ poke
0 swap i^ width share + 1 - poke
width share step ]
width share times
[ 0 swap i^ poke
0 swap i^ 1 + negate poke ] ] is clearborder ( [ --> [ )
[ dup neighbours
witheach
[ over i^ peek iff
[ 1 | 3 != if
[ 0 swap i^ poke ] ]
done
3 = if [ 1 swap i^ poke ] ]
clearborder ] is nextgen ( [ --> [ )
[ 6 random
[ table
[ 200 200 255 ] [ 200 255 200 ]
[ 255 200 200 ] [ 200 255 255 ]
[ 255 200 255 ] [ 255 255 200 ] ]
fill
[ 4 times
[ 20 1 walk -1 4 turn ] ] ] is block ( --> )
[ clear
width share 10 *
dup negate
4 - 1 fly
-1 4 turn
16 - 1 fly
1 4 turn
20 1 fly
' [ 200 200 200 ] colour
4 times
[ width share 2 - 20 * 1 walk
1 4 turn ]
-20 1 fly
' [ 63 63 63 ] colour
width share times
[ width share split
swap witheach
[ if block
20 1 fly ]
width share -20 * 1 fly
1 4 turn
20 1 fly
-1 4 turn ]
drop wait frame ] is draw ( [ --> )
[ turtle 0 frames 2 wide
dup size sqrt width put
makeborder
dup
[ dup draw nextgen
dip [ nextgen nextgen ]
2dup = until ]
5 times
[ dup draw nextgen 2dup = until ]
2drop width release ] is life ( [ --> )
' [ 0 0 0
1 1 1
0 0 0 ] life ( task requirement )
' [ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 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 1 1 0 1 1 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 0 0 0 0 0 0 0 0 0 0 0 0 ] life ( more interesting )
randomise
[ []
1089 times
[ 2 random join ]
life
again ] ( ... runs forever )
- Output:
( task requirement )
( more interesting )
( ... runs forever )
(The code runs forever, not the video. The accompanying music is also generative, and comes from the Kolakoski Sequence.)
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))
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)
Raku
(formerly Perl 6)
class Automaton {
subset World of Str where {
.lines>>.chars.unique == 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.cache;
}
method gist { join "\n", map { .join }, @!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: q:to/EOF/;
............
............
............
.......###..
.###...#....
........#...
............
EOF
for ^10 {
say $glider++;
say '--';
}
Red
Red [
Purpose: "Conway's Game of Life"
Author: "Joe Smith"
]
neigh: [[0 1] [0 -1] [1 0] [-1 0] [1 1] [1 -1] [-1 1] [-1 -1]]
conway: function [petri] [
new-petri: copy/deep petri
repeat row length? petri [
repeat col length? petri [
live-neigh: 0
foreach cell neigh [
try [
if petri/(:row + cell/1)/(:col + cell/2) = 1 [live-neigh: live-neigh + 1]
]
]
switch petri/:row/:col [
1 [if any [live-neigh < 2 live-neigh > 3]
[new-petri/:row/:col: 0]
]
0 [if live-neigh = 3 [new-petri/:row/:col: 1]]
]]]
clear insert petri new-petri
]
*3-3: [
[0 1 0]
[0 1 0]
[0 1 0]
]
*8-8: [
[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]
]
display: function [table] [
foreach row table [
print replace/all (replace/all to-string row "0" "_") "1" "#"
] print ""
]
loop 5 [
display *3-3
conway *3-3
]
loop 23 [
display *8-8
conway *8-8
]
- Output:
_#_ _#_ _#_ ___ ### ___ _#_ _#_ _#_ ___ ### ___ _#_ _#_ _#_ __#_____ ___#____ _###____ ________ ________ ________ ________ ________ ________ _#_#____ __##____ __#_____ ________ ________ ________ ________ ________ ___#____ _#_#____ __##____ ________ ________ ________ ________ ________ __#_____ ___##___ __##____ ________ ________ ________ ________ ________ ___#____ ____#___ __###___ ________ ________ ________ ________ ________ ________ __#_#___ ___##___ ___#____ ________ ________ ________ ________ ________ ____#___ __#_#___ ___##___ ________ ________ ________ ________ ________ ___#____ ____##__ ___##___ ________ ________ ________ ________ ________ ____#___ _____#__ ___###__ ________ ________ ________ ________ ________ ________ ___#_#__ ____##__ ____#___ ________ ________ ________ ________ ________ _____#__ ___#_#__ ____##__ ________ ________ ________ ________ ________ ____#___ _____##_ ____##__ ________ ________ ________ ________ ________ _____#__ ______#_ ____###_ ________ ________ ________ ________ ________ ________ ____#_#_ _____##_ _____#__ ________ ________ ________ ________ ________ ______#_ ____#_#_ _____##_ ________ ________ ________ ________ ________ _____#__ ______## _____##_ ________ ________ ________ ________ ________ ______#_ _______# _____### ________ ________ ________ ________ ________ ________ _____#_# ______## ______#_ ________ ________ ________ ________ ________ _______# _____#_# ______## ________ ________ ________ ________ ________ ______#_ _______# ______## ________ ________ ________ ________ ________ ________ _______# ______## ________ ________ ________ ________ ________ ________ ______## ______## ________ ________ ________ ________ ________ ________ ______## ______##
Retro
:w/l [ $. eq? [ #0 ] [ #1 ] choose , ] s:for-each ;
'World d:create
'.................... w/l
'.................... w/l
'.................... w/l
'..ooo............... w/l
'....o............... w/l
'...o................ w/l
'.................... w/l
'.................... w/l
'.................... w/l
'.................... w/l
'.................... w/l
'....ooo............. w/l
'.................... w/l
'.................... w/l
'.................... w/l
'........ooo......... w/l
'.......ooo.......... w/l
'.................... w/l
'.................... w/l
'.................... w/l
'Next d:create
#20 #20 * allot
{{
'Surrounding var
:get (rc-v)
dup-pair [ #0 #19 n:between?
] bi@ and
[ &World + [ #20 * ] dip + fetch ] [ drop-pair #0 ] choose ;
:neighbor? (rc-) get &Surrounding v:inc-by ;
:NW (rc-rc) dup-pair [ n:dec ] bi@ neighbor? ;
:NN (rc-rc) dup-pair [ n:dec ] dip neighbor? ;
:NE (rc-rc) dup-pair [ n:dec ] dip n:inc neighbor? ;
:WW (rc-rc) dup-pair n:dec neighbor? ;
:EE (rc-rc) dup-pair n:inc neighbor? ;
:SW (rc-rc) dup-pair [ n:inc ] dip n:dec neighbor? ;
:SS (rc-rc) dup-pair [ n:inc ] dip neighbor? ;
:SE (rc-rc) dup-pair [ n:inc ] bi@ neighbor? ;
:count (rc-rcn)
#0 !Surrounding NW NN NE
WW EE
SW SS SE @Surrounding ;
:alive (rc-n)
count #2 #3 n:between? [ #1 ] if; #0 ;
:dead (rc-n)
count #3 eq? [ #1 ] if; #0 ;
:new-state (rc-n)
dup-pair get #1 eq? &alive &dead choose ;
:set (nrc-) &Next + [ #20 * ] dip + store ;
:cols (r-)
#20 [ I over swap new-state rot rot set ] times<with-index> drop ;
:output (n-) n:-zero? [ $o ] [ $. ] choose c:put sp ;
---reveal---
:display (-)
nl &World #20 [ #20 [ fetch-next output ] times nl ] times drop ;
:gen (-)
#20 [ I cols ] times<with-index> &Next &World #20 #20 * copy ;
}}
{{
:divide #20 [ $- c:put ] times sp 'Gen:_ s:put dup n:put nl ;
---reveal---
:gens (n-) #0 swap [ display divide n:inc gen ] times drop ;
}}
#12 gens
REXX
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 large side for general viewing, and maybe a wee bit complex to demonstrate how to program for this task.
/*REXX program runs and displays the Conway's game of life, it stops after N repeats. */
signal on halt /*handle a cell growth interruptus. */
parse arg peeps '(' rows cols empty life! clearScreen repeats generations .
rows = p(rows 3) /*the maximum number of cell rows. */
cols = p(cols 3) /* " " " " " columns. */
emp = pickChar(empty 'blank') /*an empty cell character (glyph). */
clearScr = p(clearScreen 0) /* "1" indicates to clear the screen.*/
life! = pickChar(life! '☼') /*the gylph kinda looks like an amoeba.*/
reps = p(repeats 2) /*stop pgm if there are two repeats.*/
generations = p(generations 100) /*the number of generations allowed. */
sw= max(linesize() - 1, cols) /*usable screen width for the display. */
#reps= 0; $.= emp /*the universe is new, ··· and barren.*/
gens=abs(generations) /*used for a programming convenience.*/
x= space(peeps); upper x /*elide superfluous spaces; uppercase. */
if x=='' then x= "BLINKER" /*if nothing specified, use BLINKER. */
if x=='BLINKER' then x= "2,1 2,2 2,3"
if x=='OCTAGON' then x= "1,5 1,6 2,4 2,7 3,3 3,8 4,2 4,9 5,2 5,9 6,3 6,8 7,4 7,7 8,5 8,6"
call assign. /*assign the initial state of all cells*/
call showCells /*show the initial state of the cells.*/
do life=1 for gens; call assign@ /*construct next generation of cells.*/
if generations>0 | life==gens then call showCells /*should cells be displayed? */
end /*life*/ /* [↑] cell colony grows, lives, dies.*/
exit /*stick a fork in it, we're all done. */
/*──────────────────────────────────────────────────────────────────────────────────────*/
showCells: if clearScr then 'CLS' /* ◄─── change 'command' for your OS.*/
call showRows /*show the rows in the proper order. */
say right(copies('▒', sw) life, sw) /*show a fence between the generations.*/
if _=='' then exit /*if there's no life, then stop the run*/
if !._ then #reps= #reps + 1 /*we detected a repeated cell pattern. */
!._= 1 /*existence state and compare later. */
if reps\==0 & #reps<=reps then return /*so far, so good, regarding repeats.*/
say
say center('"Life" repeated itself' reps "times, simulation has ended.",sw,'▒')
exit /*stick a fork in it, we're all done. */
/*───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────*/
$: 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
assign.: do while x\==''; parse var x r "," c x; $.r.c=life!; rows=max(rows,r); cols=max(cols,c); end; life=0; !.=0; return
assign?: ?=$.r.c; n=neighbors(); if ?==emp then do;if n==3 then ?=life!; end; else if n<2 | n>3 then ?=emp; @.r.c=?; return
assign@: @.=emp; do r=1 for rows; do c=1 for cols; call assign?; end; end; call assign$; return
halt: say; say "REXX program (Conway's Life) halted."; say; exit 0
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)); arg u .; if u=='BLANK' then _=" "; L=length(_); if L==3 then _=d2c(_); if L==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',emp); say z; _=_||z; end; return
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); not all REXXes have this BIF.
The LINESIZE.REX REXX program is included here ──► LINESIZE.REX.
- output when using the default inputs:
☼☼☼ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ 0 ☼ ☼ ☼ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ 1 ☼☼☼ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ 2 ☼ ☼ ☼ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ 3 ☼☼☼ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ 4 ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ "Life" repeated itself 2 times, simulation has ended. ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
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-------* | ** | ** | * * | | | ** | | * * | **** | * * | * * | **** | * * | | * * | * * | ** ** ** | * ** * | * ** * | * * | | * * | ** ** | * * | * * | ** ** | * * | | * * | ** ** | * * | * * | ** ** | * * | | * * | * * | ** ** ** | * ** * | * ** * | * * | | * * | **** | * * | * * | **** | * * | | ** | ** | * * | | | ** | *----------*----------*----------*----------*----------*----------*
Ruby
def game_of_life(name, size, generations, initial_life=nil)
board = new_board size
seed board, size, initial_life
print_board board, name, 0
reason = generations.times do |gen|
new = evolve board, size
print_board new, name, gen+1
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
puts
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
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, name, generation)
puts "#{name}: generation #{generation}"
m.each {|row| row.each {|val| print "#{val == 1 ? '#' : '.'} "}; 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
- Output:
blinker: generation 0 . # . . # . . # . blinker: generation 1 . . . # # # . . . blinker: generation 2 . # . . # . . # . specified lifetime ended glider: generation 0 . # . . . . # . # # # . . . . . glider: generation 1 . . . . # . # . . # # . . # . . glider: generation 2 . . . . . . # . # . # . . # # . glider: generation 3 . . . . . # . . . . # # . # # . glider: generation 4 . . . . . . # . . . . # . # # # specified lifetime ended random: generation 0 . . . # # . . . # . . . . # . # . . . # # . # # # random: generation 1 . . . # # . . # # . . . . # # . # # . # . # . # # random: generation 2 . . # # # . . # . . . # . . # . # . . . . # . # # random: generation 3 . . # # . . # # . # . # # . . # # . # # . . # . . random: generation 4 . # # # . . . . . . . . . . # # . . # . . # # # . random: generation 5 . . # . . . . # # . . . . . . . # . # # . # # # . random: generation 6 . . # # . . . # # . . . . . # . # . # # . # . # # random: generation 7 . . # # . . . # . # . . . . # . . . . . . . . # # random: generation 8 . . # # . . . # . # . . . # . . . . # # . . . . . random: generation 9 . . # # . . . # . # . . # . . . . . # # . . . . . random: generation 10 . . # # . . # # . . . . # . # . . . # . . . . . . specified lifetime ended
Class version
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)
@size = size
@board = GameBoard.new size, initial_life
@board.display name, 0
reason = generations.times do |gen|
new_board = evolve
new_board.display name, gen+1
break :all_dead if new_board.barren?
break :static if @board == new_board
@board = new_board
end
case reason
when :all_dead then puts "No more life."
when :static then puts "No movement."
else puts "Specified lifetime ended."
end
puts
end
def evolve
life = @board.each_index.select {|i,j| cell_fate(i,j)}
GameBoard.new @size, life
end
def cell_fate(i, j)
left_right = [0, i-1].max .. [i+1, @size-1].min
top_bottom = [0, j-1].max .. [j+1, @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
include Enumerable
def initialize(size, initial_life=nil)
@size = size
@board = Array.new(size) {Array.new(size) {Cell.new false}}
seed_board initial_life
end
def seed_board(life)
if life.nil?
# randomly seed board
each_index.to_a.sample(10).each {|x,y| @board[y][x].live}
else
life.each {|x,y| @board[y][x].live}
end
end
def each
@size.times {|x| @size.times {|y| yield @board[y][x] }}
end
def each_index
return to_enum(__method__) unless block_given?
@size.times {|x| @size.times {|y| yield x,y }}
end
def [](x, y)
@board[y][x]
end
def ==(board)
self.life == board.life
end
def barren?
none? {|cell| cell.alive?}
end
def life
each_index.select {|x,y| @board[y][x].alive?}
end
def display(name, generation)
puts "#{name}: generation #{generation}"
puts @board.map {|row| row.map {|cell| cell.alive? ? '#' : '.'}.join(' ')}
end
def apocalypse
# utility function to entirely clear the game board
each {|cell| cell.die}
end
end
class Cell
def initialize(alive) @alive = alive end
def alive?; @alive end
def value; @alive ? 1 : 0 end
def live; @alive = true end
def die; @alive = false end
end
Game.new "blinker", 3, 2, [[1,0],[1,1],[1,2]]
Game.new "glider", 4, 4, [[1,0],[2,1],[0,2],[1,2],[2,2]]
Game.new "random", 5, 10
Rust
use std::collections::HashMap;
use std::collections::HashSet;
type Cell = (i32, i32);
type Colony = HashSet<Cell>;
fn print_colony(col: &Colony, width: i32, height: i32) {
for y in 0..height {
for x in 0..width {
print!("{} ",
if col.contains(&(x, y)) {"O"}
else {"."}
);
}
println!();
}
}
fn neighbours(&(x,y): &Cell) -> Vec<Cell> {
vec![
(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),
]
}
fn neighbour_counts(col: &Colony) -> HashMap<Cell, i32> {
let mut ncnts = HashMap::new();
for cell in col.iter().flat_map(neighbours) {
*ncnts.entry(cell).or_insert(0) += 1;
}
ncnts
}
fn generation(col: Colony) -> Colony {
neighbour_counts(&col)
.into_iter()
.filter_map(|(cell, cnt)|
match (cnt, col.contains(&cell)) {
(2, true) |
(3, ..) => Some(cell),
_ => None
})
.collect()
}
fn life(init: Vec<Cell>, iters: i32, width: i32, height: i32) {
let mut col: Colony = init.into_iter().collect();
for i in 0..iters+1
{
println!("({})", &i);
if i != 0 {
col = generation(col);
}
print_colony(&col, width, height);
}
}
fn main() {
let blinker = vec![
(1,0),
(1,1),
(1,2)];
life(blinker, 3, 3, 3);
let glider = vec![
(1,0),
(2,1),
(0,2), (1,2), (2,2)];
life(glider, 20, 8, 8);
}
Scala
See Conway's Game of Life/Scala, Scala/Spark distributed
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)
- Output:
-#- -#- -#- --- ### --- -#- -#- -#- --- ### --- -#- -#- -#- --#----- ---#---- -###---- -------- -------- -------- -------- -------- -------- -#-#---- --##---- --#----- -------- -------- -------- -------- -------- ---#---- -#-#---- --##---- -------- -------- -------- -------- -------- --#----- ---##--- --##---- -------- -------- -------- -------- -------- ---#---- ----#--- --###--- -------- -------- -------- -------- -------- -------- --#-#--- ---##--- ---#---- -------- -------- -------- -------- -------- ----#--- --#-#--- ---##--- -------- -------- -------- -------- -------- ---#---- ----##-- ---##--- -------- -------- -------- -------- -------- ----#--- -----#-- ---###-- -------- -------- -------- -------- -------- -------- ---#-#-- ----##-- ----#--- -------- -------- -------- -------- -------- -----#-- ---#-#-- ----##-- -------- -------- -------- -------- -------- ----#--- -----##- ----##-- -------- -------- -------- -------- -------- -----#-- ------#- ----###- -------- -------- -------- -------- -------- -------- ----#-#- -----##- -----#-- -------- -------- -------- -------- -------- ------#- ----#-#- -----##- -------- -------- -------- -------- -------- -----#-- ------## -----##- -------- -------- -------- -------- -------- ------#- -------# -----### -------- -------- -------- -------- -------- -------- -----#-# ------## ------#- -------- -------- -------- -------- -------- -------# -----#-# ------## -------- -------- -------- -------- -------- ------#- -------# ------## -------- -------- -------- -------- -------- -------- -------# ------## -------- -------- -------- -------- -------- -------- ------## ------## -------- -------- -------- -------- -------- -------- ------## ------## -------- -------- -------- -------- -------- -------- ------## ------## -------- -------- -------- -------- -------- -------- ------## ------## -------- -------- -------- -------- -------- -------- ------## ------## -------- -------- -------- -------- -------- -------- ------## ------## -------- -------- -------- -------- -------- -------- ------## ------## -------- -------- -------- -------- -------- -------- ------## ------## -------- -------- -------- -------- -------- -------- ------## ------##
Scilab
The game's initial state can be input manually by setting the values of Init_state
, or using a bitmap image (or other supported formats). The output can be either printed on Scilab's console, or on a graphic window. For both image input and graphic output, either SIVP or IPCV modules are required, and console_output
should be set to false.
Init_state=[0 0 0;...
1 1 1;...
0 0 0];
console_output=%T;
if (atomsIsLoaded('IPCV') | atomsIsLoaded('SIVP')) & ~console_output then
Input=imread('initial_state.bmp'); //Comment this three lines in case
Init_state=~im2bw(Input,0.1); //there is no input image but
Init_state=1.0.*Init_state; //you still want the graphic window
scf(0); clf();
imshow(~Init_state);
set(gca(),"isoview","on");
end
Curr_state=1.0.*Init_state;
Grid_size=size(Init_state);
Gens=4;
function varargout=neighbourhood(A,i,j)
R_top=i-1;
if i==1 then
R_top=1;
end
R_bottom=i+1;
if i==Grid_size(1) then
R_bottom=Grid_size(1);
end
R_left=j-1;
if j==1 then
R_left=1;
end
C_right=j+1;
if j==Grid_size(2) then
C_right=Grid_size(2);
end
varargout=list(A(R_top:R_bottom,R_left:C_right));
endfunction
function []=console_print(Grid)
String_grid=string(Grid);
for i=1:size(Grid,'r')
for j=1:size(Grid,'c')
if Grid(i,j) then
String_grid(i,j)="#";
else
String_grid(i,j)=" ";
end
end
end
disp(String_grid);
endfunction
neighbours=[];
Next_state=[];
for gen=1:Gens
Next_state=zeros(Init_state);
for i=1:Grid_size(1)
for j=1:Grid_size(2)
neighbours=zeros(3,3);
neighbours=neighbourhood(Curr_state,i,j);
Sum_neighbours=sum(neighbours)-1*Curr_state(i,j);
Alive=Curr_state(i,j);
if Alive then
if Sum_neighbours<2 then
Next_state(i,j)=0;
elseif Sum_neighbours==2 | Sum_neighbours==3 then
Next_state(i,j)=1;
elseif Sum_neighbours>3 then
Next_state(i,j)=0;
end
else
if Sum_neighbours==3 then
Next_state(i,j)=1;
end
end
end
end
if (atomsIsLoaded('IPCV') | atomsIsLoaded('SIVP')) & ~console_output then
imshow(~Next_state);
sleep(50);
else
sleep(50);
disp("Generation "+string(gen)+":")
console_print(Next_state);
end
if sum(Next_state)==0 | Curr_state==Next_state then
disp('ALL CELLS HAVE DIED OR BECAME INERT');
disp('No. of Generations: '+string(gen))
break
end
Curr_state=Next_state;
end
- Output:
Output when console_output=%T
.
Generation 1: ! # ! ! ! ! # ! ! ! ! # ! Generation 2: ! ! ! ! !# # # ! ! ! ! ! Generation 3: ! # ! ! ! ! # ! ! ! ! # ! Generation 4: ! ! ! ! !# # # ! ! ! ! !
SenseTalk
set starting_condition to ((0, 0), (1, 0), (2, 0), (-1, -1), (0, -1), (1, -1))
RunGameOfLife starting_condition
to printColony colonies
set xCoords to the first item of each item of colonies
set yCoords to the second item of each item of colonies
set min_x to the min of xCoords
set max_x to the max of xCoords
set min_y to the min of yCoords
set max_y to the max of yCoords
repeat for y in min_y..max_y
set row to ()
repeat for x in min_x..max_x
if (x, y) is in colonies
push "#" into row
else
push "-" into row
end if
end repeat
join row using ""
put row
end repeat
end printColony
to neighboursOf coordinate
return ( \
coordinate + (-1, 1), \
coordinate + (0, 1), \
coordinate + (1, 1), \
coordinate + (-1, 0), \
coordinate + (1, 0), \
coordinate + (-1, -1), \
coordinate + (0, -1), \
coordinate + (1, -1), \
)
end neighboursOf
to WillNextGenHaveCell colony, coordinate
set neighbour_count to the number of items in (each item of neighboursOf(coordinate) where each is in colony)
if coordinate is in colony
return neighbour_count is in (2, 3)
else
return neighbour_count equals 3
end if
end WillNextGenHaveCell
to RunGameOfLife colony
printColony colony
set the listInsertionMode to "nested"
repeat 10 times
set new_colony to ()
set xCoords to the first item of each item of colony
set yCoords to the second item of each item of colony
set min_x to (the min of xCoords) - 1
set max_x to (the max of xCoords) + 1
set min_y to (the min of yCoords) - 1
set max_y to (the max of yCoords) + 1
repeat for y in min_y..max_y
repeat for x in min_x..max_x
if WillNextGenHaveCell(colony, (x, y))
insert (x, y) into new_colony
end if
end repeat
end repeat
set colony to new_colony
printColony colony
wait 1 second
end repeat
end RunGameOfLife
SequenceL
SequenceL Code:
life(Cells(2))[I, J] :=
let
numNeighbors := Cells[I-1,J-1] + Cells[I-1,J] + Cells[I-1,J+1] +
Cells[I,J-1] +/*current cell*/Cells[I,J+1] +
Cells[I+1,J-1] + Cells[I+1,J] + Cells[I+1,J+1];
in
0 when I=1 or J=1 or I=size(Cells) or J=size(Cells[I]) //On Border
else
0 when numNeighbors < 2 or numNeighbors > 3 //Cell Dies
else
1 when Cells[I,J] = 1 and numNeighbors = 2 or numNeighbors = 3 //Cell lives on or is born.
else
Cells[I,J]; //No Change
stressTestInput(n(0))[y,x] :=
0 when not y = n / 2
else
1
foreach y within 1 ... n,
x within 1 ... n;
C++ Driver Code:
#include <iostream>
#include <string>
#include <vector>
#include <fstream>
#include <cerrno>
#include "Cimg.h"
#include "SL_Generated.h"
using namespace std;
using namespace cimg_library;
char titleBuffer[200];
std::string get_file_contents(const char*);
int main(int argc, char ** argv)
{
int cores = 0;
int maxSize = 700;
int drawSkip = 1;
int drawWait = 0;
string inputFile = "test";
if(argc > 1)
{
inputFile = argv[1];
}
if(argc > 2)
{
cores = atoi(argv[2]);
}
if(argc > 3)
{
maxSize = atoi(argv[3]);
}
if(argc > 4)
{
drawSkip = atoi(argv[4]);
if(drawSkip < 0)
{
drawWait = abs(drawSkip);
drawSkip = 1;
}
}
sl_init(cores);
SLTimer drawTimer;
sprintf(titleBuffer, "Conway's Game of Life in SequenceL: %d Cores", cores);
int width = 0;
int height = 0;
//Read input file--------------
Sequence<Sequence<int>> initialGrid;
if(inputFile != "test")
{
stringstream ss(get_file_contents(inputFile.c_str()));
string stringItem;
vector<int> elems;
char delim = ',';
getline(ss, stringItem, delim);
width = atoi(stringItem.c_str());
getline(ss, stringItem, delim);
height = atoi(stringItem.c_str());
while(getline(ss, stringItem, delim))
{
elems.push_back(atoi(stringItem.c_str()));
}
//Sequence Pointer Constructor----
int gridDims[] = {height, width, 0};
Sequence<Sequence<int>> tempGrid( (void*)&elems[0], gridDims);
initialGrid.hard_copy(tempGrid, 0);
//-----------------------------
}
else
{
width = maxSize;
height = maxSize;
sl_stressTestInput(maxSize, cores, initialGrid);
}
//--------
Sequence<Sequence<int>> result;
const unsigned char black[] = {0};
CImg<unsigned char> visu(width * 4, height * 4, 1, 1, 0);
CImgDisplay draw_disp(visu, titleBuffer);
cout << "Conway's Game of Life in SequenceL" << endl << "Cores: " << cores << endl;
int generations = 0;
double compTime = 0;
double drawTime = 0;
SLTimer t;
while(!draw_disp.is_closed())
{
t.start();
sl_life(initialGrid, cores, result);
generations++;
initialGrid.hard_copy(result, 0);
t.stop();
compTime += t.getTime();
drawTimer.start();
if(drawSkip > 0 && generations % drawSkip == 0)
{
visu.fill(255);
for(int i = 1; i <= result.size(); i++)
{
for(int j = 1; j <= result[i].size(); j++)
{
if(result[i][j] == 1) visu.draw_circle((j-1) * 4, (i - 1) * 4, 2, black, 1);
}
}
visu.display(draw_disp);
int drawWidth = maxSize;
int drawHeight = maxSize;
if(width < height)
{
drawWidth = ((double)drawHeight * ((double)width / (double)height));
}
else
{
drawHeight = ((double)drawWidth * ((double)height / (double)width));
}
draw_disp.resize(drawWidth, drawHeight, false);
draw_disp.wait(drawWait);
drawTimer.stop();
drawTime += drawTimer.getTime();
}
}
cout << "Total Generations: " << generations << endl;
cout << "Average Compute Time: " << compTime / generations << " seconds" << endl;
cout << "Average Draw Time: " << drawTime / generations << " seconds" << endl;
sl_done();
return 0;
}
std::string get_file_contents(const char *filename)
{
std::ifstream in(filename, std::ios::in | std::ios::binary);
if (in)
{
std::string contents;
in.seekg(0, std::ios::end);
contents.resize(in.tellg());
in.seekg(0, std::ios::beg);
in.read(&contents[0], contents.size());
in.close();
return(contents);
}
throw(errno);
}
Usage:
SequenceL Intended Use:
life(int(2)); stressTestInput(int(0))
Running the program:
- If no inputs are provided, a 700 element stress test is performed.
- Argument 1 is the input file ("test" to run stress test).
- Argument 2 is the optional number of cores which defaults to 0(all cores).
- Argument 3 is the size to make the largest dimension of the display window.
- Argument 4 is the number of frames to skip when drawing.
Sample Input:
Blinker:
5,5,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
Output: http://i.imgur.com/9ZtKw1X.gifv
Stress Test Output:
This is the output for the 700 element stress test, which is an input of a 700X700 grid with a single line of live cells horizontally in the center.
Output: http://i.imgur.com/yKTrszV.gifv
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;
Shen
Somewhat verbose but functional and type-checked implementation (tested with chibi-scheme and Shen/SBCL). Running this shows ten iterations of a toad.
(tc +)
(datatype subtype
(subtype B A); X : B;
_____________________
X : A;)
(datatype integer
if (integer? X)
___________
X: integer;
________________________
(subtype integer number);)
(datatype bit
if (< X 2)
_______
X: bit;
_____________________
(subtype bit integer);)
(datatype row
_________
[] : row;
C : bit; Row : row;
===================
[C | Row] : row;)
(datatype universe
______________
[] : universe;
R : row; Uni : universe;
========================
[R | Uni] : universe;)
(define conway-nth
\\ returns value of x from row if it exists, else 0
{ number --> row --> bit }
_ [] -> 0
N _ -> 0 where (< N 0)
0 [A|B] -> A
N [A|B] -> (conway-nth (- N 1) B))
(define row-retrieve
{ number --> universe --> row }
_ [] -> []
0 [] -> []
0 [A|B] -> A
N [A|B] -> (row-retrieve (- N 1) B))
(define cell-retrieve
{ number --> number --> universe --> bit }
X Y Universe -> (conway-nth X (row-retrieve Y Universe)))
(define neighbors
\\ takes an X and Y, retrieves the number of neighbors
{ number --> number --> universe --> number }
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
{ number --> number --> universe --> bit }
X Y Universe -> (if (or (= (neighbors X Y Universe) 2)
(= (neighbors X Y Universe) 3))
1 0))
(define handle-dead
{ number --> number --> universe --> bit }
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 universe
{ row --> number --> number --> universe --> row }
[] _ _ _ -> []
[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
{ universe --> number --> universe --> universe }
[] _ _ -> []
[Row|Rest] Y Universe -> (cons (next-row Row 0 Y Universe)
(next-universe Rest (+ Y 1) Universe)))
(define display-row
{ row --> number }
[] -> (nl)
[1|Rest] -> (do (output "* ")
(display-row Rest))
[_|Rest] -> (do (output " ")
(display-row Rest)))
(define display-universe
{ universe --> number }
[] -> (nl 2)
[Row|Rest] -> (do (display-row Row)
(display-universe Rest)))
(define iterate-universe
{ number --> universe --> number }
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]])
Sidef
var w = Num(`tput cols`)
var h = Num(`tput lines`)
var r = "\033[H"
var dirs = [[-1,-1], [-1, 0], [-1, 1], [ 0,-1],
[ 0, 1], [ 1,-1], [ 1, 0], [ 1, 1]]
var universe = h.of { w.of {1.rand < 0.1} }
func iterate {
var new = h.of { w.of(false) }
static rx = (^h ~X ^w)
for i,j in rx {
var neighbor = 0
for y,x in (dirs.map {|dir| dir »+« [i, j] }) {
universe[y % h][x % w] && ++neighbor
neighbor > 3 && break
}
new[i][j] = (universe[i][j]
? (neighbor==2 || neighbor==3)
: (neighbor==3))
}
universe = new
}
STDOUT.autoflush(true)
loop {
print r
say universe.map{|row| row.map{|cell| cell ? '#' : ' '}.join }.join("\n")
iterate()
}
Simula
COMMENT A PORT OF AN R6RS SCHEME IMPLEMENTATION OF CONWAY'S GAME OF LIFE TO SIMULA --- ASSUMES ;
COMMENT ALL CELLS OUTSIDE THE DEFINED GRID ARE DEAD ;
BEGIN
COMMENT FIRST WE NEED CONS, CAR, CDR, LENGTH AND MAP TO SIMULATE SCHEME ;
CLASS ATOM;;
ATOM CLASS CELL(ALIVE); INTEGER ALIVE;;
ATOM CLASS LIST(A1, A2); REF(ATOM) A1, A2;;
REF(LIST) PROCEDURE CONS(A1, A2); REF(ATOM) A1, A2;
CONS :- NEW LIST(A1, A2);
REF(ATOM) PROCEDURE CAR(LST); REF(LIST) LST;
CAR :- LST.A1;
REF(ATOM) PROCEDURE CDR(LST); REF(LIST) LST;
CDR :- LST.A2;
INTEGER PROCEDURE LENGTH(LST); REF(LIST) LST;
LENGTH := IF LST == NONE THEN 0 ELSE 1 + LENGTH(CDR(LST));
REF(LIST) PROCEDURE MAP(F, LST); PROCEDURE F IS REF(ATOM) PROCEDURE F(ARG); REF(ATOM) ARG;; REF(LIST) LST;
MAP :- IF LST == NONE THEN NONE ELSE CONS(F(CAR(LST)), MAP(F, CDR(LST)));
COMMENT NOW FOLLOWS THE PROBLEM SPECIFIC PART ;
COMMENT IF N IS OUTSIDE BOUNDS OF LIST, RETURN 0 ELSE VALUE AT N ;
REF(ATOM) PROCEDURE NTH(N, LST); INTEGER N; REF(LIST) LST;
NTH :- IF N > LENGTH(LST) THEN NEW CELL(0) ELSE
IF N < 1 THEN NEW CELL(0) ELSE
IF N = 1 THEN CAR(LST) ELSE NTH(N - 1, CDR(LST));
COMMENT RETURN THE NEXT STATE OF THE SUPPLIED UNIVERSE ;
REF(LIST) PROCEDURE NEXTUNIVERSE(UNIVERSE); REF(LIST) UNIVERSE;
BEGIN
COMMENT VALUE AT (X, Y) ;
INTEGER PROCEDURE VALUEAT(X, Y); INTEGER X, Y;
BEGIN
REF(ATOM) A;
A :- NTH(Y, UNIVERSE);
INSPECT A
WHEN LIST DO
VALUEAT := NTH(X, THIS LIST) QUA CELL.ALIVE
OTHERWISE
VALUEAT := 0;
END VALUEAT;
COMMENT SUM OF THE VALUES OF THE CELLS SURROUNDING (X, Y) ;
INTEGER PROCEDURE NEIGHBORSUM(X, Y); INTEGER X, Y;
NEIGHBORSUM :=
VALUEAT(X - 1, Y - 1)
+ VALUEAT(X - 1, Y )
+ VALUEAT(X - 1, Y + 1)
+ VALUEAT(X, Y - 1)
+ VALUEAT(X, Y + 1)
+ VALUEAT(X + 1, Y - 1)
+ VALUEAT(X + 1, Y )
+ VALUEAT(X + 1, Y + 1);
COMMENT NEXT STATE OF THE CELL AT (X, Y) ;
INTEGER PROCEDURE NEXTCELL(X, Y); INTEGER X, Y;
BEGIN
INTEGER CUR, NS;
CUR := VALUEAT(X, Y);
NS := NEIGHBORSUM(X, Y);
NEXTCELL := IF CUR = 1 AND (NS < 2 OR NS > 3) THEN 0 ELSE
IF CUR = 0 AND NS = 3 THEN 1 ELSE CUR;
END NEXTCELL;
COMMENT NEXT STATE OF ROW N ;
REF(LIST) PROCEDURE ROW(N, OUT); INTEGER N; REF(LIST) OUT;
BEGIN
INTEGER W, O;
W := LENGTH(CAR(UNIVERSE));
O := LENGTH(OUT);
ROW :- IF W = O THEN OUT ELSE ROW(N, CONS(NEW CELL(NEXTCELL(W - O, N)), OUT));
END ROW;
COMMENT A RANGE OF INTS FROM BOT TO TOP ;
REF(LIST) PROCEDURE INTRANGE(BOT, TOP); INTEGER BOT, TOP;
INTRANGE :- IF BOT > TOP THEN NONE ELSE CONS(NEW CELL(BOT), INTRANGE(BOT + 1, TOP));
REF(ATOM) PROCEDURE LAMBDA(N); REF(ATOM) N;
LAMBDA :- ROW(N QUA CELL.ALIVE, NONE);
NEXTUNIVERSE :- MAP(LAMBDA, INTRANGE(1, LENGTH(UNIVERSE)));
END NEXTUNIVERSE;
COMMENT DISPLAY THE UNIVERSE ;
PROCEDURE DISPLAY(LST); REF(LIST) LST;
BEGIN
WHILE LST =/= NONE DO
BEGIN
REF(LIST) LI;
LI :- CAR(LST);
WHILE LI =/= NONE DO
BEGIN
REF(CELL) CE;
CE :- CAR(LI);
OUTCHAR(IF CE.ALIVE = 1 THEN '#' ELSE '-');
LI :- CDR(LI);
END;
OUTIMAGE;
LST :- CDR(LST);
END;
END DISPLAY;
COMMENT STARTING WITH SEED, SHOW REPS STATES OF THE UNIVERSE ;
PROCEDURE CONWAY(SEED, REPS); REF(LIST) SEED; INTEGER REPS;
BEGIN
IF REPS > 0 THEN
BEGIN
DISPLAY(SEED);
OUTIMAGE;
CONWAY(NEXTUNIVERSE(SEED), REPS - 1);
END;
END CONWAY;
REF(CELL) O, L;
O :- NEW CELL(0);
L :- NEW CELL(1);
COMMENT BLINKER IN A 3X3 UNIVERSE ;
! (CONWAY '((0 1 0)
(0 1 0)
(0 1 0)) 5) ;
CONWAY(CONS(CONS(O, CONS(L, CONS(O, NONE))),
CONS(CONS(O, CONS(L, CONS(O, NONE))),
CONS(CONS(O, CONS(L, CONS(O, NONE))),
NONE))), 5);
COMMENT 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) ;
OUTIMAGE;
CONWAY(CONS(CONS(O, CONS(O, CONS(L, CONS(O, CONS(O, CONS(O, CONS(O, CONS(O, NONE)))))))),
CONS(CONS(O, CONS(O, CONS(O, CONS(L, CONS(O, CONS(O, CONS(O, CONS(O, NONE)))))))),
CONS(CONS(O, CONS(L, CONS(L, CONS(L, CONS(O, CONS(O, CONS(O, CONS(O, NONE)))))))),
CONS(CONS(O, CONS(O, CONS(O, CONS(O, CONS(O, CONS(O, CONS(O, CONS(O, NONE)))))))),
CONS(CONS(O, CONS(O, CONS(O, CONS(O, CONS(O, CONS(O, CONS(O, CONS(O, NONE)))))))),
CONS(CONS(O, CONS(O, CONS(O, CONS(O, CONS(O, CONS(O, CONS(O, CONS(O, NONE)))))))),
CONS(CONS(O, CONS(O, CONS(O, CONS(O, CONS(O, CONS(O, CONS(O, CONS(O, NONE)))))))),
CONS(CONS(O, CONS(O, CONS(O, CONS(O, CONS(O, CONS(O, CONS(O, CONS(O, NONE)))))))),
NONE)))))))), 30);
END.
- Output:
-#- -#- -#- --- ### --- -#- -#- -#- --- ### --- -#- -#- -#- --#----- ---#---- -###---- -------- -------- -------- -------- -------- -------- -#-#---- --##---- --#----- -------- -------- -------- -------- -------- ---#---- -#-#---- --##---- -------- -------- -------- -------- -------- --#----- ---##--- --##---- -------- -------- -------- -------- -------- ---#---- ----#--- --###--- -------- -------- -------- -------- -------- -------- --#-#--- ---##--- ---#---- -------- -------- -------- -------- -------- ----#--- --#-#--- ---##--- -------- -------- -------- -------- -------- ---#---- ----##-- ---##--- -------- -------- -------- -------- -------- ----#--- -----#-- ---###-- -------- -------- -------- -------- -------- -------- ---#-#-- ----##-- ----#--- -------- -------- -------- -------- -------- -----#-- ---#-#-- ----##-- -------- -------- -------- -------- -------- ----#--- -----##- ----##-- -------- -------- -------- -------- -------- -----#-- ------#- ----###- -------- -------- -------- -------- -------- -------- ----#-#- -----##- -----#-- -------- -------- -------- -------- -------- ------#- ----#-#- -----##- -------- -------- -------- -------- -------- -----#-- ------## -----##- -------- -------- -------- -------- -------- ------#- -------# -----### -------- -------- -------- -------- -------- -------- -----#-# ------## ------#- -------- -------- -------- -------- -------- -------# -----#-# ------## -------- -------- -------- -------- -------- ------#- -------# ------## -------- -------- -------- -------- -------- -------- -------# ------## -------- -------- -------- -------- -------- -------- ------## ------## -------- -------- -------- -------- -------- -------- ------## ------## -------- -------- -------- -------- -------- -------- ------## ------## -------- -------- -------- -------- -------- -------- ------## ------## -------- -------- -------- -------- -------- -------- ------## ------## -------- -------- -------- -------- -------- -------- ------## ------## -------- -------- -------- -------- -------- -------- ------## ------## -------- -------- -------- -------- -------- -------- ------## ------## -------- -------- -------- -------- -------- -------- ------## ------## 180 garbage collection(s) in 0.1 seconds.
Smalltalk
This implementation has been developed using Cuis Smalltalk [1]. Here are different configurations:
"Blinker"
GameOfLife withAliveCells: { 4@2. 4@3. 4@4. 3@3. 4@3. 5@3 } ofSize: 10@10.
"Toad"
GameOfLife withAliveCells: { 2@4. 3@4. 4@4. 3@3. 4@3. 5@3 } ofSize: 10@10
This is the implementation:
Object subclass: #GameOfLife
instanceVariableNames: 'aliveCells boardSize'
classVariableNames: ''
poolDictionaries: ''
category: 'GameOfLife'
GameOfLife class>>withAliveCells: aCollectionOfCells ofSize: aBoardSize
self assertAll: aCollectionOfCells areInside: aBoardSize.
^self new initializeWithAliveCells: aCollectionOfCells ofSize: aBoardSize
GameOfLife class>>assertAll: aCollectionOfAliveCells areInside: aSize
| origin |
origin := 0@0.
aCollectionOfAliveCells do: [:aCell |
(aCell between: origin and: aSize) ifFalse: [ self error: 'Cell ', aCell printString,' out of range' ]]
initializeWithAliveCells: aCollectionOfCells ofSize: aBoardSize
aliveCells := aCollectionOfCells asSet.
boardSize := aBoardSize
calculateNextGeneration
aliveCells := self boardCellsSelect: [ :aCell | self shouldBeAliveOnNextGeneration: aCell ]
shouldBeAliveOnNextGeneration: aCell
| numberOfAliveNeighbors |
numberOfAliveNeighbors := self numberOfAliveNeighborsOf: aCell.
^numberOfAliveNeighbors = 3 or: [ self shouldSurvive: aCell with: numberOfAliveNeighbors]
shouldSurvive: aCell with: numberOfAliveNeighbors
^ (self isAlive: aCell) and: [ numberOfAliveNeighbors = 2 ]
isAlive: aCell
^aliveCells includes: aCell
isDead: aCell
^(self isAlive: aCell) not
boardCellsDo: aClosure
0 to: boardSize x do: [ :x |
0 to: boardSize y do: [ :y | aClosure value: x@y ]]
boardCellsSelect: aCondition
| selectedCells |
selectedCells := Set new.
self boardCellsDo: [ :aCell | (aCondition value: aCell) ifTrue: [ selectedCells add: aCell ]].
^selectedCells
numberOfAliveNeighborsOf: aCell
^aCell eightNeighbors count: [ :aNeighbor | self isAlive: aNeighbor ]
The implementation was developed using TDD. This are the tests used to implement it.
TestCase subclass: #GameOfLifeTest
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'GameOfLife'
test01AliveCellWithLessThatTwoAliveCellsDies
| gameOfLife |
gameOfLife := GameOfLife withAliveCells: {1@1} ofSize: 3@3.
gameOfLife calculateNextGeneration.
self assert: (gameOfLife isDead: 1@1)
test02AliveCellWithTwoAliveNeighborsSurvives
| gameOfLife |
gameOfLife := GameOfLife withAliveCells: {1@1. 1@2. 2@1} ofSize: 3@3.
gameOfLife calculateNextGeneration.
self assert: (gameOfLife isAlive: 1@1).
test03AliveCellWithThreeAliveNeighborsSurvives
| gameOfLife |
gameOfLife := GameOfLife withAliveCells: {1@1. 1@2. 2@1. 2@2} ofSize: 3@3.
gameOfLife calculateNextGeneration.
self assert: (gameOfLife isAlive: 1@1).
test04DeadCellWithThreeAliveNeighborsBecomesAlive
| gameOfLife |
gameOfLife := GameOfLife withAliveCells: {1@1. 1@2. 2@1} ofSize: 3@3.
gameOfLife calculateNextGeneration.
self assert: (gameOfLife isAlive: 2@2).
test05DeadCellWithAliveNeighborsDifferentToThreeKeepsDead
| gameOfLife |
gameOfLife := GameOfLife withAliveCells: {1@2. 2@2. 3@2. 1@3} ofSize: 3@3.
gameOfLife calculateNextGeneration.
self assert: (gameOfLife isDead: 1@1).
self assert: (gameOfLife isDead: 2@3).
test06CanNotCreateGameWithCellOutsideXOrigin
self
should: [ GameOfLife withAliveCells: { 2@0. 1@0. -1@1 } ofSize: 3@3 ]
raise: Error - MessageNotUnderstood
withMessageText: 'Cell -1@1 out of range'
test07CanNotCreateGameWithCellOutsideYOrigin
self
should: [ GameOfLife withAliveCells: { 2@0. 1@0. 1@-1 } ofSize: 3@3 ]
raise: Error - MessageNotUnderstood
withMessageText: 'Cell 1@-1 out of range'
test08CanNotCreateGameWithCellOutsideXLimit
self
should: [ GameOfLife withAliveCells: { 2@0. 1@0. 4@1 } ofSize: 3@3 ]
raise: Error - MessageNotUnderstood
withMessageText: 'Cell 4@1 out of range'
test09CanNotCreateGameWithCellOutsideYLimit
self
should: [ GameOfLife withAliveCells: { 2@0. 1@0. 1@4 } ofSize: 3@3 ]
raise: Error - MessageNotUnderstood
withMessageText: 'Cell 1@4 out of range'
If you want to have a visual representation, here is a view for it:
ImageMorph subclass: #GameOfLifeView
instanceVariableNames: 'game'
classVariableNames: ''
poolDictionaries: ''
category: 'GameOfLife'
GameOfLifeView class>>for: aGameOfLife
^self new initializeFor: aGameOfLife
GameOfLifeView class>>openFor: aGameOfLife
^(self for: aGameOfLife) open
GameOfLifeView class>>blinker
^GameOfLife withAliveCells: { 4@2. 4@3. 4@4. 3@3. 4@3. 5@3 } ofSize: 10@10
GameOfLifeView class>>toad
^GameOfLife withAliveCells: { 2@4. 3@4. 4@4. 3@3. 4@3. 5@3 } ofSize: 10@10
GameOfLifeView class>>openBlinker
^self openFor: self blinker
GameOfLifeView class>>openToad
^self openFor: self toad
initializeFor: aGameOfLife
game := aGameOfLife.
self image: (Form extent: game boardSize * 5 depth: 2).
open
self showBoard.
self openInWorld.
self startSteppingStepTime: 500
showBoard
game boardCellsDo: [ :aPoint |
(game isAlive: aPoint)
ifTrue: [ self form fillBlack: (aPoint*5 extent: 5@5) ]
ifFalse: [ self form fillWhite: (aPoint*5 extent: 5@5) ]].
step
game calculateNextGeneration.
self showBoard.
self redrawNeeded.
SQL
Microsoft SQL here: http://www.sqlservercentral.com/articles/CTE/130407/
-- save these lines in a file called
-- setupworld.sql
-- turn off feedback for cleaner display
set feedback off
-- 3 x 3 world
-- alive has coordinates of living cells
drop table alive;
create table alive (x number,y number);
-- three alive up the middle
-- *
-- *
-- *
insert into alive values (2,1);
insert into alive values (2,2);
insert into alive values (2,3);
commit;
-- save these lines in a file called
newgeneration.sql
-- adjact contains one row for each pair of
-- coordinates that is adjacent to a living cell
drop table adjacent;
create table adjacent (x number,y number);
-- add row for each of the 8 adjacent squares
insert into adjacent select x-1,y-1 from alive;
insert into adjacent select x-1,y from alive;
insert into adjacent select x-1,y+1 from alive;
insert into adjacent select x,y-1 from alive;
insert into adjacent select x,y+1 from alive;
insert into adjacent select x+1,y-1 from alive;
insert into adjacent select x+1,y from alive;
insert into adjacent select x+1,y+1 from alive;
commit;
-- delete rows for squares that are outside the world
delete from adjacent where x<1 or y<1 or x>3 or y>3;
commit;
-- table counts is the number of live cells
-- adjacent to that point
drop table counts;
create table counts as
select x,y,count(*) n
from adjacent a
group by x,y;
-- 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
-- delete the ones who die
delete from alive a
where
((a.x,a.y) not in (select x,y from counts)) or
((select c.n from counts c where a.x=c.x and a.y=c.y) in
(1,4,5,6,7,8));
-- insert the ones that are born
insert into alive a
select x,y from counts c
where c.n=3 and
((c.x,c.y) not in (select x,y from alive));
commit;
-- create output table
drop table output;
create table output as
select rownum y,' ' x1,' ' x2,' ' x3
from dba_tables where rownum < 4;
update output set x1='*'
where (1,y) in
(select x,y from alive);
update output set x2='*'
where (2,y) in
(select x,y from alive);
update output set x3='*'
where (3,y) in
(select x,y from alive);
commit
-- output configuration
select x1||x2||x3 WLD
from output
order by y desc;
Running in sqlplus: SQL> @setupworld SQL> @newgeneration WLD --- *** SQL> @newgeneration WLD --- * * * SQL> @newgeneration WLD --- ***
Swift
struct Cell: Hashable {
var x: Int
var y: Int
}
struct Colony {
private var height: Int
private var width: Int
private var cells: Set<Cell>
init(cells: Set<Cell>, height: Int, width: Int) {
self.cells = cells
self.height = height
self.width = width
}
private func neighborCounts() -> [Cell: Int] {
var counts = [Cell: Int]()
for cell in cells.flatMap(Colony.neighbors(for:)) {
counts[cell, default: 0] += 1
}
return counts
}
private static func neighbors(for cell: Cell) -> [Cell] {
return [
Cell(x: cell.x - 1, y: cell.y - 1),
Cell(x: cell.x, y: cell.y - 1),
Cell(x: cell.x + 1, y: cell.y - 1),
Cell(x: cell.x - 1, y: cell.y),
Cell(x: cell.x + 1, y: cell.y),
Cell(x: cell.x - 1, y: cell.y + 1),
Cell(x: cell.x, y: cell.y + 1),
Cell(x: cell.x + 1, y: cell.y + 1),
]
}
func printColony() {
for y in 0..<height {
for x in 0..<width {
let char = cells.contains(Cell(x: x, y: y)) ? "0" : "."
print("\(char) ", terminator: "")
}
print()
}
}
mutating func run(iterations: Int) {
print("(0)")
printColony()
print()
for i in 1...iterations {
print("(\(i))")
runGeneration()
printColony()
print()
}
}
private mutating func runGeneration() {
cells = Set(neighborCounts().compactMap({keyValue in
switch (keyValue.value, cells.contains(keyValue.key)) {
case (2, true), (3, _):
return keyValue.key
case _:
return nil
}
}))
}
}
let blinker = [Cell(x: 1, y: 0), Cell(x: 1, y: 1), Cell(x: 1, y: 2)] as Set
var col = Colony(cells: blinker, height: 3, width: 3)
print("Blinker: ")
col.run(iterations: 3)
let glider = [
Cell(x: 1, y: 0),
Cell(x: 2, y: 1),
Cell(x: 0, y: 2),
Cell(x: 1, y: 2),
Cell(x: 2, y: 2)
] as Set
col = Colony(cells: glider, height: 8, width: 8)
print("Glider: ")
col.run(iterations: 20)
- Output:
Blinker: (0) . 0 . . 0 . . 0 . (1) . . . 0 0 0 . . . (2) . 0 . . 0 . . 0 . (3) . . . 0 0 0 . . . Glider: (0) . 0 . . . . . . . . 0 . . . . . 0 0 0 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . (1) . . . . . . . . 0 . 0 . . . . . . 0 0 . . . . . . 0 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . (2) . . . . . . . . . . 0 . . . . . 0 . 0 . . . . . . 0 0 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . (3) . . . . . . . . . 0 . . . . . . . . 0 0 . . . . . 0 0 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . (4) . . . . . . . . . . 0 . . . . . . . . 0 . . . . . 0 0 0 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . (5) . . . . . . . . . . . . . . . . . 0 . 0 . . . . . . 0 0 . . . . . . 0 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . (6) . . . . . . . . . . . . . . . . . . . 0 . . . . . 0 . 0 . . . . . . 0 0 . . . . . . . . . . . . . . . . . . . . . . . . . . . . (7) . . . . . . . . . . . . . . . . . . 0 . . . . . . . . 0 0 . . . . . 0 0 . . . . . . . . . . . . . . . . . . . . . . . . . . . . (8) . . . . . . . . . . . . . . . . . . . 0 . . . . . . . . 0 . . . . . 0 0 0 . . . . . . . . . . . . . . . . . . . . . . . . . . . (9) . . . . . . . . . . . . . . . . . . . . . . . . . . 0 . 0 . . . . . . 0 0 . . . . . . 0 . . . . . . . . . . . . . . . . . . . . (10) . . . . . . . . . . . . . . . . . . . . . . . . . . . . 0 . . . . . 0 . 0 . . . . . . 0 0 . . . . . . . . . . . . . . . . . . . (11) . . . . . . . . . . . . . . . . . . . . . . . . . . . 0 . . . . . . . . 0 0 . . . . . 0 0 . . . . . . . . . . . . . . . . . . . (12) . . . . . . . . . . . . . . . . . . . . . . . . . . . . 0 . . . . . . . . 0 . . . . . 0 0 0 . . . . . . . . . . . . . . . . . . (13) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 0 . 0 . . . . . . 0 0 . . . . . . 0 . . . . . . . . . . . (14) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 0 . . . . . 0 . 0 . . . . . . 0 0 . . . . . . . . . . (15) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 0 . . . . . . . . 0 0 . . . . . 0 0 . . . . . . . . . . (16) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 0 . . . . . . . . 0 . . . . . 0 0 0 . . . . . . . . . (17) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 0 . 0 . . . . . . 0 0 . . . . . . 0 . . (18) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 0 . . . . . 0 . 0 . . . . . . 0 0 . (19) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 0 . . . . . . . . 0 0 . . . . . 0 0 . (20) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 0 . . . . . . . . 0 . . . . . 0 0 0
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
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 {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: . . . . . . # . . . . # . # # #
Uiua
Canonical solution from Uiua homepage. Run it in Uiua Pad to see it in all its glory.
Life ← ↥⊙↧∩=3,2-,/+≡↻☇1-1⇡3_3¤.
⁅×0.6 ∵⋅⚂ ↯⟜⊚ 50 # Init
⇌◌⍥(⟜⊂Life)300⟜¤ # Run
≡(▽⟜≡▽4) # Upscale
UNIX Shell
# Shells only have 1-dimensional arrays, so each row is one string
function main {
typeset blinker=(000 111 000)
run list 4 "${blinker[@]}"
}
function run {
typeset mode=$1 # screen or list
shift
typeset -i limit=0
if (( $1 > 1 )); then
limit=$1
shift
fi
if [[ mode == screen ]]; then
clear
fi
typeset universe=("$@")
typeset -i generation=0
while (( !limit || generation < limit )); do
if [[ mode == screen ]]; then
printf '\e[H'
else
printf '\n'
fi
printf 'Generation %d\n' "$generation"
disp "${universe[@]}" | tr '01' '.@'
universe=($(next_generation "${universe[@]}"))
(( generation += 1 ))
sleep 0.125
done
}
# display a matrix
function disp {
# remove sed for more compact display
printf '%s\n' "$@" | sed 's/./ &/g'
}
# center a matrix within a given size by padding with 0s
function center {
typeset height=$1 width=$2
shift 2
typeset -i offset_i offset_j i j
if (( $# > height || ${#1} > width )); then
printf >&2 'Source larger than target.\n'
return 1
fi
(( offset_i = (height - $#) / 2 ))
(( offset_j = (width - ${#1}) / 2 ))
typeset prefix zeroes suffix
for (( j=0; j<width; ++j )); do
zeroes+=0
if (( j < offset_j )); then
prefix+=0
elif (( j >= offset_j+${#1} )); then
suffix+=0
fi
done
for (( i=0; i<offset_i; ++i )); do
printf '%s\n' "$zeroes"
done
while (( $# )); do
printf '%s%s%s\n' "$prefix" "$1" "$suffix"
shift
(( i++ ))
done
while (( i++ < height )); do
printf '%s\n' "$zeroes"
done
}
# compute the next generation
# the grid is finite; pass -t to treat as torus (wraparound), otherwise it's
# bounded by always-dead cells
next_generation() {
typeset -i torus=0
if [[ $1 == -t ]]; then
torus=1
shift
fi
# cache cells in an associative array
# (for fast lookup when counting neighbors)
typeset -A cells
typeset -i i=0 j=0 height=$# width=${#1}
while (( $# )); do
row=$1
shift
for (( j=0; j<${#row}; ++j )); do
cells[$i,$j]=${row:$j:1}
done
(( i+=1 ))
done
typeset -i di dj ni nj n cell
for (( i=0; i<height; ++i )); do
for (( j=0; j<width; ++j )); do
n=0
for (( di=-1; di<2; ++di )); do
(( ni = i + di ))
if (( torus )); then
(( ni = (ni + height) % height ))
fi
for (( dj=-1; dj<2; ++dj )); do
(( nj = j + dj ))
if (( torus )); then
(( nj = (nj + width) % width ))
fi
if (( (di || dj) && ni >= 0 && nj >= 0 && ni < height && nj < width )); then
(( n += ${cells[$ni,$nj]} ))
fi
done
done
printf '%d' "$(( ( n == 3 ) || ( ${cells[$i,$j]} && n==2 ) ))"
done
printf '\n'
done
}
main "$@"
- Output:
Generation 0 . . . @ @ @ . . . Generation 1 . @ . . @ . . @ . Generation 2 . . . @ @ @ . . . Generation 3 . @ . . @ . . @ .
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++ ++++++
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.
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
]]
}
- Output:
[ [[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
]]
}
- Output:
[ [[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]] ]
VBScript
I have used ANSI escape codes, so the program will not whork from W2000 to W8.1, where Microsoft did see fit to remove support to these codes. The game wraps around at borders, so the gliders are always alive. Must be invoked from cscript.
option explicit
const tlcr=3, tlcc=3
const maxdim=15
dim a(15,15)
dim b(15,15)
dim ans0:ans0=chr(27)&"["
dim gen,n
erase a
'glider
a(0,1)=true:a(1,2)=true:a(2,0)=true:a(2,1)=true:a(2,2)=true
'blinker
a(3,13)=true:a(4,13)=true:a(5,13)=true
'beacon
a(11,1)=true:a(11,2)=true: a(12,1)=true:a(12,2)=true
a(13,3)=true:a(13,4)=true: a(14,3)=true:a(14,4)=true
gen=0
doframe
do
display
wscript.sleep(100)
n=newgen
loop until n=0
display
sub cls() wscript.StdOut.Write ans0 &"2J"&ans0 &"?25l":end sub 'hide cursor too
sub toxy(r,c,s) wscript.StdOut.Write ans0 & r & ";" & c & "f" & s :end sub
function iif(a,b,c) if a then iif=b else iif=c end if :end function
sub doframe
dim r,c,s
const frame="@"
cls
toxy 1,tlcc-1, "Conway's Game of Life"
toxy tlcr-1,tlcc-1,string(maxdim+3,frame)
s=frame&space(maxdim+1)&frame
for r=0 to maxdim
toxy tlcr+r,tlcc-1,s
next
toxy tlcr+maxdim+1,tlcc-1,string(maxdim+3,frame)
end sub
sub display
dim r,c
const pix="#"
for r=0 to maxdim
for c=0 to maxdim
toxy tlcr+r,tlcc+c,iif (a(r,c),pix," ")
next
next
toxy tlcr+maxdim+2,tlcc-1,gen & " " & n
toxy tlcr+maxdim+3,tlcc-1,""
gen=gen+1
end sub
function newgen
dim r,c,cnt,cp,cm,rp,rm
for r=0 to maxdim
for c=0 to maxdim
cp=(c+1) and maxdim 'wrap around
cm=(c-1) and maxdim
rp=(r+1) and maxdim
rm=(r-1) and maxdim
cnt=0
cnt=- a(rp,cp) - a(rp,c) - a(rp,cm) 'true =-1
cnt=cnt- a(r,cp)- a(r,cm)
cnt=cnt- a(rm,cp)- a(rm,c) - a(rm,cm)
if a(r,c) then
b(r,c)=iif (cnt=2 or cnt=3,true,false)
else
b(r,c)=iif (cnt=3,true,false)
end if
next
next
for r=0 to maxdim
for c=0 to maxdim
a(r,c)=b(r,c)
newgen=newgen- b(r,c)
next
next
end function
Conway's Game of Life @@@@@@@@@@@@@@@@@@ @ @ @ @ @ @ @ # # @ @ # # # @ @ ## # @ @ @ @ @ @ @ @ @ @ @ @ ## @ @ # @ @ # @ @ ## @ @ @ @@@@@@@@@@@@@@@@@@ 9 14
A fast version using a thechnique from Abrash's Black Book. Set your console to 34 rows minimum, as it uses a 32x32 cells world
option explicit
const pix="##"
const cl_on=16
const cl_mnum=15
randomize timer
const tlcr=2, tlcc=2
const maxdim =31
settitle "Conway's game of life"
dim a(31,31)
dim ans0:ans0=chr(27)&"["
dim gen,n
gen=0
paintframe
'init1 'objects
init2 400 'random
do
n=nextgen
toxy tlcr-1,tlcc+maxdim*2+4,gen & " " & n
toxy tlcr,tlcc+maxdim*2+3,""
gen=gen+1
loop until n=0
sub pause() wscript.stdout.write vbcrlf & "Press Enter":wscript.stdin.readline: end sub
sub settitle(s) wscript.StdOut.Write chr(27)&"]0;"& s &chr(7):end sub
sub cls() wscript.StdOut.Write ans0 &"2J"&ans0 &"?25l":end sub
sub toxy(r,c,s) wscript.StdOut.Write ans0 & r & ";" & c & "f" & s :end sub
function iif(a,b,c) if a then iif=b else iif=c end if :end function
sub init1
dim x
update 0,1,1,x:update 1,2,1,x:update 2,0,1,x:update 2,1,1,x:update 2,2,1,x
update 3,13,1,x:update 4,13,1,x:update 5,13,1,x
update 11,1,1,x:update 11,2,1,x: update 12,1,1,x:update 12,2,1,x
update 13,3,1,x:update 13,4,1,x: update 14,3,1,x:update 14,4,1,x
end sub
sub init2 (n)
dim i,r,c,x
for i=1 to n
do
r=cint(rnd*maxdim)
c=cint(rnd*maxdim)
loop until (a(r,c) and cl_on)=0
update r,c,1,x
next
end sub
sub paintframe
dim r,c,s
const frame="@"
cls
'toxy 1,tlcc-1, "Conway's Game of Life"
toxy tlcr-1,tlcc-1,string(maxdim*2+4,frame)
s=frame&space(maxdim*2+2)&frame
for r=0 to maxdim
toxy tlcr+r,tlcc-1,s
next
toxy tlcr+maxdim+1,tlcc-1,string(maxdim*2+4,frame)
end sub
function nextgen()
dim r,c,pre,cnt,a1,onoff,chg
nextgen=0
a1=a 'does a hard a copy of a
for r=0 to maxdim
for c=0 to maxdim
if a1(r,c)then 'check only cells alive or having neighbors
pre=a1(r,c) and cl_on
cnt=a1(r,c) and cl_mnum
'check life condition and update cell
if pre<>0 and (cnt<2 or cnt>3) then
update r,c,0,nextgen
elseif pre=0 and cnt=3 then
update r,c,1,nextgen
end if
end if
next
next
end function
sub update(r,c,onoff,nextgen)
'displays cell and update neighbors count in neighbors
dim cp,cm,rp,rm,inc,mask
mask=iif(onoff,cl_on,0)
a(r,c)= (a(r,c) and cl_mnum) or mask 'update cell
toxy tlcr+r,tlcc+c*2,iif (onoff,pix," ") 'display cell
cp=(c+1) and maxdim 'wrap around
cm=(c-1) and maxdim
rp=(r+1) and maxdim
rm=(r-1) and maxdim
if onoff then inc=1 else inc=-1
'update count in neighbors
a(rp,cp)=(a(rp,cp) and cl_on) or ((a(rp,cp) and cl_mnum)+inc)
a(rp,c)=(a(rp,c) and cl_on) or ((a(rp,c) and cl_mnum)+inc)
a(rp,cm)=(a(rp,cm) and cl_on) or ((a(rp,cm) and cl_mnum)+inc)
a(r,cp)=(a(r,cp) and cl_on) or ((a(r,cp) and cl_mnum)+inc)
a(r,cm)=(a(r,cm) and cl_on) or ((a(r,cm) and cl_mnum)+inc)
a(rm,cp)=(a(rm,cp) and cl_on) or ((a(rm,cp) and cl_mnum)+inc)
a(rm,c)=(a(rm,c) and cl_on) or ((a(rm,c) and cl_mnum)+inc)
a(rm,cm)=(a(rm,cm) and cl_on) or ((a(rm,cm) and cl_mnum)+inc)
nextgen=nextgen+1
end sub
V (Vlang)
import rand
import strings
import time
struct Field {
mut:
s [][]bool
w int
h int
}
fn new_field(w int, h int) Field {
s := [][]bool{len: h, init: []bool{len: w}}
return Field{s, w, h}
}
fn (mut f Field) set(x int, y int, b bool) {
f.s[y][x] = b
}
fn (f Field) next(x int, y int) bool {
mut 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))
}
fn (f Field) state(xx int, yy int) bool {
mut x, mut y := xx, yy
for y < 0 {
y += f.h
}
for x < 0 {
x += f.w
}
return f.s[y%f.h][x%f.w]
}
struct Life {
mut:
w int
h int
a Field
b Field
}
fn new_life(w int, h int) Life {
mut a := new_field(w, h)
for _ in 0..(w * h / 2) {
a.set(rand.intn(w) or {0}, rand.intn(h) or {0}, true)
}
return Life{
a: a,
b: new_field(w, h),
w: w
h: h,
}
}
fn (mut l Life) step() {
for y in 0..l.h {
for x in 0.. l.w {
l.b.set(x, y, l.a.next(x, y))
}
}
l.a, l.b = l.b, l.a
}
fn (l Life) str() string {
mut buf := strings.new_builder(128)
for y in 0..l.h {
for x in 0..l.w {
mut b := ' '
if l.a.state(x, y) {
b = '*'
}
buf.write_string(b)
}
buf.write_string('\n')
}
return buf.str()
}
fn main() {
mut l := new_life(80, 15)
for i := 0; i < 300; i++ {
l.step()
//println("------------------------------------------------")
print('\x0c')
println(l)
time.sleep(time.second / 30)
}
}
- Output:
Generation 300:
** *** * * ** * * ** ** ** ** * ** ** ** *** * * ****** * * * ** * * ** * * ** * * ** ** * * * * ** * * ** ** * * ** * ** *
Wren
import "random" for Random
import "timer" for Timer
var Rand = Random.new(0) // using a constant seed to produce same output on each run
// patterns
var BLINKER = 0
var GLIDER = 1
var RANDOM = 2
class Field {
construct new(w, h) {
_w = w
_h = h
_s = List.filled(h, null)
for (i in 0...h) _s[i] = List.filled(w, false)
}
[x, y]=(b) { _s[y][x] = b }
state(x, y) {
if (!(0..._w).contains(x) || !(0..._h).contains(y)) return false
return _s[y][x]
}
next(x, y) {
var on = 0
for (i in -1..1) {
for (j in -1..1) {
if (state(x + i, y + j) && !(j == 0 && i == 0)) on = on + 1
}
}
return on == 3 || (on == 2 && state(x, y))
}
}
class Life {
construct new(pattern) {
if (pattern == BLINKER) {
_w = 3
_h = 3
_a = Field.new(_w, _h)
_b = Field.new(_w, _h)
_a[0, 1] = true
_a[1, 1] = true
_a[2, 1] = true
} else if (pattern == GLIDER) {
_w = 4
_h = 4
_a = Field.new(_w, _h)
_b = Field.new(_w, _h)
_a[1, 0] = true
_a[2, 1] = true
for (i in 0..2) _a[i, 2] = true
} else if(pattern == RANDOM) {
_w = 80
_h = 15
_a = Field.new(_w, _h)
_b = Field.new(_w, _h)
for (i in 0...(_w * _h).floor / 2) {
_a[Rand.int(_w), Rand.int(_h)] = true
}
}
}
step() {
for (y in 0..._h) {
for (x in 0..._w) _b[x, y] = _a.next(x, y)
}
var t = _a
_a = _b
_b = t
}
toString {
var sb = ""
for (y in 0..._h) {
for (x in 0..._w) {
var c = _a.state(x, y) ? "#" : "."
sb = sb + c
}
sb = sb + "\n"
}
return sb
}
}
var lives = [
[Life.new(BLINKER), 3, "BLINKER"],
[Life.new(GLIDER), 4, "GLIDER" ],
[Life.new(RANDOM), 100, "RANDOM" ]
]
for (t in lives) {
var game = t[0]
var gens = t[1]
var title = t[2]
System.print("%(title):\n")
for (i in 0..gens) {
System.print("Generation: %(i)\n%(game)")
Timer.sleep(30)
game.step()
}
System.print()
}
- Output:
Just shows generations 0 and 100 for RANDOM pattern:
BLINKER: Generation: 0 ... ### ... Generation: 1 .#. .#. .#. Generation: 2 ... ### ... Generation: 3 .#. .#. .#. GLIDER: Generation: 0 .#.. ..#. ###. .... Generation: 1 .... #.#. .##. .#.. Generation: 2 .... ..#. #.#. .##. Generation: 3 .... .#.. ..## .##. Generation: 4 .... ..#. ...# .### RANDOM: Generation: 0 #.#.###.#...#...#..##.....#......#....#...#.....#...#..#..#.....##.##....#..#### ..#...#....#.##..#..#...##.#.....#....#..##.#.###..#.#.#.#..#.##..#.#.......#.#. ###.###..##...#......#.#.#............#.......##.#..#...###.##..#...###...#..#.. .#.....#.#.#.#.#..###.#.#.######...##..##.###.####....#....#####..##...####.#..# ..##.###.#..###...#..#..##....##...#..##.##.#..###.#..#..#.#...##..#..###....... .####..###.##..#..#...#..#..#.##..#..........##.#..#.....#.###.#...######.#..#.. ...##...#.....##.....##.##..#..#####...##.###.#.###..#..#.#.....##.#..##....##.# ##...##.#...#.#...##.##..#.#..##.#....###.##.#.....#.##..#..#.##..#....##......# ....#.#..##.#..####..#..##....#####.#....#.#...#.#.###.#.#.#.#.#.....#...#.....# ..#..####..###..###..##.####.#....#.....#.###.#.#.###.####..#...#...#...#.##.... #.........##..#..##...###...#..#....#.###...#.##.....###..###..##....###.#..#... .#..#..#......#...##.###.#...#.#...##.#.....#...#......##..#.....###...##..#.#.. .#.#.....##..#.##.....#.#.#..#.#..######.#....#..##...##.##.....#..##...#.##.##. .#......###..#...###..........................##......#....####.#...#..#...#..#. #..#.#......#...#...#...#..#..##...#.##...#.####.##......#....#..#.##.....#####. Generation: 100 ...............####......#...................................................... ...............#..#.....#.#...................#................................. ........................#.#.................##.................................. .........................#..................##............##.................... ............................................#.....#.......##.................... .............................................#.###.............................. ...................................##...###.....#####........................... ........................##........#..#..###........#............................ .......................#..#...........#....##................................... .......................#....#.......#....###.................................... ........................######.....##....##..................................... ...........................#.##.....#..#..........................##.##......... ...........................#.#.......##...........................##.##......... ......##...................##................................................... ......##........................................................................
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:
# # # # # # # # #
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>
Z80 Assembly
This code will run on an MSX computer if progammed on a cartride or on an emulator (like openMSX) if loaded as a .rom file
The code below will show the Gosper glider gun, but if INITLINES is set to 0 the code will show the blinker
;MSX Bios calls
CHPUT equ 000A2H ;print character A
CHGET equ 0009FH ;wait for keyboard input
INITXT equ 0006CH ;init TEXT1 mode (40 x 24), based on TXTNAM,TXTCGP and LINL40
LDIRVM equ 0005CH ;write BC times MEM(HL++) to VRAM(DE++)
;MSX system variables
TXTNAM equ 0F3B3H ;pattern name table
TXTCGP equ 0F3B7H ;pattern generator table
LINL40 equ 0F3AEH ;line length
;defines
LIFE_CHAR equ 0xDB ;MSX all-pixels-on character
;variables
screen equ 0E000H ;40x24 bytes
scratch equ screen+(40*24) ;table for calculations
org 04000H
;*********************************************************************
HEADER ;first 16 bytes of the rom. (as specified by MSX standard)
;*********************************************************************
db "AB" ;id
dw Main ;init
dw 0 ;statement
dw 0 ;device
dw 0 ;text
dw 0 ;reserved
dw 0 ;reserved
dw 0 ;reserved
init_tab1 db 0x18
dc 7, 0x17
db " Conway's Game Of Life "
dc 8, 0x17
db 0x19
db 0x16
dc 38, " "
db 0x16, 0x1A
dc 38, 0x17
db 0x1B
init_tab2 dc 41, 0x80
dc 38, 0
dc 41, 0x80
INITLINES equ 10
init_tab3 db " "
db " X "
db " X X "
db " XX XX XX "
db " X X XX XX "
db " XX X X XX "
db " XX X X XX X X "
db " X X X "
db " X X "
db " XX "
Expand ld bc, 40
ldir ;copy top line
push de
ld bc, 40
ldir ;copy first middle line
ex (sp), hl
ld bc, 40*21
ldir ;repeat middle line
pop hl
ld bc, 40 ;copy bottom line
ldir
ret
InitGOL ;create empty screen
ld hl, init_tab1
ld de, screen
call Expand
;Now add intial pattern
if INITLINES
ld hl, init_tab3 ;glider gun start pattern
ld de, init_tab3 ;de=hl, so ldi does not change RAM
ld bc, 40*INITLINES
ld ix, screen+40
InitGOLl ld a, (hl)
cp 'X'
jr nz, InitGOLe
ld (ix), LIFE_CHAR
InitGOLe inc ix
ldi ;hl++ and bc-- (with flag!)
jp pe, InitGOLl
else
ld hl, screen+(4*40)+18
ld a, LIFE_CHAR ;blinker start pattern
ld (hl), a
inc hl
ld (hl), a
inc hl
ld (hl), a
endif
ret
DoGOL ld hl, init_tab2 ;initialize scratchpad (sums to zero, borders marked)
ld de, scratch
call Expand
;first loop: create sums in scratchpad
ld bc, 40*24
ld hl, screen
ld de, screen ;de=hl, so ldi does not change RAM
ld ix, scratch
DoGOLsuml ld a, (hl)
cp LIFE_CHAR ;if cell is alive increase sums of all surrounding cells
jr nz, DoGOLsume
inc (ix-41)
inc (ix-40)
inc (ix-39)
inc (ix-1)
inc (ix+1)
inc (ix+39)
inc (ix+40)
inc (ix+41)
DoGOLsume inc ix
ldi ;hl++, bc-- (with flag!)
jp pe, DoGOLsuml
;second loop: update cell based on current state and sum
ld bc, 40*24
ld hl, screen
ld de, screen ;de=hl, so ldi does not change RAM
ld ix, scratch
DoGOLupdl ld a, (ix)
cp 0x7f ;border -> keep the same
jr nc, DoGOLupde
cp 3 ;3 neighbors-> always live
jr z, DoGOLlive
cp 2
jr z, DoGOLupde ;2 -> stay the same
DoGOLdie ld (hl), 0x20 ;1,4,5,6,7,8 -> always die
jr DoGoLupde
DoGOLlive ld (hl), LIFE_CHAR
DoGOLupde inc ix
ldi ;hl++, bc-- (with flag!)
jp pe, DoGOLupdl
ret
;*********************************************************************
Main
;*********************************************************************
call INITXT
call InitGOL
Mainloop ld hl, screen
ld de, (TXTNAM)
ld bc, 40*24
call LDIRVM
;call CHGET
call DoGOL
jr Mainloop
;force 16k ROM size binary output
EndOfCode dc 0x8000 - EndOfCode, 0xFF
end
Yabasic
// Conway's_Game_of_Life
X = 59 : Y = 35 : H = 4
open window X*H,Y*H
backcolor 0, 0, 0
dim c(X,Y) : dim cn(X,Y) : dim cl(X,Y)
// Thunderbird methuselah
c(X/2-1,Y/3+1) = 1 : c(X/2,Y/3+1) = 1 : c(X/2+1,Y/3+1) = 1
c(X/2,Y/3+3) = 1 : c(X/2,Y/3+4) = 1 : c(X/2,Y/3+5) = 1
s = 0
repeat
clear window
alive = 0 : stable = 1
s = s + 1
for y = 0 to Y-1
for x = 0 to X-1
xm1 = mod(x-1+X, X) : xp1 = mod(x+1+X, X)
ym1 = mod(y-1+Y, Y) : yp1 = mod(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) color 0, 0, 255 // adult
if not cl(x,y) color 0, 255, 0 // newborn
else
if cl(x,y) color 255, 0, 0 // old
if not cl(x,y) color 255, 255, 0 // shortlived
end if
fill rect x*H,y*H,x*H+H,y*H+H
end if
next x
next y
pause 0.06
// Copy arrays
for i = 0 to X-1
for j = 0 to Y-1
if cl(i,j)<>cn(i,j) 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"
clear window
else
print "Stabilized in ", s-2, " iterations"
end if
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.
-*-- ---- -*-- -*-- ***- -*-- -*-- ---- -*-- ---- ---- ---- ==== ==== ====
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;
ZX Spectrum Basic
Play on emulator at full speed for reasonable performance.
The ZX Spectrum was shipped with a demo tape called Horizons, which included an implementation of Life; however, it relied on machine code.
10 REM Initialize
20 LET w=32*22
30 DIM w$(w): DIM n$(w)
40 FOR n=1 TO 100
50 LET w$(RND*w+1)="O"
60 NEXT n
70 REM Loop
80 FOR i=34 TO w-34
90 LET p$="": LET d=0
100 LET p$=p$+w$(i-1)+w$(i+1)+w$(i-33)+w$(i-32)+w$(i-31)+w$(i+31)+w$(i+32)+w$(i+33)
110 LET n$(i)=w$(i)
120 FOR n=1 TO LEN p$
130 IF p$(n)="O" THEN LET d=d+1
140 NEXT n
150 IF (w$(i)=" ") AND (d=3) THEN LET n$(i)="O": GO TO 180
160 IF (w$(i)="O") AND (d<2) THEN LET n$(i)=" ": GO TO 180
170 IF (w$(i)="O") AND (d>3) THEN LET n$(i)=" "
180 NEXT i
190 PRINT AT 0,0;w$
200 LET w$=n$
210 GO TO 80
- Programming Tasks
- Games
- Cellular automata
- 11l
- 6502 Assembly
- 68000 Assembly
- ABAP
- ACL2
- Ada
- ALGOL 68
- Amazing Hopper
- APL
- AppleScript
- ARM Assembly
- AutoHotkey
- AWK
- Axe
- Axe examples needing attention
- Examples needing attention
- BASIC
- BASIC256
- BBC BASIC
- CASIO BASIC
- FreeBASIC
- GFA Basic
- GW-BASIC
- Liberty BASIC
- MSX Basic
- PureBasic
- QBasic
- Sinclair ZX81 BASIC
- TI-83 BASIC
- TI-89 BASIC
- Batch File
- Befunge
- Brainf***
- Brat
- BQN
- C
- C sharp
- C++
- Chapel
- Clojure
- COBOL
- Common Lisp
- D
- Dart
- Delphi
- System.SysUtils
- Velthuis.Console
- E
- EasyLang
- EC
- Ecere
- Egel
- Elena
- Elixir
- Emacs Lisp
- Erlang
- ERRE
- F Sharp
- Windows Presentation Foundation
- Fermat
- Forth
- Fortran
- Frink
- FunL
- Furor
- Peri
- Futhark
- Futhark examples needing attention
- FutureBasic
- Go
- Groovy
- Haskell
- HolyC
- Icon
- Unicon
- INTERCAL
- J
- JAMES II/Rule-based Cellular Automata
- JAMES II
- Java
- JavaScript
- HTML5
- Jq
- Jsish
- Julia
- Kotlin
- Lambdatalk
- Lua
- Ksh
- M2000 Interpreter
- MANOOL
- Mathematica
- Wolfram Language
- MATLAB
- Maxima
- Pages with broken file links
- MiniScript
- Nim
- OCaml
- Octave
- Ol
- OpenGL
- OoRexx
- Oz
- PARI/GP
- Pascal
- Perl
- Phix
- Phix/pGUI
- Phix/online
- Picat
- PicoLisp
- PL/I
- Pointless
- PostScript
- Prolog
- Processing
- Processing Python mode
- Python
- Quackery
- R
- Racket
- Raku
- Red
- Retro
- REXX
- Ruby
- Rust
- Scala
- Scheme
- Scilab
- SenseTalk
- SequenceL
- CImg
- SETL
- Shen
- Sidef
- Simula
- Smalltalk
- SQL
- Swift
- SystemVerilog
- Tcl
- Uiua
- UNIX Shell
- Ursala
- Vedit macro language
- Wortel
- VBScript
- V (Vlang)
- Wren
- XPL0
- XSLT
- Z80 Assembly
- Yabasic
- Yabasic examples needing attention
- Zkl
- ZPL
- ZX Spectrum Basic
- Pages with too many expensive parser function calls