Skip to content

Instantly share code, notes, and snippets.

@the-bokya
Last active June 19, 2024 19:09
Show Gist options
  • Save the-bokya/6b198ad13a10a8998dbf4f7924bc2d9c to your computer and use it in GitHub Desktop.
Save the-bokya/6b198ad13a10a8998dbf4f7924bc2d9c to your computer and use it in GitHub Desktop.
A parallel implementation of Conway's Game of Life in Python for the terminal (it makes those cute gliders sometimes!).
import os
import random
import time
import copy
# import threading
class Game:
def __init__(self, n: int):
self.cols, self.rows = os.get_terminal_size() # self-explanatory
random_value = lambda n: random.randint(0, n - 1) == (
n - 1
) # Every nth cell is alive
self.grid = [
[random_value(n) for col in range(self.cols)] for row in range(self.rows)
] # initialise the grid
self.prev = copy.deepcopy(self.grid)
def get_cell_state(self, row: int, col: int) -> bool:
return self.grid[row][col]
def get_prev_cell_state(self, row: int, col: int) -> bool:
return self.prev[row][col]
def set_cell_state(self, row: int, col: int, state: bool):
self.grid[row][col] = state
def get_neighbours(self, row: int, col: int) -> int:
neighbours = 0
for i in [-1, 0, 1]:
for j in [-1, 0, 1]:
current_row = i + row
current_col = j + col
if (
(i == 0 and j == 0)
or (current_col < 0 or current_col > self.cols - 1)
or (current_row < 0 or current_row > self.rows - 1)
): # skip if on the current cell or out of bounds
continue
neighbours += int(
self.get_prev_cell_state(current_row, current_col)
) # add all neighbours with living (True) states
return neighbours
def get_next_cell_state(self, row: int, col: int) -> bool:
is_alive = self.get_cell_state(row, col)
neighbours = self.get_neighbours(row, col)
# Check https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life#Rules for this
if is_alive:
if 2 <= neighbours <= 3:
return True
return False
elif neighbours == 3:
return True
return is_alive
def set_next_game_state(self):
self.prev = copy.deepcopy(self.grid)
def _do_row(row):
for col in range(self.cols):
next_state = self.get_next_cell_state(row, col)
self.set_cell_state(row, col, next_state)
"""
# embarrassingly parallelizable implementation
threads = []
for row in range(self.rows):
thread = threading.Thread(target=_do_row, args=(row,))
thread.start()
threads.append(thread)
for thread in threads:
thread.join()
"""
# turns out serial execution is faster
for row in range(self.rows):
_do_row(row)
def display(self):
os.system("clear")
out = []
for row in self.grid:
"""
Convert all alive states to "■" and the dead ones to empty space " " on screen
"""
current_string = "".join(["■" if col else " " for col in row])
out.append(current_string)
print("\n".join(out)) # join the rows into a single string
def play(self, tick: float):
while True:
time.sleep(tick)
self.display()
self.set_next_game_state()
game = Game(10) # sweet spot for n
game.play(0.2)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment