最近闲来无事想做一个质量高一点的进阶版的迷宫小游戏,首先就要先生成最基础的迷宫地图,因此学习并整理了一下迷宫生成算法。由于python更容易实现这些算法,因此首先使用pyhon将各种生成算法整理一遍,之后再用Qt或者javascript重写一遍,再加上可视化。
大概了解了一下,生成迷宫的算法主要有三种思路,其中最小生成树算法又可以分为选点法(prim)和选边法(kruskal):
- 随机深度优先算法
- 递归分割算法
- 随机prim最小生成树算法
- *kruskal最小生成树算法(使用并查集实现)
话不多说,进入正文。
1 随机深度优先算法
之所以叫做随机深度优先算法,是因为传统的深度优先算法中已经确定了遍历上下左右四个方向的顺序,因此每次生成的地图都是一样的,而且特别简单。为了增加地图的随机性,需要在每次遍历上下左右四个方向的时候先将四个方向随机打乱再进行遍历。先看看随机深度优先算法生成的地图,效果还是不错的:
在这里总结几个我在编程实现的过程中遇到的小问题:
- 如果设置了迷宫的起点start(我设置的是左上角)和终点dest(我设置的是右下角),建议将深度优先遍历函数的起点设置为dest,将递归结束的条件设置为到达start。这样的好处是:能够增加游戏的难度,具体原因自行体会。
- 将迷宫的长宽(包括边框)设置为奇数,每次往前遍历前进的距离为2而不是1,这样能确保生成的地图更加美观。如果设置每次前进的步长为1,生成的地图可能如下图所示:
python3的源代码为:
import numpy as np
import time
import random
import copy
class Maze(object):
def __init__(self, width = 11, height = 11):
# 迷宫最小长宽为5
assert width >= 5 and height >= 5, "Length of width or height must be larger than 5."
# 确保迷宫的长和宽均为奇数
self.width = (width // 2) * 2 + 1
self.height = (height // 2) * 2 + 1
self.start = [1, 0]
self.destination = [self.height - 2, self.width - 1]
self.matrix = self.init_matrix(self.width, self.height)
def init_matrix(self, width, height):
# 将地图初始化为-1, -1表示墙
matrix = -np.ones((height, width))
# 将出口和入口处的值设置为0, 0表示通道
matrix[self.start[0], self.start[1]] = 0
matrix[self.destination[0], self.destination[1]] = 0
return matrix
def print_matrix(self):
for i in range(self.height):
for j in range(self.width):
if self.matrix[i][j] == -1:
print('□', end = '')
elif self.matrix[i][j] == 0:
print(' ', end = '')
elif self.matrix[i][j] == 1:
print('■', end = '')
print('')
def generate_matrix_dfs(self):
visit_flag = [[0 for i in range(self.width)] for j in range(self.height)]
def check(row, col, row_, col_):
temp_sum = 0
for d in [[0, 1], [0, -1], [1, 0], [-1, 0]]:
temp_sum += self.matrix[row_ + d[0]][col_ + d[1]]
return temp_sum <= -3
def dfs(row, col):
visit_flag[row][col] = 1
self.matrix[row][col] = 0
if row == self.start[0] and col == self.start[1] + 1:
return
directions = [[0, 2], [0, -2], [2, 0], [-2, 0]]
random.shuffle(directions)
for d in directions:
row_, col_ = row + d[0], col + d[1]
if row_ > 0 and row_ < self.height - 1 and col_ > 0 and col_ < self.width - 1 and visit_flag[row_][col_] == 0 and check(row, col, row_, col_):
if row == row_:
visit_flag[row][min(col, col_) + 1] = 1
self.matrix[row][min(col, col_) + 1] = 0
else:
visit_flag[min(row, row_) + 1][col] = 1
self.matrix[min(row, row_) + 1][col] = 0
dfs(row_, col_)
dfs(self.destination[0], self.destination[1] - 1)
self.matrix[self.start[0], self.start[1] + 1] = 0
# 这里的长和宽设置的是50,但是实际生成的迷宫长宽会是51
maze = Maze(50, 50)
maze.generate_matrix_dfs()
maze.print_matrix()
2 递归分割算法
// TODO
3 随机prim最小生成树算法
// TODO
4 *kruskal最小生成树算法
// TODO