利用matlab创建与解决迷宫[深度优先、Prim、递归分割、Wilson]
本文利用matlab,实现了常见的三种迷宫算法:深度优先算法、Prim算法、递归分割算法,和Wilson算法(Loop-erased random walk)。并通过求解路径,对比三种迷宫不同的特点。
迷宫均为标准网格化的2维迷宫,规定迷宫内只能进行上下左右4个方向进行移动。入口和出口均只有一个。
对于一般迷宫而言,我们要找的是一种连接所有通路,且不存在回路和死路的方法。入口和出口的位置反而是次要的,而且可以在迷宫生成之后再定义。
基于马尔科夫链思想的MarkovJunior方法生成迷宫可见:
利用MarkovJunior方法生成迷宫和图形的MATLAB演示[迷宫生成、贪吃蛇、地图生成、图案生成]
1 深度优先算法
深度优先(递归回溯)算法可以表示为:
1.设置一个起点。将起点作为当前迷宫单元,并标记为已访问
2.当还存在未标记的迷宫单元,进行循环
1.如果当前迷宫单元有未被访问过的的相邻的迷宫单元
1.随机选择一个未访问的相邻迷宫单元
2.将当前迷宫单元入栈
3.移除当前迷宫单元与相邻迷宫单元的墙
4.标记相邻迷宫单元已访问,并用它作为当前迷宫单元
2.如果当前迷宫单元不存在未访问的相邻迷宫单元,并且栈不空
1.栈顶的迷宫单元出栈
2.令其成为当前迷宫单元
大概思路是先随机走,如果走不通了,则保存路径。之后逐步后退,直到能继续随机走。最终走完所有格点,则停止程序。
具体过程可以参考下面动图:
matlab的实现的代码为:
% 深度优先算法
clear
clc
%初始迷宫
X=8;
Y=8;
Maze=zeros(2*Y+1,2*X+1);
Maze(2:2:end-1,2:2:end-1)=1;%1代表不是墙wall,可以行走的区域room
%未访问
R_n=1:X*Y;
%已访问
R_y=[];
%临时栈
R_t=[];
%定义起点
R_Start=1;
R_Now=R_Start;%将起点作为当前迷宫单元
R_y=[R_y,R_Start];%标记已访问
R_n(R_n==R_Start)=[];%删除未访问
while ~isempty(R_n)
%当还存在未标记的迷宫单元,进行循环
R_N_Beside=R_Not_Beside(X,Y,R_Now,R_n);
if ~isempty(R_N_Beside)
%如果当前迷宫单元有未被访问过的的相邻的迷宫单元
%随机选择一个未访问的相邻迷宫单元
R_N_Beside_Ri=R_N_Beside(randi(length(R_N_Beside)));
%将当前迷宫单元入栈
if ismember(R_Now,R_t)
Ri=find(R_t==R_Now);
R_t([Ri,length(R_t)])=R_t([length(R_t),Ri]);
else
R_t=[R_t;R_Now];
end
%移除当前迷宫单元与相邻迷宫单元的墙
Maze_Sub_New=Move_R1_R2_Wall(X,Y,R_N_Beside_Ri,R_Now);
Maze(Maze_Sub_New(1),Maze_Sub_New(2))=1;
%标记相邻迷宫单元并用它作为当前迷宫单元
R_y=[R_y,R_N_Beside_Ri];%加入已访问
R_n(R_n==R_N_Beside_Ri)=[];%删除未访问
R_Now=R_N_Beside_Ri;
elseif ~isempty(R_t)
%如果当前迷宫单元不存在未访问的相邻迷宫单元,并且栈不空
%栈顶的迷宫单元出栈,令其成为当前迷宫单元
R_Now=R_t(end);
R_t(end)=[];
end
end
%定义终点
Maze(end,end-1)=1;
Maze(1,2)=1;%把起点的墙挖开
figure(1)
imagesc(Maze)
figure(2)
DrawMaze(Maze,1,3)
%记录当前迷宫单元有未被访问过的的相邻的迷宫单元
function R_N_Beside=R_Not_Beside(X,Y,R_Now,R_n)
[I,J] = ind2sub([Y,X],R_Now);
P4=zeros(4,2);
P4(1,:)=[I-1,J];%上
P4(2,:)=[I+1,J];%下
P4(3,:)=[I,J-1];%左
P4(4,:)=[I,J+1];%右
R_N_Beside=[];
for k=1:4
%对相邻的边依次循环
if (~all(P4(k,:))) || (P4(k,1)==Y+1) || (P4(k,2)==X+1)
%如果超出边界,跳过
continue
else
R_temp=sub2ind([Y,X],P4(k,1),P4(k,2));
if ismember(R_temp,R_n)
%如果该单元属于未被访问过
R_N_Beside=[R_N_Beside;R_temp];
end
end
end
end
function Maze_Sub_New=Move_R1_R2_Wall(X,Y,R1,R2)
[I1,J1]=ind2sub([Y,X],R1);
[I2,J2]=ind2sub([Y,X],R2);
Maze_Sub_New=[0,0];
if I1==I2
Maze_Sub_New=[2*I1,2*min(J1,J2)+1];
elseif J1==J2
Maze_Sub_New=[2*min(I1,I2)+1,2*J1];
end
end
function DrawMaze(Maze,Wall_Width,Room_Width)
[I,J]=size(Maze);
Y=(I-1)/2;
X=(J-1)/2;
%先,列方向
Maze_New1=zeros(Y*Room_Width+(Y+1)*Wall_Width,J);
%最开始
t=1;
Maze_New1(t:t+Wall_Width-1,:)=repmat(Maze(1,:),Wall_Width,1);
t=t+Wall_Width;
for k=1:Y
%先Room行
Maze_New1(t:t+Room_Width-1,:)=repmat(Maze(2*k,:),Room_Width,1);
t=t+Room_Width;
%后Wall行
Maze_New1(t:t+Wall_Width-1,:)=repmat(Maze(2*k+1,:),Wall_Width,1);
t=t+Wall_Width;
end
%后,行方向
Maze_New2=zeros(Y*Room_Width+(Y+1)*Wall_Width,X*Room_Width+(X+1)*Wall_Width);
%最开始
t=1;
Maze_New2(:,t:t+Wall_Width-1)=repmat(Maze_New1(:,1),1,Wall_Width);
t=t+Wall_Width;
for k=1:X
%先Room行
Maze_New2(:,t:t+Room_Width-1)=repmat(Maze_New1(:,2*k),1,Room_Width);
t=t+Room_Width;
%后Wall行
Maze_New2(:,t:t+Wall_Width-1)=repmat(Maze_New1(:,2*k+1),1,Wall_Width);
t=t+Wall_Width;
end
colormap(gray)
imagesc(Maze_New2)
axis equal
axis off
end
迷宫求解算法采用最普通的搜索路径方法,遇到死胡同则后退继续前进。
%求解迷宫
M_In=[2,2];%迷宫入口
M_Out=[2*Y,2*X];%迷宫出口
k=0;
M_Now=M_In;%起始点
Maze_S=Maze;%定义求解迷宫
Maze_S(1,:)=0;Maze_S(:,1)=0;Maze_S(:,end)=0;Maze_S(end,