I've just finished implementing Conway's Game of Life in python-3.6. Here's the code:
To run the code, just invoke python3.6 conway.py
. The board shape is fixed; but the configuration is randomly generated with numpyNumPy.
My aim with this project was:
- implementImplement a concise and pythonic version of the algorithm
- getGet better with python'sPython's OOP constructs
- killKill time :-)
I'd appreciate any feedback, criticism and suggestions here.
My next step with this program would likely be to host it on a server and let people run it on their browser with a Step and Run button. This might influence how I'd need to structure my code. Thanks!
Changelog:
- removed the
Cell
class, it wasn't necessary. - reorganise code
- add comments and type hints
I've just finished implementing Conway's Game of Life in python-3.6. Here's the code:
To run the code, just invoke python3.6 conway.py
. The board shape is fixed; but the configuration is randomly generated with numpy.
My aim with this project was
- implement a concise and pythonic version of the algorithm
- get better with python's OOP constructs
- kill time :-)
I'd appreciate any feedback, criticism and suggestions here.
My next step with this program would likely be to host it on a server and let people run it on their browser with a Step and Run button. This might influence how I'd need to structure my code. Thanks!
Changelog:
- removed the
Cell
class, it wasn't necessary. - reorganise code
- add comments and type hints
I've just finished implementing Conway's Game of Life in python-3.6:
To run the code, just invoke python3.6 conway.py
. The board shape is fixed; but the configuration is randomly generated with NumPy.
My aim with this project was:
- Implement a concise and pythonic version of the algorithm
- Get better with Python's OOP constructs
- Kill time :-)
My next step with this program would likely be to host it on a server and let people run it on their browser with a Step and Run button. This might influence how I'd need to structure my code.
fromimport enumcurses
import numpy as np
import Enumtime
from collections import defaultdict
importfrom curses
enum import numpyEnum
from astyping np
import timeList
class State(Enum):
''' enum class to represent possible cell states '''
dead = 0
alive = 1
class Board:
''' Board class to represent the game board '''
def __init__(self, m : int, n : int, init : List[List[int]]):
self.m = m # the self.nnumber =of nrows
self.sizen = mn * n # the number of columns
self.board_ = [
[State(init[i][j]) for j in range(self.n)] for i in range(self.m)
]
def __str__(self) -> str:
''' return the __str__ representation of a Board object
* represents a live cell, and a space represents a dead one
'''
return '\n'.join([
''.join(
['*' if cell.value else ' ' for cell in row]
) for row in self.board_]
)
@property
def population(self):
''' population — the number of live cells on the board '''
return sum(cell.value for row in self.board_ for cell in row)
def count_live_neighbours(self, x : int, y : int) -> int:
''' count the live neighbours of a cell '''
count = 0
for i in range(x - 1, x + 2):
for j in range(y - 1, y + 2):
if (i == x and j == y) or i < 0 or j < 0:
continue
# handle IndexErrors raised during invalid indexing operations
try:
count += self.board_[i][j].value
except IndexError:
continue
return count
def next_cell_state(self, x : int, y : int) -> State:
count = self.count_live_neighbours(x, y)
cur_state = self.board_[x][y]
# determine the next state based on the current state and
# number of live neighbours
if count in {2, 3} and cur_state == State.alive:
return cur_state
elif count == 3 and cur_state == State.dead:
return State.alive
return State.dead
def next_board_state(self) -> List[List[State]]:
''' return board configuration for the next state '''
return [
[self.next_cell_state(i, j) for j in range(self.n)] for i in range(self.m)
]
def advance_state(self):
self.board_''' =update self.next_board_state()
the board configuration with defthe has_live_cells(self):
config for the next state '''
return any(cell.value for row in self.board_ for cell in= rowself.next_board_state()
@property
def populationhas_live_cells(self):
-> bool:
return sum(cell.value for row in''' self.board_return forwhether cellthere inare row)
any live cells or defnot __str__(self):'''
return '\n'.join(
[''.joinany(['*' if cell.value else ' ' for cell in row]) for row in self.board_]
board_ for cell in row)
if __name__ == '__main__':
arr = np.random.choice([0, 1], (20, 100), p=[0.90, 0.1]) #np.loadtxt('glider.dat', dtype=int)
step = 0
board = Board(arr.shape[0], arr.shape[1], init=arr.tolist())
step = 0
while board.has_live_cells():
step += 1
print(board)
print('f{step,} {board.population}')
board.advance_state()
time.sleep(.1)
Changelog: removed the Cell
class, it wasn't necessary.
- removed the
Cell
class, it wasn't necessary. - reorganise code
- add comments and type hints
from enum import Enum
from collections import defaultdict
import curses
import numpy as np
import time
class State(Enum):
dead = 0
alive = 1
class Board:
def __init__(self, m, n, init):
self.m = m self.n = n
self.size = m * n
self.board_ = [
[State(init[i][j]) for j in range(self.n)] for i in range(self.m)
]
def count_live_neighbours(self, x, y):
count = 0
for i in range(x - 1, x + 2):
for j in range(y - 1, y + 2):
if i == x and j == y or i < 0 or j < 0:
continue
try:
count += self.board_[i][j].value
except IndexError:
continue
return count
def next_cell_state(self, x, y):
count = self.count_live_neighbours(x, y)
cur_state = self.board_[x][y]
if count in {2, 3} and cur_state == State.alive:
return cur_state
elif count == 3 and cur_state == State.dead:
return State.alive
return State.dead
def next_board_state(self):
return [
[self.next_cell_state(i, j) for j in range(self.n)] for i in range(self.m)
]
def advance_state(self):
self.board_ = self.next_board_state()
def has_live_cells(self):
return any(cell.value for row in self.board_ for cell in row)
@property
def population(self):
return sum(cell.value for row in self.board_ for cell in row)
def __str__(self):
return '\n'.join(
[''.join(['*' if cell.value else ' ' for cell in row]) for row in self.board_]
)
if __name__ == '__main__':
arr = np.random.choice([0, 1], (20, 100), p=[0.90, 0.1]) #np.loadtxt('glider.dat', dtype=int)
step = 0
board = Board(arr.shape[0], arr.shape[1], init=arr.tolist())
while board.has_live_cells():
step += 1
print(board)
print(step, board.population)
board.advance_state()
time.sleep(.1)
Changelog: removed the Cell
class, it wasn't necessary.
import curses
import numpy as np
import time
from collections import defaultdict
from enum import Enum
from typing import List
class State(Enum):
''' enum class to represent possible cell states '''
dead = 0
alive = 1
class Board:
''' Board class to represent the game board '''
def __init__(self, m : int, n : int, init : List[List[int]]):
self.m = m # the number of rows
self.n = n # the number of columns
self.board_ = [
[State(init[i][j]) for j in range(self.n)] for i in range(self.m)
]
def __str__(self) -> str:
''' return the __str__ representation of a Board object
* represents a live cell, and a space represents a dead one
'''
return '\n'.join([
''.join(
['*' if cell.value else ' ' for cell in row]
) for row in self.board_]
)
@property
def population(self):
''' population — the number of live cells on the board '''
return sum(cell.value for row in self.board_ for cell in row)
def count_live_neighbours(self, x : int, y : int) -> int:
''' count the live neighbours of a cell '''
count = 0
for i in range(x - 1, x + 2):
for j in range(y - 1, y + 2):
if (i == x and j == y) or i < 0 or j < 0:
continue
# handle IndexErrors raised during invalid indexing operations
try:
count += self.board_[i][j].value
except IndexError:
continue
return count
def next_cell_state(self, x : int, y : int) -> State:
count = self.count_live_neighbours(x, y)
cur_state = self.board_[x][y]
# determine the next state based on the current state and
# number of live neighbours
if count in {2, 3} and cur_state == State.alive:
return cur_state
elif count == 3 and cur_state == State.dead:
return State.alive
return State.dead
def next_board_state(self) -> List[List[State]]:
''' return board configuration for the next state '''
return [
[self.next_cell_state(i, j) for j in range(self.n)] for i in range(self.m)
]
def advance_state(self):
''' update the board configuration with the config for the next state '''
self.board_ = self.next_board_state()
def has_live_cells(self) -> bool:
''' return whether there are any live cells or not '''
return any(cell.value for row in self.board_ for cell in row)
if __name__ == '__main__':
arr = np.random.choice([0, 1], (20, 100), p=[0.90, 0.1])
board = Board(arr.shape[0], arr.shape[1], init=arr.tolist())
step = 0
while board.has_live_cells():
step += 1
print(board)
print('f{step} {board.population}')
board.advance_state()
time.sleep(.1)
Changelog:
- removed the
Cell
class, it wasn't necessary. - reorganise code
- add comments and type hints
from enum import Enum
from collections import defaultdict
import curses
import numpy as np
import time
class State(Enum):
dead = 0
alive = 1
class Cell:
def __init__(self, x, y, state):
self.x = x
self.y = y
self.state = State.dead if not state else state
self.visited = False
class Board:
def __init__(self, m, n, init):
self.m = m
self.n = n
self.size = m * n
self.board_ = []
[
for i in range [State(self.minit[i][j]):
for j in range(self.board_.append([]n)] for ji in range(self.nm):
self.board_[-1].append(Cell(i, j, state=State(init[i][j])))]
def count_live_neighbours(self, x, y):
count = 0
for i in range(x - 1, x + 2):
for j in range(y - 1, y + 2):
if i == x and j == y or i < 0 or j < 0:
continue
try:
count += self.board_[i][j].state.value
except IndexError:
continue
return count
def next_cell_state(self, x, y):
count = self.count_live_neighbours(x, y)
cur_state = self.board_[x][y].state
if count in {2, 3} and cur_state == State.alive:
return cur_state
elif count == 3 and cur_state == State.dead:
return State.alive
return State.dead
def next_board_state(self):
return [
[Cell(i, j, self[self.next_cell_state(i, j)) for j in range(self.n)] for i in range(self.m)
]
def advance_state(self):
self.board_ = self.next_board_state()
def has_live_cells(self):
return any(cell.state.value for row in self.board_ for cell in row)
@property
def population(self):
return sum(cell.state.value for row in self.board_ for cell in row)
def __str__(self):
return '\n'.join(
[''.join(['*' if cell.state.value else ' ' for cell in row]) for row in self.board_]
)
if __name__ == '__main__':
arr = np.random.choice([0, 1], (20, 100), p=[0.90, 0.1]) #np.loadtxt('glider.dat', dtype=int)
step = 0
board = Board(arr.shape[0], arr.shape[1], init=arr.tolist())
while board.has_live_cells():
print(board)step += 1
board.advance_stateprint(board)
print(step +=, 1board.population)
print(step, board.populationadvance_state()
time.sleep(.1)
My next step with this program would likely be to host it on a server and let people run it on their browser with a Step and Run button. This might influence how I'd need to structure my code. Thanks!
Changelog: removed the Cell
class, it wasn't necessary.
from enum import Enum
from collections import defaultdict
import numpy as np
import time
class State(Enum):
dead = 0
alive = 1
class Cell:
def __init__(self, x, y, state):
self.x = x
self.y = y
self.state = State.dead if not state else state
self.visited = False
class Board:
def __init__(self, m, n, init):
self.m = m
self.n = n
self.size = m * n
self.board_ = []
for i in range(self.m):
self.board_.append([]) for j in range(self.n):
self.board_[-1].append(Cell(i, j, state=State(init[i][j])))
def count_live_neighbours(self, x, y):
count = 0
for i in range(x - 1, x + 2):
for j in range(y - 1, y + 2):
if i == x and j == y or i < 0 or j < 0:
continue
try:
count += self.board_[i][j].state.value
except IndexError:
continue
return count
def next_cell_state(self, x, y):
count = self.count_live_neighbours(x, y)
cur_state = self.board_[x][y].state
if count in {2, 3} and cur_state == State.alive:
return cur_state
elif count == 3 and cur_state == State.dead:
return State.alive
return State.dead
def next_board_state(self):
return [
[Cell(i, j, self.next_cell_state(i, j)) for j in range(self.n)] for i in range(self.m)
]
def advance_state(self):
self.board_ = self.next_board_state()
def has_live_cells(self):
return any(cell.state.value for row in self.board_ for cell in row)
@property
def population(self):
return sum(cell.state.value for row in self.board_ for cell in row)
def __str__(self):
return '\n'.join(
[''.join(['*' if cell.state.value else ' ' for cell in row]) for row in self.board_]
)
if __name__ == '__main__':
arr = np.random.choice([0, 1], (20, 100), p=[0.90, 0.1])
step = 0
board = Board(arr.shape[0], arr.shape[1], init=arr.tolist())
while board.has_live_cells():
print(board)
board.advance_state()
step += 1
print(step, board.population)
time.sleep(.1)
My next step with this program would likely be to host it on a server and let people run it on their browser with a Step and Run button. This might influence how I'd need to structure my code. Thanks!
from enum import Enum
from collections import defaultdict
import curses
import numpy as np
import time
class State(Enum):
dead = 0
alive = 1
class Board:
def __init__(self, m, n, init):
self.m = m
self.n = n
self.size = m * n
self.board_ = [
[State(init[i][j]) for j in range(self.n)] for i in range(self.m)
]
def count_live_neighbours(self, x, y):
count = 0
for i in range(x - 1, x + 2):
for j in range(y - 1, y + 2):
if i == x and j == y or i < 0 or j < 0:
continue
try:
count += self.board_[i][j].value
except IndexError:
continue
return count
def next_cell_state(self, x, y):
count = self.count_live_neighbours(x, y)
cur_state = self.board_[x][y]
if count in {2, 3} and cur_state == State.alive:
return cur_state
elif count == 3 and cur_state == State.dead:
return State.alive
return State.dead
def next_board_state(self):
return [
[self.next_cell_state(i, j) for j in range(self.n)] for i in range(self.m)
]
def advance_state(self):
self.board_ = self.next_board_state()
def has_live_cells(self):
return any(cell.value for row in self.board_ for cell in row)
@property
def population(self):
return sum(cell.value for row in self.board_ for cell in row)
def __str__(self):
return '\n'.join(
[''.join(['*' if cell.value else ' ' for cell in row]) for row in self.board_]
)
if __name__ == '__main__':
arr = np.random.choice([0, 1], (20, 100), p=[0.90, 0.1]) #np.loadtxt('glider.dat', dtype=int)
step = 0
board = Board(arr.shape[0], arr.shape[1], init=arr.tolist())
while board.has_live_cells():
step += 1
print(board)
print(step, board.population)
board.advance_state()
time.sleep(.1)
My next step with this program would likely be to host it on a server and let people run it on their browser with a Step and Run button. This might influence how I'd need to structure my code. Thanks!
Changelog: removed the Cell
class, it wasn't necessary.