Created
August 14, 2018 19:10
-
-
Save anabarasan/d222d67dfc4737b598c8b44c5decbb05 to your computer and use it in GitHub Desktop.
Conway's 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
#!/usr/bin/env python3 | |
from argparse import ArgumentParser | |
from enum import Enum | |
from sys import argv | |
from time import sleep | |
BLOCK = [(5,5), (5,6), (6,5), (6,6)] | |
BLINKER = [(4,4), (4,5), (4,6)] | |
BEACON = [(4,4), (4,5), (5,4), (5,5), (6,6), (6,7), (7,6), (7,7)] | |
GLIDER = [(1,2), (2,3), (3,1), (3,2), (3,3)] | |
CUSTOM = [] | |
def clear_screen(): | |
print(chr(27)+'[2j') | |
print('\033c') | |
print('\x1bc') | |
class Patterns(Enum): | |
block = "BLOCK" | |
blinker = "BLINKER" | |
beacon = "BEACON" | |
glider = "GLIDER" | |
custom = "CUSTOM" | |
def __str__(self): | |
return self.value | |
class Extinction(Exception): | |
pass | |
class Cell: | |
"""Co-ordinates start from 1,1 not 0,0""" | |
def __init__(self, x, y): | |
self.x = x | |
self.y = y | |
self.alive = False | |
def __repr__(self): | |
return "(%d,%d)" % (self.x, self.y) | |
def __eq__(self, cell): | |
return self.x == cell.x and self.y == cell.y | |
def neighbours(self): | |
n = (self.x-1, self.y) | |
s = (self.x+1, self.y) | |
e = (self.x, self.y+1) | |
w = (self.x, self.y-1) | |
nw = (self.x-1, self.y-1) | |
ne = (self.x-1, self.y+1) | |
sw = (self.x+1, self.y-1) | |
se = (self.x+1, self.y+1) | |
return [nw, n, ne, w, e, sw, s, se] | |
class GameOfLife: | |
def __init__(self, size=10, start_sequence=None): | |
self.size = size | |
self.start_sequence = start_sequence if start_sequence is not None else [] | |
self.create_grid() | |
self._generation = 0 | |
self._display() | |
def create_grid(self): | |
grid = [] | |
for row in range(1, self.size+1): | |
for col in range(1, self.size+1): | |
cell = Cell(row, col) | |
if (row, col) in self.start_sequence: | |
cell.alive = True | |
grid.append(cell) | |
self.grid = grid | |
def _display(self): | |
clear_screen() | |
print("Generation : %d" % (self._generation,)) | |
for idx, cell in enumerate(self.grid): | |
if (idx % self.size) == 0: | |
print("") | |
print(1 if cell.alive else ' ', end=' ') | |
print("") | |
def _get_cell(self, target): | |
for cell in self.grid: | |
if cell == target: | |
return cell | |
def _is_cell_alive_in_grid(self, cell): | |
neighbours = cell.neighbours() | |
status = [] | |
for row, col in neighbours: | |
if (0 < row < self.size+1) and (0 < col < self.size+1): | |
neighbour = self._get_cell(Cell(row, col)) | |
status.append(neighbour.alive) | |
else: | |
status.append(False) | |
alive_neighbours = status.count(True) | |
if cell.alive: # a live cell | |
if alive_neighbours < 2 or alive_neighbours > 3: | |
alive = False # isolation / over population | |
else: | |
alive = True # lives on | |
else: # dead cell | |
if alive_neighbours == 3: # reproduction | |
alive = True | |
else: # baren | |
alive = False | |
cell = Cell(cell.x, cell.y) | |
cell.alive = alive | |
# if cell == Cell(3,5) or cell == Cell(4,5) or cell == Cell(5,5): | |
# print("*"*80, "%s:%s => %s ==> %s" % (cell, cell.alive, status, msg), "*"*80, sep="\n") | |
return cell | |
def next_generation(self): | |
grid = [] | |
for row in range(1, self.size+1): | |
for col in range(1, self.size+1): | |
cell = self._get_cell(Cell(row, col)) | |
cell = self._is_cell_alive_in_grid(cell) | |
grid.append(cell) | |
if grid == self.grid and not any([cell.alive for cell in self.grid]): | |
raise Extinction | |
self.grid = grid | |
self._generation += 1 | |
self._display() | |
def parse_args(args): | |
parser = ArgumentParser() | |
parser.add_argument('-g', '--generations', type=int, | |
help="Number of generations to display", | |
default=10) | |
parser.add_argument('-s', '--size', type=int, help="Grid size", default=20) | |
parser.add_argument('-p', '--pattern', type=Patterns, | |
help="Starting pattern", choices=list(Patterns)) | |
args = parser.parse_args(args) | |
return args.generations, args.size, args.pattern | |
def get_user_pattern(): | |
while ip: | |
ip = input("Enter Coordinate x,y. Leave empty to finish") | |
ip = ip.strip() | |
coords = ip.split(",") | |
coords = tuple([int(i) for i in coords]) | |
CUSTOM.append(coords) | |
def main(args): | |
generations, size, pattern = parse_args(args) | |
pattern = globals()[str(pattern)] | |
if not pattern: | |
pattern = get_user_pattern() | |
game = GameOfLife(size=size, start_sequence=pattern) | |
sleep(2) | |
for i in range(generations): | |
try: | |
game.next_generation() | |
except Extinction: | |
clear_screen() | |
print("Extinction!") | |
input() # wait for user to press a key | |
break | |
sleep(2) | |
sleep(10) | |
clear_screen() | |
if __name__ == '__main__': | |
main(argv[1:]) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment