1、迷宫游戏简介
迷宫游戏存在一个起点与一个终点,设计算法,在N*N的正方形迷宫中寻找从起点到终点的路径,路径不允许回头。
2、迷宫游戏问题的状态空间法表示
对于所给定的迷宫建立相应的坐标系,每一个点有自己相应的坐标以及该点按西北东南方向上下一个点的序号。例如:
有如下状态空间表:
将信息提炼出来,生成状态表的txt文件供程序使用。
1 1 -1 4 -1 -1
1 4 -1 -1 5 -1
2 2 -1 4 7 6
2 3 1 5 -1 3
2 4 2 -1 9 4
3 1 3 7 8 -1
3 2 3 8 -1 6
3 3 -1 9 6 7
3 4 5 -1 10 8
4 4 9 -1 -1 -1
其中,按每行顺序所表示的意义为:点的x坐标,点的y坐标,西北东南方向上的点序号(-1表示该方向上没有可行点)。在程序中按上述建立结点的结构体
3、迷宫游戏问题的盲目搜索技术概述
1、BFS(宽度优先搜索):
宽度优先搜索算法(又称广度优先搜索)是最简便的图的搜索算法之一。其别名又叫BFS,属于一种盲目搜寻法,目的是系统地展开并检查图中的所有节点,以找寻结果。换句话说,它并不考虑结果的可能位置,彻底地搜索整张图,直到找到结果为止。它以与起始结点的接近程度为依据依次扩展结点(即:离起始结点近的先被扩展),先被扩展出来的结点存放在OPEN表中,在下一次循环中优先被扩展,即一个先进先出的队列,已被扩展过的结点被放在CLOSED表中记录。
流程图:
2、DFS(深度优先搜索):
深度优先搜索属于图算法的一种,英文缩写为DFS,其过程简要来说是对每一个可能的分支路径深入到不能再深入为止,而且每个节点只能访问一次。先扩展最深的节点。当出现没有后继节点时,换到旁边的次深节点进行扩展。与BFS不同的是它将扩展的结点放入OPEN的首端,并且带有深度限制,以防出现过深的路径,但可能会丢失解。
流程图:
4.迷宫游戏问题的启发式搜索技术
估价函数:
其中为权值,为某点到起点的水平距离与垂直距离之和,为某点到目标点的水平距离与垂直距离之和。
启发式搜索技术:
以估价函数为标准,按照权值从小到大排序将节点以及某个节点的后继节点存放于OPEN表中,每一次拓展权值最小的结点,顺着这条路下去可能会找到目标节点,若未找到再拓展其他节点,仍根据权值进行后续拓展,寻找目标节点。
流程图:
4、实验代码
代码注释不多,算法在上文已有详细解释,下文代码使用了c++STL容器。
#include <iostream>
#include <vector>
#include <queue>
#include <fstream>
#include <stack>
#include <list>
#include <algorithm>
#include <ctime>
#define SIZE 64
#define DEEP_MAX 100
using namespace std;
struct Point {
int x,y;//坐标
int dir[4];//四个方向的状态
int pre = -1;//记录前驱结点的ID
int ID;
int weight = 0;//A*的权值,bfs与dfs未使用
};
void DisPlay(vector<Point> point,Point p) {
vector<Point>Dis;
Dis.push_back(p);
int tp = p.pre;
while (tp != -1)
{
Dis.push_back(point[tp-1]);
tp = point[tp - 1].pre;
}
reverse(Dis.begin(), Dis.end());
for (int i = 0; i < Dis.size(); i++)
{
if (i != Dis.size() - 1)
cout << "(" << Dis[i].x << "," << Dis[i].y << ")" << "->";
else
cout << "(" << Dis[i].x << "," << Dis[i].y << ")" << endl;
}
}
void bfs(vector<Point> point,int x,int y) {
int flag = 1;
int maxelement_OPEN = 0;
int maxelement_CLOSED = 0;
queue<Point> OPEN;
int CLOSED[SIZE] = { 0 };//已被扩展过的结点
/*for (int i = 0; i < SIZE; i++)
cout << point[i].x << " " << point[i].y << endl;*/
OPEN.push(point[0]);
if (point[0].x == x && point[0].y == y)
cout << "(1,1)->(1,1)" << endl;
else {
while (flag) {
if (OPEN.empty())
{
cout << "搜索出口失败!" << endl;
flag = 0;
}
else
{
if (OPEN.size() > maxelement_OPEN)
maxelement_OPEN = OPEN.size();
Point temppoint = OPEN.front(); OPEN.pop();
CLOSED[temppoint.ID - 1] = 1;
for (int i = 0; i < 4; i++)
{
int temp = temppoint.dir[i];
if (temp != -1&&CLOSED[temp-1]!=1)
{
OPEN.push(point[temp - 1]);
point[temp - 1].pre = temppoint.ID;
if (point[temp - 1].x == x && point[temp - 1].y == y)
{
DisPlay(point,point[temp - 1]);
flag = 0;
for (int j = 0; j < SIZE; j++)
{
if (CLOSED[j] == 1)
maxelement_CLOSED++;
}
cout << "OPEN表中的最大元素个数为:" << maxelement_OPEN<<" ," <<"CLOSED表中的最大元素个数为:"<<maxelement_CLOSED<< endl;
}
}
}
}
}
}
}
void dfs(vector<Point> point, int x, int y)
{
int deep = 1;
int maxelement_OPEN = 0;
int maxelement_CLOSED = 0;
stack<Point> OPEN;
int CLOSED[SIZE] = { 0 };
int OPEN_F[SIZE] = { 0 };//标记是否已经在OPEN表中
OPEN.push(point[0]);
OPEN_F[point[0].ID - 1] = 1;
int flag = 1;
if (point[0].x == x && point[0].y == y)
cout << "(1,1)->(1,1)" << endl;
else
{
while (flag)
{
if (OPEN.empty() || deep > DEEP_MAX)
{
cout << "搜索出口失败!" << endl;
flag = 0;
}
else
{
if (OPEN.size() > maxelement_OPEN)
maxelement_OPEN = OPEN.size();
Point temppoint = OPEN.top(); OPEN.pop();
OPEN_F[temppoint.ID - 1] = 0;
CLOSED[temppoint.ID - 1] = 1;
for (int i = 0; i < 4; i++)
{
int temp = temppoint.dir[i];
if (temp != -1 && CLOSED[temp - 1] != 1 && OPEN_F[temp - 1] != 1)
{
OPEN.push(point[temp - 1]);
point[temp - 1].pre = temppoint.ID;
if (point[temp - 1].x == x && point[temp - 1].y == y)
{
DisPlay(point, point[temp - 1]);
flag = 0;
for (int j = 0; j < SIZE; j++)
{
if (CLOSED[j] == 1)
maxelement_CLOSED++;
}
cout << "OPEN表中的最大元素个数为:" << maxelement_OPEN<<" ," << "CLOSED表中的最大元素个数为:" << maxelement_CLOSED << endl;
}
}
}
deep++;
}
}
}
}
int cmp(Point a, Point b)
{
return a.weight < b.weight;
}
int cweight(int x,int y,int ex,int ey)//计算权值
{
return abs(x - 1) + abs(y - 7) + abs(x - ex) + abs(y - ey);
}
void A_star(vector<Point> point, int x, int y)
{
int maxelement_OPEN = 0;
int maxelement_CLOSED = 0;
point[0].weight = cweight(point[0].x, point[0].y, x, y);
list<Point> OPEN;
int CLOSED[SIZE] = { 0 };
int flag = 1;
OPEN.push_back(point[0]);
if (point[0].x == x && point[0].y == y)
cout << "(1,1)->(1,1)" << endl;
else
{
while (flag)
{
if (OPEN.empty())
{
cout << "搜索出口失败!" << endl;
flag = 0;
}
else
{
OPEN.sort(cmp);//按权值排序
if (OPEN.size() > maxelement_OPEN)
maxelement_OPEN = OPEN.size();
Point temppoint = OPEN.front(); OPEN.pop_front();
CLOSED[temppoint.ID - 1] = 1;
for (int i = 0; i < 4; i++)
{
int temp = temppoint.dir[i];
if (temp != -1 && CLOSED[temp - 1] != 1)
{
point[temp - 1].weight = cweight(point[temp - 1].x, point[temp - 1].y, x, y);
OPEN.push_back(point[temp - 1]);
point[temp - 1].pre = temppoint.ID;
if (point[temp - 1].x == x && point[temp - 1].y == y)
{
DisPlay(point, point[temp - 1]);
flag = 0;
for (int j = 0; j < SIZE; j++)
{
if (CLOSED[j] == 1)
maxelement_CLOSED++;
}
cout << "OPEN表中的最大元素个数为:" << maxelement_OPEN<<" ," << "CLOSED表中的最大元素个数为:" << maxelement_CLOSED << endl;
}
}
}
}
}
}
}
int main(void)
{
int x, y;
clock_t startTime, endTime;
vector<Point> point;
ifstream input("input.txt", ios::in);
if (!input)
{
cout << "打开文件失败!" << endl;
exit(-1);
}
for (int i = 0; i < SIZE; i++) {
Point tempp;
tempp.ID = i + 1;
int k1 = 0, k2 = 0;
input >> k1 >> k2;
tempp.x = k1;
tempp.y = k2;
for (int j = 0; j < 4; j++) {
int n;
input >> n;
tempp.dir[j] = n;
}
point.push_back(tempp);
}
input.close();
cout << "请输入目标位置:" << endl;
cin >> x >> y;
cout << "起点:(1,1)" << " " << "终点:" << "(" << x << "," << y << ")" << endl;
cout << "宽度优先搜索:" << endl;
startTime = clock();
bfs(point, x, y);
endTime = clock();
cout << "程序运行时间为:" << (double)(endTime - startTime)/CLK_TCK <<"s"<< endl;
cout << "深度优先搜索:" << endl;
startTime = clock();
dfs(point, x, y);
endTime = clock();
cout << "程序运行时间为:" << (double)(endTime - startTime)/CLK_TCK<<"s"<< endl;
cout << "启发式搜索:" << endl;
startTime = clock();
A_star(point, x, y);
endTime = clock();
cout << "程序运行时间为:" << (double)(endTime - startTime)/CLK_TCK <<"s"<< endl;
system("pause");
return 0;
}
5、案例说明
对于上文中的例子,运行结果:
6、实验程序简单说明
1、对于不同迷宫,需要改写程序文件夹下的input文本文件(状态表)。
2、注意源程序中的宏定义,涉及深搜的深度界限以及CLOSED表的SIZE。