迷宫求解(包含随机迷宫、求解动画演示)——C语言 数据结构
•
算法结构
该程序是一项 “迷宫求解” 类问题,主要功能包含:
①25 X 25迷宫的随机生成
②迷宫求解的动画演示(DFS)
完整代码附最后 : )
功能演示:

界面展示:

迷宫展示:

结果展示:

首先是随机迷宫部分:
大概思路就是先初始化一个矩阵,外圈为“通路”,内层为“墙体”。
1.定义vector容器,用于存放墙体坐标,先将起点装入容器
2.在容器中随机选取一个墙体,满足“四周无通路或只有一个通路”条件时,将墙体拆除(改为通路)并从容器中移除,随后将该墙体四周的墙体装入容器
流程图演示:
在此使用较小矩阵用于演示

代码部分:
void create_mg()//随机生成迷宫
{
//初始化迷宫:内部为墙体;外围为通路,当做屏障
for(int i=0; i QX,QY;
QX.push_back(start_x);//从起点开始
QY.push_back(start_y);
while(QX.size())//拆墙
{
//根据当前时间,生成一个随机数种子
srand((unsigned int)(time(NULL)));
int index=rand()%(QX.size());//随机生成一个坐标,选取一堵墙
int x=QX[index];
int y=QY[index];
int flag=0;//记录该墙四周通路个数
int r,c;
for(int i=0; i<4; i++)
{
r=x+dRow[i];
c=y+dCol[i];
if(mg[r][c]==0)
flag++;
}
if(flag<=1)//当该墙体四周通路有一条或没有时,则该墙改为道路
{
mg[x][y]=0;
for(int i=0; i<4; i++)
{
r=x+dRow[i];
c=y+dCol[i];
if(mg[r][c]==1)//将该墙四周的墙加入队列
{
QX.push_back(r);
QY.push_back(c);
}
}
}
//将当前墙移除队列
QX.erase(QX.begin()+index);
QY.erase(QY.begin()+index);
}
}
其次是迷宫求解部分:
为了方便理解,我设计了box结构体,用于表示路径的坐标与方向
struct box
{
int x,y;//坐标
int dir;//方向:右1 下2 左3 上4
};
迷宫求解问题,路径部分当然要用栈来存放啦,方便回溯
思路很简单:
按照“右下左上”顺时针方向进行摸索,能走通的就走,死路就回溯,即将该点路径出栈,并将已走过的路线做上标记。回溯到起点即代表迷宫无解,到终点就是求解完成。
但为了完成动画效果还是需要下亿点功夫:
①每走一步需重新打印一次迷宫,这就少不了清屏功能,使用system("cls")虽然方便,但会出现较为严重的闪屏现象,做过动画演示程序的应该体验过,虽然不影响功能,但视觉体验很糟糕。我也是通过网上学习两个函数来代替system("cls"),解决闪屏。
void gotoxy(int x,int y)//将光标移动到(x,y)位置
{
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); //获取标准输出设备句柄
COORD pos= {x,y}; //坐标位置
SetConsoleCursorPosition(handle,pos); //设置控制台光标位置
}
void HideCursor()//隐藏光标
{
CONSOLE_CURSOR_INFO cursor_info= {1,0}; //0表示隐藏光标
SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE),&cursor_info);
}
②为了有更好的视觉体验,我用了不同颜色区分墙体,路线与已走路线。即打印路线前调用变色函数,打印后恢复原色
HANDLE hout = GetStdHandle(STD_OUTPUT_HANDLE);//颜色
void cout_green()//绿色
{
SetConsoleTextAttribute(hout, 2);
}
void cout_red()//红色
{
SetConsoleTextAttribute(hout, 4);
}
void recover()//白色
{
SetConsoleTextAttribute(hout, 1|2|4);
}
代码部分:
void play_mg()
{
//分别对应着上下左右方向
int dRow[] = {0, 1, 0, -1};
int dCol[] = {1, 0, -1, 0};
box start;//起始位置
start.x=start_x;
start.y=start_y;
path.push(start);
while(!path.empty())
{
box temp;
bool flag=false;//判断该坐标是否有通路
int x = path.top().x;
int y = path.top().y;
mg[x][y]=1;//该坐标已经过,设置为墙
for(int i=0; i<4; i++) //向四个位置进行试探,方向:右1 下2 左3 上4
{
int r=x+dRow[i];
int c=y+dCol[i];
if(mg[r][c]==0)
{
temp.x=r;
temp.y=c;
temp.dir=i+1;
path.push(temp);
flag=true;
break;
}
}
if(x==X-3&&y==Y-3)//到达终点
break;
mglx[x][y]=temp.dir+1;//该坐标已经过,右2 下3 左4 上5
if(!flag)//若该坐标点无通路,则回溯
{
box t=path.top();
mglx[t.x][t.y]=6;//回溯后要把该坐标设为不可走路线
path.pop();
}
HideCursor();
gotoxy(0,0);
Sleep(speed);//设置速度
show_mg();
}
}
其余部分代码就是一些简单的界面设计与细节处理,很好理解
完整代码附上:
#include#include //栈 #include //容器 #include //获取当前时间 #include //颜色改变,时停 #include //清屏 using namespace std; #define X 27//迷宫大小 #define Y 27 #define start_x 2//起点 #define start_y 2 #define speed 30//速度 struct box { int x,y;//坐标 int dir;//方向:右1 下2 左3 上4 }; int mg[X][Y];//初始迷宫 int mglx[X][Y];//展示迷宫路线,因原迷宫会被打乱 stack path;//路线,包含坐标与方向信息 HANDLE hout = GetStdHandle(STD_OUTPUT_HANDLE);//颜色 void create_mg();//随机生成迷宫 void copy_mg();//复制迷宫 void play_mg();//完成迷宫路线求解 void show_path();//坐标路线展示 void show_mg();//迷宫路线展示 void cout_green();//打印绿色 void cout_red();//打印红色 void recover();//恢复白色 //清屏函数,代替system("cls"),防止闪烁,网上学习 void gotoxy(int x,int y);//将光标移动到(x,y)位置 void HideCursor();//隐藏光标 int main() { while(true) { system("cls"); char choice; cout< >choice; if(choice=='1') { system("cls"); while(true) { create_mg(); if(mg[X-3][Y-3]==0)//若终点是一堵墙,则重新生成迷宫 break; } copy_mg(); show_mg(); system("pause"); play_mg(); show_path(); } else if(choice=='2') return 0; else cout<<"请输入正确的选择!"< QX,QY; QX.push_back(start_x);//从起点开始 QY.push_back(start_y); while(QX.size())//拆墙 { //根据当前时间,生成一个随机数种子 srand((unsigned int)(time(NULL))); int index=rand()%(QX.size());//随机生成一个坐标,选取一堵墙 int x=QX[index]; int y=QY[index]; int flag=0;//记录该墙四周通路个数 int r,c; for(int i=0; i<4; i++) { r=x+dRow[i]; c=y+dCol[i]; if(mg[r][c]==0) flag++; } if(flag<=1)//当该墙体四周通路有一条或没有时,则该墙改为道路 { mg[x][y]=0; for(int i=0; i<4; i++) { r=x+dRow[i]; c=y+dCol[i]; if(mg[r][c]==1)//将该墙四周的墙加入队列 { QX.push_back(r); QY.push_back(c); } } } //将当前墙移除队列 QX.erase(QX.begin()+index); QY.erase(QY.begin()+index); } } void play_mg() { //分别对应着上下左右方向 int dRow[] = {0, 1, 0, -1}; int dCol[] = {1, 0, -1, 0}; box start;//起始位置 start.x=start_x; start.y=start_y; path.push(start); while(!path.empty()) { box temp; bool flag=false;//判断该坐标是否有通路 int x = path.top().x; int y = path.top().y; mg[x][y]=1;//该坐标已经过,设置为墙 for(int i=0; i<4; i++) //向四个位置进行试探,方向:右1 下2 左3 上4 { int r=x+dRow[i]; int c=y+dCol[i]; if(mg[r][c]==0) { temp.x=r; temp.y=c; temp.dir=i+1; path.push(temp); flag=true; break; } } if(x==X-3&&y==Y-3)//到达终点 break; mglx[x][y]=temp.dir+1;//该坐标已经过,右2 下3 左4 上5 if(!flag)//若该坐标点无通路,则回溯 { box t=path.top(); mglx[t.x][t.y]=6;//回溯后要把该坐标设为不可走路线 path.pop(); } HideCursor(); gotoxy(0,0); Sleep(speed);//设置速度 show_mg(); } } void show_path()//展示坐标路线 { if(path.empty()) { cout<<"此迷宫无解"< "; for(int i=length-1; i>=0; i--) { if(temp[i].dir==1) cout<<"右=>"; else if(temp[i].dir==2) cout<<"下=>"; else if(temp[i].dir==3) cout<<"左=>"; else if(temp[i].dir==4) cout<<"上=>"; } cout<<"终点"< "; for(int i=length-1; i>=0; i--) { cout<<"("< "; } cout<<"终点"< 本文来自网络,不代表协通编程立场,如若转载,请注明出处:https://net2asp.com/d473f3988f.html
