Bootstrap

算法:A星寻路(含实例)

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表,核心还是启发式寻路!

;