Python-迷宫问题-递归非递归

本文使用递归,非递归,深度优先,广度优先,算法探索迷宫,且用Python turtle包绘制出迷宫,使迷宫过程可视化

迷宫类

# 迷宫类
class Maze(object):
# 读取迷宫数据,初始化迷宫内部,并找到海龟初始位置。
    def __init__(self, mazeFileName):
        rowsInMaze = 0                            #初始化迷宫行数
        columnsInMaze = 0                         #初始化迷宫列数
        self.mazelist = []                        #初始化迷宫列表
        mazeFile = open(mazeFileName, 'r')        #读取迷宫文件
        for line in mazeFile:                    #按行读取
            rowList = []                         #初始化行列表
            col = 0                             #初始化列
            # for ch in line[:-1]:                #这样会丢失最后一列
            for ch in line:                        #按列读取
                rowList.append(ch)                #添加到行列表
                if ch == 'S':                    #S为乌龟初始位置,即迷宫起点
                    self.startRow = rowsInMaze    #乌龟初始行
                    self.startCol = col         #乌龟初始列
                col = col + 1                     #下一列
            rowsInMaze = rowsInMaze + 1         #下一行
            self.mazelist.append(rowList)        #行列表添加到迷宫列表
            columnsInMaze = len(rowList)         #获取迷宫总列数
        self.rowsInMaze = rowsInMaze             #设置迷宫总行数
        self.columnsInMaze = columnsInMaze        #设置迷宫总列数
        self.xTranslate = -columnsInMaze/2         #设置迷宫左上角的初始x坐标
        self.yTranslate = rowsInMaze/2             #设置迷宫左上角的初始y坐标
        self.t = turtle.Turtle()                #创建一个海龟对象
        self.t.shape('turtle')                    #给当前指示点设置样式(类似鼠标箭头),海龟形状为参数指定的形状名,指定的形状名应存在于TurtleScreen的shape字典中。多边形的形状初始时有以下几种:"arrow", "turtle", "circle", "square", "triangle", "classic"。
        self.wn = turtle.Screen()                #创建一个能在里面作图的窗口
        self.wn.setworldcoordinates(-columnsInMaze/2, -rowsInMaze/2, columnsInMaze/2, rowsInMaze/2)            #设置世界坐标系,原点在迷宫正中心。参数依次为画布左下角x轴坐标、左下角y轴坐标、右上角x轴坐标、右上角y轴坐标

    # 在屏幕上绘制迷宫
    def drawMaze(self):
        self.t.speed(20)                        #绘图速度
        for y in range(self.rowsInMaze):        #按单元格依次循环迷宫
            for x in range(self.columnsInMaze):
                if self.mazelist[y][x] == OBSTACLE:    #如果迷宫列表的该位置为障碍物,则画方块
                    self.drawCenteredBox(x + self.xTranslate, -y + self.yTranslate, 'orange')

    # 画方块
    def drawCenteredBox(self, x, y, color):
        self.t.up()                                #画笔抬起
        self.t.goto(x - 0.5, y - 0.5)            #前往参数位置,此处0.5偏移量的作用是使乌龟的探索路线在单元格的正中心位置
        self.t.color(color)                        #方块边框为橙色
        self.t.fillcolor('green')                #方块内填充绿色
        self.t.setheading(90)                    #设置海龟的朝向,标准模式:0 - 东,90 - 北,180 - 西,270 - 南。logo模式:0 - 北,90 - 东,180 - 南,270 - 西。
        self.t.down()                            #画笔落下
        self.t.begin_fill()                        #开始填充
        for i in range(4):                        #画方块边框
            self.t.forward(1)                    #前进1个单位
            self.t.right(90)                    #右转90度
        self.t.end_fill()                        #结束填充

    # 移动海龟
    def moveTurtle(self, x, y):
        self.t.up()                                #画笔抬起
        self.t.setheading(self.t.towards(x + self.xTranslate, -y + self.yTranslate))    #setheading()设置海龟朝向,towards()从海龟位置到由(x, y),矢量或另一海龟位置连线的夹角。此数值依赖于海龟初始朝向,由"standard"、"world"或"logo" 模式设置所决定。
        self.t.goto(x + self.xTranslate, -y + self.yTranslate)    #前往目标位置

    # 画路径圆点
    def dropBreadcrumb(self, color):
        self.t.dot(color)                        #dot(size=None, color)画路径圆点

    # 用以更新迷宫内的状态及在窗口中改变海龟位置,行列参数为乌龟的初始坐标。
    def updatePosition(self, row, col, val):
        self.mazelist[row][col] = val             #设置该标记状态为当前单元格的值
        self.moveTurtle(col, row)                #移动海龟
        if val == PART_OF_PATH:                 #其中一条成功路径的圆点的颜色
            color = 'green'
        elif val == TRIED:                        #尝试用的圆点的颜色
            color = 'black'
        elif val == DEAD_END:                    #死胡同用的圆点的颜色
            color = 'red'
        self.dropBreadcrumb(color)                #画路径圆点并上色

    # 用以判断当前位置是否为出口。
    def isExit(self, row, col):
        # 根据海龟位置是否在迷宫的4个边线位置判断
        # return (row == 0 or row == self.rowsInMaze - 1 or col == 0 or col == self.columnsInMaze - 1)
        return self.mazelist[row][col] == '0'

    # 返回键对应的值,影响searchFrom()中maze[startRow][startColumn]值的获取
    def __getitem__(self, key):
        return self.mazelist[key]

    # 将mazelist的内容写入到文件中
    def save(self,mazeFileName):

        self.mazelist[self.startRow][self.startCol] = 's'
        with open(mazeFileName, 'w',encoding='utf-8') as fp:
            str = ''
            for rowlist in self.mazelist:
                for ch in rowlist:
                    str += ch
                fp.write(str)
                str = ''

解析:重点讲一下save成员函数,后面的三种算法更新位置都是更新迷宫类中的mazelist,在探索迷宫后,利用save即可将mazelist写入到文件中,得到迷宫的解。

走迷宫算法中会用的栈和队列,下面是栈和队列的实现

栈类

# 栈 右边为栈顶
class Stark:
    def __init__(self):
        self.stark_list = []

    def push(self, item):
        self.stark_list.append(item)

    def pop(self):
        return  self.stark_list.pop()

    def peek(self):
        return self.stark_list[-1]

    def is_empty(self):
        return self.stark_list == []

    def size(self):
        return len(self.stark_list)

    def show(self):
        print(self.stark_list)

队列

# 队列
class Queue:
    def __init__(self):
        self.items = []

    def is_empty(self):
        return self.items == []

    def push(self,item):
        self.items.insert(0,item)

    def pop(self):
        return self.items.pop()

    def size(self):
        return len(self.items)

    def show(self):
        print(self.items)

递归求解

递归求解的基本思路是:

走迷宫问题可化为若干个子问题,向某个方向走一格,然后以当前点为起点走迷宫,再向某个方向走一格,再以当前点为起点走迷宫......直到当前点就是起点结束递归

详细为:

  • 判断当前点是不是障碍或者已走过的路径,如果是则结束此次递归调用

  • 判断当前点是不是出口,如果是则结束整个函数递归

  • 更新当前点的位置,更新为已走过的路径

  • 依次尝试四个方向,进入更深一层的递归

def searchFrom(maze, startRow, startColumn):
    # 从初始位置开始尝试四个方向,直到找到出路。
    # 1. 遇到障碍
    if maze[startRow][startColumn] == OBSTACLE:
        return False
    # 2. 发现已经探索过的路径或死胡同
    if maze[startRow][startColumn] == TRIED or maze[startRow][startColumn] == DEAD_END:
        return False
    # 3. 发现出口
    if maze.isExit(startRow, startColumn):
        maze.updatePosition(startRow, startColumn, PART_OF_PATH)#显示出口位置,注释则不显示此点
        return True
    maze.updatePosition(startRow, startColumn, TRIED)#更新迷宫状态、设置海龟初始位置并开始尝试
    # 4. 依次尝试每个方向
    found = searchFrom(maze, startRow - 1, startColumn) or \
            searchFrom(maze, startRow + 1, startColumn) or \
            searchFrom(maze, startRow, startColumn - 1) or \
            searchFrom(maze, startRow, startColumn + 1)
    if found:                                                    #找到出口
        maze.updatePosition(startRow, startColumn, PART_OF_PATH)#返回其中一条正确路径
    else:                                                        #4个方向均是死胡同
        maze.updatePosition(startRow, startColumn, DEAD_END)
    return found

注:代码中found 的部分会在searchForm结束调用后,根据四个方向的探索结果,更新上一层函数探索的点位,其主要作用是为了将正确路径返回到迷宫文件中(后面会提到),与走迷宫关系不大,没有这一步也可以走迷宫,只是拿不到走迷宫的路径。

非递归求解

深度优先算法(利用栈)

深度优先算法的思路与递归算法无二,毕竟所有的递归算法都可以用栈改写为非递归算法

基本思路:

在此算法中,主要是用栈来存储可以探索的位置。利用栈后进先出的特点,在一条分路上探索失败时,回到最近一次存储的可探索位置,然后继续探索。

详细为:

  • 判断当前点是不是出口,是的话就结束循环

  • 判断当前点是不是以探索过的路径,是的话就弹出栈,回到上一个位置探索剩余方向,如果没有剩余探索方向继续弹出栈,直到回到有可以探索的方向

  • 如果不是以前探索过过的路径,就在当前点依次探索四个方向,并且把当前位置入栈

def searchFrom_non_rec(maze, startRow, startColumn):

    sta = Stark()
    sta.push((startRow,startColumn))
    find = False

    while not sta.is_empty():
        temp = sta.peek()
        # 1.判断当前点是不是出口
        if maze.isExit(temp[0],temp[1]):
            find = True
            break

        # 2. 发现已经探索过的路径 回退专用,因为我判断的存在,我不可能进入已经走过的路径
        if maze[temp[0]][temp[1]] == TRIED:
            sta.pop()
            temp = sta.peek()

        # 3. 依次尝试每个方向
        if maze[temp[0]][temp[1] + 1] != TRIED and maze[temp[0]][temp[1] + 1] != OBSTACLE:
            sta.push((temp[0], temp[1] + 1))
            maze.updatePosition(temp[0], temp[1], TRIED)
            continue

        elif maze[temp[0]][temp[1] - 1] != TRIED and maze[temp[0]][temp[1] - 1] != OBSTACLE:
            sta.push((temp[0], temp[1] - 1))
            maze.updatePosition(temp[0], temp[1], TRIED)
            continue

        elif maze[temp[0] + 1][temp[1]] != TRIED and maze[temp[0] + 1][temp[1]] != OBSTACLE:
            sta.push((temp[0] + 1, temp[1]))
            maze.updatePosition(temp[0], temp[1], TRIED)
            continue

        elif maze[temp[0] - 1][temp[1]] != TRIED and maze[temp[0] - 1][temp[1]] != OBSTACLE:
            sta.push((temp[0] - 1, temp[1]))
            maze.updatePosition(temp[0], temp[1], TRIED)
            continue

        else:
            maze.updatePosition(temp[0], temp[1], TRIED)
            continue

    if find:
        # 找到后依次弹出栈中元素
        while not sta.is_empty():
            temp = sta.peek()
            maze.updatePosition(temp[0], temp[1], PART_OF_PATH)
            sta.pop()
            continue

注:本代码中的find部分是在找到出口后,依次弹出栈中位置,并更新位置为迷宫解路径的一部分,是为了得到迷宫的解所用,与走迷宫无关

广度优先算法(利用队列)(最短路径问题)

在本算法中我们要新定义一个节点类来辅助我们走迷宫,来得到迷宫的解

最短路径节点类

# 最短路径节点类
class Node():
    def __init__(self,row,col,fat,st): # xy为当前位置,fat为父节点,st为当前步数
        self.row_cur = row
        self.col_cur = col
        self.father = fat
        self.step = st

解析:三个值,一个为当前位置,一个为父节点,一个为已走步数

不同于深度优先算法和递归算法的一条路走到黑,不碰墙壁不回头,广度优先算法是以入口为中心,一层层往外铺,就像打水漂泛起的涟漪

基本思路:

队列求解算法中,以队列存储可以探索的位置。利用队列先进先出的特点,实现在每个分支上同时进行搜索路径,直到找到出口。这是一种广度优先搜索的方法。

详细为:

  • 从队列中弹出元素,判断当前点是不是出口,如果是出口就结束循环,否则标记当前点为已探索过路径

  • 探索四个方向,新节点已当前节点为父节点入队列,四个方向能探索的都要探索到(不同于深度优先四个方向是if,elif 的关系,这四个方向都为if)

def search_short_path(maze, startRow, startColumn):

    find = False
    que = Queue()
    # 1.用起点节点初始化列表
    node = Node(startRow,startColumn,None,0)
    que.push(node)

    # 2.依次处理链表中数据,当某个节点的下一个元素是出口时停止,并保留此元素
    while(not que.is_empty()):
        node = que.pop()
        if maze[node.row_cur][node.col_cur] == '0':
            find = True
            break

        maze[node.row_cur][node.col_cur] = TRIED
        # 向下
        if maze[node.row_cur+1][node.col_cur] != OBSTACLE and maze[node.row_cur+1][node.col_cur] != TRIED:

            new_node = Node(node.row_cur + 1,node.col_cur,node,node.step+1)
            que.push(new_node)
        # 向上
        if maze[node.row_cur-1][node.col_cur] != OBSTACLE and maze[node.row_cur-1][node.col_cur] != TRIED:

            new_node = Node(node.row_cur - 1, node.col_cur, node, node.step + 1)
            que.push(new_node)
        # 向右
        if maze[node.row_cur][node.col_cur+1] != OBSTACLE and maze[node.row_cur][node.col_cur+1] != TRIED:

            new_node = Node(node.row_cur, node.col_cur + 1, node, node.step + 1)
            que.push(new_node)
        # 向左
        if maze[node.row_cur][node.col_cur-1] != OBSTACLE and maze[node.row_cur][node.col_cur-1] != TRIED:

            new_node = Node(node.row_cur, node.col_cur - 1, node, node.step + 1)
            que.push(new_node)

    if find:
        # 3.一直找此元素的父节点,入栈
        sta = Stark()
        while (node.father):
            sta.push((node.row_cur, node.col_cur))
            node = node.father
        # 4.用栈中提供的位置控制乌龟移动
        while(not sta.is_empty()):
            temp = sta.pop()
            maze.updatePosition(temp[0], temp[1], PART_OF_PATH)

注:此代码中的find部分一样是为了得到迷宫的解而写。利用得到出口的节点,一步步往前找其父节点,并将其父节点的坐标压栈,然后一步步弹出栈,将其坐标更新为路径的一部分,得到迷宫的解

迷宫的实现(利用文件)

此案例中的迷宫使用文件实现,迷宫类的初始化会从文件中读取迷宫

PART_OF_PATH = '*'  # 部分路径
TRIED = '.'  # 尝试
OBSTACLE = '+'  # 障碍
DEAD_END = '-'  # 死胡同 非递归不用死胡同

示例代码:

s为出生点,0为出口

++++++++++++++++++++++
+      ++ ++         0
+    +      + ++++++++
+ +  + ++   + +++++ ++
+ +++++ +++   +      +
+          ++++ +  + +
+++++ + +       +  + +
+++++ +++  + +  ++   +
+          + + S+ ++ +
+++++ +  + + +       +
++++++++++++++++++++++

随机迷宫实现:

随机迷宫还是写在文件中,整个案例都是从文件中读取迷宫

这个随机迷宫实现存在三个问题:

  1. 存在空心墙

  1. 有时出口会被堵死

  1. 出生点和出口的位置都是固定的

因为博主懒,此案例只是应付课程设计,所以没有去解决这三个问题

def produce_maze(maze_name:str):
    my_dict = {1:'+',2:' ',3:' '}
    maze_list = []

    # 1.列表中随机填入障碍和通路
    for x in range(15):
        row_list = []
        for y in range(15):
            row_list.append(my_dict[random.randint(1, 3)])
        maze_list.append(row_list)

    # 2.最外圈改为墙壁
    for x in range(15):
        maze_list[0][x] = '+'
        maze_list[14][x] = '+'
        maze_list[x][0] = '+'
        maze_list[x][14] = '+'

    # 3.最外圈加入出口
    # 4.随机一点加入出生地

    # 固定利于走迷宫
    maze_list[3][0] = '0'
    maze_list[11][11] = 'S'
    # 5.将列表写入文件
    with open(maze_name, 'w', encoding='utf-8') as fp:
        str = ''
        for rowlist in maze_list:
            for ch in rowlist:
                str += ch
            str+='\n'
            fp.write(str)
            str = ''

解析:简单的列表个点生成随机数1,2,3。1对应墙壁,2,3对应路径,再讲最外层一圈设置为墙壁,再开孔为出口,设置一点为出生点,最后写入文件中

全部代码,案例实现

import turtle
import random
# 栈 右边为栈顶
class Stark:
    def __init__(self):
        self.stark_list = []

    def push(self, item):
        self.stark_list.append(item)

    def pop(self):
        return  self.stark_list.pop()

    def peek(self):
        return self.stark_list[-1]

    def is_empty(self):
        return self.stark_list == []

    def size(self):
        return len(self.stark_list)

    def show(self):
        print(self.stark_list)

# 队列
class Queue:
    def __init__(self):
        self.items = []

    def is_empty(self):
        return self.items == []

    def push(self,item):
        self.items.insert(0,item)

    def pop(self):
        return self.items.pop()

    def size(self):
        return len(self.items)

    def show(self):
        print(self.items)

# 最短路径节点类
class Node():
    def __init__(self,row,col,fat,st): # xy为当前位置,fat为父节点,st为当前步数
        self.row_cur = row
        self.col_cur = col
        self.father = fat
        self.step = st


# 迷宫类
class Maze(object):
# 读取迷宫数据,初始化迷宫内部,并找到海龟初始位置。
    def __init__(self, mazeFileName):
        rowsInMaze = 0                            #初始化迷宫行数
        columnsInMaze = 0                         #初始化迷宫列数
        self.mazelist = []                        #初始化迷宫列表
        mazeFile = open(mazeFileName, 'r')        #读取迷宫文件
        for line in mazeFile:                    #按行读取
            rowList = []                         #初始化行列表
            col = 0                             #初始化列
            # for ch in line[:-1]:                #这样会丢失最后一列
            for ch in line:                        #按列读取
                rowList.append(ch)                #添加到行列表
                if ch == 'S':                    #S为乌龟初始位置,即迷宫起点
                    self.startRow = rowsInMaze    #乌龟初始行
                    self.startCol = col         #乌龟初始列
                col = col + 1                     #下一列
            rowsInMaze = rowsInMaze + 1         #下一行
            self.mazelist.append(rowList)        #行列表添加到迷宫列表
            columnsInMaze = len(rowList)         #获取迷宫总列数
        self.rowsInMaze = rowsInMaze             #设置迷宫总行数
        self.columnsInMaze = columnsInMaze        #设置迷宫总列数
        self.xTranslate = -columnsInMaze/2         #设置迷宫左上角的初始x坐标
        self.yTranslate = rowsInMaze/2             #设置迷宫左上角的初始y坐标
        self.t = turtle.Turtle()                #创建一个海龟对象
        self.t.shape('turtle')                    #给当前指示点设置样式(类似鼠标箭头),海龟形状为参数指定的形状名,指定的形状名应存在于TurtleScreen的shape字典中。多边形的形状初始时有以下几种:"arrow", "turtle", "circle", "square", "triangle", "classic"。
        self.wn = turtle.Screen()                #创建一个能在里面作图的窗口
        self.wn.setworldcoordinates(-columnsInMaze/2, -rowsInMaze/2, columnsInMaze/2, rowsInMaze/2)            #设置世界坐标系,原点在迷宫正中心。参数依次为画布左下角x轴坐标、左下角y轴坐标、右上角x轴坐标、右上角y轴坐标

    # 在屏幕上绘制迷宫
    def drawMaze(self):
        self.t.speed(20)                        #绘图速度
        for y in range(self.rowsInMaze):        #按单元格依次循环迷宫
            for x in range(self.columnsInMaze):
                if self.mazelist[y][x] == OBSTACLE:    #如果迷宫列表的该位置为障碍物,则画方块
                    self.drawCenteredBox(x + self.xTranslate, -y + self.yTranslate, 'orange')

    # 画方块
    def drawCenteredBox(self, x, y, color):
        self.t.up()                                #画笔抬起
        self.t.goto(x - 0.5, y - 0.5)            #前往参数位置,此处0.5偏移量的作用是使乌龟的探索路线在单元格的正中心位置
        self.t.color(color)                        #方块边框为橙色
        self.t.fillcolor('green')                #方块内填充绿色
        self.t.setheading(90)                    #设置海龟的朝向,标准模式:0 - 东,90 - 北,180 - 西,270 - 南。logo模式:0 - 北,90 - 东,180 - 南,270 - 西。
        self.t.down()                            #画笔落下
        self.t.begin_fill()                        #开始填充
        for i in range(4):                        #画方块边框
            self.t.forward(1)                    #前进1个单位
            self.t.right(90)                    #右转90度
        self.t.end_fill()                        #结束填充

    # 移动海龟
    def moveTurtle(self, x, y):
        self.t.up()                                #画笔抬起
        self.t.setheading(self.t.towards(x + self.xTranslate, -y + self.yTranslate))    #setheading()设置海龟朝向,towards()从海龟位置到由(x, y),矢量或另一海龟位置连线的夹角。此数值依赖于海龟初始朝向,由"standard"、"world"或"logo" 模式设置所决定。
        self.t.goto(x + self.xTranslate, -y + self.yTranslate)    #前往目标位置

    # 画路径圆点
    def dropBreadcrumb(self, color):
        self.t.dot(color)                        #dot(size=None, color)画路径圆点

    # 用以更新迷宫内的状态及在窗口中改变海龟位置,行列参数为乌龟的初始坐标。
    def updatePosition(self, row, col, val):
        self.mazelist[row][col] = val             #设置该标记状态为当前单元格的值
        self.moveTurtle(col, row)                #移动海龟
        if val == PART_OF_PATH:                 #其中一条成功路径的圆点的颜色
            color = 'green'
        elif val == TRIED:                        #尝试用的圆点的颜色
            color = 'black'
        elif val == DEAD_END:                    #死胡同用的圆点的颜色
            color = 'red'
        self.dropBreadcrumb(color)                #画路径圆点并上色

    # 用以判断当前位置是否为出口。
    def isExit(self, row, col):
        # 根据海龟位置是否在迷宫的4个边线位置判断
        # return (row == 0 or row == self.rowsInMaze - 1 or col == 0 or col == self.columnsInMaze - 1)
        return self.mazelist[row][col] == '0'

    # 返回键对应的值,影响searchFrom()中maze[startRow][startColumn]值的获取
    def __getitem__(self, key):
        return self.mazelist[key]

    def save(self,mazeFileName):

        self.mazelist[self.startRow][self.startCol] = 's'
        with open(mazeFileName, 'w',encoding='utf-8') as fp:
            str = ''
            for rowlist in self.mazelist:
                for ch in rowlist:
                    str += ch
                fp.write(str)
                str = ''



# 探索迷宫,注意此函数包括三个参数:一个迷宫对象、起始行、起始列。
def searchFrom(maze, startRow, startColumn):
    # 从初始位置开始尝试四个方向,直到找到出路。
    # 1. 遇到障碍
    if maze[startRow][startColumn] == OBSTACLE:
        return False
    # 2. 发现已经探索过的路径或死胡同
    if maze[startRow][startColumn] == TRIED or maze[startRow][startColumn] == DEAD_END:
        return False
    # 3. 发现出口
    if maze.isExit(startRow, startColumn):
        maze.updatePosition(startRow, startColumn, PART_OF_PATH)#显示出口位置,注释则不显示此点
        return True
    maze.updatePosition(startRow, startColumn, TRIED)#更新迷宫状态、设置海龟初始位置并开始尝试
    # 4. 依次尝试每个方向
    found = searchFrom(maze, startRow - 1, startColumn) or \
            searchFrom(maze, startRow + 1, startColumn) or \
            searchFrom(maze, startRow, startColumn - 1) or \
            searchFrom(maze, startRow, startColumn + 1)
    if found:                                                    #找到出口
        maze.updatePosition(startRow, startColumn, PART_OF_PATH)#返回其中一条正确路径
    else:                                                        #4个方向均是死胡同
        maze.updatePosition(startRow, startColumn, DEAD_END)
    return found

def searchFrom_non_rec(maze, startRow, startColumn):

    sta = Stark()
    sta.push((startRow,startColumn))
    find = False

    while not sta.is_empty():
        temp = sta.peek()
        # 1.判断当前点是不是出口
        if maze.isExit(temp[0],temp[1]):
            find = True
            break

        # 2. 发现已经探索过的路径 回退专用,因为我判断的存在,我不可能进入已经走过的路径
        if maze[temp[0]][temp[1]] == TRIED:
            sta.pop()
            temp = sta.peek()

        # 3. 依次尝试每个方向
        if maze[temp[0]][temp[1] + 1] != TRIED and maze[temp[0]][temp[1] + 1] != OBSTACLE:
            sta.push((temp[0], temp[1] + 1))
            maze.updatePosition(temp[0], temp[1], TRIED)
            continue

        elif maze[temp[0]][temp[1] - 1] != TRIED and maze[temp[0]][temp[1] - 1] != OBSTACLE:
            sta.push((temp[0], temp[1] - 1))
            maze.updatePosition(temp[0], temp[1], TRIED)
            continue

        elif maze[temp[0] + 1][temp[1]] != TRIED and maze[temp[0] + 1][temp[1]] != OBSTACLE:
            sta.push((temp[0] + 1, temp[1]))
            maze.updatePosition(temp[0], temp[1], TRIED)
            continue

        elif maze[temp[0] - 1][temp[1]] != TRIED and maze[temp[0] - 1][temp[1]] != OBSTACLE:
            sta.push((temp[0] - 1, temp[1]))
            maze.updatePosition(temp[0], temp[1], TRIED)
            continue

        else:
            maze.updatePosition(temp[0], temp[1], TRIED)
            continue

    if find:
        # 找到后依次弹出栈中元素
        while not sta.is_empty():
            temp = sta.peek()
            maze.updatePosition(temp[0], temp[1], PART_OF_PATH)
            sta.pop()
            continue

def search_short_path(maze, startRow, startColumn):

    find = False
    que = Queue()
    # 1.用起点节点初始化列表
    node = Node(startRow,startColumn,None,0)
    que.push(node)

    # 2.依次处理链表中数据,当某个节点的下一个元素是出口时停止,并保留此元素
    while(not que.is_empty()):
        node = que.pop()
        if maze[node.row_cur][node.col_cur] == '0':
            find = True
            break

        maze[node.row_cur][node.col_cur] = TRIED
        # 向下
        if maze[node.row_cur+1][node.col_cur] != OBSTACLE and maze[node.row_cur+1][node.col_cur] != TRIED:

            new_node = Node(node.row_cur + 1,node.col_cur,node,node.step+1)
            que.push(new_node)
        # 向上
        if maze[node.row_cur-1][node.col_cur] != OBSTACLE and maze[node.row_cur-1][node.col_cur] != TRIED:

            new_node = Node(node.row_cur - 1, node.col_cur, node, node.step + 1)
            que.push(new_node)
        # 向右
        if maze[node.row_cur][node.col_cur+1] != OBSTACLE and maze[node.row_cur][node.col_cur+1] != TRIED:

            new_node = Node(node.row_cur, node.col_cur + 1, node, node.step + 1)
            que.push(new_node)
        # 向左
        if maze[node.row_cur][node.col_cur-1] != OBSTACLE and maze[node.row_cur][node.col_cur-1] != TRIED:

            new_node = Node(node.row_cur, node.col_cur - 1, node, node.step + 1)
            que.push(new_node)

    if find:
        # 3.一直找此元素的父节点,入栈
        sta = Stark()
        while (node.father):
            sta.push((node.row_cur, node.col_cur))
            node = node.father
        # 4.用栈中提供的位置控制乌龟移动
        while(not sta.is_empty()):
            temp = sta.pop()
            maze.updatePosition(temp[0], temp[1], PART_OF_PATH)


def produce_maze(maze_name:str):
    my_dict = {1:'+',2:' ',3:' '}
    maze_list = []

    # 1.列表中随机填入障碍和通路
    for x in range(15):
        row_list = []
        for y in range(15):
            row_list.append(my_dict[random.randint(1, 3)])
        maze_list.append(row_list)

    # 2.最外圈改为墙壁
    for x in range(15):
        maze_list[0][x] = '+'
        maze_list[14][x] = '+'
        maze_list[x][0] = '+'
        maze_list[x][14] = '+'

    # 3.最外圈加入出口
    # 4.随机一点加入出生地

    # 固定利于走迷宫
    maze_list[3][0] = '0'
    maze_list[11][11] = 'S'
    # 5.将列表写入文件
    with open(maze_name, 'w', encoding='utf-8') as fp:
        str = ''
        for rowlist in maze_list:
            for ch in rowlist:
                str += ch
            str+='\n'
            fp.write(str)
            str = ''


PART_OF_PATH = '*'  # 部分路径
TRIED = '.'  # 尝试
OBSTACLE = '+'  # 障碍
DEAD_END = '-'  # 死胡同 非递归不用死胡同

def test():
    myMaze = Maze('maze.txt')  # 实例化迷宫类,maze文件是使用“+”字符作为墙壁围出空心正方形空间,并用字母“S”来表示起始位置的迷宫文本文件。
    myMaze.drawMaze()  # 在屏幕上绘制迷宫。
    searchFrom_non_rec(myMaze, myMaze.startRow, myMaze.startCol)  # 探索迷宫
    myMaze.save('maze_result.txt')

def test1(): # 随机迷宫
    produce_maze('maze1.txt') # 向文件maze1.txt中写入随机迷宫
    myMaze = Maze('maze1.txt')  # 实例化迷宫类,maze文件是使用“+”字符作为墙壁围出空心正方形空间,并用字母“S”来表示起始位置的迷宫文本文件。
    myMaze.drawMaze()  # 在屏幕上绘制迷宫。
    searchFrom_non_rec(myMaze, myMaze.startRow, myMaze.startCol)  # 探索迷宫
    myMaze.save('maze1_result.txt')


def test2():
    myMaze = Maze('maze.txt')  # 实例化迷宫类,maze文件是使用“+”字符作为墙壁围出空心正方形空间,并用字母“S”来表示起始位置的迷宫文本文件。
    myMaze.drawMaze()  # 在屏幕上绘制迷宫。
    search_short_path(myMaze, myMaze.startRow, myMaze.startCol)  # 探索迷宫
    myMaze.save('maze_result.txt')

if __name__ == '__main__':
    # test() # 深度优先走迷宫
    # test1() # 随机迷宫+深度优先走迷宫
    # test2() # 最短路径走迷宫

最后祝愿各位学业有成

  • 2
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值