一、实验目的
1.迷宫游戏是非常经典的游戏,在该题中要求随机生成一个迷宫,并求解迷宫;
2.要求查找并理解迹;系统提示迷宫路径要求基于A*算法实现,输出玩家当前位置到迷宫出口的最优路径。设计迷宫生成的算法,并尝试用两种不同的算法来生成随机的迷宫。
3.要求迷宫游戏支持玩家走迷宫,和系统走迷宫路径两种模式。玩家走迷宫,通过键盘方向键控制,并在行走路径上留下痕交互友好的游戏图形界面。
二、编程语言与开发环境
本次开发采用C++与QT结合的形式。
三、游戏的逻辑设计
游戏预设:我想做一个人物地点选择界面分为两个部分,左边的话是就是人物选择可以选择小灰灰、美羊羊、沸洋洋三种人物选择,右边是地方选择,可以选择两种模式,进入游戏界面用prime算法随机生成迷宫,再用A*算法实现搜寻到最优路径,同时增加背景音乐的设置。
四、实验核心
①prime算法
其基本步骤为:
(1)初始化地图。地图中的每个位置代表迷宫中的一个单元格,初始时所有的单元格都设置为障碍物(例如黑色)。
(2)选择一个障碍物作为起点,并将其在地图上标记为已访问(例如白色)。
(3)在已访问的单元格中选择一个与未访问的单元格相邻的单元格,并将其在地图上标记为已访问。
(4)重复步骤3,直到地图上所有的单元格都被访问过,或者没有与未访问的单元格相邻的已访问单元格。
(5)如果地图上还有未访问的单元格,则从这些单元格中选择一个作为新的起点,重复步骤2-4,直到地图上所有的单元格都被访问过。
(6)如果迷宫中存在环路(例如两个单元格之间存在两条路径),则删除其中的一条路径。
(7)最后,根据需要将迷宫的出口标记为终点(例如红色)。
Prime算法的主要优点是可以快速生成迷宫,但是生成的迷宫可能不是最优解,即可能存在一些不必要的曲折或者不是最短路径。为了生成更加复杂的迷宫,可以在算法中加入随机性或者其他规则。
prime算法的代码:
while(true){//利用prim算法生成迷宫
temp.i=i;//起始点
temp.j=j;
int randnum=rand()%4;
switch (randnum) {
case 0: if(!up&&i>2&&maze[i-2][j].state==0){
build_maze_stack.push(temp);
maze[i-2][j].state=1;
maze[i-1][j].state=1;
i=i-2;
if(maze[i-1][j].state==0)
up=false;
else
up=true;
if(maze[i+1][j].state==0)
down=false;
else
down=true;
if(maze[i][j-1].state==0)
left=false;
else
left=true;
if(maze[i][j+1].state==0)
right=false;
else
right=true;
}
else{
up=true;
}
break;
case 1: if(!down&&i<maze_row-3&&maze[i+2][j].state==0)
{
build_maze_stack.push(temp);
maze[i+2][j].state=1;
maze[i+1][j].state=1;
i=i+2;
if(maze[i-1][j].state==0)
up=false;
else
up=true;
if(maze[i+1][j].state==0)
down=false;
else
down=true;
if(maze[i][j-1].state==0)
left=false;
else
left=true;
if(maze[i][j+1].state==0)
right=false;
else
right=true;
}
else{
down=true;
}
break;
case 2: if(!left&&j>2&&maze[i][j-2].state==0)
{
build_maze_stack.push(temp);
maze[i][j-2].state=1;
maze[i][j-1].state=1;
j=j-2;
if(maze[i-1][j].state==0)
up=false;
else
up=true;
if(maze[i+1][j].state==0)
down=false;
else
down=true;
if(maze[i][j-1].state==0)
left=false;
else
left=true;
if(maze[i][j+1].state==0)
right=false;
else
right=true;
}
else{
left=true;
}
break;
case 3: if(!right&&j<maze_col-3&&maze[i][j+2].state==0)
{
build_maze_stack.push(temp);
maze[i][j+2].state=1;
maze[i][j+1].state=1;
j=j+2;
if(maze[i-1][j].state==0)
up=false;
else
up=true;
if(maze[i+1][j].state==0)
down=false;
else
down=true;
if(maze[i][j-1].state==0)
left=false;
else
left=true;
if(maze[i][j+1].state==0)
right=false;
else
right=true;
}
else{
right=true;
}
break;
}
if(up&&down&&left&&right){//上下左右都是通路,则此点为圆环,舍弃
if(!build_maze_stack.isEmpty()){
i=build_maze_stack.top().i;
j=build_maze_stack.top().j;
build_maze_stack.pop();
if(maze[i-1][j].state==0)
up=false;
else
up=true;
if(maze[i+1][j].state==0)
down=false;
else
down=true;
if(maze[i][j-1].state==0)
left=false;
else
left=true;
if(maze[i][j+1].state==0)
right=false;
else
right=true;
}
else{
maze[1][1].state=2;
maze[maze_row-3][maze_col-3].state=3;
creat_maze=true;
for(int i=0; i<maze_row; i++)//这一段是防止生成迷宫后依旧显示路线
for(int j=0; j<maze_col; j++){
path[i][j].state=0;//在这里的状态表示父节点,1,2,3,4分别表示从上下左右发展过来
path[i][j].i=i;
path[i][j].j=j;
}
return;
}
}
}
②A*搜索算法
A*搜索算法是一种静态路网中求解最短路最有效的方法。公式表示为:f(n)=g(n)+h(n),其中f(n)是节点n从初始点到目标点的估价函数,g(n)是在状态空间中从初始节点到n节点的实际代价,h(n)是从n到目标节点最佳路径的估计代价。保证找到最短路径(最优解的)条件,关键在于估价函数h(n)的选取:估价值h(n)小于等于n到目标节点的距离实际值,这种情况下,搜索的点数多,搜索范围大,效率低,但能得到最优解。如果估价值大于实际值,搜索的点数少,搜索范围小,效率高,但不能保证得到最优解。A*搜索算法的流程图:
A*搜索算法的求解步骤说明:
1.将起点方格(节点)加入A_search栈中,作为寻路开始的起点;
2.循环执行以下步骤,直到A_search栈为空结束或者终点(节点)加入到A_search中:
(1)在A_search中找到F值最小的那个节点(如果有相等的情况,就选择最先找到的那个节点)作为[ 当前节点 ];
(2)在A_search中将第1步找到的那个F值最小的节点,从A_search中删除掉;
(3)在auto_path中将第1步找到的那个F值最小的节点,加入auto_path中;
(4)围绕第1步找到的那个F值最小的(当前节点),找到与它相邻的四个方向(上下左右)上的节点,这里要判断邻居节点的有效性,看邻居节点是否满足以下条件:
<1>邻居节点是否越界—节点的x或者y是否在可视范围内;
<2>邻居节点是否为障碍方格(节点)—比如本程序中的 MAZE[i][j]=1 即为障碍,MAZE[i][j]=0 即为可用;
<3>邻居节点是否已经在A_search栈中或者auto_path数组中;
A*搜索算法的代码
void play_maze::on_A_search_clicked()//A*算法
{
if(flag_click||flag_success){
return;
}
if(!creat_maze){
return;
}
A_search.clear();
//int md;//曼哈顿距离
// qDebug()<<"begin find path";
ftime.start();
for(int i=0; i<maze_row; i++)
for(int j=0; j<maze_col; j++){
path[i][j].state=0;//在这里的状态表示父节点,1,2,3,4分别表示从上下左右发展过来
path[i][j].i=i;
path[i][j].j=j;
}
//用于记录该点是否搜索的矩阵
for(int i=0; i<maze_row; i++)
for(int j=0; j<maze_col; j++){
if(maze[i][j].state==0)
graph[i][j]=1;
else
graph[i][j]=0;//初始化未被搜索
}
QString message;
int searchnum=0;
point top;
top.i=control_X;
top.j=control_Y;
top.state=abs(control_X-target_X)+abs(control_Y-target_Y);
A_search.push_back(top);//这里的状态什么也不表示
graph[top.i][top.j]=0;
while(!A_search.isEmpty()){
qSort(A_search.begin(),A_search.end(),cmp);
top=A_search.front();
//qDebug()<<top.i<<" "<<top.j<<" "<<top.state<<endl;
A_search.pop_front();
if(graph[top.i][top.j]==0){
searchnum+=1;
if(maze[top.i][top.j].state==3){
break;
}
//将此点标记为访问过
//并将真实路径代价计算进去,用graph存储
switch (path[top.i][top.j].state) {
case 1:
graph[top.i][top.j]=graph[top.i-1][top.j]+1;
break;
case 2:
graph[top.i][top.j]=graph[top.i+1][top.j]+1;
break;
case 3:
graph[top.i][top.j]=graph[top.i][top.j-1]+1;
break;
case 4:
graph[top.i][top.j]=graph[top.i][top.j+1]+1;
break;
default:
graph[top.i][top.j]=1;
break;
}
//将未访问的子节点放入开节点表
if((graph[top.i+1][top.j]==0)&&(maze[top.i+1][top.j].state!=0)){
A_search.push_back(point(top.i+1,top.j,(graph[top.i][top.j]+1+abs(top.i+1-target_X)+abs(top.j-target_Y))));
path[top.i+1][top.j].state=1;
//graph[top.i+1][top.j]=graph[top.i][top.j]+1;
}
if((graph[top.i-1][top.j]==0)&&(maze[top.i-1][top.j].state!=0)){
A_search.push_back(point(top.i-1,top.j,(graph[top.i][top.j]+1+abs(top.i-1-target_X)+abs(top.j-target_Y))));
path[top.i-1][top.j].state=2;
//graph[top.i-1][top.j]=graph[top.i][top.j]+1;
} if((graph[top.i][top.j+1]==0)&&(maze[top.i][top.j+1].state!=0)){ A_search.push_back(point(top.i,top.j+1,(graph[top.i][top.j]+1+abs(top.i-target_X)+abs(top.j+1-target_Y))));
path[top.i][top.j+1].state=3;
}
if((graph[top.i][top.j-1]==0)&&(maze[top.i][top.j-1].state!=0)){
A_search.push_back(point(top.i,top.j-1,(graph[top.i][top.j]+1+abs(top.i-target_X)+abs(top.j-1-target_Y))));
path[top.i][top.j-1].state=4;
/[表情]archnum+=1;
//graph[top.i][top.j-1]=graph[top.i][top.j]+1;
}
}
}
// qDebug()<<"find the path!"<<endl;
}
path[top.i][top.j+1].state=3;
}
if((graph[top.i][top.j-1]==0)&&(maze[top.i][top.j-1].state!=0)){
A_search.push_back(point(top.i,top.j-1,(graph[top.i][top.j]+1+abs(top.i-target_X)+abs(top.j-1-target_Y))));
path[top.i][top.j-1].state=4;
/[表情]archnum+=1;
//graph[top.i][top.j-1]=graph[top.i][top.j]+1;
}
}
}
// qDebug()<<"find the path!"<<endl;
}