Last active
June 19, 2024 19:09
-
-
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!).
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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