6
\$\begingroup\$

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:
 #print("adding 1 - ")
 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
 def deadCellCheck(self):
 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:
 my_game.board[a][b].deadCellCheck()
 input()
 my_game.drawbox()
Jamal
35.2k13 gold badges134 silver badges238 bronze badges
asked Dec 26, 2014 at 16:20
\$\endgroup\$
2
  • 1
    \$\begingroup\$ 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. \$\endgroup\$ Commented Dec 26, 2014 at 21:46
  • \$\begingroup\$ 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. \$\endgroup\$ Commented Dec 27, 2014 at 9:24

1 Answer 1

3
\$\begingroup\$

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.

(Source: https://stackoverflow.com/questions/22492162/understanding-the-main-method-of-python)

answered Dec 26, 2014 at 16:33
\$\endgroup\$
4
  • \$\begingroup\$ I've added that - how can I utilise the functionality that this statement adds? \$\endgroup\$ Commented Dec 28, 2014 at 16:34
  • \$\begingroup\$ 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? \$\endgroup\$ Commented Dec 28, 2014 at 18:25
  • \$\begingroup\$ 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! \$\endgroup\$ Commented Dec 29, 2014 at 7:17
  • \$\begingroup\$ 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 \$\endgroup\$ Commented Dec 29, 2014 at 10:20

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.