文章目录
随机迷宫算法
迷宫生成算法也有很多种,也正是由于结果随机,反过来讲就意味着该问题由无数种解,所以它完全可以利用回溯的思想。
常见算法由三种:
- 深度优先算法
- 随机prim算法
- 十字分割法
1. 深度优先算法
想象你是一个土拨鼠在挖洞,你不知道往哪个方向挖?这时候应该怎么做呢?通常来说,你会在这个点做上标记,然后随机选择一个方向挖,挖通一堵墙之后,再随机选择一个方向,并做上标记,然后继续挖挖墙。当你挖到了死路,也就是再挖就挖穿了的情况下,你只能往回走,走到上一个标记的岔路口,然后选择其他的方向继续挖墙。如此往复,直到你挖遍整个地图,你回头一看,哇,一个迷宫就挖好了!
初始迷宫
首先确定标准初始迷宫形式:
- 长宽必须为奇数
- 所有的双奇数坐标为道路,其余为墙
算法
- 初始化迷宫,如 初始迷宫 所示,将所有的道路加入未访问列表
- 随意选择一个道路作为出发点,并将之移除未访问列表
- 维护一个分支栈
- 只要迷宫中还存在未访问的道路,进入循环:
- 对于当前点,维护一个相邻点列表。
- 搜索当前道路的相邻道路,如果该带路未被访问过,则加入相邻点列表。
- 如果存在这样的相邻点:
- 将当前道路压入分支栈
- 随机选择一个相邻点
- 移除相邻点与当前点之间的墙壁
- 将相邻点作为出发点,并将之移除未访问列表,进入循环。
- 如果找不到符合要求的相邻点
- 就意味着此条分支已经走到尽头
- 此时从分支栈当中弹出一个新的元素,进入循环。
代码
import random
import numpy as np
import matplotlib.pyplot as plt
# 深度优先算法:
# 1. 初始化迷宫,如 **初始迷宫** 所示,将所有的道路加入未访问列表
# 2. 随意选择一个道路作为出发点,并将之移除未访问列表
# 3. 维护一个分支栈
# 4. 只要迷宫中还存在未访问的道路,进入循环:
# 1. 对于当前点,维护一个相邻点列表。
# 2. 搜索当前道路的相邻道路,如果该带路未被访问过,则加入相邻点列表。
# 3. 如果存在这样的相邻点:
# 1. 将当前道路压入分支栈
# 2. 随机选择一个相邻点
# 3. 移除相邻点与当前点之间的墙壁
# 4. 将相邻点作为出发点,并将之移除未访问列表,进入循环。
# 4. 如果找不到符合要求的相邻点
# 1. 就意味着此条分支已经走到尽头
# 2. 此时从分支栈当中弹出一个新的元素,进入循环。
row = 31
col = 73
shape = (row, col)
unvisited = []
# 生成矩阵
matrix = np.ones(shape)
for i in range(row):
for j in range(col):
if i % 2 != 0 and j % 2 != 0:
matrix[i, j] = 0
# 未访问道路列表
unvisited.append((i, j))
# 起点
r, c = 1, 1
# 删除未访问
unvisited.remove((r, c))
# 栈
stack = []
# 当还存在未访问的迷宫单元,进入循环
while unvisited:
# 当前单元必须在矩阵之中,否者退出并从未访问列表当中移除
if r in range(1, row - 1) and c in range(1, col - 1):
# 搜索当前单元是否有未被访问过的相邻单元
neighbour = []
if (r - 2, c) in unvisited:
neighbour.append((r - 2, c))
if (r + 2, c) in unvisited:
neighbour.append((r + 2, c))
if (r, c - 2) in unvisited:
neighbour.append((r, c - 2))
if (r, c + 2) in unvisited:
neighbour.append((r, c + 2))
# 如果有相邻的单元
if neighbour:
# 将当前迷宫单元入栈
stack.append((r, c))
# 随机选择一个相邻单元
r_new, c_new = random.choice(neighbour)
# 移除当前迷宫单元与相邻迷宫单元的墙,中间的墙坐标就是横纵坐标的平均值
r_wall = int((r + r_new) / 2)
c_wall = int((c + c_new) / 2)
matrix[r_wall, c_wall] = False
# 用相邻迷宫单元作为当前迷宫单元
r, c = r_new, c_new
# 从未访问列表当中移除
unvisited.remove((r, c))
# 如果没有相邻单元,意味着该条线路已经挖到底,则试图返回上一个分支节点,这时需要判断栈空不空,若不空,则弹出一个元素
elif stack:
# 栈顶的迷宫单元出栈
r, c = stack.pop()
else:
unvisited.remove((r, c))
# 入口
for r in range(1