Skip to content

Instantly share code, notes, and snippets.

@dbechrd
Created February 23, 2022 22:35
Show Gist options
  • Save dbechrd/7034ba2ad91149411d70683aec5cc7f1 to your computer and use it in GitHub Desktop.
Save dbechrd/7034ba2ad91149411d70683aec5cc7f1 to your computer and use it in GitHub Desktop.
Game of Life
# Copyright 2022 Ben Groves & Dan Bechard @ Avvir.io
# MIT License
import os
import time
import random
from copy import deepcopy
def create_board(size):
board = [[0] * size for i in range(size)]
return board
def create_glider(board, x, y):
assert(len(board) >= 3)
board[y + 0][x] = 0; board[y + 0][x + 1] = 1; board[y + 0][x + 2] = 0
board[y + 1][x] = 0; board[y + 1][x + 1] = 0; board[y + 1][x + 2] = 1
board[y + 2][x] = 1; board[y + 2][x + 1] = 1; board[y + 2][x + 2] = 1
return board
def simulate_cell(cell, live_neighbors):
new_cell = cell
if cell == 1 and (live_neighbors < 2 or live_neighbors > 3):
new_cell = 0
elif cell == 0 and live_neighbors == 3:
new_cell = 1
return new_cell
def simulate(board):
assert(len(board) > 0)
assert(len(board) == len(board[0]))
new_board = deepcopy(board)
# For each cell in board
for y in range(0, len(board)):
for x in range(0, len(board)):
# TODO(dlb): Separate out into cell_live_neighbor_count and test
# For each neighbor of this cell
live_neighbors = 0
for y2 in range(y - 1, y + 2):
for x2 in range(x - 1, x + 2):
if y2 < 0: y2 += len(board)
if x2 < 0: x2 += len(board)
y2 %= len(board)
x2 %= len(board)
if (y2 != y or x2 != x) and board[y2][x2] == 1:
live_neighbors += 1
new_board[y][x] = simulate_cell(board[y][x], live_neighbors)
return new_board
def clear():
os.system('clear')
def create_starfield(size):
starfield = [[0] * size for i in range(size)]
for y in range(0, size):
for x in range(0, size):
starfield[y][x] = random.randint(1, 20) == 20
return starfield
def display(board, starfield, starfield_offset):
clear()
for y in range(0, len(board)):
for x in range(0, len(board)):
star_y = (y + starfield_offset) % len(board)
star_x = (x + starfield_offset) % len(board)
star = starfield[star_y][star_x]
cell = board[y][x]
print('■ ' if cell else ('* ' if star else ' '), end='')
print('')
if __name__ == "__main__":
board_size = 32
board = create_board(board_size)
create_glider(board,
random.randint(1, len(board) - 3),
random.randint(1, len(board) - 3)
)
starfield = create_starfield(board_size)
delay = 0.05
display(board, starfield, 0)
time.sleep(delay)
frame = 0
while True:
if frame % 50 == 0:
create_glider(board,
random.randint(1, len(board) - 3),
random.randint(1, len(board) - 3)
)
board = simulate(board)
display(board, starfield, frame % len(board))
time.sleep(delay)
frame += 1
from pynetest.expectations import expect
from pynetest.pyne_test_collector import describe, it, before_each
from pynetest.pyne_tester import pyne
import game_of_life
@pyne
def game_of_life_test():
@before_each
def _(self):
pass
@describe("#create_board")
def _():
@it("returns a board as 2D array for given size")
def _(self):
expect(game_of_life.create_board(5)).to_be(
[
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
]
)
@describe("#create_glider")
def _():
@it("adds a glider to top left corner of board")
def _(self):
board = game_of_life.create_board(5)
expect(game_of_life.create_glider(board, 1, 1)).to_be(
[
[0, 0, 0, 0, 0],
[0, 0, 1, 0, 0],
[0, 0, 0, 1, 0],
[0, 1, 1, 1, 0],
[0, 0, 0, 0, 0],
]
)
# Any live cell with fewer than two live neighbours dies.
# Any live cell with two or three live neighbours lives on to the next generation.
# Any live cell with more than three live neighbours dies, as if by overpopulation.
# Any dead cell with exactly three live neighbours becomes a live cell
@describe("#simulate_cell")
def _():
@describe("when cell is alive")
def _():
@describe("when cell has fewer than two live neighbors")
def _():
@it("dies")
def _(self):
expect(game_of_life.simulate_cell(1, 0)).to_be(0)
expect(game_of_life.simulate_cell(1, 1)).to_be(0)
@describe("when cell has two or three live neighbors")
def _():
@it("lives on")
def _(self):
expect(game_of_life.simulate_cell(1, 2)).to_be(1)
expect(game_of_life.simulate_cell(1, 3)).to_be(1)
@describe("when cell has more than three live neighbors")
def _():
@it("lives on")
def _(self):
expect(game_of_life.simulate_cell(1, 4)).to_be(0)
expect(game_of_life.simulate_cell(1, 5)).to_be(0)
expect(game_of_life.simulate_cell(1, 6)).to_be(0)
expect(game_of_life.simulate_cell(1, 7)).to_be(0)
expect(game_of_life.simulate_cell(1, 8)).to_be(0)
@describe("when cell is dead")
def _():
@describe("when cell has exactly 3 live neighbors")
def _():
@it("is born")
def _(self):
expect(game_of_life.simulate_cell(0, 3)).to_be(1)
@describe("when cell does not have exactly 3 live neighbors")
def _():
@it("is born")
def _(self):
expect(game_of_life.simulate_cell(0, 0)).to_be(0)
expect(game_of_life.simulate_cell(0, 1)).to_be(0)
expect(game_of_life.simulate_cell(0, 2)).to_be(0)
# note: case 3 missing on purpose
expect(game_of_life.simulate_cell(0, 4)).to_be(0)
expect(game_of_life.simulate_cell(0, 5)).to_be(0)
expect(game_of_life.simulate_cell(0, 6)).to_be(0)
expect(game_of_life.simulate_cell(0, 7)).to_be(0)
expect(game_of_life.simulate_cell(0, 8)).to_be(0)
@describe("#simulate")
def _():
@it("glider proceeds to next frame")
def _(self):
# NOTE(dlb): Because we added silly infinite wrapping, this test
# needs extra empty cells around it to pass according to the game's
# original rules.
board = [
[0, 0, 0, 0, 0],
[0, 0, 1, 0, 0],
[0, 0, 0, 1, 0],
[0, 1, 1, 1, 0],
[0, 0, 0, 0, 0]
]
expect(game_of_life.simulate(board)).to_be(
[
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 1, 0, 1, 0],
[0, 0, 1, 1, 0],
[0, 0, 1, 0, 0]
]
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment