Rompiendo el laberinto basado en el algoritmo de profundidad primero


Prefacio

Prim puede generar diferentes mapas y el problema del laberinto puede resolverse mediante el algoritmo A *


1. El algoritmo Prim genera un mapa

El algoritmo Prim genera el mapa y lo resuelve a través del algoritmo A *, pero después de unos días, el algoritmo A * no salió, así que cambié el algoritmo, el algoritmo de profundidad primero (DFS).

Dos, date cuenta del laberinto

2.1 Primer algoritmo de profundidad

La búsqueda en profundidad es una especie de algoritmo gráfico. La abreviatura en inglés es DFS (búsqueda primero en profundidad). En resumen, el proceso consiste en profundizar cada ruta de bifurcación posible hasta el punto en que ya no puede ser profunda y solo se puede acceder a cada nodo una vez. El siguiente ejemplo: en

comparación con el algoritmo A *, este algoritmo es más engorroso y consume más que A *, pero es fácil de entender. Un ejemplo de la vida: Perdí una llave en casa, pero no sé la ubicación específica de la llave. Estaré en cualquier posición. En este momento, necesito mirar cada esquina, y las esquinas en las que he estado están en mi mente, y no volveré a mirar. Este tipo de búsqueda de alfombras hasta encontrar la posición clave.

2.2 Importar biblioteca

Esta vez hice referencia a la biblioteca de pyxel . Pyxel es un motor de producción de juegos de estilo de píxeles clásico de Python.
El laberinto se puede visualizar.

2.3 Proceso de implementación

Prim genera el laberinto
todavía usando Prim para generar el laberinto. Definimos una clase Maze y usamos una matriz bidimensional para representar el mapa del laberinto, donde 1 representa la pared y 0 representa la carretera. Luego, inicializamos la esquina superior izquierda como entrada, la esquina inferior derecha como salida y finalmente definimos el vector de dirección hacia abajo .

class Maze:
    def __init__(self, width, height):
        self.width = width
        self.height = height
        self.map = [[0 if x % 2 == 1 and y % 2 == 1 else 1 for x in range(width)] for y in range(height)]
        self.map[1][0] = 0  # 入口
        self.map[height - 2][width - 1] = 0  # 出口
        self.visited = []
        self.dx = [1, 0, -1, 0]
        self.dy = [0, -1, 0, 1]

El siguiente paso es generar la función principal del laberinto. Hay dos funciones principales en esta función: get_neighbor_road (point) y deal_with_not_visited (), la primera obtendrá los nodos vecinos del punto de coordenadas entrante, el valor de retorno es una matriz bidimensional y la última deal_with_not_visited () selecciona aleatoriamente una pared de la lista , Si solo se ha visitado una celda de las dos celdas separadas por esta pared, elimine esta pared de la lista y abra la pared al mismo tiempo. Marque la
celda como la pared adyacente de la celda no visitada Únase al muro de la lista.

def generate(self):
    start = [1, 1]
    self.visited.append(start)
    wall_list = self.get_neighbor_wall(start)
    while wall_list:
        wall_position = random.choice(wall_list)
        neighbor_road = self.get_neighbor_road(wall_position)
        wall_list.remove(wall_position)
        self.deal_with_not_visited(neighbor_road[0], wall_position, wall_list)
        self.deal_with_not_visited(neighbor_road[1], wall_position, wall_list)

El algoritmo de Prim es seleccionar aleatoriamente todas las celdas de la lista. No hay diferencia entre las celdas recién agregadas y las antiguas, y ambas tienen una probabilidad del 50%. De esta manera, cuando se genera un laberinto,
DFS saldrá naturalmente del laberinto para
realizar DFS. Es implementar un programa recursivo. Cuando la coordenada del nodo está fuera de límites, el nodo ha sido visitado, o el nodo es un muro, regresa directamente, porque no es una ruta fuera del laberinto. Si es así, agrégalo al nodo y ruta visitados.
Entonces, si el nodo es una salida, significa que la ejecución del programa ha terminado y se encuentra la ruta. De lo contrario, recorra los cuatro vectores de dirección, pase la ruta vecina del nodo a la función dfs y continúe con los pasos anteriores hasta que se encuentre una ruta o se recorran todos los nodos del programa.

 
def dfs(self, x, y, path, visited=[]):
    # outOfIndex
    if self.is_out_of_index(x, y):
        return False
 
    # visited or is wall
    if [x, y] in visited or self.get_value([x, y]) == 1:
        return False
 
    visited.append([x, y])
    path.append([x, y])
 
    # end...
    if x == self.width - 2 and y == self.height - 2:
        return True
 
    # recursive
    for i in range(4):
        if 0 < x + self.dx[i] < self.width - 1 and 0 < y + self.dy[i] < self.height - 1 and \
                self.get_value([x + self.dx[i], y + self.dy[i]]) == 0:
            if self.dfs(x + self.dx[i], y + self.dy[i], path, visited):
                return True
            elif not self.is_out_of_index(x, y) and path[-1] != [x, y]:
                path.append([x, y])


La lógica de ejecución de pyxel para realizar la clase de visualización Vision es llamar continuamente a la función de actualización y la función de dibujo, para que pueda actualizar las coordenadas del objeto en la función de actualización y luego dibujar la imagen en la pantalla en la función de dibujo.
Así que primero dibujamos el laberinto y luego renderizamos la animación transversal de dfs.

import pyxel
 
class Vision:
    def __init__(self):
        pyxel.init(160, 120)
        self.x = 0
        pyxel.run(self.update, self.draw)
 
    def update(self):
        self.x = (self.x + 1) % pyxel.width
 
    def draw(self):
        pyxel.cls(0)
        pyxel.rect(self.x, 0, 8, 8, 9)
 
Vision()

El ancho y la altura del laberinto generado se establecen en 48, 36

width, height = 48, 36
my_maze = Maze(width, height)
my_maze.generate()
 
class Vision:
    def __init__(self):
        pyxel.init(width * pixel, height * pixel)
        pyxel.run(self.update, self.draw)
 
    def update(self):
        if pyxel.btn(pyxel.KEY_Q):
            pyxel.quit()
 
        if pyxel.btn(pyxel.KEY_S):
            self.death = False
 
    def draw(self):
        # draw maze
        for x in range(height):
            for y in range(width):
                color = road_color if my_maze.map[x][y] is 0 else wall_color
                pyxel.rect(y * pixel, x * pixel, pixel, pixel, color)
        pyxel.rect(0, pixel, pixel, pixel, start_point_color)
        pyxel.rect((width - 1) * pixel, (height - 2) * pixel, pixel, pixel, end_point_color)
 
Vision()

Modifique la función de actualización y la función de dibujo para representar la ruta

self.index = 0
self.route = [] # 用于记录待渲染的路径
self.step = 1  # 步长,数值越小速度越快,1:每次1格
self.color = start_point_color
self.bfs_route = my_maze.bfs_route()

Entre ellos, el índice y el paso se utilizan para controlar la velocidad de renderizado. En la función de dibujo, el índice se incrementa en 1 cada vez, y luego el paso se calcula para obtener el subíndice real actual de la
función real_index draw ()

def draw(self):
    # draw maze
    for x in range(height):
        for y in range(width):
            color = road_color if my_maze.map[x][y] is 0 else wall_color
            pyxel.rect(y * pixel, x * pixel, pixel, pixel, color)
    pyxel.rect(0, pixel, pixel, pixel, start_point_color)
    pyxel.rect((width - 1) * pixel, (height - 2) * pixel, pixel, pixel, end_point_color)
 
    if self.index > 0:
        # draw route
        offset = pixel / 2
        for i in range(len(self.route) - 1):
            curr = self.route[i]
            next = self.route[i + 1]
            self.color = backtrack_color if curr in self.route[:i] and next in self.route[:i] else route_color
            pyxel.line(curr[0] + offset, (curr[1] + offset), next[0] + offset, next[1] + offset, self.color)
        pyxel.circ(self.route[-1][0] + 2, self.route[-1][1] + 2, 1, head_color)

Actualización de función ()

def update(self):
    if pyxel.btn(pyxel.KEY_ESCAPE):
        pyxel.quit()
 
    if pyxel.btn(pyxel.KEY_SPACE):
        self.death = False
 
    if not self.death:
        self.check_death()
        self.update_route()
 
def check_death(self):
    if self.dfs_model and len(self.route) == len(self.dfs_route) - 1:
        self.death = True
    elif not self.dfs_model and len(self.route) == len(self.bfs_route) - 1:
        self.death = True
 
def update_route(self):
    index = int(self.index / self.step)
    self.index += 1
    if index == len(self.route):  # move
        if self.dfs_model:
            self.route.append([pixel * self.dfs_route[index][0], pixel * self.dfs_route[index][1]])
        else:
            self.route.append([pixel * self.bfs_route[index][0], pixel * self.bfs_route[index][1]])
 
Vision()

Finalmente, la tecla ESC puede salir y la tecla espaciadora puede ejecutar la búsqueda automática de rutas del laberinto.


para resumir

Usamos el algoritmo de profundidad primero para lograr el recorrido del laberinto. En comparación con el algoritmo A , la idea de recursividad puede ser aceptable, pero de hecho es mucho más lento que el algoritmo A. Como tienes que atravesar cada esquina, tienes tiempo para usar el algoritmo A *. Para mejorar el laberinto.
Gif de pantalla adjunta
Inserte la descripción de la imagen aquí

Supongo que te gusta

Origin blog.csdn.net/weixin_44120833/article/details/111310293
Recomendado
Clasificación