'''队列迷宫'''
import numpy as np
import networkx as nx
import matplotlib.pylab as plt
import random
class Deque:
def __init__(self) -> None:
'''认为最左即索引0为尾部,-1索引为头部'''
self.items=[]
def addFront(self,item):
self.items.append(item)
def addRear(self,item):
self.items.insert(0,item)
def removeFront(self):
return self.items.pop()
def removeRear(self):
return self.items.pop(0)
def isEmpty(self):
if self.items==[]:
return True
else:
return False
def size(self):
return len(self.items)
class Maze:
def __init__(self) -> None:
self.maze=[
[1,1,1,1,1,1,1,1,1,1],
[1,0,0,1,0,0,0,1,0,1],
[1,0,0,1,0,0,0,1,0,1],
[1,0,0,0,0,1,1,0,0,1],
[1,0,1,1,1,0,0,0,0,1],
[1,0,0,0,1,0,0,0,0,1],
[1,0,1,0,0,0,1,0,0,1],
[1,0,1,1,1,0,1,1,0,1],
[1,1,0,0,0,0,0,0,0,1],
[1,1,1,1,1,1,1,1,1,1]
]
self.route=[]
self.path=[]
def reset_default(self):
'''恢复默认迷宫'''
self.maze=[
[1,1,1,1,1,1,1,1,1,1],
[1,0,0,1,0,0,0,1,0,1],
[1,0,0,1,0,0,0,1,0,1],
[1,0,0,0,0,1,1,0,0,1],
[1,0,1,1,1,0,0,0,0,1],
[1,0,0,0,1,0,0,0,0,1],
[1,0,1,0,0,0,1,0,0,1],
[1,0,1,1,1,0,1,1,0,1],
[1,1,0,0,0,0,0,0,0,1],
[1,1,1,1,1,1,1,1,1,1]
]
def maze_generalizer(self,m=5,n=5):
random.seed(114514)
'''来源于https://zhuanlan.zhihu.com/p/363956880'''
def inborder(x,y):
return x<n and x>=0 and y<m and y>=0
# 记录每个点是否经过过
visited = [[False for i in range(m)] for j in range(n)]
# 记录迷宫形状,注意n行,m列的迷宫应该包含2n+1行,2m+1列
# 因为a条平行的通路之间和两侧一共还有a+1堵墙
map = [[0 for i in range(2*m+1)] for j in range(2*n+1)]
dx=[-1,1,0,0]
dy=[0,0,-1,1]
#分别是上下左右的位移矢量
#起始点位置和终止点位置
startposx=0
startposy=0
endposx=m-1
endposy=n-1
#px,py是指针当前指向的位置,visited[px][py]对应于map[2px+1][2py+1]
px = startposx
py = startposy
visited[px][py]=True
visitedpos=[(px,py)]#之前走过的所有点构成的列表
map[2*px+1][2*py+1]=1
sum=1 #经过点的总数,当达到mn时迷宫就构建好了
while(sum<m*n):
walkable=[] #存储px,py点前后左右四个点中能到达的点对应的i
for i in range(4):
#计算新的点的位置
tx=px+dx[i]
ty=py+dy[i]
if inborder(tx,ty) and not visited[tx][ty]:#能到达就将i加入walkable
walkable.append(i)
if not walkable or (px==endposx and py==endposy):
#如果walkable为空,或已到达终点,则向之前的位置随机传送
px,py=random.choice(visitedpos)
else:
#随机选择方向行进
i = random.choice(walkable)
px += dx[i]
py += dy[i]
visited[px][py]=True
visitedpos.append((px,py))
#将到达的点和前后两点之间的墙赋值为1(路)
map[2*px+1][2*py+1]=1
map[1+2*px-dx[i]][1+2*py-dy[i]]=1
sum+=1
self.maze=np.where(np.array(map)>0,0,1).tolist() #替换0,1
print('成功生成新迷宫')
def four_direction(self,x,y):
'''返回上,下,左,右的坐标'''
return (x+1,y),(x-1,y),(x,y-1),(x,y+1)
def get_path(self):
'''这个path里有很多路径,只有一个是到终点的'''
current_node=self.path[-1] #终点节点
while current_node[1]!=-1: #只要没到起点
self.route.append(current_node[0])
current_node=self.path[current_node[1]]
self.route.append(current_node[0]) #将起点加入
self.route.reverse() #反转列表,否则是反的
def dfssearch(self,start_x,start_y,end_x,end_y):
'''搜索路径,修改迷宫并修改可行路径'''
stack=Stack()
stack.push((start_x,start_y)) #添加起始位置
while stack.size()>0:
current_node=stack.peek() #当前节点
if current_node==(end_x,end_y):#如果是终点
self.route=stack.items
self.maze[start_x][start_y]='S' #标记起点
self.maze[end_x][end_y]='E' #标记终点
return True
#搜索上下左右四个方向,看能不能走
for dir in self.four_direction(*current_node): #元组解包
if self.maze[dir[0]][dir[1]]==0:
stack.push(dir) #可以走,压入栈中
self.maze[dir[0]][dir[1]]=2 #标记为已经走过
break
else: #四个方向都行不通(注意只有四个方向都行不通才会到这步,否则在内部就break了)
self.maze[current_node[0]][current_node[1]]=2 #标记为已经走过
stack.pop() #回退(即最后走的路出栈)
'''如果到这了,说明栈空,没有路'''
print('没有路')
return False
def bfssearch(self,start_x,start_y,end_x,end_y):
'''搜索路径,修改迷宫并修改可行路径'''
queue=Deque()
queue.addFront(((start_x,start_y),-1)) #添加起始位置
self.maze[start_x][start_y]='S' #标记起点
'''这里队列存储的数据结构为((x坐标,y坐标),指示队列索引)'''
while queue.size()>0:
current_node=queue.removeRear() #初始点出列
self.path.append(current_node) #加入到出列路径列表
if current_node[0]==(end_x,end_y):
'''找到了终点,这时path最后一个元素就是终点,处理path就能得到路径'''
self.maze[end_x][end_y]='E' #标记终点
self.get_path()
return True
#搜索上下左右四个方向,看能不能走
for dir in self.four_direction(*current_node[0]): #元组解包
if self.maze[dir[0]][dir[1]]==0: #如果某方向能走
queue.addFront((dir,len(self.path)-1)) #入列时标注索引
#将这个方向的点加入队列(这里可能加入多个索引相同点,因为只出列一个)
self.maze[dir[0]][dir[1]]=2 #标记为已经走过
else:
print('没有搜索到路')
return False
def draw(self,x=12,y=12):
'''允许设置图像大小'''
'''接受二维数组和路径集,返回图'''
route=self.route #路径集,形如[(1,2),(2,3)]
plt.figure(figsize=(x,y))
plt.subplot(111)
G=nx.Graph()
rownum,colnum=len(self.maze),len(self.maze[0])
map_data={(i,j):self.maze[i][j] for i in range(rownum) for j in range(colnum)} #地图信息,主要是利用二维数组的坐标和值
pos={}
for coordinate,attr in map_data.items():
pos[coordinate]=coordinate
G.add_node(coordinate,attribute=attr) #节点的坐标为id,属性代表是哪种(障碍还是通路)
nx.draw_networkx_nodes(G,nodelist=[coordinate],pos={coordinate:coordinate},
node_color='red' if attr==1 else 'yellow' if attr==2 else 'grey' if attr==0 else 'green' if attr=='E' else 'orange',label=coordinate)
if self.route:
link=[(route[nodenum],route[nodenum+1]) for nodenum in range(len(route)-1)]
for index,node in enumerate(link):
G.add_edge(node[0],node[1],order=index+1) #顺序
nx.draw_networkx_edges(G,edgelist=[(node[0],node[1])],pos=pos,width=8)
maze=Maze()
maze.maze_generalizer(10,10) #生成m*2+1的迷宫
maze.dfssearch(1,1,19,19)
maze.draw(14,14)
maze.maze_generalizer(10,10) #生成m*2+1的迷宫
maze.bfssearch(1,1,19,19)
maze.draw(14,14)
结果:
深度优先搜索
广度优先搜索