深度優先アルゴリズムに基づいて迷路を割る


序文

プリムによってさまざまなマップを生成でき、迷路の問題はA *アルゴリズムによって解決できます。


1.プリムアルゴリズムがマップを生成します

Primアルゴリズムはマップを生成し、A *アルゴリズムを使用して解決しますが、数日後、A *アルゴリズムが出てこなかったため、アルゴリズム、深度優先アルゴリズム(DFS)を変更しました。

二、迷路を実現する

2.1デプスファーストアルゴリズム

深度優先検索は一種のグラフアルゴリズムです。英語の略語はDFS(Depth First Search)です。簡単に言うと、プロセスは、可能な分岐パスをそれぞれ、深くできなくなるまで深くすることであり、各ノードには1回しかアクセスできません。次の例:

A *アルゴリズムと比較すると、このアルゴリズムはA *よりも扱いにくく、消費量も多くなりますが、理解しやすいです。実例:自宅で鍵を紛失しましたが、鍵の具体的な場所がわかりません。どんな位置でも構いませんが、このときは隅々まで覗く必要があり、今までの隅々を頭に入れて二度と見ないので、鍵となる位置が見つかるまでこのようなカーペット探しをします。

2.2ライブラリのインポート

今回はpyxelライブラリを参照しました。Pyxelはpythonクラシックピクセルスタイルのゲーム制作エンジンです。
迷路を視覚化することができます。

2.3実装プロセス

Primは、
引き続きPrimを使用して迷路を生成します。迷路クラスを定義し、2次元配列を使用して迷路マップを表します。ここで、1は壁を表し、0は道路を表します。次に、左上隅を入口、右下隅を出口として初期化し、最後に下方向ベクトルを定義します。 。

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]

次のステップは、迷路の主な機能を生成することです。この関数には、get_neighbor_road(point)とdeal_with_not_visited()の2つの主要な関数があります。前者は入力座標点ポイントの隣接ノードを取得し、戻り値は2次元配列です。後者のdeal_with_not_visited()は、リストから壁をランダムに選択します。 、この壁で区切られた2つのセルのうち1つのセルのみがアクセスされた場合は、リストからこの壁を削除し、同時に壁を開きます。
セルを未アクセスのセルの隣接する壁としてマークします。リストの壁に参加します。

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)

Primのアルゴリズムは、リスト内のすべてのセルをランダムに選択することです。新しく追加されたセルと古いセルに違いはなく、どちらも50%の確率です。このように、迷路が生成されると、
DFSは自然に迷路から出て
DFS実現します。再帰プログラムを実装することです。ノードの座標が範囲外の場合、ノードが訪問された場合、またはノードが壁の場合は、迷路の外のパスではないため、直接戻ります。範囲外の場合は、訪問したノードとパスに追加します。
次に、ノードが出口の場合、プログラムの実行が終了し、パスが見つかったことを意味します。それ以外の場合は、4つの方向ベクトルをトラバースし、ノードの隣接パスを関数dfsに渡し、ウェイが見つかるか、プログラムのすべてのノードがトラバースされるまで、上記の手順を続けます。

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

視覚化
クラスVisionを実現するためpyxelの実行ロジックは、常にupdate関数とdraw関数を呼び出すことです。これにより、update関数でオブジェクトの座標を更新し、draw関数で画面に画像を描画できます。
したがって、最初に迷路を描き、次に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()

生成された迷路の幅と高さは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()

パスをレンダリングするように更新関数と描画関数を変更します

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

その中で、インデックスとステップはレンダリング速度を制御するために使用されます。描画関数では、インデックスは毎回1ずつインクリメントされ、ステップが計算されて現在の実際の添え字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)

関数update()

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

最後に、ESCキーを終了し、スペースキーで迷路の自動パス検索を実行できます。


総括する

迷路トラバーサルを実現するために深度優先アルゴリズムを使用します.Aアルゴリズムと比較して、再帰のアイデアは受け入れられるかもしれませんが、実際にはAアルゴリズムよりもはるかに遅いです。隅々までトラバースする必要があるため、A *アルゴリズムを使用する時間があります。迷路を改善する。
添付のディスプレイgif
ここに写真の説明を挿入

おすすめ

転載: blog.csdn.net/weixin_44120833/article/details/111310293