Game of Life
Conway's Game of Life on a 12 x 20 board: edit cells or pick a ready-made pattern, then run a generation at a time or many at once. Every board is remembered with the generation it first appeared in, so the program says when the board has died, stopped changing, or started to repeat - and with what period.
Every screen below was recorded under CPython. When this page was built, the EML interpreter replayed each session from the same input and printed the same bytes.
About
Conway's Game of Life on a board of 12 rows and 20 columns. Cells can be turned on and off one at a time, or a ready-made pattern placed; then the board runs a generation at a time or many at once. Every board is remembered with the generation it first appeared in, so the program says when everything has died, when nothing changes any more, or when the board starts to repeat - and with what period.
main.eml- the menu, the board on screen, editing, stepping and running, and the remembered boardslife.eml- the board and the rule: one generation, the live count, a board as one string, flipping a cell, placing a shapepatterns.eml- six ready-made patterns and where they go
How each part works:
- Every cell outside the board counts as dead, so the board has a hard edge. A generation is worked out into a new grid, as in the corpus case
conway-game-of-life: each cell must read the previous generation, and writing into the grid being read would let the cells already updated change the neighbour counts of the rest. - Each board is turned into one string and kept in a dictionary with the generation it first appeared in. A step that makes a board already in it has found a cycle: one generation after its first appearance means a still life, more than one means the board repeats with that period. A board with no live cell is reported as dead at once. There are only finitely many boards of this size, so every run ends in one of these ways sooner or later, and a run of many generations stops there.
- The patterns show the endings: the blinker, the toad and the beacon repeat every 2 generations and the pentadecathlon every 15; the glider travels down to the bottom edge and turns into a still block at generation 35; the R-pentomino, five cells, changes for 76 generations before it settles into a block.
- Editing the board or placing a pattern starts again at generation 0 and forgets the remembered boards.
What is checked: a row from 1 to 12 and a column from 1 to 20, typed as 3 5 or 3,5; 1 to 500 generations for a run; a pattern number from the list. An empty answer cancels, or ends the editing.
Sessions: sessions/basic.in places the glider, steps twice and runs on until it settles into a block (generation 36, still life since 35); places the pentadecathlon and runs until generation 15 turns out to be generation 0 again; clears the board, draws a blinker by hand and steps twice to see it come back (period 2). sessions/bad-input.in gives menu choices 0 and x, a step on the empty board (everything has died), runs of 0, 501 and abc generations, cells 13 1, 1 21, x, 3 and 1 2 3, a cell turned on and off again, a lone cell that dies at the next step, an L of three cells that becomes a block, a domino that dies during a run, pattern numbers 0, 7 and x, and then runs the R-pentomino until it settles (generation 77, still life since 76).
Built on the verified corpus case conway-game-of-life (one generation of Life into a fresh grid, with a glider as the self-checking pattern).
Recorded sessions
What the screen shows while someone uses the program. Each typed line appears after its prompt, the way a terminal shows it.
bad-input
interpreter: byte-equal== Game of Life ==
A 12 x 20 board; cells outside it count as dead. A live cell stays alive
with 2 or 3 live neighbours, and a dead cell comes alive with exactly 3.
1) step 2) run 3) toggle cells 4) pattern 5) clear 6) show 7) quit
choice> 0
Pick a number from 1 to 7.
1) step 2) run 3) toggle cells 4) pattern 5) clear 6) show 7) quit
choice> x
Pick a number from 1 to 7.
1) step 2) run 3) toggle cells 4) pattern 5) clear 6) show 7) quit
choice> 1
Generation 1: 0 alive (0 born, 0 died)
12345678901234567890
1 ....................
2 ....................
3 ....................
4 ....................
5 ....................
6 ....................
7 ....................
8 ....................
9 ....................
10 ....................
11 ....................
12 ....................
Everything has died.
1) step 2) run 3) toggle cells 4) pattern 5) clear 6) show 7) quit
choice> 2
generations (1 to 500)> 0
Type a number from 1 to 500.
generations (1 to 500)> 501
Type a number from 1 to 500.
generations (1 to 500)> abc
Type a number from 1 to 500.
generations (1 to 500)>
Cancelled.
1) step 2) run 3) toggle cells 4) pattern 5) clear 6) show 7) quit
choice> 3
cell (row column, empty to finish)> 13 1
Type a row from 1 to 12 and a column from 1 to 20, like 3 5.
cell (row column, empty to finish)> 1 21
Type a row from 1 to 12 and a column from 1 to 20, like 3 5.
cell (row column, empty to finish)> x
Type a row from 1 to 12 and a column from 1 to 20, like 3 5.
cell (row column, empty to finish)> 3
Type a row from 1 to 12 and a column from 1 to 20, like 3 5.
cell (row column, empty to finish)> 1 2 3
Type a row from 1 to 12 and a column from 1 to 20, like 3 5.
cell (row column, empty to finish)>
1) step 2) run 3) toggle cells 4) pattern 5) clear 6) show 7) quit
choice> 3
cell (row column, empty to finish)> 1 1
Row 1, column 1 is now alive.
cell (row column, empty to finish)> 1 1
Row 1, column 1 is now dead.
cell (row column, empty to finish)> 12 20
Row 12, column 20 is now alive.
cell (row column, empty to finish)>
Generation 0: 1 alive
12345678901234567890
1 ....................
2 ....................
3 ....................
4 ....................
5 ....................
6 ....................
7 ....................
8 ....................
9 ....................
10 ....................
11 ....................
12 ...................#
1) step 2) run 3) toggle cells 4) pattern 5) clear 6) show 7) quit
choice> 1
Generation 1: 0 alive (0 born, 1 died)
12345678901234567890
1 ....................
2 ....................
3 ....................
4 ....................
5 ....................
6 ....................
7 ....................
8 ....................
9 ....................
10 ....................
11 ....................
12 ....................
Everything has died.
1) step 2) run 3) toggle cells 4) pattern 5) clear 6) show 7) quit
choice> 3
cell (row column, empty to finish)> 2 2
Row 2, column 2 is now alive.
cell (row column, empty to finish)> 2 3
Row 2, column 3 is now alive.
cell (row column, empty to finish)> 3 2
Row 3, column 2 is now alive.
cell (row column, empty to finish)>
Generation 0: 3 alive
12345678901234567890
1 ....................
2 .##.................
3 .#..................
4 ....................
5 ....................
6 ....................
7 ....................
8 ....................
9 ....................
10 ....................
11 ....................
12 ....................
1) step 2) run 3) toggle cells 4) pattern 5) clear 6) show 7) quit
choice> 2
generations (1 to 500)> 5
Stopped after 2 generations: this board was seen before.
Generation 2: 4 alive (0 born, 0 died)
12345678901234567890
1 ....................
2 .##.................
3 .##.................
4 ....................
5 ....................
6 ....................
7 ....................
8 ....................
9 ....................
10 ....................
11 ....................
12 ....................
Still life: nothing has changed since generation 1.
1) step 2) run 3) toggle cells 4) pattern 5) clear 6) show 7) quit
choice> 5
The board is empty.
1) step 2) run 3) toggle cells 4) pattern 5) clear 6) show 7) quit
choice> 3
cell (row column, empty to finish)> 9 9
Row 9, column 9 is now alive.
cell (row column, empty to finish)> 9 10
Row 9, column 10 is now alive.
cell (row column, empty to finish)>
Generation 0: 2 alive
12345678901234567890
1 ....................
2 ....................
3 ....................
4 ....................
5 ....................
6 ....................
7 ....................
8 ....................
9 ........##..........
10 ....................
11 ....................
12 ....................
1) step 2) run 3) toggle cells 4) pattern 5) clear 6) show 7) quit
choice> 2
generations (1 to 500)> 5
Stopped after 1 generation: no cell is left.
Generation 1: 0 alive (0 born, 2 died)
12345678901234567890
1 ....................
2 ....................
3 ....................
4 ....................
5 ....................
6 ....................
7 ....................
8 ....................
9 ....................
10 ....................
11 ....................
12 ....................
Everything has died.
1) step 2) run 3) toggle cells 4) pattern 5) clear 6) show 7) quit
choice> 4
1) glider
2) blinker
3) toad
4) beacon
5) pentadecathlon
6) R-pentomino
pattern> 0
Type a number from 1 to 6.
pattern> 7
Type a number from 1 to 6.
pattern> x
Type a number from 1 to 6.
pattern>
Cancelled.
1) step 2) run 3) toggle cells 4) pattern 5) clear 6) show 7) quit
choice> 4
1) glider
2) blinker
3) toad
4) beacon
5) pentadecathlon
6) R-pentomino
pattern> 6
Placed: R-pentomino.
Generation 0: 5 alive
12345678901234567890
1 ....................
2 ....................
3 ....................
4 ....................
5 ....................
6 ..........##........
7 .........##.........
8 ..........#.........
9 ....................
10 ....................
11 ....................
12 ....................
1) step 2) run 3) toggle cells 4) pattern 5) clear 6) show 7) quit
choice> 2
generations (1 to 500)> 100
Stopped after 77 generations: this board was seen before.
Generation 77: 4 alive (0 born, 0 died)
12345678901234567890
1 ....................
2 ....................
3 ....................
4 ....................
5 ....................
6 ....................
7 ....##..............
8 ....##..............
9 ....................
10 ....................
11 ....................
12 ....................
Still life: nothing has changed since generation 76.
1) step 2) run 3) toggle cells 4) pattern 5) clear 6) show 7) quit
choice> 6
Generation 77: 4 alive
12345678901234567890
1 ....................
2 ....................
3 ....................
4 ....................
5 ....................
6 ....................
7 ....##..............
8 ....##..............
9 ....................
10 ....................
11 ....................
12 ....................
1) step 2) run 3) toggle cells 4) pattern 5) clear 6) show 7) quit
choice> 7
Bye.
What was typed (46 lines)
0
x
1
2
0
501
abc
3
13 1
1 21
x
3
1 2 3
3
1 1
1 1
12 20
1
3
2 2
2 3
3 2
2
5
5
3
9 9
9 10
2
5
4
0
7
x
4
6
2
100
6
7
basic
interpreter: byte-equal== Game of Life ==
A 12 x 20 board; cells outside it count as dead. A live cell stays alive
with 2 or 3 live neighbours, and a dead cell comes alive with exactly 3.
1) step 2) run 3) toggle cells 4) pattern 5) clear 6) show 7) quit
choice> 4
1) glider
2) blinker
3) toad
4) beacon
5) pentadecathlon
6) R-pentomino
pattern> 1
Placed: glider.
Generation 0: 5 alive
12345678901234567890
1 ....................
2 ..#.................
3 ...#................
4 .###................
5 ....................
6 ....................
7 ....................
8 ....................
9 ....................
10 ....................
11 ....................
12 ....................
1) step 2) run 3) toggle cells 4) pattern 5) clear 6) show 7) quit
choice> 1
Generation 1: 5 alive (2 born, 2 died)
12345678901234567890
1 ....................
2 ....................
3 .#.#................
4 ..##................
5 ..#.................
6 ....................
7 ....................
8 ....................
9 ....................
10 ....................
11 ....................
12 ....................
1) step 2) run 3) toggle cells 4) pattern 5) clear 6) show 7) quit
choice> 1
Generation 2: 5 alive (2 born, 2 died)
12345678901234567890
1 ....................
2 ....................
3 ...#................
4 .#.#................
5 ..##................
6 ....................
7 ....................
8 ....................
9 ....................
10 ....................
11 ....................
12 ....................
1) step 2) run 3) toggle cells 4) pattern 5) clear 6) show 7) quit
choice> 2
generations (1 to 500)> 50
Stopped after 34 generations: this board was seen before.
Generation 36: 4 alive (0 born, 0 died)
12345678901234567890
1 ....................
2 ....................
3 ....................
4 ....................
5 ....................
6 ....................
7 ....................
8 ....................
9 ....................
10 ....................
11 ..........##........
12 ..........##........
Still life: nothing has changed since generation 35.
1) step 2) run 3) toggle cells 4) pattern 5) clear 6) show 7) quit
choice> 4
1) glider
2) blinker
3) toad
4) beacon
5) pentadecathlon
6) R-pentomino
pattern> 5
Placed: pentadecathlon.
Generation 0: 12 alive
12345678901234567890
1 ....................
2 ....................
3 ....................
4 ....................
5 .......#....#.......
6 .....##.####.##.....
7 .......#....#.......
8 ....................
9 ....................
10 ....................
11 ....................
12 ....................
1) step 2) run 3) toggle cells 4) pattern 5) clear 6) show 7) quit
choice> 2
generations (1 to 500)> 20
Stopped after 15 generations: this board was seen before.
Generation 15: 12 alive (8 born, 12 died)
12345678901234567890
1 ....................
2 ....................
3 ....................
4 ....................
5 .......#....#.......
6 .....##.####.##.....
7 .......#....#.......
8 ....................
9 ....................
10 ....................
11 ....................
12 ....................
It repeats: generation 15 is generation 0 again, a period of 15.
1) step 2) run 3) toggle cells 4) pattern 5) clear 6) show 7) quit
choice> 5
The board is empty.
1) step 2) run 3) toggle cells 4) pattern 5) clear 6) show 7) quit
choice> 3
cell (row column, empty to finish)> 6 9
Row 6, column 9 is now alive.
cell (row column, empty to finish)> 6 10
Row 6, column 10 is now alive.
cell (row column, empty to finish)> 6,11
Row 6, column 11 is now alive.
cell (row column, empty to finish)>
Generation 0: 3 alive
12345678901234567890
1 ....................
2 ....................
3 ....................
4 ....................
5 ....................
6 ........###.........
7 ....................
8 ....................
9 ....................
10 ....................
11 ....................
12 ....................
1) step 2) run 3) toggle cells 4) pattern 5) clear 6) show 7) quit
choice> 1
Generation 1: 3 alive (2 born, 2 died)
12345678901234567890
1 ....................
2 ....................
3 ....................
4 ....................
5 .........#..........
6 .........#..........
7 .........#..........
8 ....................
9 ....................
10 ....................
11 ....................
12 ....................
1) step 2) run 3) toggle cells 4) pattern 5) clear 6) show 7) quit
choice> 1
Generation 2: 3 alive (2 born, 2 died)
12345678901234567890
1 ....................
2 ....................
3 ....................
4 ....................
5 ....................
6 ........###.........
7 ....................
8 ....................
9 ....................
10 ....................
11 ....................
12 ....................
It repeats: generation 2 is generation 0 again, a period of 2.
1) step 2) run 3) toggle cells 4) pattern 5) clear 6) show 7) quit
choice> 7
Bye.
What was typed (19 lines)
4
1
1
1
2
50
4
5
2
20
5
3
6 9
6 10
6,11
1
1
7
Modules
The program as written, entry module first. Each module transpiles to its own Python file, which is what eml project run executes.
main.eml(entry)
eml# P036 Game of Life: a 12 x 20 board to edit cell by cell or fill with a
# ready-made pattern, then run a generation at a time or many at once. Every
# board is remembered with the generation it first appeared in, so the
# program notices when the board stops changing, starts repeating, or dies.
import life
import patterns
500 => most_generations
def trim(s):
0 => i
len(s) => j
while i < j and s[i] == " ":
i + 1 => i
while j > i and s[j - 1] == " ":
j - 1 => j
return s[i:j]
def whole_number(s):
# The value of 1 to 3 digits, otherwise -1.
if s == "" or len(s) > 3:
return -1
0 => n
for c in s:
if not (c in "0123456789"):
return -1
n * 10 + int(c) => n
return n
def two_numbers(s):
# "3 5" or "3,5" as [3, 5]; [] if it is not two whole numbers.
[] => parts
"" => word
for c in s + " ":
if c == " " or c == ",":
if word != "":
parts + [word] => parts
"" => word
else:
word + c => word
if len(parts) != 2:
return []
whole_number(parts[0]) => a
whole_number(parts[1]) => b
if a < 0 or b < 0:
return []
return [a, b]
def show(grid, generation, change):
# change is [born, died] after a step, or [] for a board just set up.
("Generation " + str(generation) + ": " + str(life.alive(grid)) + " alive") => line
if len(change) > 0:
line + " (" + str(change[0]) + " born, " + str(change[1]) + " died)" => line
line ^0
" " => header
for c in [1:life.cols]:
header + str(c % 10) => header
header ^0
for r in [0:life.rows - 1]:
str(r + 1) => label
while len(label) < 3:
" " + label => label
label + " " => line
for cell in grid[r]:
if cell == 1:
line + "#" => line
else:
line + "." => line
line ^0
def generations(n):
if n == 1:
return "1 generation"
return str(n) + " generations"
def verdict(grid, generation, first):
# What it means that the board has died, or already appeared in
# generation first.
if life.alive(grid) == 0:
return "Everything has died."
if generation - first == 1:
return "Still life: nothing has changed since generation " + str(first) + "."
return "It repeats: generation " + str(generation) + " is generation " + str(first) + " again, a period of " + str(generation - first) + "."
def fresh(grid):
# A new start from this board: generation 0, and only it remembered.
{} => seen
0 => seen[life.key(grid)]
return [grid, 0, seen]
def advance(state):
# One generation. Returns [state, change, first], where first is the
# generation this board first appeared in, or -1 if it is new.
life.step(state[0]) => s
s[0] => grid
state[1] + 1 => generation
state[2] => seen
life.key(grid) => k
-1 => first
if k in seen:
seen[k] => first
else:
generation => seen[k]
return [[grid, generation, seen], [s[1], s[2]], first]
def run(state):
while True:
trim(input("generations (1 to " + str(most_generations) + ")> ")) => answer
if answer == "":
"Cancelled." ^0
return state
whole_number(answer) => n
if n >= 1 and n <= most_generations:
0 => done
-1 => first
[] => change
while done < n and first == -1 and (done == 0 or life.alive(state[0]) > 0):
advance(state) => a
a[0] => state
a[1] => change
a[2] => first
done + 1 => done
if life.alive(state[0]) == 0:
("Stopped after " + generations(done) + ": no cell is left.") ^0
elif first == -1:
("Ran " + generations(done) + ".") ^0
else:
("Stopped after " + generations(done) + ": this board was seen before.") ^0
show(state[0], state[1], change)
if first != -1 or life.alive(state[0]) == 0:
verdict(state[0], state[1], first) ^0
return state
("Type a number from 1 to " + str(most_generations) + ".") ^0
def toggle(state):
state[0] => grid
0 => changed
True => editing
while editing:
trim(input("cell (row column, empty to finish)> ")) => answer
if answer == "":
False => editing
else:
two_numbers(answer) => rc
if len(rc) == 0 or rc[0] < 1 or rc[0] > life.rows or rc[1] < 1 or rc[1] > life.cols:
("Type a row from 1 to " + str(life.rows) + " and a column from 1 to " + str(life.cols) + ", like 3 5.") ^0
else:
life.toggled(grid, rc[0] - 1, rc[1] - 1) => grid
changed + 1 => changed
"dead" => now
if grid[rc[0] - 1][rc[1] - 1] == 1:
"alive" => now
("Row " + str(rc[0]) + ", column " + str(rc[1]) + " is now " + now + ".") ^0
if changed == 0:
return state
fresh(grid) => state
show(state[0], 0, [])
return state
def choose_pattern(state):
for i in [0:len(patterns.patterns) - 1]:
(" " + str(i + 1) + ") " + patterns.patterns[i][0]) ^0
while True:
trim(input("pattern> ")) => answer
if answer == "":
"Cancelled." ^0
return state
whole_number(answer) => n
if n >= 1 and n <= len(patterns.patterns):
patterns.patterns[n - 1] => p
fresh(life.placed(p[1], p[2], p[3])) => state
("Placed: " + p[0] + ".") ^0
show(state[0], 0, [])
return state
("Type a number from 1 to " + str(len(patterns.patterns)) + ".") ^0
"== Game of Life ==" ^0
"A 12 x 20 board; cells outside it count as dead. A live cell stays alive" ^0
"with 2 or 3 live neighbours, and a dead cell comes alive with exactly 3." ^0
fresh(life.empty()) => state
True => running
while running:
"" ^0
"1) step 2) run 3) toggle cells 4) pattern 5) clear 6) show 7) quit" ^0
trim(input("choice> ")) => choice
if choice == "1":
advance(state) => a
a[0] => state
show(state[0], state[1], a[1])
if a[2] != -1 or life.alive(state[0]) == 0:
verdict(state[0], state[1], a[2]) ^0
elif choice == "2":
run(state) => state
elif choice == "3":
toggle(state) => state
elif choice == "4":
choose_pattern(state) => state
elif choice == "5":
fresh(life.empty()) => state
"The board is empty." ^0
elif choice == "6":
show(state[0], state[1], [])
elif choice == "7":
False => running
else:
"Pick a number from 1 to 7." ^0
"Bye." ^0
Python projection (main.py)
import life
import patterns
most_generations = 500
def trim(s):
i = 0
j = len(s)
while i < j and s[i] == " ":
i = i + 1
while j > i and s[j - 1] == " ":
j = j - 1
return s[i:j]
def whole_number(s):
if s == "" or len(s) > 3:
return -1
n = 0
for c in s:
if not c in "0123456789":
return -1
n = n * 10 + int(c)
return n
def two_numbers(s):
parts = []
word = ""
for c in s + " ":
if c == " " or c == ",":
if word != "":
parts = parts + [word]
word = ""
else:
word = word + c
if len(parts) != 2:
return []
a = whole_number(parts[0])
b = whole_number(parts[1])
if a < 0 or b < 0:
return []
return [a, b]
def show(grid, generation, change):
line = "Generation " + str(generation) + ": " + str(life.alive(grid)) + " alive"
if len(change) > 0:
line = line + " (" + str(change[0]) + " born, " + str(change[1]) + " died)"
print(line)
header = " "
for c in range(1, life.cols+1):
header = header + str(c % 10)
print(header)
for r in range(0, life.rows):
label = str(r + 1)
while len(label) < 3:
label = " " + label
line = label + " "
for cell in grid[r]:
if cell == 1:
line = line + "#"
else:
line = line + "."
print(line)
def generations(n):
if n == 1:
return "1 generation"
return str(n) + " generations"
def verdict(grid, generation, first):
if life.alive(grid) == 0:
return "Everything has died."
if generation - first == 1:
return "Still life: nothing has changed since generation " + str(first) + "."
return "It repeats: generation " + str(generation) + " is generation " + str(first) + " again, a period of " + str(generation - first) + "."
def fresh(grid):
seen = {}
seen[life.key(grid)] = 0
return [grid, 0, seen]
def advance(state):
s = life.step(state[0])
grid = s[0]
generation = state[1] + 1
seen = state[2]
k = life.key(grid)
first = -1
if k in seen:
first = seen[k]
else:
seen[k] = generation
return [[grid, generation, seen], [s[1], s[2]], first]
def run(state):
while True:
answer = trim(input("generations (1 to " + str(most_generations) + ")> "))
if answer == "":
print("Cancelled.")
return state
n = whole_number(answer)
if n >= 1 and n <= most_generations:
done = 0
first = -1
change = []
while done < n and first == -1 and (done == 0 or life.alive(state[0]) > 0):
a = advance(state)
state = a[0]
change = a[1]
first = a[2]
done = done + 1
if life.alive(state[0]) == 0:
print("Stopped after " + generations(done) + ": no cell is left.")
elif first == -1:
print("Ran " + generations(done) + ".")
else:
print("Stopped after " + generations(done) + ": this board was seen before.")
show(state[0], state[1], change)
if first != -1 or life.alive(state[0]) == 0:
print(verdict(state[0], state[1], first))
return state
print("Type a number from 1 to " + str(most_generations) + ".")
def toggle(state):
grid = state[0]
changed = 0
editing = True
while editing:
answer = trim(input("cell (row column, empty to finish)> "))
if answer == "":
editing = False
else:
rc = two_numbers(answer)
if len(rc) == 0 or rc[0] < 1 or rc[0] > life.rows or rc[1] < 1 or rc[1] > life.cols:
print("Type a row from 1 to " + str(life.rows) + " and a column from 1 to " + str(life.cols) + ", like 3 5.")
else:
grid = life.toggled(grid, rc[0] - 1, rc[1] - 1)
changed = changed + 1
now = "dead"
if grid[rc[0] - 1][rc[1] - 1] == 1:
now = "alive"
print("Row " + str(rc[0]) + ", column " + str(rc[1]) + " is now " + now + ".")
if changed == 0:
return state
state = fresh(grid)
show(state[0], 0, [])
return state
def choose_pattern(state):
for i in range(0, len(patterns.patterns)):
print(" " + str(i + 1) + ") " + patterns.patterns[i][0])
while True:
answer = trim(input("pattern> "))
if answer == "":
print("Cancelled.")
return state
n = whole_number(answer)
if n >= 1 and n <= len(patterns.patterns):
p = patterns.patterns[n - 1]
state = fresh(life.placed(p[1], p[2], p[3]))
print("Placed: " + p[0] + ".")
show(state[0], 0, [])
return state
print("Type a number from 1 to " + str(len(patterns.patterns)) + ".")
print("== Game of Life ==")
print("A 12 x 20 board; cells outside it count as dead. A live cell stays alive")
print("with 2 or 3 live neighbours, and a dead cell comes alive with exactly 3.")
state = fresh(life.empty())
running = True
while running:
print("")
print("1) step 2) run 3) toggle cells 4) pattern 5) clear 6) show 7) quit")
choice = trim(input("choice> "))
if choice == "1":
a = advance(state)
state = a[0]
show(state[0], state[1], a[1])
if a[2] != -1 or life.alive(state[0]) == 0:
print(verdict(state[0], state[1], a[2]))
elif choice == "2":
state = run(state)
elif choice == "3":
state = toggle(state)
elif choice == "4":
state = choose_pattern(state)
elif choice == "5":
state = fresh(life.empty())
print("The board is empty.")
elif choice == "6":
show(state[0], state[1], [])
elif choice == "7":
running = False
else:
print("Pick a number from 1 to 7.")
print("Bye.")
life.eml
eml# P036 Game of Life - the board and its rule. The board has 12 rows and 20
# columns, and every cell outside it counts as dead. A grid is a list of rows
# of 0 (dead) and 1 (alive).
#
# A generation is worked out into a brand-new grid, as in the corpus case
# conway-game-of-life: every cell must read the previous generation, so
# writing into the grid being read would let early cells change the
# neighbour counts of later ones.
12 => rows
20 => cols
def empty():
[] => grid
for r in [1:rows]:
grid + [[0] * cols] => grid
return grid
def neighbours(grid, r, c):
0 => n
for dr in [0:2]:
for dc in [0:2]:
r + dr - 1 => y
c + dc - 1 => x
if (dr != 1 or dc != 1) and y >= 0 and y < rows and x >= 0 and x < cols:
n + grid[y][x] => n
return n
def step(grid):
# The next generation: a live cell stays alive with 2 or 3 live
# neighbours, a dead cell comes alive with exactly 3. Returns
# [next grid, cells born, cells that died].
[] => out
0 => born
0 => died
for r in [0:rows - 1]:
[] => row
for c in [0:cols - 1]:
neighbours(grid, r, c) => n
0 => cell
if grid[r][c] == 1:
if n == 2 or n == 3:
1 => cell
else:
died + 1 => died
elif n == 3:
1 => cell
born + 1 => born
row + [cell] => row
out + [row] => out
return [out, born, died]
def alive(grid):
0 => n
for row in grid:
for cell in row:
n + cell => n
return n
def key(grid):
# The whole board as one string, to recognise a board seen before.
"" => s
for row in grid:
for cell in row:
if cell == 1:
s + "#" => s
else:
s + "." => s
s + "/" => s
return s
def toggled(grid, r, c):
# The grid with cell (r, c) flipped; rows and columns count from 0.
[] => out
for y in [0:rows - 1]:
if y == r:
grid[y][0:c] + [1 - grid[y][c]] + grid[y][c + 1:cols] => row
out + [row] => out
else:
out + [grid[y]] => out
return out
def placed(shape, top, left):
# A board with only the shape on it: shape rows are strings of "#" and
# ".", their top-left corner at (top, left).
empty() => grid
for i in [0:len(shape) - 1]:
for j in [0:len(shape[i]) - 1]:
if shape[i][j] == "#":
toggled(grid, top + i, left + j) => grid
return grid
Python projection (life.py)
rows = 12
cols = 20
def empty():
grid = []
for r in range(1, rows+1):
grid = grid + [[0] * cols]
return grid
def neighbours(grid, r, c):
n = 0
for dr in range(0, 3):
for dc in range(0, 3):
y = r + dr - 1
x = c + dc - 1
if (dr != 1 or dc != 1) and y >= 0 and y < rows and x >= 0 and x < cols:
n = n + grid[y][x]
return n
def step(grid):
out = []
born = 0
died = 0
for r in range(0, rows):
row = []
for c in range(0, cols):
n = neighbours(grid, r, c)
cell = 0
if grid[r][c] == 1:
if n == 2 or n == 3:
cell = 1
else:
died = died + 1
elif n == 3:
cell = 1
born = born + 1
row = row + [cell]
out = out + [row]
return [out, born, died]
def alive(grid):
n = 0
for row in grid:
for cell in row:
n = n + cell
return n
def key(grid):
s = ""
for row in grid:
for cell in row:
if cell == 1:
s = s + "#"
else:
s = s + "."
s = s + "/"
return s
def toggled(grid, r, c):
out = []
for y in range(0, rows):
if y == r:
row = grid[y][0:c] + [1 - grid[y][c]] + grid[y][c + 1:cols]
out = out + [row]
else:
out = out + [grid[y]]
return out
def placed(shape, top, left):
grid = empty()
for i in range(0, len(shape)):
for j in range(0, len(shape[i])):
if shape[i][j] == "#":
grid = toggled(grid, top + i, left + j)
return grid
patterns.eml
eml# P036 Game of Life - the ready-made patterns, each with where it is placed
# on the board (top row and left column, counting from 0).
[
["glider", [".#.", "..#", "###"], 1, 1],
["blinker", ["###"], 5, 8],
["toad", [".###", "###."], 5, 8],
["beacon", ["##..", "##..", "..##", "..##"], 4, 8],
["pentadecathlon", ["..#....#..", "##.####.##", "..#....#.."], 4, 5],
["R-pentomino", [".##", "##.", ".#."], 5, 9],
] => patterns
Python projection (patterns.py)
patterns = [["glider", [".#.", "..#", "###"], 1, 1], ["blinker", ["###"], 5, 8], ["toad", [".###", "###."], 5, 8], ["beacon", ["##..", "##..", "..##", "..##"], 4, 8], ["pentadecathlon", ["..#....#..", "##.####.##", "..#....#.."], 4, 5], ["R-pentomino", [".##", "##.", ".#."], 5, 9]]