Python 8益智BFS无限循环

时间:2015-10-10 08:42:15

标签: python python-3.x

我正在尝试使用BFS搜索来解决8个难题,但是,我的代码似乎陷入无限循环,它只会来回移动零区块,直到队列内存以错误结束程序

import collections
import queue

class Node:

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

    @property
    def seq(self): # to keep track of the sequence used to get to the goal
        node, seq = self, []
        while node:
            seq.append(node)
            node = node.last
        yield from reversed(seq)

    @property
    def state(self):
        return str(self) # hashable so it can be compared in sets

    @property
    def isSolved(self):
        return self.puzzle.isSolved

    @property
    def getMoves(self):
        return self.puzzle.getMoves

class Puzzle:

    def __init__(self, startBoard):
        self.board = startBoard

    @property
    def getMoves(self):

        possibleNewBoards = []

        zeroPos = self.board.index(0) # find the zero tile to determine possible moves

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

        return possibleNewBoards # returns Puzzle objects (maximum of 4 at a time)

    def move(self, current, to):

        changeBoard = self.board[:] # create a copy
        changeBoard[to], changeBoard[current] = changeBoard[current], changeBoard[to] # switch the tiles at the passed positions
        return Puzzle(changeBoard) # return a new Puzzle object

    def printPuzzle(self): # prints board in 8 puzzle style

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

    @property
    def isSolved(self):
        return self.board == [0,1,2,3,4,5,6,7,8] # goal board

class Solver:

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

    def solveBFS(self):
        startNode = Node(self.puzzle)
        myQueue = collections.deque([startNode])
        visited = set()
        visited.add(myQueue[0].state)
        while myQueue:
            currentNode = myQueue.pop()
            # currentNode.puzzle.printPuzzle() # used for testing
            if currentNode.puzzle.isSolved:
                return currentNode.seq

            for board in currentNode.getMoves:
                nextNode = Node(board, currentNode)

                if nextNode.state not in visited:
                    myQueue.appendleft(nextNode)
                    visited.add(nextNode.state)

startingBoard = [7,2,4,5,0,6,8,3,1]

myPuzzle = Puzzle(startingBoard)
mySolver = Solver(myPuzzle)
goalSeq = mySolver.solveBFS()

counter = -1 # starting state doesn't count as a move
for node in goalSeq:
    counter = counter + 1
    node.puzzle.printPuzzle()
print("Total number of moves: " + counter)

我认为将每个节点添加到set()会阻止代码陷入循环。这不是真的吗?

1 个答案:

答案 0 :(得分:1)

@property
def state(self):
    return str(self) # hashable so it can be compared in sets

这将返回看起来像<__main__.Node object at 0x02173A90>的内容。每个Node对象的地址是唯一的,因此具有相同电路板的两个节点的state仍然被认为是不同的。

相反,请尝试:

@property
def state(self):
    return str(self.puzzle.board)

现在两个具有相同电路板的节点将被视为相同。

另外,将脚本的最后一行更改为

print("Total number of moves: " + str(counter))

现在你会得到一个结果:

7 2 4
5 0 6
8 3 1

7 2 4
0 5 6
8 3 1

(snip)

1 0 2
3 4 5
6 7 8

0 1 2
3 4 5
6 7 8

Total number of moves: 26