python——还没有成品的迷宫(未完成待续)


前言

如何用python来开发一个迷宫,在上次利用python中的pygame包开发一个贪吃蛇后,这次依旧可以运用上次贪吃蛇的设计思路,想要实现迷宫主要是算法,生成不同的地图,三大迷宫生成算法 (Maze generation algorithm) – 深度优先,随机Prim,递归分割,


一、三大迷宫算法是什么?

1.1深度优先算法

深度优先构建迷宫的思想就是,每次把新找到的未访问迷宫单元作为优先,寻找其相邻的未访问过的迷宫单元,直到所有的单元都被访问到。通俗的说,就是从起点开始随机走,走不通了就返回上一步,从下一个能走的地方再开始随机走。一般来说,深度优先法生成的迷宫极度扭曲,有着一条明显的主路。

1.2随机Prim

然后是随机Prim,随机Prim算法生成的迷宫岔路较多,整体上较为自然而又复杂,其算法为

1.让迷宫全是墙.
2.选一个单元格作为迷宫的通路,然后把它的邻墙放入列表
3.当列表里还有墙时
1.从列表里随机选一个墙,如果这面墙分隔的两个单元格只有一个单元格被访问过
1.那就从列表里移除这面墙,即把墙打通,让未访问的单元格成为迷宫的通路
2.把这个格子的墙加入列表
2.如果墙两面的单元格都已经被访问过,那就从列表里移除这面墙

1.3递归分割

最后是递归分割,递归分割法生成的迷宫较为简单,有点像四叉树,直路多且不扭曲。通俗的说,就是把空间用十字分成四个子空间,然后在三面墙上挖洞(为了确保连通),之后对每个子空间继续做这件事直到空间不足以继续分割为止。此算法十分高效。

二、设计思路

2.1迷宫要求

1)迷宫游戏是非常经典的游戏,要求随机生成一个迷宫,并求解迷宫;
2)要求查找并理解迷宫生成的算法,并尝试用两种不同的算法来生成随机的迷宫。
3)要求游戏支持玩家走迷宫,和系统走迷宫路径两种模式。玩家走迷宫,通过键盘
方向键控制,并在行走路径上留下痕迹;系统走迷宫路径要求基于 A*算法实现,输出走迷宫的最优路径并显示。设计交互友好的游戏图形界面。

2.2算法选择Prim

借鉴 三大迷宫算法

迷宫的关键时生成随机的迷宫地图,这里算法选用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
生成下图迷宫
在这里插入图片描述

2.3Recursive division (递归分割算法)

递归分割法生成的迷宫较为简单,有点像四叉树,直路多且不扭曲。通俗的说,就是把空间用十字分成四个子空间,然后在三面墙上挖洞(为了确保连通),之后对每个子空间继续做这件事直到空间不足以继续分割为止。此算法十分高效。
代码如下

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()

运行后结果如下
在这里插入图片描述

2.4 A*算法

关于寻求一条路径,从起点到终点,大家能想到很多算法,而最常用的就是A*算法。该算法的讲解可看b站这个视频

【编程三分钟】一看就懂的 A*寻路算法讲解 启发式搜索入门


接下来是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)

2.5界面设计

界面的设计可以看我之前制作贪吃蛇的博客,方法思想差不多,这里不再过多赘述python做个贪吃蛇


猜你喜欢

转载自blog.csdn.net/weixin_44120833/article/details/111275284