zoukankan      html  css  js  c++  java
  • [python实现] 递归回溯(深度优先)构造随机迷宫

    最近对迷宫相关的算法产生了兴趣,可能因为了解了一点点图论和算法,突然发现自己这种菜鸡也能试着实现一下迷宫生成

    目前主要打算使用Python实现, 之后还会用Unity实现更加形象的生成过程

    主要参考:python_marble_xu的博客-CSDN博客https://blog.csdn.net/marble_xu/category_8691561.html

    和国外热爱迷宫算法的大佬Jamis Buck的书《 Mazes for Programmers 》 

    目录

    一.预演

    二. 破壁

    三.完整代码

    四. 效果展示



    一.预演

    首先思考如何存储, 当然二维数组比较方便

    那如何区分 路与墙 呢? 主要靠如下的映射

     灰色的我们会标志为1, 观察白色的点有何特点, 是的, 他们的横纵坐标均为奇数, 达成这种映射我们可以较为方便的初始化所有空地, 接下来要做的就是打通中间的一些墙, (最后输出的时候,0为可行走的空地,1为墙)

    开始的时候设为全都是墙,这样我们在构建迷宫的过程中无需另设表示是否访问的标志

    map = [[1 for x in range(self.width)] for y in range(self.height)]

    二. 破壁

    这个过程和深度优先走迷宫算是互逆了,这里是在"解放",走迷宫是在"占领"

    随机找个横纵坐标奇数的起点开始并设为空

    startX, startY = (randint(0, widthLimited - 1), randint(0, heightLimited - 1))
    map.setMap(2 * startX + 1, 2 * startY + 1, ITEM_TYPE.EMPTY)

    checklist用来存坐标对,在之后的一系列过程中, 我们会将随机选择的方向上(且合理)的点存入其中, 

    if x > 0:
        if not map.isVisited(2 * (x - 1) + 1, 2 * y + 1):
            directions.append(DIRECTION.LEFT)
    if y > 0:
        if not map.isVisited(2 * x + 1, 2 * (y - 1) + 1):
            directions.append(DIRECTION.UP)
    if x < widthLimited - 1:
        if not map.isVisited(2 * (x + 1) + 1, 2 * y + 1):
            directions.append(DIRECTION.RIGHT)
    if y < heightLimited - 1:
        if not map.isVisited(2 * x + 1, 2 * (y + 1) + 1):
            directions.append(DIRECTION.DOWN)

    注意:

    我们判断的是"奇数对"坐标,

    别忘了将中间的墙打通哦

    if len(directions):
        # 随机选择方向
        direction = choice(directions)
    
        if direction == DIRECTION.LEFT:
            map.setMap(2 * (x - 1) + 1, 2 * y + 1, ITEM_TYPE.EMPTY)
            map.setMap(2 * x, 2 * y + 1, ITEM_TYPE.EMPTY)
            waitinglist.append((x - 1, y))
        elif direction == DIRECTION.UP:
            map.setMap(2 * x + 1, 2 * (y - 1) + 1, ITEM_TYPE.EMPTY)
            map.setMap(2 * x + 1, 2 * y, ITEM_TYPE.EMPTY)
            waitinglist.append((x, y - 1))
        elif direction == DIRECTION.RIGHT:
            map.setMap(2 * (x + 1) + 1, 2 * y + 1, ITEM_TYPE.EMPTY)
            map.setMap(2 * x + 2, 2 * y + 1, ITEM_TYPE.EMPTY)
            waitinglist.append((x + 1, y))
        elif direction == DIRECTION.DOWN:
            map.setMap(2 * x + 1, 2 * (y + 1) + 1, ITEM_TYPE.EMPTY)
            map.setMap(2 * x + 1, 2 * y + 2, ITEM_TYPE.EMPTY)
            waitinglist.append((x, y + 1))
        return True
    else:
        return False

    在无路可通时(即走到某个犄角旮旯,周围的奇数坐标均已开通过), 就返回错误, 并删除 待处理队列waitinglist(整体上看这其实是在模拟栈) 最后一个坐标对, 实现回溯的效果

        waitinglist = []
        waitinglist.append((startX, startY))
        while len(waitinglist):
            if not checkPostion(map, waitinglist[-1][0], waitinglist[-1][1], widthLimited, heightLimited, waitinglist):
                waitinglist.remove(waitinglist[-1])

    三.完整代码

    from random import randint, choice
    from enum import Enum
    
    
    class ITEM_TYPE(Enum):
        EMPTY = 0,
        BLOCK = 1,
    
    class DIRECTION(Enum):
        LEFT = 0,
        UP = 1,
        RIGHT = 2,
        DOWN = 3,
    
    
    class Map():
        def __init__(self, width, height):
            self.width = width
            self.height = height
    
            # 初始化均为墙
            self.map = [[1 for x in range(self.width)] for y in range(self.height)]
    
        def resetMap(self, value):
            for y in range(self.height):
                for x in range(self.width):
                    self.setMap(x, y, value)
    
        def setMap(self, x, y, value):
            if value == ITEM_TYPE.EMPTY:
                self.map[y][x] = 0
            elif value == ITEM_TYPE.BLOCK:
                self.map[y][x] = 1
    
        def isVisited(self, x, y):
            return self.map[y][x] == 0
    
        def showMap(self):
            for row in self.map:
                for item in row:
                    if item == 0:
                        print('  ', end='')
                    elif item == 1:
                        print('@ ', end='')
                print('')
    
    
    def checkPostion(map, x, y, widthLimited, heightLimited, waitinglist):
        directions = []
        if x > 0:
            if not map.isVisited(2 * (x - 1) + 1, 2 * y + 1):
                directions.append(DIRECTION.LEFT)
        if y > 0:
            if not map.isVisited(2 * x + 1, 2 * (y - 1) + 1):
                directions.append(DIRECTION.UP)
        if x < widthLimited - 1:
            if not map.isVisited(2 * (x + 1) + 1, 2 * y + 1):
                directions.append(DIRECTION.RIGHT)
        if y < heightLimited - 1:
            if not map.isVisited(2 * x + 1, 2 * (y + 1) + 1):
                directions.append(DIRECTION.DOWN)
    
        if len(directions):
            # 随机选择方向
            direction = choice(directions)
    
            if direction == DIRECTION.LEFT:
                map.setMap(2 * (x - 1) + 1, 2 * y + 1, ITEM_TYPE.EMPTY)
                map.setMap(2 * x, 2 * y + 1, ITEM_TYPE.EMPTY)
                waitinglist.append((x - 1, y))
            elif direction == DIRECTION.UP:
                map.setMap(2 * x + 1, 2 * (y - 1) + 1, ITEM_TYPE.EMPTY)
                map.setMap(2 * x + 1, 2 * y, ITEM_TYPE.EMPTY)
                waitinglist.append((x, y - 1))
            elif direction == DIRECTION.RIGHT:
                map.setMap(2 * (x + 1) + 1, 2 * y + 1, ITEM_TYPE.EMPTY)
                map.setMap(2 * x + 2, 2 * y + 1, ITEM_TYPE.EMPTY)
                waitinglist.append((x + 1, y))
            elif direction == DIRECTION.DOWN:
                map.setMap(2 * x + 1, 2 * (y + 1) + 1, ITEM_TYPE.EMPTY)
                map.setMap(2 * x + 1, 2 * y + 2, ITEM_TYPE.EMPTY)
                waitinglist.append((x, y + 1))
            return True
        else:
            return False
    
    
    def recursive(map, widthLimited, heightLimited):
        startX, startY = (randint(0, widthLimited - 1), randint(0, heightLimited - 1))
        map.setMap(2 * startX + 1, 2 * startY + 1, ITEM_TYPE.EMPTY)
    
        waitinglist = []
        waitinglist.append((startX, startY))
        while len(waitinglist):
            if not checkPostion(map, waitinglist[-1][0], waitinglist[-1][1], widthLimited, heightLimited, waitinglist):
                waitinglist.remove(waitinglist[-1])
    
    # 开始构造迷宫
    def startCreateMaze(map):
        recursive(map, map.width // 2, map.height // 2)
    
    def run():
        WIDTH = 27
        HEIGHT = 17
        map = Map(WIDTH, HEIGHT)
        startCreateMaze(map)
        map.showMap()
    
    
    if __name__ == "__main__":
        run()

    四. 效果展示

    注意地图的宽高均设为奇数哦, 可参见(一)中的图, 这样才能形成合理映射

    本文来自博客园,作者:泥烟,CSDN同名, 转载请注明原文链接:https://www.cnblogs.com/Knight02/p/15799035.html

  • 相关阅读:
    web页面中四种常见必测控件
    python03
    python基础2
    python基础
    如何定位测试用例的作用?
    需求测试的注意事项有哪些?
    性能测试的流程?
    简述bug的生命周期?
    Python字符串
    Python基础语法
  • 原文地址:https://www.cnblogs.com/Knight02/p/15799035.html
Copyright © 2011-2022 走看看