Skip to content

Commit ce6e919

Browse files
jenrapoyea
authored andcommitted
Hacktoberfest 2020: Conway's Game of Life (TheAlgorithms#3070)
* Created conways_game_of_life.py * Added new_generation(list[int[int]]) -> list[list[int]] * Added glider example * Added comments and shortened glider example * Fixed index out of bounds error * Added test * Added blinker example * Added ability to generate images * Moved image generating code into a separate function * Added comments * Comment * Reformatted file * Formatting * Removed glider test * Update cellular_automata/conways_game_of_life.py Co-authored-by: John Law <[email protected]> * Update conways_game_of_life.py * Update conways_game_of_life.py Co-authored-by: John Law <[email protected]>
1 parent fa34105 commit ce6e919

File tree

1 file changed

+100
-0
lines changed

1 file changed

+100
-0
lines changed

Diff for: cellular_automata/conways_game_of_life.py

+100
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
"""
2+
Conway's Game of Life implemented in Python.
3+
https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life
4+
"""
5+
6+
from __future__ import annotations
7+
8+
from typing import List
9+
10+
from PIL import Image
11+
12+
# Define glider example
13+
GLIDER = [
14+
[0, 1, 0, 0, 0, 0, 0, 0],
15+
[0, 0, 1, 0, 0, 0, 0, 0],
16+
[1, 1, 1, 0, 0, 0, 0, 0],
17+
[0, 0, 0, 0, 0, 0, 0, 0],
18+
[0, 0, 0, 0, 0, 0, 0, 0],
19+
[0, 0, 0, 0, 0, 0, 0, 0],
20+
[0, 0, 0, 0, 0, 0, 0, 0],
21+
[0, 0, 0, 0, 0, 0, 0, 0],
22+
]
23+
24+
# Define blinker example
25+
BLINKER = [[0, 1, 0], [0, 1, 0], [0, 1, 0]]
26+
27+
28+
def new_generation(cells: List[List[int]]) -> List[List[int]]:
29+
"""
30+
Generates the next generation for a given state of Conway's Game of Life.
31+
>>> new_generation(BLINKER)
32+
[[0, 0, 0], [1, 1, 1], [0, 0, 0]]
33+
"""
34+
next_generation = []
35+
for i in range(len(cells)):
36+
next_generation_row = []
37+
for j in range(len(cells[i])):
38+
# Get the number of live neighbours
39+
neighbour_count = 0
40+
if i > 0 and j > 0:
41+
neighbour_count += cells[i - 1][j - 1]
42+
if i > 0:
43+
neighbour_count += cells[i - 1][j]
44+
if i > 0 and j < len(cells[i]) - 1:
45+
neighbour_count += cells[i - 1][j + 1]
46+
if j > 0:
47+
neighbour_count += cells[i][j - 1]
48+
if j < len(cells[i]) - 1:
49+
neighbour_count += cells[i][j + 1]
50+
if i < len(cells) - 1 and j > 0:
51+
neighbour_count += cells[i + 1][j - 1]
52+
if i < len(cells) - 1:
53+
neighbour_count += cells[i + 1][j]
54+
if i < len(cells) - 1 and j < len(cells[i]) - 1:
55+
neighbour_count += cells[i + 1][j + 1]
56+
57+
# Rules of the game of life (excerpt from Wikipedia):
58+
# 1. Any live cell with two or three live neighbours survives.
59+
# 2. Any dead cell with three live neighbours becomes a live cell.
60+
# 3. All other live cells die in the next generation.
61+
# Similarly, all other dead cells stay dead.
62+
alive = cells[i][j] == 1
63+
if (
64+
(alive and 2 <= neighbour_count <= 3)
65+
or not alive
66+
and neighbour_count == 3
67+
):
68+
next_generation_row.append(1)
69+
else:
70+
next_generation_row.append(0)
71+
72+
next_generation.append(next_generation_row)
73+
return next_generation
74+
75+
76+
def generate_images(cells: list[list[int]], frames) -> list[Image.Image]:
77+
"""
78+
Generates a list of images of subsequent Game of Life states.
79+
"""
80+
images = []
81+
for _ in range(frames):
82+
# Create output image
83+
img = Image.new("RGB", (len(cells[0]), len(cells)))
84+
pixels = img.load()
85+
86+
# Save cells to image
87+
for x in range(len(cells)):
88+
for y in range(len(cells[0])):
89+
colour = 255 - cells[y][x] * 255
90+
pixels[x, y] = (colour, colour, colour)
91+
92+
# Save image
93+
images.append(img)
94+
cells = new_generation(cells)
95+
return images
96+
97+
98+
if __name__ == "__main__":
99+
images = generate_images(GLIDER, 16)
100+
images[0].save("out.gif", save_all=True, append_images=images[1:])

0 commit comments

Comments
 (0)