Created
February 23, 2022 22:35
-
-
Save dbechrd/7034ba2ad91149411d70683aec5cc7f1 to your computer and use it in GitHub Desktop.
Game of Life
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
# 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 |
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
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