I'm a Python novice and decided to give Game of Life a try in Python 3:
import os
import time
import curses
class Cell:
newlabel = ''
def __init__(self,row,col,label):
self.row = row
self.col = col
self.label = label
def updatecell(self,celllist, boardsize):
liveneighbours = self.getliveneighbours(celllist, boardsize)
if liveneighbours < 2 or liveneighbours > 3: #Kills cell if it has <2 or >3 neighbours
self.newlabel = ' '
elif liveneighbours == 2 and self.label == ' ': #Cell doesn't come alive if it is dead and only has 2 neighbours
self.newlabel = ' '
else: #Cell comes alive if it is already alive and has 2/3 neighbours or if it is dead and has 3 neighbours
self.newlabel = 0
def getliveneighbours(self,celllist,boardsize):
count = 0
for row in range(self.row-1,self.row+2):
try:
celllist[row]
except: #Handles vertical wrapping. If there's an error, it checks the wrapped cell
if row < 0:
row += boardsize[0]
else:
row -= boardsize[0]
for col in range(self.col-1,self.col+2):
try: #Handles horizontal wrapping. If there's an error, it checks the wrapped cell
celllist[row][col]
except:
if col < 0:
col += boardsize[1]
else:
col -= boardsize[1]
if not celllist[row][col].label:
count += 1
if not self.label: #Subtracts the cell from the neighbours count
return count-1
else:
return count
def updatelabel(self): #Updates the cell's label after all cell updates have been processes
self.label = self.newlabel
class Board:
celllist = {} #Dict of rows
def __init__(self, rows, columns):
self.rows = rows
self.columns = columns
self.generate()
def printboard(self): #Prints the board to the terminal using curses
for num, row in self.celllist.items():
line = ''
for col, cell in enumerate(row):
line += str(cell.label)
terminal.addstr(num, 0, line)
terminal.refresh()
def generate(self): #Adds all the cells to the board
for row in range(self.rows-1):
self.celllist[row] = []
for col in range(self.columns):
self.celllist[row].append(Cell(row,col,' '))
def updateboard(self): #Prompts each cell to update and then update their label
for row in self.celllist:
for cell in self.celllist[row]:
cell.updatecell(self.celllist, (self.rows-1, self.columns))
for row in self.celllist:
for cell in self.celllist[row]:
cell.updatelabel()
if __name__ == "__main__":
terminal = curses.initscr() # Opens a curses window
curses.noecho()
curses.cbreak()
terminal.nodelay(1) #Don't wait for user input later
rows, columns = os.popen('stty size', 'r').read().split()
board = Board(int(rows), int(columns))
board.celllist[6][8].label = 0
board.celllist[7][9].label = 0
board.celllist[7][10].label = 0
board.celllist[8][8].label = 0
board.celllist[8][9].label = 0
while 1:
board.printboard()
time.sleep(0.1)
board.updateboard()
char = terminal.getch()
if char == 113: #Checks for ASCII Char code for q and then breaks loop
break
curses.endwin() #Closes curses window
I've written it to work based on your terminal size and I've made the sides wrap around because it was the only solution I could think of. Also, at the end, I've included a glider example as a test.
Questions:
Is my implementation pythonic enough (particularly concerning ranges and my constant use of iteration)?
Are the data structures that I've used for the celllist (arrays in a dict) suitable?
Is curses a good way of displaying the game? Would pyGame be better?
Is the overall code style good?
To further develop this, is there a better algorithm I could implement or a better method (other than wrapping the sides) of displaying all the cells?