3

I am trying to program a version of conway's game of life however I keep getting the message 'Cell' Object Has no attribute 'nextState' when it seems like it should declare the value of nextState before asking to reference it. here is my code:

from tkinter import *
root = Tk()

class Cell (Button):
    Dead = 0
    Live = 1

    def __init__ (self,parent):
        Button.__init__(self,parent, relief = "raised" , width = 2 , borderwidth = 1 , command = self.onpress)
        self.displayState(Cell.Dead)

    def onpress (self):
        if self.state == Cell.Live:
            self.displayState(Cell.Dead)
        elif self.state == Cell.Dead:
            self.displayState(Cell.Live)

    def setNextState (self , Neighbours):
        if self.state == Cell.Live and (Neighbours < 2 or Neighbours > 3):
            self.nextState = Cell.Dead
        elif self.state == Cell.Dead and Neighbours == 3:
            self.nextState = Cell.Live
        elif self.state == Cell.Dead and Neighbours != 3:
            self.nextState = self.state

    def stepToNextState(self):
        self.displayState(self.nextState)

    def displayState (self , newstate):
        self.state = newstate
        if self.state == Cell.Live:
            self["bg"] = "black"
        if self.state == Cell.Dead:
            self["bg"] = "white"

class Grid:
    def __init__(self,parent,sizex,sizey):
        self.sizex = sizex
        self.sizey = sizey
        self.cells = []
        for a in range (0,self.sizex):
            rowcells = []
            for b in range (0, self.sizey):
                c = Cell(parent)
                c.grid(row=b , column=a)
                rowcells.append(c)
            self.cells.append(rowcells)

    def step (self):
        cells = self.cells
        for x in range (0,self.sizex):
            if x==0: x_down = self.sizex-1
            else: x_down = x-1
            if x==self.sizex-1: x_up = 0
            else: x_up = x+1
            for y in range(0,self.sizey):
                if y==0: y_down = self.sizey-1
                else: Y_down = y-1
                if y==self.sizey-1: y_up = 0
                else: y_up = y+1
                sum = cells[x_down][y].state + cells[x_up][y].state + cells[x][y_down].state + cells[x][y_up].state + cells[x_down][y_down].state +cells[x_up][y_up].state + cells[x_down][y_up].state + cells[x_up][y_down].state
                cells[x][y].setNextState(sum)
            for row in cells:
                for cell in row:
                    cell.stepToNextState()

    def clear(self):
        for row in self.cells:
            for cell in row:
                cell.displayState(Cell.Dead)

if __name__ == "__main__":
    frame = Frame(root)
    frame.pack()
    grid = Grid(frame,25,25)
    bottomFrame = Frame(root)
    bottomFrame.pack (side = BOTTOM)
    buttonStep = Button(bottomFrame , text="Step" , command=grid.step)
    buttonStep.pack(side = LEFT)
    buttonClear = Button(bottomFrame, text = "Clear", command=grid.clear)
    buttonClear.pack(side=LEFT , after=buttonStep)
    root.mainloop()

The error message is:

Exception in Tkinter callback
Traceback (most recent call last):
  File "C:\Python33\lib\tkinter\__init__.py", line 1442, in __call__
    return self.func(*args)
  File "C:\Users\Owner\Desktop\Other\Programs & Misc\Python\Tyler's Game of Life.py", line 65, in step
    cell.stepToNextState()
  File "C:\Users\Owner\Desktop\Other\Programs & Misc\Python\Tyler's Game of Life.py", line 27, in stepToNextState
    self.displayState(self.nextState)
AttributeError: 'Cell' object has no attribute 'nextState'

If anyone could point out where the error is occuring / what is causing it and possibly a way to fix it I would be very grateful.

Alok
  • 2,629
  • 4
  • 28
  • 40
Chubzorz
  • 135
  • 1
  • 2
  • 7
  • 1
    Looks like you have lots of indentation problems, and correct indentation is critical in Python. Is that just an error in copying to SO? – Barmar Apr 03 '14 at 04:24
  • I indented your question & after that if was working fine... – Alok Apr 03 '14 at 04:27
  • 1
    @shaktimaan If his problem is with indentation, don't correct it in the question -- post it as an answer. – Barmar Apr 03 '14 at 04:28
  • @Barmar: I just indented the question for other people to understand & help solve the issue,after that i ran it & voila...didn't knew it was the only issue – Alok Apr 03 '14 at 04:31
  • That was the point of my question in the first comment. It's important to know whether his actual code is indented correctly or not. In the case of Python, indentation isn't just a readability issue, it affects whether the code is correct or not. – Barmar Apr 03 '14 at 04:37
  • okay,thanks sir..will keep that in mind :) – Alok Apr 03 '14 at 04:38
  • All of the indentation errors are just from copying to stack overflow and not from the actual program. – Chubzorz Apr 03 '14 at 13:48

1 Answers1

2

The issue appears to be that your for row in cells loop is inside of your previous for x in range(0, self.sizex) loop. Here's what the step method should look like if you get it correctly indented:

def step (self):
    cells = self.cells
    for x in range (0,self.sizex):
        if x==0: x_down = self.sizex-1
        else: x_down = x-1
        if x==self.sizex-1: x_up = 0
        else: x_up = x+1
        for y in range(0,self.sizey):
            if y==0: y_down = self.sizey-1
            else: Y_down = y-1
            if y==self.sizey-1: y_up = 0
            else: y_up = y+1
            sum = cells[x_down][y].state + cells[x_up][y].state + cells[x][y_down].state + cells[x][y_up].state + cells[x_down][y_down].state +cells[x_up][y_up].state + cells[x_down][y_up].state + cells[x_up][y_down].state
            cells[x][y].setNextState(sum)
    for row in cells:                     # unindent these
        for cell in row:                  # lines by one
            cell.stepToNextState()        # level each

If all the indentation issues are taken care of (or were not in the original code), there's still an issue that may cause an issue in certain situations. The issue is that the Cell.setNextState method doesn't handle every situation. Specifically, it doesn't set nextState if a cell is alive and should stay so (it has two or three living neighbors). The lack of an else on your chain of if and elif statements should have raised this as a red flag for me, but I overlooked it the first time I examined the function.

Here's how it can be fixed:

def setNextState (self , Neighbours):
    if self.state == Cell.Live and (Neighbours < 2 or Neighbours > 3):
        self.nextState = Cell.Dead
    elif self.state == Cell.Dead and Neighbours == 3:
        self.nextState = Cell.Live
    else: # remove the conditions on this block, all the state changes were handled above
        self.nextState = self.state
Blckknght
  • 100,903
  • 11
  • 120
  • 169