Skip to content

Instantly share code, notes, and snippets.

@mikelane
Last active March 21, 2022 22:52
Show Gist options
  • Save mikelane/89c580b7764f04cf73b32bf4e94fd3a3 to your computer and use it in GitHub Desktop.
Save mikelane/89c580b7764f04cf73b32bf4e94fd3a3 to your computer and use it in GitHub Desktop.
Conway's Game of Life implemented using a 2d convolution.
import click
import difflib
import numpy as np
import random
import sys
import time
from os import name, system
from scipy.ndimage import convolve
np.set_printoptions(threshold=20000, linewidth=350, formatter={"str_kind": lambda x: x})
def _clear_screen():
if name == "nt":
system("cls")
else:
system("clear")
def _print_board(board, mark="0"):
_clear_screen()
to_print = np.where(board == 1, mark, " ")
print(to_print)
def _is_similar(board, next_board, cutoff):
return np.sum(board == next_board) / board.size > cutoff
@click.command()
@click.option(
"--seed",
"board_seed",
type=click.Choice(["random", "gliders"]),
default="random",
help="How to initialize the board, default random",
)
@click.option(
"-w",
"--width",
"board_width",
type=int,
default=75,
help="board width, default 101 columns",
)
@click.option(
"-h",
"--height",
"board_height",
type=int,
default=37,
help="board height, default 51 rows",
)
@click.option(
"--stop_after",
"number_of_iterations",
default=float("inf"),
help="Stop after this many iterations, default is no limit",
)
@click.option(
"--sleep",
"sleep_duration",
type=float,
default=0.25,
help="How long to sleep before updating state",
)
@click.option(
"--alive_mark",
"mark",
type=str,
default="•",
help="What mark to use for a living cell",
)
def run_game(
board_width, board_height, board_seed, number_of_iterations, sleep_duration, mark
):
global boards
# Convolving on this kernel does a count of cells around the center
kernel = np.array([[1, 1, 1],
[1, 0, 1],
[1, 1, 1]], dtype=np.uint8)
# A well-known game of life stable, moving character
glider = np.array([[0, 1, 0],
[0, 0, 1],
[1, 1, 1]], dtype=np.uint8)
if board_seed == "gliders":
board = np.zeros(shape=(board_height, board_width), dtype=np.uint8)
h, w = glider.shape
num_gliders = board.size // (9 * 25)
for _ in range(num_gliders):
i, j = (
random.randint(0, board_height - h),
random.randint(0, board_width - w),
)
board[i : i + h, j : j + w] = glider
else:
board = np.random.randint(
0, 2, size=(board_height, board_width), dtype=np.uint8
)
_print_board(board, mark=mark)
count = 0
cutoff = 1
while count < number_of_iterations:
time.sleep(sleep_duration)
count += 1
# Run a single 2D convolutional filter over the board with constant 0 padding
convolved_board = convolve(board, kernel, mode="wrap")
# The kernel we used finds the sum of the 8 cells around a given cell
# So we can do a bit of fancy numpy work to get the next board
next_board = (
((board == 1) & (convolved_board > 1) & (convolved_board < 4))
| ((board == 0) & (convolved_board == 3))
).astype(np.uint8)
if count % 10 == 0:
cutoff -= 0.001
_print_board(next_board, mark=mark)
print(
f"count: {count}, cutoff: {cutoff:0.3f}, diff: {np.sum(board == next_board)}/{board.size} ({np.sum(board == next_board)/board.size:0.4f})"
)
if _is_similar(board, next_board, cutoff):
sys.exit(0)
board = next_board
if __name__ == "__main__":
run_game()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy