欢迎来到小K的数据结构专栏的第十小节,本节将为大家带来A*寻路算法的图形化详解,学了之后寻路不再迷路(✨当然也为大家准备了完整的源码 )~希望你看完之后,能对你有所帮助,不足请指正!共同学习交流
✨效果如下:
A*寻路算法图形化演示
由来
✨在 A * 算法之前有一种基于启发式探索的方法来提高Dijkstra算法的速度,这个算法叫做A1。后来的改进算法被称为A * 。 * 这个符号是从统计文献中借鉴来的,用来表示相对一个旧有标准的最优估计
启发式探索是利用问题拥有的启发信息来引导搜索,达到减少探索范围,降低问题复杂度的目的。
✨A*寻路算法就是启发式探索的一个典型实践,在寻路的过程中,给每个节点绑定了一个估计值(即启发式),在对节点的遍历过程中是采取估计值优先原则,估计值更优的节点会被优先遍历。所以估计函数的定义十分重要,显著影响算法效率。
那么在上图中我们应该怎么评估出最短路径呐既然要评估,那肯定要有评估规则了,首先明确三个概念,H值,目前点到终点的曼哈顿距离(曼哈顿距离,就是两个位置长度差值和高度差值的和),G值,目前点到起点的消耗代价值,如果只是寻找路径,可以将该值也看成是这两点的曼哈顿距离,F值,H值和G值的和。所以A*寻路算法的评估由公示F=G+H
来评估
✨我们先来尝试一下,假设每个格子的直线代价为10,斜线代价为14,则我们评估的起点周围的八个点的代价如下图所示:
✨怎么算的呐?我们以(2,2)为例,它到终点的曼哈顿距离我用黄色的矩形框起来了,横4纵4,然后乘上直线代价10,所以H为80,G一眼就可以看出,只有一个斜线代价,所以F为94
在简单了解了A * 寻路算法了,我们不由得想,该怎么来寻?该用什么数据结构来描述?
✨该怎么来寻?这个问题其实上边已经给出答案了,用F=G+H
来评估,在这之前我们需要一个点类型,H比较好求,我们计算出当前点和终点之间的横纵坐标差,然后相加,乘上直线代价就好了,G值呐?我们通过下面的八叉树类型来解决~
typedef struct Mypos
{
int row, col;
int f, g, h;
}Mypos;
//计算H值
int getH(Mypos* pos, Mypos* endPos)
{
int x = ((pos->row > endPos->row) ? (pos->row - endPos->row) : (endPos->row - pos->row));
int y = ((pos->col > endPos->col) ? (pos->col - endPos->col) : (endPos->col - pos->col));
return (x + y) * ZXDJ;
}
✨该用什么数据结构?我首先想到的是八叉树,因为每个点周围都有八个点需要试探,下面是一个八叉树类型
typedef struct MythreeNode
{
Mypos pos; //点
struct MythreeNode* child[CHILD_NUM]; //孩子节点
struct MythreeNode* partent; //父节点
int child_Num; //当前孩子数量
}MythreeNode;
✨具体的寻路过程如下
第一步,先遍历周围的八个节点,把他们的斜线代价计算出来
第二步,判断能不能走,能走就计算出F,存入树和数组中,不能走直接把该孩子删掉
第三步,从buffer数组中找到最小的F值,走,然后用辅助地图标记走过
第四步,我们要判断找没找到终点,退出循环有两种情况,要么是找到终点了,要么是buffer数组为空了
上述步骤中有一个小问题,就是如果遇到死胡同问题怎么办?比如下图:
✨第一步直接走到黄色的圈圈了,发现没路了,怎么办?我们思路回退一下,如果我们走完buffer数组中最小的,再把最小的删了不就可以了,这样下一步就会回到起点,这个问题就解决了
✨A.h
#ifndef _A_H_
#define _A_H_
#include
#include
#include
#include
#include
#include
//行列
#define ROWS 10
#define COLS 10
//代价
#define ZXDJ 10
#define XXDJ 14
//最大孩子数量
#define CHILD_NUM 8
//临时数组容量
#define NUMS_SIZE 1024
//路
enum type { road, wall };
//方向
enum Mydirect { p_up, p_down, p_left, p_right, p_upleft, p_upright, p_downleft, p_downright };
//点类型
typedef struct Mypos
{
int row, col;
int f, g, h;
}Mypos;
//八叉树类型
typedef struct MythreeNode
{
Mypos pos; //点
struct MythreeNode* child[CHILD_NUM]; //孩子节点
struct MythreeNode* partent; //父节点
int child_Num; //当前孩子数量
}MythreeNode;
//获得H值
int getH(Mypos* pos, Mypos* endPos);
//创建八叉树节点
MythreeNode* create_ThreeNode(Mypos* pos);
//判断能不能走
bool Can_Walk(Mypos* pos, bool map[ROWS][COLS], bool Pathmap[ROWS][COLS]);
//加载图片
SDL_Texture* load_BMP(SDL_Renderer* Ren, const char* fillname);
//绘图
void draw_Map(bool map[ROWS][COLS], Mypos* pos, SDL_Renderer* Ren, SDL_Texture** tex);
#endif // _A_H_
✨A.c
#include "A.h"
int getH(Mypos* pos, Mypos* endPos)
{
int x = ((pos->row > endPos->row) ? (pos->row - endPos->row) : (endPos->row - pos->row));
int y = ((pos->col > endPos->col) ? (pos->col - endPos->col) : (endPos->col - pos->col));
return (x + y) * ZXDJ;
}
MythreeNode* create_ThreeNode(Mypos* pos)
{
MythreeNode* newNode = (MythreeNode*)malloc(sizeof(MythreeNode));
if (NULL == newNode) return newNode;
memset(newNode, 0, sizeof(MythreeNode));
newNode->pos.row = pos->row;
newNode->pos.col = pos->col;
newNode->pos.g = pos->g;
return newNode;
}
bool Can_Walk(Mypos* pos, bool map[ROWS][COLS], bool Pathmap[ROWS][COLS])
{
//越界
if (pos->row < 0 || pos->row >= ROWS || pos->col < 0 || pos->col >= ROWS) return false;
//是墙
if (map[pos->row][pos->col]) return false;
//走过
if (Pathmap[pos->row][pos->col]) return false;
return true;
}
SDL_Texture* load_BMP(SDL_Renderer* Ren, const char* fillname)
{
SDL_Surface* sfc = SDL_LoadBMP(fillname);
if (!sfc)
{
SDL_Log("sfc filed %s", SDL_GetError());
return NULL;
}
SDL_Texture* tex = SDL_CreateTextureFromSurface(Ren, sfc);
if (!tex)
{
SDL_Log("tex failed %s", SDL_GetError());
SDL_FreeSurface(sfc);
return NULL;
}
SDL_FreeSurface(sfc);
return tex;
}
void draw_Map(bool map[ROWS][COLS], Mypos* pos,SDL_Renderer* Ren,SDL_Texture** tex)
{
for (int i = 0; i < ROWS; i++)
{
for (int j = 0; j < COLS; j++)
{
SDL_Rect rect = { j * 64,i * 64,64,64 };
if (!map[i][j])
{
SDL_RenderCopy(Ren, tex[2], NULL, &rect);
}
else if (map[i][j])
{
SDL_RenderCopy(Ren, tex[3], NULL, &rect);
}
if (pos->row == i && pos->col == j)
{
SDL_RenderCopy(Ren, tex[0], NULL, &rect);
}
if (7 == i && 6 == j)
{
SDL_RenderCopy(Ren, tex[1], NULL, &rect);
}
}
}
}
✨main.c
✨本节讲解的数据结构——A*寻路算法,他不仅是一种算法思想,它还是路径规划,游戏中普通人物挂机状态的寻路的灵魂,所以它是值得我们花费时间去掌握的~下节见!