人工智能8拼图解决方案Python龟

时间:2018-04-22 19:50:23

标签: python turtle-graphics solver 8-puzzle

由于乌龟的问题,随着时间的推移,这个程序会变慢。我试图解决这个问题,但它应该起作用。我知道需要在def play()进行更改,但我不确定如何进行更改。我试图找到一种更快的方式来运行这个,所有反馈都非常感谢!

import math  
import turtle  
import random

class Puzzle:
def __init__(self, sideLength, x, y, col):
    self.cells = [[1, 2, 3],[4, 5, 6],[7, 8, 0]]
    self.SIZE = 100
    self.posX = 0
    self.posY = 0
    self.t = turtle.Turtle()
    turtle.delay(0)
    turtle.tracer(0,0)
    self.SIZE = sideLength
    self.posX = x
    self.posY = y
    self.t.width = 4
    self.t.hideturtle()
    self.t.speed(0)
    self.t.color(col,'white')
    self.t.penup()
    self.t.goto(0,0)
    self.t.pendown()

def drawSquare(self, t, side):
    [x,y] = self.t.position()
    self.t.pendown()
    self.t.begin_fill()
    self.t.begin_poly()
    for s in range(4):
        self.t.forward(side)
        self.t.right(90)
    self.t.end_poly()
    self.t.end_fill()
    self.t.penup()
    self.t.goto(x,y)
    self.t.pendown()        

def isGoal(self):
    temp = []
    for row in range(3):
            for col in range(3):
                temp.append(self.cells[row][col])

    if temp[0]==0:
        for i in range(8):
            if temp[i]>temp[i+1]:
                return False
        return True
    if temp[-1]==0:
        for i in range(7):
            if temp[i]>temp[i+1]:
                return False
        return True         
    else:
        return False

def solvable(self):
    temp = []
    for row in range(3):
            for col in range(3):
                temp.append(self.cells[row][col])
    inv_count = 0;
    for i in range(len(temp)):
        for j in range(i+1,len(temp)):
            if temp[j]!=0 and temp[i]!=0 and temp[i] > temp[j]:
                inv_count += 1
    return (inv_count%2==0)

def shuffle(self):
    temp = []
    for row in range(3):
            for col in range(3):
                temp.append(self.cells[row][col])
    for i in range(len(temp)):
        r = random.randint(0, len(temp)-1)
        t = temp[i]
        temp[i] = temp[r]
        temp[r] = t

    for i in range(len(temp)):
        self.cells[i//3][i%3] = temp[i]

def utility(self):
    manhattan_dist = 0
    for row in range(3):
        for col in range(3):
            tile = self.cells[row][col]
            if tile == 0:
                    continue
            finalRow = (tile-1)//3
            finalCol = (tile-1)%3
            manhattan_dist += abs(row-finalRow)+abs(col-finalCol)
    return manhattan_dist

def display(self):
    print(self.cells)

def up(self, visible):
    found = False
    for row in range(3):
        for col in range(3):
            if self.cells[row][col]==0:
                found = True
                break
        if found:
            break
    if row==2:
        return False
    else:
        self.cells[row][col] = self.cells[row+1][col]
        self.cells[row+1][col] = 0
        if visible:
            self.updateBoard(row+1, col, row, col)
        return True

def down(self, visible):
    found = False
    for row in range(3):
        for col in range(3):
            if self.cells[row][col]==0:
                found = True
                break
        if found:
            break
    if row==0:
        return False
    else:
        self.cells[row][col] = self.cells[row-1][col]
        self.cells[row-1][col] = 0
        if visible:
            self.updateBoard(row-1, col, row, col)
        return True

def left(self, visible):
    found = False
    for row in range(3):
        for col in range(3):
            if self.cells[row][col]==0:
                found = True
                break
        if found:
            break
    if col==2:
        return False
    else:
        self.cells[row][col] = self.cells[row][col+1]
        self.cells[row][col+1] = 0

        if visible:
            self.updateBoard(row, col+1, row, col)
        return True

def right(self, visible):
    found = False
    for row in range(3):
        for col in range(3):
            if self.cells[row][col]==0:
                found = True
                break
        if found:
            break

    if col==0:
        return False
    else:
        self.cells[row][col] = self.cells[row][col-1]
        self.cells[row][col-1] = 0
        if visible:
            self.updateBoard(row, col-1, row, col)
        return True

def displayWin(self, count):
    self.t.penup()      
    outputX = self.posX
    outputY = self.posY - 5 *self.SIZE
    self.t.goto(outputX, outputY)
    self.t.pendown()
    self.t.write("Solved in " + str(count) + " moves!", move=False, align="center", font=("Arial", int(0.75*self.SIZE), "normal"))  

def updateBoard(self, row1, col1, row2, col2):
    self.t.penup()      
    topLeftX = self.posX + col1*self.SIZE
    topLeftY = self.posY - row1*self.SIZE
    self.t.goto(topLeftX, topLeftY)
    self.t.pendown()
    self.drawSquare(self.t, self.SIZE)
    self.t.penup()
    self.t.right(90)
    self.t.forward(self.SIZE)
    self.t.left(90)
    self.t.forward(self.SIZE/2)
    self.t.pendown()
    if self.cells[row1][col1] != 0:
        self.t.write(self.cells[row1][col1], move=False, align="center", font=("Arial", int(0.75*self.SIZE), "normal"))
    self.t.penup()      
    topLeftX = self.posX + col2*self.SIZE
    topLeftY = self.posY - row2*self.SIZE
    self.t.goto(topLeftX, topLeftY)
    self.t.pendown()
    self.drawSquare(self.t, self.SIZE)
    self.t.penup()
    self.t.right(90)
    self.t.forward(self.SIZE)
    self.t.left(90)
    self.t.forward(self.SIZE/2)
    self.t.pendown()
    if self.cells[row2][col2] != 0:
        self.t.write(self.cells[row2][col2], move=False, align="center", font=("Arial", int(0.75*self.SIZE), "normal"))
    self.t.penup()
    self.t.goto(0,0)
    self.t.pendown()


def drawBoard(self):
    for row in range(3):
        for col in range(3):
            self.t.penup()      
            topLeftX = self.posX + col*self.SIZE
            topLeftY = self.posY - row*self.SIZE
            self.t.goto(topLeftX, topLeftY)
            self.drawSquare(self.t, self.SIZE)
            self.t.penup()
            self.t.goto(topLeftX, topLeftY)
            self.t.right(90)
            self.t.forward(self.SIZE)
            self.t.left(90)
            self.t.forward(self.SIZE/2)
            self.t.pendown()
            if self.cells[row][col] != 0:
                self.t.write(self.cells[row][col], move=False, align="center", font=("Arial", int(0.75*self.SIZE), "normal"))
    self.t.penup()
    self.t.goto(0,0)
    self.t.pendown()

def play():
p = Puzzle(50, 0, 0, 'blue')
p.shuffle()
while(not p.solvable()):
        p.shuffle() 
p.display()

p.drawBoard()
prevMove = 1
count = 0
while (not p.isGoal()):
            #Create a new puzzle and copy the current state to it
    q = Puzzle(30,-200,200, 'red')
    #p.display()
    for row in range(3):
        for col in range(3):
            q.cells[row][col] = p.cells[row][col]
    # Disable this next line if you don't want to display this board
    q.drawBoard()
    bestMove = 0
    minimum = 1000
    validMoves = []
    # Try out all possible moves on the new puzzle and choose the one that leads to the lowest utility
    # Make the q method calls with False if you don't want to display this board
    if q.down(True):
        validMoves.append(0)
        bestMove = 0
        minimum = q.utility()
        q.up(True)
    if q.right(True):
        validMoves.append(1)
        if q.utility()<minimum:
            minimum = q.utility()
            bestMove = 1
        q.left(True)
    if q.up(True):
        validMoves.append(2)
        if q.utility()<minimum:
            minimum = q.utility()
            bestMove = 2
        q.down(True)
    if q.left(True):
        validMoves.append(3)
        if q.utility()<minimum:
            minimum = q.utility()
            bestMove = 3
        q.right(True)

    # If the best move is the opposite of the one just taken...
    if(abs(bestMove - prevMove)==2): #UP and DOWN, LEFT and RIGHT are numbered 2 values apart
                    #... flip a coin and if it is heads...
        r = random.randint(1,2)
        #... remove the best move from the list of valid moves...
        validMoves.remove(bestMove)
        if r == 1:
                            # Randomly choose one of the other valid moves
            index = random.randint(0, len(validMoves)-1)
            bestMove = validMoves[index]
                            # If the coin shows tails, then allow the solver to backtrack over the last move
    if bestMove==0:
        p.down(True)
        print('down',p.utility())
        count += 1
    elif bestMove==1:
        p.right(True)
        print('right', p.utility())
        count += 1
    elif bestMove==2:
        p.up(True)
        print('up', p.utility())
        count += 1
    else:
        p.left(True)
        print('left', p.utility())
        count += 1
    prevMove = bestMove;
    print(count)
print('Solved in ' + str(count) + ' moves')
p.displayWin(count)
#mov = input()


play()

1 个答案:

答案 0 :(得分:1)

由于图形是您的瓶颈,请确保在运行时,图形尽可能少地工作。在您的代码中,在运行时,将乌龟移动到正确的位置,绘制一个带有白色内部的新方块,稍微调整位置,然后绘制一个数字。相反,让我们单独离开网格并在正确的位置创建一个海龟矩阵,以清除并绘制一个新数字:

from turtle import Turtle, Screen
import random

class Puzzle:
    def __init__(self, sideLength, x, y, color):
        self.cells = [[1, 2, 3], [4, 5, 6], [7, 8, 0]]
        self.markers = [[None, None, None], [None, None, None], [None, None, None]]
        self.t = Turtle(visible=False)
        self.s = Screen()
        self.s.tracer(False)
        self.SIZE = sideLength
        self.font = ("Arial", int(0.75 * self.SIZE), "normal")
        self.posX, self.posY = x, y
        self.t.width = 4
        self.t.speed('fastest')
        self.t.color(color, 'white')

    def drawSquare(self, side):
        position = self.t.position()

        self.t.pendown()

        for _ in range(4):
            self.t.forward(side)
            self.t.right(90)

        self.t.penup()
        self.t.goto(position)
        self.t.pendown()

    def isGoal(self):
        temp = []

        for row in range(3):
            for col in range(3):
                temp.append(self.cells[row][col])

        if temp[0] == 0:
            for i in range(8):
                if temp[i] > temp[i + 1]:
                    return False
            return True

        if temp[-1] == 0:
            for i in range(7):
                if temp[i] > temp[i + 1]:
                    return False
            return True

        return False

    def solvable(self):
        temp = []

        for row in range(3):
            for col in range(3):
                temp.append(self.cells[row][col])

        inv_count = 0

        for i in range(len(temp)):
            for j in range(i + 1, len(temp)):
                if temp[j] != 0 and temp[i] != 0 and temp[i] > temp[j]:
                    inv_count += 1

        return inv_count % 2 == 0

    def shuffle(self):
        temp = []

        for row in range(3):
            for col in range(3):
                temp.append(self.cells[row][col])

        for i in range(len(temp)):
            r = random.randint(0, len(temp) - 1)
            t = temp[i]
            temp[i] = temp[r]
            temp[r] = t

        for i in range(len(temp)):
            self.cells[i // 3][i % 3] = temp[i]

    def utility(self):
        manhattan_dist = 0

        for row in range(3):
            for col in range(3):
                tile = self.cells[row][col]
                if tile == 0:
                    continue
                finalRow = (tile - 1) // 3
                finalCol = (tile - 1) % 3
                manhattan_dist += abs(row - finalRow) + abs(col - finalCol)

        return manhattan_dist

    def display(self):
        print(self.cells)

    def up(self, visible):
        found = False
        row, col = 0, 0

        for row in range(3):
            for col in range(3):
                if self.cells[row][col] == 0:
                    found = True
                    break
            if found:
                break

        if row == 2:
            return False

        self.cells[row][col] = self.cells[row + 1][col]
        self.cells[row + 1][col] = 0

        if visible:
            self.updateBoard(row + 1, col, row, col)

        return True

    def down(self, visible):
        found = False
        row, col = 0, 0

        for row in range(3):
            for col in range(3):
                if self.cells[row][col] == 0:
                    found = True
                    break
            if found:
                break

        if row == 0:
            return False

        self.cells[row][col] = self.cells[row - 1][col]
        self.cells[row-1][col] = 0

        if visible:
            self.updateBoard(row - 1, col, row, col)

        return True

    def left(self, visible):
        found = False
        row, col = 0, 0

        for row in range(3):
            for col in range(3):
                if self.cells[row][col] == 0:
                    found = True
                    break
            if found:
                break

        if col == 2:
            return False

        self.cells[row][col] = self.cells[row][col + 1]
        self.cells[row][col + 1] = 0

        if visible:
            self.updateBoard(row, col + 1, row, col)

        return True

    def right(self, visible):
        found = False
        row, col = 0, 0

        for row in range(3):
            for col in range(3):
                if self.cells[row][col] == 0:
                    found = True
                    break
            if found:
                break

        if col == 0:
            return False

        self.cells[row][col] = self.cells[row][col - 1]
        self.cells[row][col - 1] = 0

        if visible:
            self.updateBoard(row, col - 1, row, col)

        return True

    def displayWin(self, count):
        self.t.penup()
        outputX = self.posX
        outputY = self.posY - 5 * self.SIZE
        self.t.goto(outputX, outputY)
        self.t.pendown()
        self.t.write("Solved in " + str(count) + " moves!", move=False, align="center", font=self.font)

    def updateBoard(self, row1, col1, row2, col2):

        data = self.cells[row1][col1] or ''
        marker = self.markers[row1][col1]
        marker.undo()
        marker.write(data, align="center", font=self.font)
        self.s.update()  # don't rely on this happening as a side effect of other turtle functions

        data = self.cells[row2][col2] or ''
        marker = self.markers[row2][col2]
        marker.undo()
        marker.write(data, align="center", font=self.font)
        self.s.update()

    def drawBoard(self):

        for row in range(3):
            for col in range(3):
                self.t.penup()

                topLeftX = self.posX + col * self.SIZE
                topLeftY = self.posY - row * self.SIZE
                self.t.goto(topLeftX, topLeftY)
                self.drawSquare(self.SIZE)

                self.t.penup()
                self.t.goto(topLeftX, topLeftY)
                self.t.right(90)
                self.t.forward(self.SIZE)
                self.t.left(90)
                self.t.forward(self.SIZE / 2)

                data = self.cells[row][col] if self.cells[row][col] != 0 else ''
                marker = self.t.clone()
                marker.write(data, align="center", font=self.font)

                self.markers[row][col] = marker

        self.t.penup()
        self.t.goto(0, 0)
        self.t.pendown()

def play():
    p = Puzzle(50, 0, 0, 'blue')
    p.shuffle()
    while not p.solvable():
        p.shuffle()
    p.display()

    p.drawBoard()
    prevMove = 1
    count = 0

    q = Puzzle(30, -200, 200, 'red')
    q.drawBoard()

    while not p.isGoal():
        # Copy the current state to new puzzle (probably should be a method)

        for row in range(3):
            for col in range(3):
                q.cells[row][col] = p.cells[row][col]

                data = q.cells[row][col] or ''
                marker = q.markers[row][col]
                marker.undo()
                marker.write(data, align="center", font=q.font)
        q.s.update()

        bestMove = 0
        minimum = 1000
        validMoves = []

        # Try out all possible moves on the new puzzle and choose the one that leads to the lowest utility
        # Make the q method calls with False if you don't want to display this board
        if q.down(True):
            validMoves.append(0)
            bestMove = 0
            minimum = q.utility()
            q.up(True)

        if q.right(True):
            validMoves.append(1)
            if q.utility() < minimum:
                minimum = q.utility()
                bestMove = 1
            q.left(True)

        if q.up(True):
            validMoves.append(2)
            if q.utility() < minimum:
                minimum = q.utility()
                bestMove = 2
            q.down(True)

        if q.left(True):
            validMoves.append(3)
            if q.utility() < minimum:
                minimum = q.utility()
                bestMove = 3
            q.right(True)

        # If the best move is the opposite of the one just taken...
        if abs(bestMove - prevMove) == 2:  # UP and DOWN, LEFT and RIGHT are numbered 2 values apart
            #... flip a coin and if it is heads...
            r = random.randint(False, True)
            #... remove the best move from the list of valid moves...
            validMoves.remove(bestMove)
            if r:
                # Randomly choose one of the other valid moves
                index = random.randint(0, len(validMoves) - 1)
                bestMove = validMoves[index]
                # If the coin shows tails, then allow the solver to backtrack over the last move

        if bestMove == 0:
            p.down(True)
            print('down', p.utility())
            count += 1
        elif bestMove == 1:
            p.right(True)
            print('right', p.utility())
            count += 1
        elif bestMove == 2:
            p.up(True)
            print('up', p.utility())
            count += 1
        else:
            p.left(True)
            print('left', p.utility())
            count += 1

        prevMove = bestMove
        print(count)

    print('Solved in ' + str(count) + ' moves')
    p.displayWin(count)

play()

我也做了一些其他的代码清理,可能会,也可能不会影响性能。

相关问题