James Rolfe
James Rolfe

Reputation: 77

Python AttributeError: 'list' object has no attribute after using queue

I am trying to create a BFS solution to the 8 puzzle problem in python. However after I 'get()' a node from the queue and find the puzzle state inside that node it seems the puzzle object has turned into a list object without the method 'getMoves()'. I am wondering why it is not still a Puzzle object?

import collections
import queue

class Node:

    def __init__(self, Puzzle, last=None):
        self.puzzle = Puzzle
        self.last = last

    def getPuzzle(self):
        return self.puzzle

    def getLast(self):
        return self.last

class Puzzle:

    def __init__(self, startState, goalState):
        self.state = startState
        self.goal = goalState

    def getState():
        return self.state

    def getMoves(self):

        currentState = self.state

        possibleNewStates = []

        zeroPos = currentState.index(0)

        if zeroPos == 0:
            possibleNewStates.append(move(0,1))
            possibleNewStates.append(move(0,3))
        elif zeroPos == 1:
            possibleNewStates.append(move(1,0))
            possibleNewStates.append(move(1,2))
            possibleNewStates.append(move(1,4))
        elif zeroPos == 2:
            possibleNewStates.append(move(2,1))
            possibleNewStates.append(move(2,5))
        elif zeroPos == 3:
            possibleNewStates.append(move(3,0))
            possibleNewStates.append(move(3,4))
            possibleNewStates.append(move(3,6))
        elif zeroPos == 4:
            possibleNewStates.append(self.move(4,1))
            possibleNewStates.append(self.move(4,3))
            possibleNewStates.append(self.move(4,5))
            possibleNewStates.append(self.move(4,7))
        elif zeroPos == 5:
            possibleNewStates.append(move(5,2))
            possibleNewStates.append(move(5,4))
            possibleNewStates.append(move(5,8))
        elif zeroPos == 6:
            possibleNewStates.append(move(6,3))
            possibleNewStates.append(move(6,7))
        elif zeroPos == 7:
            possibleNewStates.append(move(7,4))
            possibleNewStates.append(move(7,6))
            possibleNewStates.append(move(7,8))
        else:
            possibleNewStates.append(move(8,5))
            possibleNewStates.append(move(8,7))

        return possibleNewStates

    def move(self, current, to):

        changeState = self.state

        save = changeState[to]
        changeState[to] = changeState[current]
        changeState[current] = save

        return changeState

    def printPuzzle(self):

        copyState = self.state
        print(copyState)
        '''
        for i in range(9):
            if i == 2 or i == 5:
                print((str)(copyState[i]))
            else:
                print((str)(copyState[i])+" ", end="")
            print()
        '''

    def isSolved(self):
        return self.state == self.goal

class Solver:

    def __init__(self, Puzzle):
        self.puzzle = Puzzle

    def solveBFS(self):
        puzzle = self.puzzle
        startNode = Node(puzzle)
        myQueue = queue.Queue(0)
        myQueue.put(startNode)
        myPuzzle = startNode.getPuzzle()
        print(myPuzzle.isSolved())
        while myQueue:
            currentNode = myQueue.get()
            currentPuzzle = currentNode.puzzle

            if currentPuzzle == [0,1,2,3,4,5,6,7,8]:
                return currentNode

            nextMoves = currentPuzzle.getMoves() # << ERROR HERE
            for state in nextMoves:
                nextNode = Node(state, currentNode)
                myQueue.put(nextNode)

    startingState = [7,2,4,5,0,6,8,3,1]
    goalState = [0,1,2,3,4,5,6,7,8]
    myPuzzle = Puzzle(startingState, goalState)
    mySolver = Solver(myPuzzle)
    goalNode = mySolver.solveBFS()
    goalPuzzle = goalNode.getPuzzle()
    goalPuzzle.printPuzzle()

Upvotes: 1

Views: 2334

Answers (2)

hsfzxjy
hsfzxjy

Reputation: 1322

All move() in getMoves() should be changed to self.move(). Otherwise you are working with an unbound Puzzle.

Upvotes: 0

Martijn Pieters
Martijn Pieters

Reputation: 1124948

That's because in some cases, state must be a list when you create a new node:

nextMoves = currentPuzzle.getMoves()
for state in nextMoves:
    nextNode = Node(state, currentNode)

Here state must've been a list returned from the Puzzle.getMoves() list:

def getMoves(self):
    # ...

    possibleNewStates = []

    # append various objects to possibleNewStates.

    return possibleNewStates

In getMoves() you use two callables to produces new states:

possibleNewStates.append(move(0,1))

and

possibleNewStates.append(self.move(4,1))

Puzzle.move() produces a list; you do some swapping on self.state which you set to a list:

def move(self, current, to):
    changeState = self.state

    save = changeState[to]
    changeState[to] = changeState[current]
    changeState[current] = save

    return changeState

Note that this mutates self.state in-place; you did not create a copy of the value here.

self.state was set from the first argument to Puzzle(), which was a list:

startingState = [7,2,4,5,0,6,8,3,1]
goalState = [0,1,2,3,4,5,6,7,8]
myPuzzle = Puzzle(startingState, goalState)

so self.move() returns a list, not a Puzzle() instance.

You didn't share the definition of the move() global function; if this also produces a list then that's another cause of your Node.puzzle values becoming lists.

You probably want to correct all move() calls to use self.move(), and for self.move() to return a new Puzzle() instance. You'll need to create a copy of self.state list here too:

def move(self, current, to):
    changeState = self.state[:]  # create a copy    
    changeState[to], changeState[current] = changeState[current], changeState[to]
    return Puzzle(changeState, self.goal)

Upvotes: 2

Related Questions