迷路生成のアルゴリズム
迷路生成のアルゴリズムは数多くあります。
Maze Classification -Maze Creation Algorithms
迷路生成の各種アルゴリズムのC++実装 (Win/Mac両対応)
前回は棒倒し法を用いて迷路を作成してみました。
今回は穴掘り法を使ってみます。
穴掘り法
穴掘り法は、四方を通路に、中を全て壁にした四角形の中で、2つ先が壁でない限り、どんどんと壁を壊して穴を掘り通路にすることで、最終的に迷路を作ります。
最初に四方を道にするのは、道を伸ばす際に、迷路が外に出ないようにするためです。
棒倒し法よりは、複雑な迷路を作ることができるそうです。
下のサイトはどれも説明が上手です。
pythonで実装してみる
import random class Maze(): """ 迷路を作るクラス """ PATH = 0 WALL = 1 def __init__(self, width, height): self.maze = [] self.width = width self.height = height # 迷路は、幅高さ5以上の奇数で生成する。 if(self.height < 5 or self.width < 5): print('at least 5') exit() if (self.width % 2) == 0: self.width += 1 if (self.height % 2) == 0: self.height += 1 class MazeDig(Maze): """ 穴掘り法で迷路を作るクラス """ # 既に堀った場所をスタックしておくための配列 _stack_path = [] def set_inner_wall_dig_point(self): """ 穴を掘る前の処理。""" # 四方を通路に中を壁にする。 for _x in range(0, self.width): row = [] for _y in range(0, self.height): if (_x == 0 or _y == 0 or _x == self.width-1 or _y == self.height -1): cell = self.PATH else: cell = self.WALL row.append(cell) self.maze.append(row) # 一番最初に穴を掘り始める座標をランダムに決める。 # 座標は、xyとも奇数でなければならない。 x_start_dig = random.randrange(1, self.width-1, 2) y_start_dig = random.randrange(1, self.height-1, 2) self.maze[x_start_dig][y_start_dig] = self.PATH self._stack_path.append([x_start_dig, y_start_dig]) def dig_maze(self): """ 穴を掘る。 """ while True: # もし既に掘った場所で戻れる場所が無い場合、ループを抜ける。 if self._stack_path == []: break # 穴を掘り始めるxy座標をスタックからランダムにポップする。 _x, _y = self._stack_path.pop(random.randrange(0, len(self._stack_path))) while True: # 一つ先、二つ先とも壁である場合は穴を掘ることが出来る。 lst_direction_dig = [] if self.maze[_x][_y-1] == self.WALL and self.maze[_x][_y-2] == self.WALL: lst_direction_dig.append('up') if self.maze[_x+1][_y] == self.WALL and self.maze[_x+2][_y] == self.WALL: lst_direction_dig.append('right') if self.maze[_x][_y+1] == self.WALL and self.maze[_x][_y+2] == self.WALL: lst_direction_dig.append('down') if self.maze[_x-1][_y] == self.WALL and self.maze[_x-2][_y] == self.WALL: lst_direction_dig.append('left') # 穴を掘ることが出来ない場合は、ループを抜ける。 if lst_direction_dig == []: break # 掘る方向をランダムに決める。 else: direction_dig = random.choice(lst_direction_dig) # 穴を掘り、xyとも奇数の座標をスタックに入れる。 if direction_dig == 'up': self.maze[_x][_y-1] = self.PATH self.maze[_x][_y-2] = self.PATH self._stack_path.append([_x, _y-2]) elif direction_dig == 'right': self.maze[_x+1][_y] = self.PATH self.maze[_x+2][_y] = self.PATH self._stack_path.append([_x+2, _y]) elif direction_dig == 'down': self.maze[_x][_y+1] = self.PATH self.maze[_x][_y+2] = self.PATH self._stack_path.append([_x, _y+2]) elif direction_dig == 'left': self.maze[_x-1][_y] = self.PATH self.maze[_x-2][_y] = self.PATH self._stack_path.append([_x-2, _y]) def set_outer_wall_start_goal(self): """ 迷路に対して最後に行う処理。""" # 外周を壁にする for _x in range(0, self.width): for _y in range(0, self.height): if (_x == 0 or _y == 0 or _x == self.width-1 or _y == self.height -1): self.maze[_x][_y] = self.WALL # スタートとゴールをセットする。 self.maze[1][1] = 'S' self.maze[self.width-2][self.height-2] = 'G' def print_maze(self): """ 迷路を出力する。""" for row in self.maze: for cell in row: if cell == self.PATH: print(' ', end='') elif cell == self.WALL: print('###', end='') elif cell == 'S': print('STR', end='') elif cell == 'G': print('GOL', end='') print() maze_dig = MazeDig(20, 20) maze_dig.set_inner_wall_dig_point() maze_dig.dig_maze() maze_dig.set_outer_wall_start_goal() maze_dig.print_maze()
次回は壁伸ばし法による迷路生成を行います。