Как заставить спрайт (черепаху) перемещаться по наилучшему пути ячейка за ячейкой

#python #algorithm #turtle-graphics #python-turtle

#python #алгоритм #черепаха-графика #python-turtle

Вопрос:

Прямо сейчас я создаю алгоритм поиска пути в лабиринте, используя поиск в ширину. Алгоритм поиска «наилучшего пути», похоже, работает. Проблема, однако, в том, что я не уверен, как заставить спрайт (черепаху) перемещаться по этому наилучшему пути.

Вот код:

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

Изображение лабиринта в настоящее время выглядит следующим образом:

введите описание изображения здесь

Итак, что мне нужно сделать, это заставить фиолетовую черепаху (которую трудно разглядеть) перемещаться из зеленого квадрата в красный квадрат по наилучшему пути (желтые квадраты).

Ответ №1:

Шаг 1: В функции, где программа определяет правильный маршрут, запишите каждую координату в список и верните список (перевернутый), чтобы к нему можно было получить доступ вне функции:

 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]
  

Шаг 2: Задайте 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)