寻路算法 --- A星寻路算法

深度寻路算法应用场景 

仅用于空旷地形,小游戏或者大游戏的某个小模块,点击地图,人物一步步试探

广度寻路算法应用场景 

只适用于小地图,回合制的走格子游戏,上帝视角,在走之前已经把路都找出来了

A星寻路算法应用场景

应用场景广泛

RPG游戏:为什么小兵追着人物砍?怎么样知道人物在哪?怎么样给它自动规划路径?人物自动跑图,怎么实现?

通过A星算法实现

以A星为基础的寻路算法:遗传算法,蚁群算法. . .

寻路算法 --- A星寻路算法_第1张图片

A星寻路算法排除了广度的劣势:循环比较多,每一个点都要涉及到,集合了深度的思想实现的-> 通过代价的概念来量化每走一步的消耗 f = g + h + w

A星会把一些无关紧要的分支不纳入考虑,会计算哪一种选择最佳-> 采用广度的方式,如果8 个点都能走,每个点都会纳入考虑,然后无限延伸

广度寻路算法只能走直线,A星寻路算法允许走斜线

A星寻路算法的核心是量化,选择最佳路径,应该选择代价低的(最近的)

f :最终做选择的依据、代价-> 用来评价寻路的快慢

g:从起点到当前点已经付出的代价-> 沉没成本

h:当前点到终点的预估代价

w:权重

这里举一个简单的例子:

沉没成本 g:小明和小红去创业,小明无工作 0 收入,小红工作每月收入2 W,小明的沉没成本就是 0元 / 月,小红的沉没成本就是 2W / 月,如果去创业,小红就相当于每个月损失2W,小明没有损失

预估代价 h:假设小明和小红创业的收益分别为:小明1.5W /月,小红3W / 月

最终整合起来:小明去创业更合适,他的整体代价最小

权重 w:创业过程中间可能存在一些问题

A星寻路算法也是每一步付出最小的代价达到结果,整体一路到终点,自然得到最佳路径

这里举一个简单的例子:

从广州到北京的代价?

        飞机         高铁         火车         摩托          走路

钱      2k           1k            300           . . .            . . .

时间  2h           5h            48h           . . .            . . .

需要设置预先代价,并且设置时要满足基本法:直角三角形三条边的长度需要满足勾股定理

蓝色部分为 g 值  红色部分为 h 值  紫色部分为最佳路径

准备一棵树:记录整个过程中的路线、经过

准备一个数组:纳入考虑的点要找出来哪一个 f 值最小

注意存在死胡同的情况

从起点(树的根节点)开始,每一步都是去找它周围能走的点,然后入树,放到容器中,找完之后,从容器中挑出来 f 值最小的那一个,把它从容器中删掉,让它变成树的当前点,再循环,从当前点开始,找当前点周围能走的点,入树,一路去找. . .

#include 
#include 
using namespace std;

//直线代价
#define ZXDJ  10
//斜线代价
#define XXDJ  14

//行数
#define ROWS  10
//列数
#define COLS  10

//枚举 0 ~ 7 8个方向
enum direct{p_up,p_down,p_left,p_right,p_lup,p_ldown,p_rup,p_rdown};

//描述一个点
struct MyPoint{
//private:
    
    //xy坐标
	int		row;
	int		col;
    //需要比较的值
	int		f;
	int		g;
	int		h;
public:
	int getRow()const{ return row; }
	int getCol()const{ return col; }
	void setH(int h){
		this->h = h;
	}
	//计算f值
	void getF(){ f = g + h; }

    //构造函数
	MyPoint(){		row = col = f = g = h = 0;	}
    //有参构造函数
	MyPoint(int y, int x){
		row = y; col = x;
		f = g = h = 0;
	}

	friend bool operator==(const MyPoint& p1, const MyPoint& p2);
	friend ostream& operator<<(ostream& o, const MyPoint& p);
};
ostream& operator<<(ostream& o, const MyPoint& p){
	return cout << "(" << p.row << "," << p.col << ")";
}
bool operator==(const MyPoint& p1, const MyPoint& p2){
	if ((p1.row == p2.row) && (p1.col == p2.col)) return true;
	return false;
}

//树的节点类型
struct treeNode{
	MyPoint				pos;        //点-> 在地图中的下标    
	treeNode*			pParent;    //指向父节点的指针
	vector	child;      //指向孩子节点的指针-> 有多少个孩子节点就有多少个指针

	treeNode(){ pParent = NULL; }   //构建一棵树
	treeNode(const MyPoint& p){ this->pos = p; pParent = NULL; }

	//void getH(const MyPoint& p){	}
};
//判断pos点能不能走,传入两个地图和点 能走返回true 否则返回false
bool canWalk(int map[ROWS][COLS], bool pathMap[ROWS][COLS], const MyPoint& pos);
//计算出pos的h值并返回
int getH(const MyPoint& pos, const MyPoint& endPos);


int main(){
	//1 地图
	int map[ROWS][COLS] = {
		{ 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 },
		{ 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 },
		{ 0, 1, 0, 0, 1, 0, 0, 0, 0, 0 },
		{ 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 },
		{ 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 },
		{ 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 },
		{ 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 },
		{ 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 },
		{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
		{ 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }
	};

	//辅助地图用于标记已经走过-> 0表示false 表示没有走过
	bool pathMap[ROWS][COLS] = { 0 };

	//2 起点 终点
	MyPoint begPos = { 1, 1 };
	MyPoint endPos = { 7, 8 };

	//3 准备一颗树
	treeNode* pRoot = NULL;
	//起点入树成为树的根节点
	pRoot = new treeNode(begPos);
	//标记起点走过
	pathMap[begPos.getRow()][begPos.getCol()] = true;

	//4 准备一个数组用来存储所有需要找 最小f的节点指针
	vector buff;
	vector::iterator it;		//用来遍历
	vector::iterator itMin;	//保存f最小的元素地址
	//5 寻路
	treeNode* pCurrent = pRoot;         //当前位置-> 树根 从树根开始赋值
	treeNode* pChild = NULL;            //当前点
	bool isFindEnd = false;
	while (!isFindEnd){
		//5.1 找到周围能走的点-> 有8个点
		for (int i = 0; i < 8; i++){
			pChild = new treeNode;      
			pChild->pos = pCurrent->pos;//新的点和当前点位置相同
			switch (i){
			case p_up:		pChild->pos.row--; pChild->pos.g += ZXDJ;   break;
			case p_down:	pChild->pos.row++; pChild->pos.g += ZXDJ;   break;
			case p_left:	pChild->pos.col--; pChild->pos.g += ZXDJ;   break;
			case p_right:	pChild->pos.col++; pChild->pos.g += ZXDJ;   break;
			case p_lup:		pChild->pos.row--; pChild->pos.col--; pChild->pos.g += XXDJ; break;
			case p_ldown:	pChild->pos.row++; pChild->pos.col--; pChild->pos.g += XXDJ; break;
			case p_rup:		pChild->pos.row--; pChild->pos.col++; pChild->pos.g += XXDJ; break;
			case p_rdown:	pChild->pos.row++; pChild->pos.col++; pChild->pos.g += XXDJ; break;
			}//end of switch
            //判断点能不能走
			if (canWalk(map,pathMap,pChild->pos)){//能走
				pChild->pos.h = getH(pChild->pos, endPos);//先算出h-> 当前点跟终点的直线距离 无视障碍
				pChild->pos.getF();//再算出f

				//入树-> 所有满足条件的点都入树
				pCurrent->child.push_back(pChild);        //如果能走 在树中pChild一定是pCurrent的孩子
				pChild->pParent = pCurrent;               //pChild的父指针指向当前点
				//入buff
				buff.push_back(pChild);
			}
			else{//不能走-> 释放
				delete pChild;
				pChild = NULL;
			}
		}//end of for 
		//5.2 找出buff中f最小的点-> 以当前点为中心的8个点都放在buff中了-> 遍历一个数组从里面找最大或者最小的
		itMin = buff.begin();//假设第一个最小

		for (it = buff.begin(); it != buff.end(); it++){//遍历
			itMin = ( ( (*itMin)->pos.f<(*it)->pos.f ) ? itMin : it ); //根据f值大小赋值-> 最终itMin存储的是最小的
		}
		//5.3 pCurrent指向buff中f最小的点
		pCurrent = *itMin;
        //5.4 把最小的点从buff中删掉-> 否则每次都是这个点最小-> 先指向再删除 删除某个元素迭代器就失效了
		buff.erase(itMin);
		//5.5 判断是否找到终点 或者 永远找不到终点
		if (pCurrent->pos == endPos){//找到终点
			isFindEnd = true;
		}

		if (0 == buff.size()) break;//永远找不到终点
	}//end of while

	//6 显示路径
	if (isFindEnd){
		cout << "找到终点了:";
		while (pCurrent){
			cout << pCurrent->pos << " ";
			pCurrent = pCurrent->pParent;  //往前移动
		}
		cout << endl;
	}
	
	while (1);
	return 0;
}

//判断pos点能不能走,能走返回true 否则返回false
bool canWalk(int map[ROWS][COLS], bool pathMap[ROWS][COLS], const MyPoint& pos){
	//越界-> 超出数组范围
	if (pos.row < 0 || pos.col < 0 || pos.row >= ROWS || pos.col >= COLS) return false;
    //是障碍物
	if (map[pos.row][pos.col]) return false;
    //走过
	if (pathMap[pos.row][pos.col]) return false;
    //最终返回真
	return true;
}

//计算出pos的h值并返回-> 传入当前点和终点
int getH(const MyPoint& pos, const MyPoint& endPos){
	int x =  ((endPos.col > pos.col) ? (endPos.col - pos.col) : (pos.col - endPos.col)); //大的减小的
	int y = ((endPos.row > pos.row) ? (endPos.row - pos.row) : (pos.row - endPos.row));  //大的减小的
	return ZXDJ*(x + y);  
}

/*输出*/

找到终点了:(7,8) (7,7) (7,6) (7,5) (8,4) (7,3) (6,2) (5,2) (4,2) (3,2) (2,2) (1,1)

寻路算法 --- A星寻路算法_第2张图片

你可能感兴趣的:(算法篇,A星算法)