如何让精灵(乌龟)逐个单元地通过最佳路径

How to make sprite (turtle) move through the best path cell by cell

现在我正在创建一个使用广度优先搜索的迷宫寻路算法。找到 'best path' 的算法似乎有效。然而,问题是我不确定如何让精灵(海龟)通过最佳路径。

代码如下:

import turtle
import time
import sys
from collections import deque

bs = turtle.Screen()
bs.bgcolor("black")
bs.setup(1300,700)

class Building(turtle.Turtle):                             # Define building class
    def __init__(self):
        turtle.Turtle.__init__(self)
        self.shape("square")                               # Shape of the building
        self.color("grey")                                 # Colour of the building
        self.penup()                                       # Lift the pen up so it does not leave a trail
        self.speed('fast')                                 # Sets the speed that the building is drawn on the screen
        
class Road(turtle.Turtle):                                 # Define road class
    def __init__(self):
        turtle.Turtle.__init__(self)
        self.shape("square")                               # Shape of the road
        self.color("white")                                # Colour of the road
        self.penup()                                       # Lift the pen up so it does not leave a trail
        self.hideturtle()
        self.speed('fast')                                 # Sets the speed that the road is drawn on the screen
        
class Start(turtle.Turtle):                                # Define start class
    def __init__(self):
        turtle.Turtle.__init__(self)
        self.shape("square")                               # Shape of the start point
        self.color('green')                                # Colour of the start point 
        self.penup()                                       # Lift the pen up so it does not leave a trail
        self.hideturtle()
        self.speed('fast')
        
class Route(turtle.Turtle):
    def __init__(self):
        turtle.Turtle.__init__(self)
        self.shape("square")
        self.color("yellow")
        self.penup()
        self.hideturtle()
        self.speed('fast')
        
class End(turtle.Turtle):                                  # Define end class
    def __init__(self):
        turtle.Turtle.__init__(self)
        self.shape("square")                               # Shape of the end point
        self.color("red")                                  # Colour of the end point
        self.penup()                                       # Lift the pen up so it does not leave a trail
        self.hideturtle()
        self.speed('fast')    
        
class Searcher(turtle.Turtle):
    def __init__(self):
        turtle.Turtle.__init__(self)
        self.shape("square")
        self.color("white")
        self.penup()
        self.hideturtle()
        self.speed('fast')
        
class sprite(turtle.Turtle):                               # Define sprite class
    noOfSteps = 0                                          # Declare variable noOfSteps and instantiate it as 0
    def __init__(self):
        turtle.Turtle.__init__(self)
        self.shape("turtle")                               # Shape of the sprite
        self.color("purple")                               # Colour of the sprite
        self.penup()                                       # Lift the pen up so it does not leave a trail
        self.hideturtle()
        self.speed('fast')

    def moveSprite(self):
        start.color('green')
        start.stamp()
        searcher.color('red')
        searcher.stamp()
        self.goto(start_x, start_y)
        self.showturtle()
        # self.goto(solution[x,y])
        
# Read maze txt file
def readMaze(mazeSet, filename):
    mazeFile = open(filename, "r")
    lines = mazeFile.readlines()
    for line in lines:
        line = line.strip()
        row = [c for c in line]
        mazeSet.append(row)

mazeSet = []  # This declares the maze as an empty list
readMaze(mazeSet, "CA1_Map.txt") # This reads the maze into the list

# Setting up of maze
def setupMaze(mazeSet):
    global start_x, start_y, end_x, end_y
    m_height, m_width = len(mazeSet), len(mazeSet[0])     # Define maze height and maze width

    for y in range(m_height):                             # Select each line in the maze
        for x in range(m_width):                          # Identify each character in the line
            character = mazeSet[y][x]                     # Assign the maze reference to the variable 'character'
            screen_x = ((x - m_width) * 24) + 150         # Assign screen_x to screen starting position for x coords
            screen_y = ((m_width - y) * 24) - 200         # Assign screen_y to screen starting position for y coords

            if character == "X":                          
                building.goto(screen_x, screen_y)         
                building.stamp()                          
                walls.append((screen_x, screen_y))       
                
            if character == "." or character == 'e':      
                path.append((screen_x, screen_y))

            if character == "e":   
                end_x, end_y = screen_x, screen_y
                searcher.color('red')
                searcher.goto(screen_x, screen_y)
                searcher.stamp()
                searcher.color('white')
                finish.append((screen_x, screen_y))
                
            if character == "s":
                start_x, start_y = screen_x, screen_y                                  
                start.goto(screen_x,screen_y)
                start.stamp()
    
def search(x,y):
    frontier.append((x, y))
    solution[x,y] = x,y

    while len(frontier) > 0:       
        time.sleep(0)
        x, y = frontier.popleft()     

        if(x - 24, y) in path and (x - 24, y) not in visited:  
            cell = (x - 24, y)
            solution[cell] = x, y   
            frontier.append(cell)  
            visited.add((x-24, y)) 

        if (x, y - 24) in path and (x, y - 24) not in visited: 
            cell = (x, y - 24)
            solution[cell] = x, y
            frontier.append(cell)
            visited.add((x, y - 24))

        if(x + 24, y) in path and (x + 24, y) not in visited:  
            cell = (x + 24, y)
            solution[cell] = x, y
            frontier.append(cell)
            visited.add((x + 24, y))

        if(x, y + 24) in path and (x, y + 24) not in visited:  
            cell = (x, y + 24)
            solution[cell] = x, y
            frontier.append(cell)
            visited.add((x, y + 24))
            
        searcher.goto(x,y)
        searcher.stamp()

def correctRoute(x, y):
    route.goto(x, y)
    route.stamp()
    while (x, y) != (start_x, start_y):    
        route.goto(solution[x, y])     
        route.stamp()
        x, y = solution[x, y]
 
def endProgram():
    bs.exitonclick()
    sys.exit()


# Main program

# Setting up classes
building = Building()
road = Road()
start = Start()
end = End()
searcher = Searcher()
route = Route()
sprite = sprite()

# Setting up lists
walls = []
path = []
finish = []
visited = set()
frontier = deque()
solution = {}                   

setupMaze(mazeSet)
search(start_x, start_y)
correctRoute(end_x, end_y)

while True:
    sprite.moveSprite()

目前迷宫的图像是这样的:

所以我需要做的是让紫龟(有点难看)通过最佳路径(黄色方块)从绿色方块移动到红色方块。

第一步:在程序判断正确路线的函数处, 将每个坐标记录到列表中, 和 return 列表(倒置)以便可以在函数外访问它:

def correctRoute(x, y):
    routes = [(x, y)]
    route.goto(x, y)
    route.stamp()
    while (x, y) != (start_x, start_y):    
        route.goto(solution[x, y])     
        route.stamp()
        x, y = solution[x, y]
        routes.append((x, y))
    return routes[::-1]

第二步:moveSprite函数另一个参数,参数就是坐标列表。 然后列表将分为两部分:起始坐标和路径。在调用 self.showturtle() 之前, 首先确保乌龟处于正确的起始位置。 使用 for 循环,使用 time.sleep 遍历路径列表并使海龟到达每个坐标:

def moveSprite(self, routes):
    starting, path = routes[0], routes[1:]
    start.color('green')
    start.stamp()
    searcher.color('red')
    searcher.stamp()
    self.goto(starting)
    self.showturtle()
    for path in path:
        time.sleep(0.3)
        self.goto(path)

第 3 步: 在代码的最底部,将 correctRoute 调用分配给变量以检索坐标。 移除 while 循环,将坐标列表放入 moveSprite 函数:

routes = correctRoute(end_x, end_y)

sprite.moveSprite(routes)

总计:

import turtle
import time
import sys
from collections import deque

bs = turtle.Screen()
bs.bgcolor("black")
bs.setup(1300,700)

class Building(turtle.Turtle):                             # Define building class
    def __init__(self):
        turtle.Turtle.__init__(self)
        self.shape("square")                               # Shape of the building
        self.color("grey")                                 # Colour of the building
        self.penup()                                       # Lift the pen up so it does not leave a trail
        self.speed('fast')                                 # Sets the speed that the building is drawn on the screen
        
class Road(turtle.Turtle):                                 # Define road class
    def __init__(self):
        turtle.Turtle.__init__(self)
        self.shape("square")                               # Shape of the road
        self.color("white")                                # Colour of the road
        self.penup()                                       # Lift the pen up so it does not leave a trail
        self.hideturtle()
        self.speed('fast')                                 # Sets the speed that the road is drawn on the screen
        
class Start(turtle.Turtle):                                # Define start class
    def __init__(self):
        turtle.Turtle.__init__(self)
        self.shape("square")                               # Shape of the start point
        self.color('green')                                # Colour of the start point 
        self.penup()                                       # Lift the pen up so it does not leave a trail
        self.hideturtle()
        self.speed('fast')
        
class Route(turtle.Turtle):
    def __init__(self):
        turtle.Turtle.__init__(self)
        self.shape("square")
        self.color("yellow")
        self.penup()
        self.hideturtle()
        self.speed('fast')
        
class End(turtle.Turtle):                                  # Define end class
    def __init__(self):
        turtle.Turtle.__init__(self)
        self.shape("square")                               # Shape of the end point
        self.color("red")                                  # Colour of the end point
        self.penup()                                       # Lift the pen up so it does not leave a trail
        self.hideturtle()
        self.speed('fast')    
        
class Searcher(turtle.Turtle):
    def __init__(self):
        turtle.Turtle.__init__(self)
        self.shape("square")
        self.color("white")
        self.penup()
        self.hideturtle()
        self.speed('fast')
        
class sprite(turtle.Turtle):                               # Define sprite class
    noOfSteps = 0                                          # Declare variable noOfSteps and instantiate it as 0
    def __init__(self):
        turtle.Turtle.__init__(self)
        self.shape("turtle")                               # Shape of the sprite
        self.color("purple")                               # Colour of the sprite
        self.penup()                                       # Lift the pen up so it does not leave a trail
        self.hideturtle()
        self.speed('fast')

    def moveSprite(self, routes):
        starting, path = routes[0], routes[1:]
        start.color('green')
        start.stamp()
        searcher.color('red')
        searcher.stamp()
        self.goto(starting)
        self.showturtle()
        for path in path:
            time.sleep(0.3)
            self.goto(path)

# Read maze txt file
def readMaze(mazeSet, filename):
    mazeFile = open(filename, "r")
    lines = mazeFile.readlines()
    for line in lines:
        line = line.strip()
        row = [c for c in line]
        mazeSet.append(row)

mazeSet = []  # This declares the maze as an empty list
readMaze(mazeSet, "CA1_Map.txt") # This reads the maze into the list

# Setting up of maze
def setupMaze(mazeSet):
    global start_x, start_y, end_x, end_y
    m_height, m_width = len(mazeSet), len(mazeSet[0])     # Define maze height and maze width

    for y in range(m_height):                             # Select each line in the maze
        for x in range(m_width):                          # Identify each character in the line
            character = mazeSet[y][x]                     # Assign the maze reference to the variable 'character'
            screen_x = ((x - m_width) * 24) + 150         # Assign screen_x to screen starting position for x coords
            screen_y = ((m_width - y) * 24) - 200         # Assign screen_y to screen starting position for y coords

            if character == "X":                          
                building.goto(screen_x, screen_y)         
                building.stamp()                          
                walls.append((screen_x, screen_y))       
                
            if character == "." or character == 'e':      
                path.append((screen_x, screen_y))

            if character == "e":   
                end_x, end_y = screen_x, screen_y
                searcher.color('red')
                searcher.goto(screen_x, screen_y)
                searcher.stamp()
                searcher.color('white')
                finish.append((screen_x, screen_y))
                
            if character == "s":
                start_x, start_y = screen_x, screen_y                                  
                start.goto(screen_x,screen_y)
                start.stamp()
    
def search(x,y):
    frontier.append((x, y))
    solution[x,y] = x,y

    while len(frontier) > 0:       
        time.sleep(0)
        x, y = frontier.popleft()     

        if(x - 24, y) in path and (x - 24, y) not in visited:  
            cell = (x - 24, y)
            solution[cell] = x, y   
            frontier.append(cell)  
            visited.add((x-24, y)) 

        if (x, y - 24) in path and (x, y - 24) not in visited: 
            cell = (x, y - 24)
            solution[cell] = x, y
            frontier.append(cell)
            visited.add((x, y - 24))

        if(x + 24, y) in path and (x + 24, y) not in visited:  
            cell = (x + 24, y)
            solution[cell] = x, y
            frontier.append(cell)
            visited.add((x + 24, y))

        if(x, y + 24) in path and (x, y + 24) not in visited:  
            cell = (x, y + 24)
            solution[cell] = x, y
            frontier.append(cell)
            visited.add((x, y + 24))
            
        searcher.goto(x,y)
        searcher.stamp()

def correctRoute(x, y):
    routes = [(x, y)]
    route.goto(x, y)
    route.stamp()
    while (x, y) != (start_x, start_y):    
        route.goto(solution[x, y])     
        route.stamp()
        x, y = solution[x, y]
        routes.append((x, y))
    return routes[::-1]
 
def endProgram():
    bs.exitonclick()
    sys.exit()


# Main program

# Setting up classes
building = Building()
road = Road()
start = Start()
end = End()
searcher = Searcher()
route = Route()
sprite = sprite()

# Setting up lists
walls = []
path = []
finish = []
visited = set()
frontier = deque()
solution = {}                   

setupMaze(mazeSet)
search(start_x, start_y)
routes = correctRoute(end_x, end_y)

sprite.moveSprite(routes)

如果你想让乌龟在每一轮都改变它的方向,使用这个作为moveSprite函数:

    def moveSprite(self, routes):
        starting, routes = routes[0], routes[1:]
        start.color('green')
        start.stamp()
        searcher.color('red')
        searcher.stamp()
        self.goto(starting)
        self.showturtle()
        for path1, path2 in zip(routes, routes[1:]):
            self.goto(path1)
            time.sleep(0.3)
            if path1[0] == path2[0]: # If the x coordinates of two consecutive moves are equal, that means that the turtle is moving along the `y` axis
                if path1[1] > path2[1]:
                    self.setheading(270)
                else:
                    self.setheading(90)
            else: # If the x coordinates of two consecutive moves aren't equal, that means that the turtle is moving along the `x` axis
                if path1[0] > path2[0]:
                    self.setheading(180)
                else:
                    self.setheading(0)
        self.goto(path2)