简体   繁体   English

人工智能8解谜器Python Turtle

[英]Artificial Intelligence 8 Puzzle Solver Python Turtle

Due to a problem with turtle, this program slows down as time goes on. 由于乌龟问题,随着时间的推移,该程序会变慢。 I'm trying to fix that issue, but it should work otherwise. 我正在尝试解决该问题,但应该可以正常工作。 I know that changes need to be made in def play() , but I'm not exactly sure how. 我知道需要在def play()进行更改,但我不确定如何进行更改。 I'm trying to find a quicker way to run this and any and all feedback is much appreciated! 我正在尝试找到一种更快的方式来运行此功能,因此,所有反馈都非常感谢!

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()

Since graphics are your bottleneck, make sure at runtime, the graphics do as little work as possible. 由于图形是您的瓶颈,因此请确保在运行时图形尽可能少地工作。 In your code, at runtime, you move the turtle to the correct location, draw a new square with a white interior, adjust the position slightly, and draw a number. 在您的代码中,在运行时,您将乌龟移动到正确的位置,绘制一个内部为白色的新正方形,略微调整位置并绘制一个数字。 Instead, let's leave the grid alone and create a matrix of turtles all at the correct locations to clear and draw a new number: 取而代之的是,让我们单独放置网格,并在正确的位置创建所有的海龟矩阵,以清除并绘制新的数字:

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()

I've also done some other code cleanup as well, that may, or may not, affect performance. 我还做了其他一些代码清理,这可能会或可能不会影响性能。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM