Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 19 additions & 68 deletions cellular_automata/conways_game_of_life.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
"""
Conway's Game of Life implemented in Python.
https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life
"""
"""Conway's Game of Life implemented in Python.
https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life"""

from __future__ import annotations

from PIL import Image

# Define glider example
GLIDER = [

Check failure on line 7 in cellular_automata/conways_game_of_life.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (I001)

cellular_automata/conways_game_of_life.py:4:1: I001 Import block is un-sorted or un-formatted
[0, 1, 0, 0, 0, 0, 0, 0],
[0, 0, 1, 0, 0, 0, 0, 0],
[1, 1, 1, 0, 0, 0, 0, 0],
Expand All @@ -19,80 +15,35 @@
[0, 0, 0, 0, 0, 0, 0, 0],
]

# Define blinker example
BLINKER = [[0, 1, 0], [0, 1, 0], [0, 1, 0]]

BLINKER = [
[0, 1, 0],
[0, 1, 0],
[0, 1, 0]
]

def new_generation(cells: list[list[int]]) -> list[list[int]]:
"""
Generates the next generation for a given state of Conway's Game of Life.
>>> new_generation(BLINKER)
[[0, 0, 0], [1, 1, 1], [0, 0, 0]]
"""
next_generation = []
for i in range(len(cells)):
next_generation_row = []
for j in range(len(cells[i])):
# Get the number of live neighbours
neighbour_count = 0
if i > 0 and j > 0:
neighbour_count += cells[i - 1][j - 1]
if i > 0:
neighbour_count += cells[i - 1][j]
if i > 0 and j < len(cells[i]) - 1:
neighbour_count += cells[i - 1][j + 1]
if j > 0:
neighbour_count += cells[i][j - 1]
if j < len(cells[i]) - 1:
neighbour_count += cells[i][j + 1]
if i < len(cells) - 1 and j > 0:
neighbour_count += cells[i + 1][j - 1]
if i < len(cells) - 1:
neighbour_count += cells[i + 1][j]
if i < len(cells) - 1 and j < len(cells[i]) - 1:
neighbour_count += cells[i + 1][j + 1]

# Rules of the game of life (excerpt from Wikipedia):
# 1. Any live cell with two or three live neighbours survives.
# 2. Any dead cell with three live neighbours becomes a live cell.
# 3. All other live cells die in the next generation.
# Similarly, all other dead cells stay dead.
alive = cells[i][j] == 1
if (
(alive and 2 <= neighbour_count <= 3)
or not alive
and neighbour_count == 3
):
next_generation_row.append(1)
else:
next_generation_row.append(0)

next_generation.append(next_generation_row)
return next_generation

"""Generates the next generation for a given state of Conway's Game of Life."""
next_gen = [[0] * len(row) for row in cells]
for i, row in enumerate(cells):
for j, cell in enumerate(row):
neighbours = sum(cells[x][y] for x in range(i-1, i+2) for y in range(j-1, j+2) if 0 <= x < len(cells) and 0 <= y < len(row) and (x != i or y != j))

Check failure on line 29 in cellular_automata/conways_game_of_life.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (E501)

cellular_automata/conways_game_of_life.py:29:89: E501 Line too long (159 > 88)
next_gen[i][j] = 1 if cell == 1 and 2 <= neighbours <= 3 or cell == 0 and neighbours == 3 else 0

Check failure on line 30 in cellular_automata/conways_game_of_life.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (E501)

cellular_automata/conways_game_of_life.py:30:89: E501 Line too long (108 > 88)
return next_gen

def generate_images(cells: list[list[int]], frames: int) -> list[Image.Image]:
"""
Generates a list of images of subsequent Game of Life states.
"""
"""Generates a list of images of subsequent Game of Life states."""
images = []
for _ in range(frames):
# Create output image
img = Image.new("RGB", (len(cells[0]), len(cells)))
pixels = img.load()

# Save cells to image
for x in range(len(cells)):
for y in range(len(cells[0])):
colour = 255 - cells[y][x] * 255
pixels[x, y] = (colour, colour, colour)

# Save image
for x, row in enumerate(cells):
for y, cell in enumerate(row):
colour = 255 - cell * 255
pixels[y, x] = (colour, colour, colour)
images.append(img)
cells = new_generation(cells)
return images


if __name__ == "__main__":
images = generate_images(GLIDER, 16)
images[0].save("out.gif", save_all=True, append_images=images[1:])
Loading