相信大家都玩过迷宫的游戏,对于简单的迷宫,我们可以一眼就看出通路,但是对于复杂的迷宫,可能要仔细寻找好久,甚至耗费数天,然后可能还要分别从入口和出口两头寻找才能找的到通路,甚至也可能找不到通路。
虽然走迷宫问题对于我们人类来讲比较复杂,但对于计算机来说却是很简单的问题。为什么这样说呢,因为看似复杂实则是有规可循的。
我们可以这么做,携带一根很长的绳子,从入口出发一直走,如果有岔路口就走最左边的岔口,直到走到死胡同或者找到出路。如果是死胡同则退回上一个岔路口,我们称之为岔口 A,
这时进入左边第二个岔口,进入第二个岔口后重复第一个岔口的步骤,直到找到出路或者死胡同退回来。当把该岔路口所有的岔口都走了一遍,还未找到出路就沿着绳子往回走,走到岔口 A 的前一个路口 B,重复上面的步骤。
不知道你有没有发现,这其实就是一个不断递归的过程,而这正是计算机所擅长的。
上面这种走迷宫的算法就是我们常说的深度优先遍历算法,与之相对的是广度优先遍历算法。有了理论基础,下面我们就来试着用 程序来实现一个走迷宫的小程序。
生成迷宫
生成迷宫有很多种算法,常用的有递归回溯法、递归分割法和随机 Prim 算法,我们今天是用的最后一种算法。
该算法的主要步骤如下:
1、迷宫行和列必须为奇数
2、奇数行和奇数列的交叉点为路,其余点为墙,迷宫四周全是墙
3、选定一个为路的单元格(本例选 [1,1]),然后把它的邻墙放入列表 wall
4、当列表 wall 里还有墙时:
4.1、从列表里随机选一面墙,如果这面墙分隔的两个单元格只有一个单元格被访问过
4.1.1、那就从列表里移除这面墙,同时把墙打通
4.1.2、将单元格标记为已访问
4.1.3、将未访问的单元格的邻墙加入列表 wall
4.2、如果这面墙两面的单元格都已经被访问过,那就从列表里移除这面墙
我们定义一个 Maze 类,用二维数组表示迷宫地图,其中 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 = [] # right up left down self.dx = [1, 0, -1, 0] self.dy = [0, -1, 0, 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)
该函数里面有两个主要函数 get_neighbor_road(point) 和 deal_with_not_visited(),前者会获得传入坐标点 point 的邻路节点,返回值是一个二维数组,后者 deal_with_not_visited() 函数处理步骤 4.1 的逻辑。
由于 Prim 随机算法是随机的从列表中的所有的单元格进行随机选择,新加入的单元格和旧加入的单元格被选中的概率是一样的,因此其分支较多,生成的迷宫较复杂,难度较大,当然看起来也更自然些。生成的迷宫。
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]
[1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1]
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]
[1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1]
[1, 0, 0本文来源gaodai$ma#com搞$$代**码网, 0, 1, 0, 1, 0, 1, 0, 1]
[1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1]
[1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1]
[1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1]
[1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0]
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]