a星寻路是寻路算法中比较常用的一种,核心是启发式寻路算法,实际开发中常用于计算最优路径,自动寻路等。
原理
启发式寻路
假设从A点到B点,中间有障碍物;寻路中,肯定要绕过障碍物到达B点,所以,我们要寻找点c为过渡点。所以,引出两个关键词,实际消耗G和预估消耗H。过渡到c点后,实际消耗G为上一个点到c点的距离,然后在c点对目标点B点的距离进行估值,也就是预估消耗H,这边可以用曼哈顿距离去算,也就是从C点到终点忽略障碍物和对角移动要走多少个网格,去求出预估距离H;每走到一个新点时再次进行如上操作。最后得出可表达式最短权值F=G+H。
A星寻路
从起点A开始,把它作为当前待处理点存入一个OPEN表(OPEN表中放所有待处理的点)。
寻找起点周围所有可到达或者可通过的方格,障碍物方格不考虑。把他们加入OPEN表。这些周围节点的父节点设为当前点。(保存父节点是因为我们找到终点时需通过回溯到起点!)
在OPEN表中删除当前点,把它加入到一个CLOSE表中(CLOSE表中放所有不再检查的点)。
接下来通过启发式寻路找出OPEN表中F(权值)最小的点作为当前点(此步可用堆排序处理);再次判断其周围点,如此循环往复,直到加入CLOSE表的点为终点(寻路结束,找到最优路径)或OPEN表空(没找到路径),找到终点后通过父节点指向回溯到起点得到最优path。
注意事项
判断当前待处理点的周围点时,若其中某个周围点A是障碍物或者已经在CLOSE表中,则不考虑;若其中某个周围点A在OPEN表中,那么要将点A先前的G值和当前待处理点的G值+当前待处理点到点A的距离作比较,若点A先前的G值更小,则说明有比从当前待处理点到达点A更快的点,所以不改变点A的权值和父节点指向,若当前待处理点的G值+当前待处理点到点A的距离更小,则说明有从当前待处理点到达点A是更快的,要改变点A的权值,并且父节点指向当前待处理点。
实例
代码如下:
#include <iostream>
#include <list>
#include <stack>
#include <windows.h>
using namespace std;
#define X 8
#define Y 8
/*
利用队列搜索路径
*/
//色彩函数的声明
void COLOR(const char* s, int color);
//用二维数组创建一个地图
int GameMap[10][10] = {
{ 1, 3, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 1, 0, 0, 1, 0, 0, 0, 0, 1, 1 },
{ 1, 0, 0, 0, 0, 0, 1, 0, 0, 1 },
{ 1, 1, 0, 1, 0, 1, 1, 1, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 1, 0, 0, 1 },
{ 1, 0, 0, 1, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 1, 1, 1, 0, 1, 0, 1, 1 },
{ 1, 0, 0, 1, 0, 0, 0, 0, 0, 1 },
{ 1, 1, 0, 0, 0, 0, 1, 0, 4, 1 },
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
};
//渲染地图
void RenderMap(int GameMap[10][10])
{
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 10; j++)
{
switch (GameMap[i][j])
{
case 0:
cout << " ";
break;
case 1:
COLOR("■", 4);
break;
case 2:
COLOR("♀", 1);
break;
case 3:
cout << "入";
break;
case 4:
cout << "出";
break;
}
}
cout << endl;
}
}
//位置结构体的定义
struct Apos
{
int x;
int y;
int f;
int g;
int h;
Apos *pPar;
};
//遍历close链表寻找元素
bool isHaveclose(list<Apos*> close, int x, int y)
{
if (close.size()>0)
{
list<Apos*>::iterator it = close.begin();
while (it != close.end())
{
Apos* pn = *it;
if (pn->x == x&&pn->y == y)
return true;
it++;
}
}
return false;
}
//遍历open链表寻找元素
Apos* isHaveopen(list<Apos*> open, int x, int y)
{
list<Apos*>::iterator it = open.begin();
while (it != open.end())
{
Apos* pn = *it;
if (pn->x == x&&pn->y == y)
return pn;
it++;
}
return nullptr;
}
//加入open表
void EnterOpen(list<Apos*> &open, int offx, int offy)
{
Apos *pnew = new Apos;
pnew->x = open.front()->x + offx;
pnew->y = open.front()->y + offy;
if (0 != offx && 0 != offy)
pnew->g = open.front()->g + 14;
else
pnew->g = open.front()->g + 10;
pnew->h = abs(open.front()->x + offx - X) * 10 + abs(open.front()->y + offy - Y) * 10;
pnew->f = pnew->g + pnew->h;
pnew->pPar = open.front();
open.push_back(pnew);
}
//比较G值
void cmpg(list<Apos*> &open, int offx, int offy)
{
Apos *p = isHaveopen(open, open.front()->x + offx, open.front()->y + offy);
int s;
if (0 != offx && 0 != offy)
s = 14;
else
s = 10;
if (p->g > open.front()->g + s)
{
p->g = open.front()->g + s;
p->f = p->g + p->h;
p->pPar = open.front();
}
}
//链表排序
bool sortopen(Apos* pos, Apos* pos1)
{
return pos->f < pos1->f;
}
void FindRoad(int GameMap[10][10], list<Apos*> &open,
list<Apos*> &close)
{
if (open.front()->x - 1 >= 0 && open.front()->x - 1 <= 9 &&
0 == GameMap[open.front()->x - 1][open.front()->y] || 4 == GameMap[open.front()->x - 1][open.front()->y])//上
{
if (!isHaveclose(close, open.front()->x - 1, open.front()->y))
{
if (nullptr == isHaveopen(open, open.front()->x - 1, open.front()->y))
EnterOpen(open, -1, 0);
else
cmpg(open, -1, 0);
}
}
if (open.front()->x + 1 >= 0 && open.front()->x + 1 <= 9 &&
0 == GameMap[open.front()->x + 1][open.front()->y] || 4 == GameMap[open.front()->x + 1][open.front()->y])//下
{
if (!isHaveclose(close, open.front()->x + 1, open.front()->y))
{
if (nullptr == isHaveopen(open, open.front()->x + 1, open.front()->y))
EnterOpen(open, 1, 0);
else
cmpg(open, 1, 0);
}
}
if (open.front()->y - 1 >= 0 && open.front()->y - 1 <= 9 &&
0 == GameMap[open.front()->x][open.front()->y - 1] || 4 == GameMap[open.front()->x][open.front()->y - 1])//左
{
if (!isHaveclose(close, open.front()->x, open.front()->y - 1))
{
if (nullptr == isHaveopen(open, open.front()->x, open.front()->y - 1))
EnterOpen(open, 0, -1);
else
cmpg(open, 0, -1);
}
}
if (open.front()->y + 1 >= 0 && open.front()->y + 1 <= 9 &&
0 == GameMap[open.front()->x][open.front()->y + 1] || 4 == GameMap[open.front()->x][open.front()->y + 1])//右
{
if (!isHaveclose(close, open.front()->x, open.front()->y + 1))
{
if (nullptr == isHaveopen(open, open.front()->x, open.front()->y + 1))
EnterOpen(open, 0, 1);
else
cmpg(open, 0, 1);
}
}
if (open.front()->x - 1 >= 0 && open.front()->x - 1 <= 9 && open.front()->y - 1 >= 0 && open.front()->y - 1 <= 9 &&
(0 == GameMap[open.front()->x - 1][open.front()->y - 1] || 4 == GameMap[open.front()->x - 1][open.front()->y - 1]) &&
1 != GameMap[open.front()->x][open.front()->y - 1]&&
1 != GameMap[open.front()->x - 1][open.front()->y])//上左
{
if (!isHaveclose(close, open.front()->x - 1, open.front()->y - 1))
{
if (nullptr == isHaveopen(open, open.front()->x - 1, open.front()->y - 1))
EnterOpen(open, -1, -1);
else
cmpg(open, -1, -1);
}
}
if (open.front()->x - 1 >= 0 && open.front()->x - 1 <= 9 && open.front()->y + 1 >= 0 && open.front()->y + 1 <= 9 &&
(0 == GameMap[open.front()->x - 1][open.front()->y + 1] || 4 == GameMap[open.front()->x - 1][open.front()->y + 1]) &&
1 != GameMap[open.front()->x][open.front()->y + 1] &&
1 != GameMap[open.front()->x - 1][open.front()->y])//上右
{
if (!isHaveclose(close, open.front()->x - 1, open.front()->y + 1))
{
if (nullptr == isHaveopen(open, open.front()->x - 1, open.front()->y + 1))
EnterOpen(open, -1, 1);
else
cmpg(open, -1, 1);
}
}
if (open.front()->x + 1 >= 0 && open.front()->x + 1 <= 9 && open.front()->y - 1 >= 0 && open.front()->y - 1 <= 9 &&
(0 == GameMap[open.front()->x + 1][open.front()->y - 1] || 4 == GameMap[open.front()->x + 1][open.front()->y - 1]) &&
1 != GameMap[open.front()->x][open.front()->y - 1] &&
1 != GameMap[open.front()->x + 1][open.front()->y])//下左
{
if (!isHaveclose(close, open.front()->x + 1, open.front()->y - 1))
{
if (nullptr == isHaveopen(open, open.front()->x + 1, open.front()->y - 1))
EnterOpen(open, 1, -1);
else
cmpg(open, 1, -1);
}
}
if (open.front()->x + 1 >= 0 && open.front()->x + 1 <= 9 && open.front()->y + 1 >= 0 && open.front()->y + 1 <= 9 &&
(0 == GameMap[open.front()->x + 1][open.front()->y + 1] || 4 == GameMap[open.front()->x + 1][open.front()->y + 1]) &&
1 != GameMap[open.front()->x][open.front()->y + 1] &&
1 != GameMap[open.front()->x + 1][open.front()->y])//下右
{
if (!isHaveclose(close, open.front()->x + 1, open.front()->y + 1))
{
if (nullptr == isHaveopen(open, open.front()->x + 1, open.front()->y + 1))
EnterOpen(open, 1, 1);
else
cmpg(open, 1, 1);
}
}
close.push_back(open.front());
if ((open.front()->x == X&&open.front()->y == Y)||open.empty())
return;
open.pop_front();
open.sort(sortopen);
FindRoad(GameMap, open, close);
}
int main()
{
RenderMap(GameMap);
//三个链表的定义
list<Apos*> open;
list<Apos*> close;
list<Apos*> path;
Apos *pstar = new Apos;
pstar->x = 0;
pstar->y = 1;
pstar->f = 0;
pstar->g = 0;
pstar->h = 0;
pstar->pPar = nullptr;
open.push_back(pstar);
FindRoad(GameMap, open, close);
if (open.empty())
cout << "找不到成功路径!" << endl;
else
{
Apos* p = close.back();
while (p->pPar != nullptr)
{
path.push_front(p);
p = p->pPar;
}
list<Apos*>::iterator itpath = path.begin();
while (itpath!=path.end())
{
p = (*itpath);
GameMap[p->x][p->y] = 2;
itpath++;
Sleep(300);
system("CLS");
RenderMap(GameMap);
}
}
cout << "蓝色路径为最优路径!" << endl;
return 0;
}
//色彩函数
void COLOR(const char* s, int color)
{
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleTextAttribute(handle, FOREGROUND_INTENSITY | color);
printf(s);
SetConsoleTextAttribute(handle, FOREGROUND_INTENSITY | 7);
}
总结
A星必不可少的就是OPEN表:存放待处理点;CLOSE表:存放不再处理的点;权值F;实际消耗G和预估消耗H;可能再多一个存放路径的PATH表,核心还是启发式寻路!