# Object-oriented Conway's Game of Life

I have Conway's Game of Life working now and was hoping for direction on:

1. Unclear code - where would comments make it clearer?
2. Poor design choices - I already know of one in the countNeighbors method, as it needs knowledge of the game object's name
3. Improvements to make - I have already noted that I wish to add more templates and the ability to rotate them. What would be a good idea to implement that would improve/demonstrate a coding concept?
#TODO - automate the generation cyling
#TODO - allow rotation of templates
#TODO - generate more templates (gun, oscillator,LWSS etc)
#TODO - don't require knowledge of the board object in the methods of the cell object!

from tkinter import *
import time

class game:
"""An object to store the game"""

def __init__(self, size):
self.size = size
self.cell_size = 25
window = Tk()
self.canvas = Canvas(window, width=size * self.cell_size, height=size * self.cell_size)
self.canvas.pack()

self.board = [[cell(i, j) for i in range(size)] for j in range(size)]

return None

def drawbox(self):
for j in range(self.size):
for i in range(self.size):
if self.board[i][j].alive:
self.box_colour = "#220C65"
else:
self.box_colour = "#B7F3D5"

self.canvas.create_rectangle(self.cell_size * i, self.cell_size * j,self.cell_size * i + self.cell_size,
self.cell_size * j + self.cell_size, fill=self.box_colour, outline="#FFFFFF",width=2)

return

def createGlider(self, x, y):
self.board[x - 1][y - 1].alive = True
self.board[x][y - 1].alive = True
self.board[x + 1][y - 1].alive = True
self.board[x + 1][y].alive = True
self.board[x][y + 1].alive = True

def createGun(self,x,y):
self.board

class cell:
"""An object for the cells in Conway's Game of Life"""

def __init__(self, y, x):
self.x = x
self.y = y
self.alive = False

#self.num_of_neighbors = 0

def countNeighbors(self):
self.num_of_neighbors = 0
for j in [-1, 0, 1]:
for i in [-1, 0, 1]:
#print("Testing coordinate: " + str(self.x+i)+","+str(self.y+j))
if i == 0 and j == 0:
#print("Not counting self")
continue
elif (self.x + i) < 0 or (self.y + j < 0):
#print("Avoiding negative indexing")
continue
try:
if my_game.board[self.x + i][self.y + j].alive:
self.num_of_neighbors += 1
continue
else:
#print("passing over a blank square")
continue
except IndexError:
#print("Index error caught - attempted to go out of bounds")
continue

def livingCellCheck(self):
if self.num_of_neighbors in [2,3]:
self.alive = True
else:
self.alive = False

if self.num_of_neighbors == 3:
self.alive = True
else:
self.alive = False

######################################################################################

num_of_generations = int(input("How many generations do you wish to simulate?\n"))

my_game = game(20)

my_game.drawbox()

my_game.board[1][1].alive = True
my_game.board[1][2].alive = True
my_game.board[2][2].alive = True

my_game.createGlider(5, 10)
my_game.drawbox()

for _ in range(num_of_generations):
for a in range(my_game.size):
for b in range(my_game.size):
my_game.board[a][b].countNeighbors()
#print(my_game.board[a][b].x, my_game.board[a][b].y)
#print("number of neighbors: " + str(my_game.board[a][b].num_of_neighbors))
#print(my_game.board[a][b].alive)
#print("*x*x*x*x*x*x*x*x*x*x*x*x*x*x*x*x*x*x*x*x*x*x*x*x*x*x*x*x*x*x*x*x*")

for a in range(my_game.size):
for b in range(my_game.size):
#print(my_game.board[a][b].num_of_neighbors)
if my_game.board[a][b].alive:
my_game.board[a][b].livingCellCheck()
else:
input()

my_game.drawbox()

• Why do you insist on making it OO? OOP is not very well suited for this particular simulation and the program ends up being more complicated than it needs to. – Paul Dec 26 '14 at 21:46
• I had it written simply using individual functions to manage each part of the process, but I wanted to have a bit of a play around with OO - partially to show that I understand the basics and can apply them. – KBhas Dec 27 '14 at 9:24

In my opinion, I believe your code is pretty straight forward and doesn't need much extra comments (Perhaps to explain your steps in the 'countNeighbors' method).

Perhaps also consider to use

if __name__ == '__main__':


for the later part of your code (under the '####'),

which has two primary use cases:

• Allow a module to provide functionality for import into other code while also providing useful semantics as a standalone script (a command line wrapper around the functionality)

• Allow a module to define a suite of unit tests which are stored with (in the same file as) the code to be tested and which can be executed independently of the rest of the codebase.

• I've added that - how can I utilise the functionality that this statement adds? – KBhas Dec 28 '14 at 16:34
• You create a new function called def main(): in which you do what you already did, and at the bottom you add the line if name == 'main': where you call main(). This way your main() function will only be called when the python file itself is executed. Pretty easy, right? – DJanssens Dec 28 '14 at 18:25
• Weren't the content of my main function already only being used when the python file was ran? Is this designed to give me more flexibility moving forward? i.e. I can design and write unit tests (or simply cases I want to visualise in this instance) and throw it under the if__name__=='main' part? Sorry for not following first time - this stuff is outside what I've learned so far! – KBhas Dec 29 '14 at 7:17
• When you want to reuse your code in the future. eg:if you extend your class 'game' with '2playergame' in a new file(module), you probably want to import this file. However by importing it will also perform the global written code, in this example (the part underneath '######', which you don't want, since you just want to import and reuse a part of its functionality. The f__name__=='main' part will only be executed if the file is directly executed. It makes your code more reusable. If you have more questions, feel free to ask me in chat chat.stackexchange.com/rooms/8595/the-2nd-monitor – DJanssens Dec 29 '14 at 10:20