游戏中的AI---寻路算法A* [1](下篇 A*算法)

0.简介

前面有了Dijkstra算法的铺垫以后,我们来看A*算法。

1.启发式

这个启发式从字面理解就是启发,受到启发,算法在寻路的时候会受到启发?差不多是这样。看下面这句代码

float t_cost = t.cost+ mapNodes[node.x][node.y].cost+qf*1000;//此节点与父节点距离+父节点到起点距离+启发因子

我们发现,这个与Dijkstra的主要区别就是在更新路径长度的时候,多了一个启发因子,这个启发因子我们可以设置成当前点到目标点的欧拉距离,或者是曼哈顿距离。

这里用的是曼哈顿距离。

float qf = abs(t.x- disX)+abs(t.y- disY);

本质上就是在Dijkstra算法求路径长度上添加了一个额外的权重。

看下图。

路径计算

图中蓝色是走过的路线,橙色是当前点到终点的连线。

假设 S1 = X + Y,S2 = A + B,如果不是启发式搜索,那么算法要搜索全部路径后才能知道最短路径是哪条,现在是启发式算法,也就是有了图中Y和B的长度作为参考,我们发现虽然X比A大,应该选A这条路走, 但是由于B比Y大很多,橙色线告诉我们,走A的话就会使得我们距离目标更远了,走X的话会让我们距离目标更近了,所以我们不再只看X与A的大小,而是看S1与S2的大小,也就是算法有了一定的预判能力,这样可以预先筛选掉许多路线,把搜索范围缩小。

那么这个筛选能有多大效率提升呢?

我将启发因子权重调整为0,此时算法退化为Dijkstra算法,下图中灰色部分是搜索的范围,中间白色的线是最短路径。

启发权值0

我们发现算法几乎搜索了整个地图,黑色部分是不可走的地方。

如果将启发权值设置成了1000

权值1000

搜索范围大大减少,虽然得到的最短路径和上面的有所不同,但是相比来说,也是最短。算法在寻路的过程中更加有目的性,不想上面的方法漫无目的的搜索。

2.实现

本篇内容其实并不多,所以直接看代码吧,毕竟算法比较简单。代码需要配置opencv

#include<iostream>
#include<unordered_map>
#include<list>
#include<queue>
#include<opencv2/opencv.hpp>
#include<windows.h>
using namespace std;
using namespace cv;
class GEdge
{
public:
	int begin, end;
	double cost;
	GEdge() {}
	GEdge(int _begin,int _end,double _cost):begin(_begin),end(_end),cost(_cost){}
	GEdge(int _begin, int _end) :begin(_begin), end(_end), cost(1.0) {}

};
class GNode
{
public:
	int id;
	GNode(int _id) : id(_id) {}
	GNode(){}
	list<GEdge>edges;
};
class SelectNode
{
public:
	int id;
	double cost;
	SelectNode() {}
	SelectNode(int _id,double _cost) : id(_id),cost(_cost){}
	bool operator<(const SelectNode& a) const
	{
		return cost > a.cost; 
	}
};

class mapNode
{
public:
	mapNode() {}
	mapNode(int _x,int _y,double pc,double _cost):x(_x),y(_y), pathCost(pc),cost(_cost) {}
	int x, y;
	double cost;
	double pathCost;
	bool operator<(const mapNode& a) const
	{
		return (pathCost >  a.pathCost);
	}
};
mapNode getNode(int x, int y,int index)
{
	float cost = 0;
	if (index == 0)
	{
		x--;
		y--;
		cost = 14;
	}
	else if (index == 1)
	{
		y--;
		cost = 10;
	}
	else if (index == 2)
	{
		x++;
		y--;
		cost = 14;
	}
	else if (index == 3)
	{
		x--;
		cost = 10;
	}
	else if (index == 4)
	{
		x++;
		cost = 10;
	}
	else if (index == 5)
	{
		x--;
		y++;
		cost = 14;
	}
	else if (index == 6)
	{
		y++;
		cost = 10;
	}
	else if (index == 7)
	{
		x++;
		y++;
		cost = 14;
	}
	if (x < 0 || y < 0 || x >= 100 || y >= 100)
		cost = 999;
	return mapNode(x, y,0,cost);
}

int main()
{
	Mat bmp = imread("123.bmp");
	cvtColor(bmp, bmp, CV_BGR2GRAY);
	mapNode **mapNodes = new mapNode*[600];
	for (int i = 0; i < 600; i++)
	{
		mapNodes[i] = new mapNode[600];
		memset(mapNodes[i], 0, 600  * sizeof(mapNode));
	}
	
	priority_queue<mapNode> queue;
	queue.push(mapNode(10, 23,0,0));
	int disX = 71, disY =71;
	while (!queue.empty())
	{

		mapNode node = queue.top();
		queue.pop();
		for (int i = 0; i < 8; i++)
		{	
			mapNode t = getNode(node.x, node.y, i);
			//这里根据图片提前判断是否有墙壁
			if (bmp.at<uchar>(t.x, t.y) < 20)
				continue;
			//float qf = sqrt((t.x - disX) * (t.x - disX) + (t.y - disY) * (t.y - disY));
			float qf = abs(t.x- disX)+abs(t.y- disY);
			float t_cost = t.cost+ mapNodes[node.x][node.y].cost+qf*1000;//此节点与父节点距离+父节点到起点距离+启发因子
			if (mapNodes[t.x][t.y].pathCost == 0|| t_cost < mapNodes[t.x][t.y].pathCost)
			{
				//这里存储最短路径的结果
				//这是启发式路径长度
				mapNodes[t.x][t.y].pathCost = t_cost;
				//这是之前走过的实际路径长度
				mapNodes[t.x][t.y].cost = t.cost+ mapNodes[node.x][node.y].cost;
				mapNodes[t.x][t.y].x = node.x;
				mapNodes[t.x][t.y].y = node.y;
				queue.push(mapNode(t.x,t.y,t_cost, mapNodes[t.x][t.y].cost));
				uchar* data = bmp.ptr<uchar>(t.x);
				data[t.y] = 100;
				if(node.x==93&& node.y==67)
					cout << t.x << " " << t.y << endl;
				if (t.x == disX && t.y == disY)
				{
					while (!queue.empty())
						queue.pop();
					break;
				}
				Mat showim = bmp;
				resize(bmp, showim, Size(500, 500));
				imshow("map_path", showim);
				waitKey(1);

			}			
		}	
	}
	int startX= disX, startY= disY;
	while (1)
	{
		int t_startX = mapNodes[startX][startY].x;
		int t_startY = mapNodes[startX][startY].y;
		startX = t_startX;
		startY = t_startY;
		uchar* data = bmp.ptr<uchar>(startX);
		data[startY] = 200;
		if (startX == 2 && startY == 2)
			break;
		Mat showim = bmp;
		resize(bmp, showim, Size(500, 500));
		imshow("map_path", showim);
		waitKey(20);
	}

	imwrite("111.bmp",bmp);
	resize(bmp,bmp,Size(600,600));
	imshow("map_path",bmp);
	waitKey(0);

	return 0;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值