如何用python来开发一个迷宫,在上次利用python中的pygame包开发一个贪吃蛇后,这次依旧可以运用上次贪吃蛇的设计思路,想要实现迷宫主要是算法,生成不同的地图,三大迷宫生成算法 (Maze generation algorithm) – 深度优先,随机Prim,递归分割,
深度优先构建迷宫的思想就是,每次把新找到的未访问迷宫单元作为优先,寻找其相邻的未访问过的迷宫单元,直到所有的单元都被访问到。通俗的说,就是从起点开始随机走,走不通了就返回上一步,从下一个能走的地方再开始随机走。一般来说,深度优先法生成的迷宫极度扭曲,有着一条明显的主路。
然后是随机Prim,随机Prim算法生成的迷宫岔路较多,整体上较为自然而又复杂,其算法为
1.让迷宫全是墙.
2.选一个单元格作为迷宫的通路,然后把它的邻墙放入列表
3.当列表里还有墙时
1.从列表里随机选一个墙,如果这面墙分隔的两个单元格只有一个单元格被访问过
1.那就从列表里移除这面墙,即把墙打通,让未访问的单元格成为迷宫的通路
2.把这个格子的墙加入列表
2.如果墙两面的单元格都已经被访问过,那就从列表里移除这面墙
最后是递归分割,递归分割法生成的迷宫较为简单,有点像四叉树,直路多且不扭曲。通俗的说,就是把空间用十字分成四个子空间,然后在三面墙上挖洞(为了确保连通),之后对每个子空间继续做这件事直到空间不足以继续分割为止。此算法十分高效。
1)迷宫游戏是非常经典的游戏,要求随机生成一个迷宫,并求解迷宫;
2)要求查找并理解迷宫生成的算法,并尝试用两种不同的算法来生成随机的迷宫。
3)要求游戏支持玩家走迷宫,和系统走迷宫路径两种模式。玩家走迷宫,通过键盘
方向键控制,并在行走路径上留下痕迹;系统走迷宫路径要求基于 A*算法实现,输出走迷宫的最优路径并显示。设计交互友好的游戏图形界面。
借鉴 三大迷宫算法
迷宫的关键时生成随机的迷宫地图,这里算法选用Prim,原理在上面讲的清楚了,接下来我们首先要导入三个包,跟之前贪吃蛇讲的如何下载pygame很像,如果下载速度慢的话,可以将下载网址换成国内的镜像站,这里我们用的是阿里云,如下是替换过程
安装的三个包
①random() 方法返回随机生成的一个实数,它在[0,1)范围内。
②NumPy 是一个 Python 包。 它代表 “Numeric Python”。 它是一个由多维数组对象和用于处理数组的例程集合组成的库。
③matplotlib.pyplot是一个有命令风格的函数集合,它看起来和MATLAB很相似。每一个pyplot函数都使一副图像做出些许改变,例如创建一幅图,在图中创建一个绘图区域,在绘图区域中添加一条线等等。在matplotlib.pyplot中,各种状态通过函数调用保存起来,以便于可以随时跟踪像当前图像和绘图区域这样的东西。绘图函数是直接作用于当前axes(matplotlib中的专有名词,图形中组成部分,不是数学中的坐标系。)
我们看下代码:
import random
import numpy as np
from matplotlib import pyplot as plt
import matplotlib.cm as cm
num_rows = int(input("Rows: ")) # number of rows
num_cols = int(input("Columns: ")) # number of columns
#数组M将保存每个单元的数组信息。
#前四个坐标表示这些边上是否有墙
#第五个指示在搜索中是否访问过该区域。
#M(左、上、右、下,如果你访问过,请检查)
M = np.zeros((num_rows, num_cols, 5), dtype=np.uint8)
# 数组图像将是要显示的输出图像
image = np.zeros((num_rows * 10, num_cols * 10), dtype=np.uint8)
# 设置行列的初始值
r = 0
c = 0
history = [(r, c)] # 历史是访问地点的堆栈。
#沿着迷宫的小格子和敞开的墙壁,沿着小路画出一条小路。
#我们用while循环,重复循环直到没有历史记录,
#这意味着我们回到了最初的开始。
while history:
#从历史中随机选出行和列
r, c = random.choice(history)
M[r, c, 4] = 1 #将此位置指定为已访问
history.remove((r, c))
check = []
# 如果选中的小格子有多个边缘
# 使其链接已知的迷宫
if c > 0:
if M[r, c - 1, 4] == 1:
check.append('L')
elif M[r, c - 1, 4] == 0:
history.append((r, c - 1))
M[r, c - 1, 4] = 2
if r > 0:
if M[r - 1, c, 4] == 1:
check.append('U')
elif M[r - 1, c, 4] == 0:
history.append((r - 1, c))
M[r - 1, c, 4] = 2
if c < num_cols - 1:
if M[r, c + 1, 4] == 1:
check.append('R')
elif M[r, c + 1, 4] == 0:
history.append((r, c + 1))
M[r, c + 1, 4] = 2
if r < num_rows - 1:
if M[r + 1, c, 4] == 1:
check.append('D')
elif M[r + 1, c, 4] == 0:
history.append((r + 1, c))
M[r + 1, c, 4] = 2
# 随机选择其中一个边缘
if len(check):
move_direction = random.choice(check)
if move_direction == 'L':
M[r, c, 0] = 1
c = c - 1
M[r, c, 2] = 1
if move_direction == 'U':
M[r, c, 1] = 1
r = r - 1
M[r, c, 3] = 1
if move_direction == 'R':
M[r, c, 2] = 1
c = c + 1
M[r, c, 0] = 1
if move_direction == 'D':
M[r, c, 3] = 1
r = r + 1
M[r, c, 1] = 1
# 设置迷宫的入口和出口
M[0, 0, 0] = 1
M[num_rows - 1, num_cols - 1, 2] = 1
#生成迷宫
for row in range(0, num_rows):
for col in range(0, num_cols):
cell_data = M[row, col]
for i in range(10 * row + 2, 10 * row + 8):
image[i, range(10 * col + 2, 10 * col + 8)] = 255
if cell_data[0] == 1:
image[range(10 * row + 2, 10 * row + 8), 10 * col] = 255
image[range(10 * row + 2, 10 * row + 8), 10 * col + 1] = 255
if cell_data[1] == 1:
image[10 * row, range(10 * col + 2, 10 * col + 8)] = 255
image[10 * row + 1, range(10 * col + 2, 10 * col + 8)] = 255
if cell_data[2] == 1:
image[range(10 * row + 2, 10 * row + 8), 10 * col + 9] = 255
image[range(10 * row + 2, 10 * row + 8), 10 * col + 8] = 255
if cell_data[3] == 1:
image[10 * row + 9, range(10 * col + 2, 10 * col + 8)] = 255
image[10 * row + 8, range(10 * col + 2, 10 * col + 8)] = 255
# 迷宫形成
plt.imshow(image, cmap=cm.Greys_r, interpolation='none')
plt.show()
接下来我们自己定义row和Columns
row:13
Columns:14
生成下图迷宫
递归分割法生成的迷宫较为简单,有点像四叉树,直路多且不扭曲。通俗的说,就是把空间用十字分成四个子空间,然后在三面墙上挖洞(为了确保连通),之后对每个子空间继续做这件事直到空间不足以继续分割为止。此算法十分高效。
代码如下
import random
import numpy as np
from matplotlib import pyplot as plt
import matplotlib.cm as cm
# 这个函数将当前区域划分为四个小区域,并随机的在三个区域挖洞,
# 让四个区域彼此联通,分隔与挖洞点都是随机生成的。
def Recursive_division(r1, r2, c1, c2, M, image):
if r1 < r2 and c1 < c2:
rm = random.randint(r1, r2 - 1)
cm = random.randint(c1, c2 - 1)
cd1 = random.randint(c1, cm)
cd2 = random.randint(cm + 1, c2)
rd1 = random.randint(r1, rm)
rd2 = random.randint(rm + 1, r2)
d = random.randint(1, 4)
if d == 1:
M[rd2, cm, 2] = 1
M[rd2, cm + 1, 0] = 1
M[rm, cd1, 3] = 1
M[rm + 1, cd1, 1] = 1
M[rm, cd2, 3] = 1
M[rm + 1, cd2, 1] = 1
elif d == 2:
M[rd1, cm, 2] = 1
M[rd1, cm + 1, 0] = 1
M[rm, cd1, 3] = 1
M[rm + 1, cd1, 1] = 1
M[rm, cd2, 3] = 1
M[rm + 1, cd2, 1] = 1
elif d == 3:
M[rd1, cm, 2] = 1
M[rd1, cm + 1, 0] = 1
M[rd2, cm, 2] = 1
M[rd2, cm + 1, 0] = 1
M[rm, cd2, 3] = 1
M[rm + 1, cd2, 1] = 1
elif d == 4:
M[rd1, cm, 2] = 1
M[rd1, cm + 1, 0] = 1
M[rd2, cm, 2] = 1
M[rd2, cm + 1, 0] = 1
M[rm, cd1, 3] = 1
M[rm + 1, cd1, 1] = 1
Recursive_division(r1, rm, c1, cm, M, image)
Recursive_division(r1, rm, cm + 1, c2, M, image)
Recursive_division(rm + 1, r2, cm + 1, c2, M, image)
Recursive_division(rm + 1, r2, c1, cm, M, image)
elif r1 < r2:
rm = random.randint(r1, r2 - 1)
M[rm, c1, 3] = 1
M[rm + 1, c1, 1] = 1
Recursive_division(r1, rm, c1, c1, M, image)
Recursive_division(rm + 1, r2, c1, c1, M, image)
elif c1 < c2:
cm = random.randint(c1, c2 - 1)
M[r1, cm, 2] = 1
M[r1, cm + 1, 0] = 1
Recursive_division(r1, r1, c1, cm, M, image)
Recursive_division(r1, r1, cm + 1, c2, M, image)
num_rows = int(input("Rows: ")) # 行
num_cols = int(input("Columns: ")) # 列
r1 = 0
r2 = num_rows - 1
c1 = 0
c2 = num_cols - 1
#数组M将保存每个单元的数组信息。
#前四个坐标表示这些边上是否有墙
#第五个指示在搜索中是否访问过该格子。
#M(左、上、右、下,如果你访问过,请检查)
M = np.zeros((num_rows, num_cols, 5), dtype=np.uint8)
# The array image is going to be the output image to display
image = np.zeros((num_rows * 10, num_cols * 10), dtype=np.uint8)
Recursive_division(r1, r2, c1, c2, M, image)
# 设置迷宫出入口
M[0, 0, 0] = 1
M[num_rows - 1, num_cols - 1, 2] = 1
# 生成迷宫
for row in range(0, num_rows):
for col in range(0, num_cols):
cell_data = M[row, col]
for i in range(10 * row + 2, 10 * row + 8):
image[i, range(10 * col + 2, 10 * col + 8)] = 255
if cell_data[0] == 1:
image[range(10 * row + 2, 10 * row + 8), 10 * col] = 255
image[range(10 * row + 2, 10 * row + 8), 10 * col + 1] = 255
if cell_data[1] == 1:
image[10 * row, range(10 * col + 2, 10 * col + 8)] = 255
image[10 * row + 1, range(10 * col + 2, 10 * col + 8)] = 255
if cell_data[2] == 1:
image[range(10 * row + 2, 10 * row + 8), 10 * col + 9] = 255
image[range(10 * row + 2, 10 * row + 8), 10 * col + 8] = 255
if cell_data[3] == 1:
image[10 * row + 9, range(10 * col + 2, 10 * col + 8)] = 255
image[10 * row + 8, range(10 * col + 2, 10 * col + 8)] = 255
# 迷宫
plt.imshow(image, cmap=cm.Greys_r, interpolation='none')
plt.show()
关于寻求一条路径,从起点到终点,大家能想到很多算法,而最常用的就是A*算法。该算法的讲解可看b站这个视频
【编程三分钟】一看就懂的 A*寻路算法讲解 启发式搜索入门
def a_star_search(graph,start,goal):
frontier = PriorityQueue() # 存放我们这一轮探测过的所有边界方块
frontier.put(start,0)
came_from = {} #路径的来向
cost_so_far = {} # 当前代价
came_from[start] = None
cost_so_far[start] = 0
while not frontier.empty():
current = frontier.get() # 抽出代价最低的方块
if current == goal:
break
for next in graaph.neighbors(current):
new_cost = cost_so_far[current] + graph.cost(current,next) #新代价 = 当前代价+从current到next块的代价
if next not in cost_so_far or new_cost <cost_so_far[next]:
cost_so_far[next] = new_cost
priority = new_cost + heuristic(goal,next) #总代价 =当前代价+优先代价
frontier.put(next,priority)
came_from[next] = current
return came_from,cost_so_far
"""预估代价(曼哈顿距离)"""
def heuristic(a,b):
(x1,y1) = a
(x2,y2) = b
return abs(x1-x2)+abs(y1-y2)
界面的设计可以看我之前制作贪吃蛇的博客,方法思想差不多,这里不再过多赘述python做个贪吃蛇