如何用C++实现A*寻路算法

目录
  • 一、A*算法介绍
  • 二、A*算法步骤解析
  • 三、A*算法优化思路
    • 3.1、openList使用优先队列(二叉堆)
    • 3.2、障碍物列表,closeList 使用二维表(二维数组)
    • 3.3、深度限制
  • 四、A*算法实现(C++代码)

一、A*算法介绍

寻路,即找到一条从某个起点到某个终点的可通过路径。而因为实际情况中,起点和终点之间的直线方向往往有障碍物,便需要一个搜索的算法来解决。

有一定算法基础的同学可能知道从某个起点到某个终点通常使用深度优先搜索(DFS),DFS搜索的搜索方向一般是8个方向(如果不允许搜索斜向,则有4个),但是并无优先之分。

为了让DFS搜索更加高效,结合贪心思想,我们给搜索方向赋予了优先级,直观上离终点最近的方向(直观上的意思是无视障碍物的情况下)为最优先搜索方向,这就是A*算法。

二、A*算法步骤解析

(如下图,绿色为起点,红色为终点,蓝色为不可通过的墙。)

从起点开始往四周各个方向搜索。

(这里的搜索方向有8个方向)

为了区分搜索方向的优先级,我们给每个要搜索的点赋予2个值。

G值(耗费值):指从起点走到该点要耗费的值。

H值(预测值):指从该点走到终点的预测的值(从该点到终点无视障碍物情况下预测要耗费的值,也可理解成该点到终点的直线距离的值)

在这里,值 = 要走的距离

(实际上,更复杂的游戏,因为地形不同(例如陷阱,难走的沙地之类的),还会有相应不同的权值:值 = 要走的距离 * 地形权值)

我们还定义直着走一格的距离等于10,斜着走一格的距离等于14(因为45°斜方向的长度= sqrt(10^2+10^2) ≈ 14)

F值(优先级值):F = G + H

这条公式意思:F是从起点经过该点再到达终点的预测总耗费值。通过计算F值,我们可以优先选择F值最小的方向来进行搜索。

(每个点的左上角为F值,左下角为G值,右下角为H值)

计算出每个方向对应点的F,G,H值后,

还需要给这些点赋予当前节点的指针值(用于回溯路径。因为一直搜下去搜到终点后,如果没有前一个点的指针,我们将无从得知要上次经过的是哪个点,只知道走到终点最终耗费的最小值是多少)

然后我们将这些点放入openList(开启列表:用于存放可以搜索的点)

然后再将当前点放入closeList(关闭列表:用于存放已经搜索过的点,避免重复搜索同一个点)

然后再从openList取出一个F值最小(最优先方向)的点,进行上述同样的搜索。

在搜索过程中,如果搜索方向上的点是障碍物或者关闭列表里的点,则跳过之。

通过递归式的搜索,多次搜索后,最终搜到了终点。

搜到终点后,然后通过前一个点的指针值,我们便能从终点一步步回溯通过的路径点。

(红色标记了便是回溯到的点)

三、A*算法优化思路

3.1、openList使用优先队列(二叉堆)

可以看到openlist(开启列表),需要实时添加点,还要每次取出最小值的点。

所以我们可以使用优先队列(二叉堆)来作为openList的容器。

优先队列(二叉堆):插入一个点的复杂度为O(logN),取出一个最值点复杂度为O(logN)

3.2、障碍物列表,closeList 使用二维表(二维数组)

由于障碍物列表和closeList仅用来检测是否能通过,所以我们可以使用bool二维表来存放。

//假设已经定义Width和Height分别为地图的长和宽
bool barrierList[Width][Height];
bool closetList[Width][Height];

有某个点(Xa,Yb),可以通过

if(barrierList[Xa][Yb]&&closeList[Xa][Yb])来判断。

因为二维表用下标访问,效率很高,但是耗空间比较多。(三维地图使用三维表则更耗内存。不过现在计算机一般都不缺内存空间,所以尽量提升运算时间为主)

这是一个典型的牺牲内存空间换取运算时间的例子。

3.3、深度限制

有时要搜的路径非常长,利用A*算法搜一次付出的代价很高,造成游戏的卡顿。

那么为了保证每次搜索不会超过一定代价,可以设置深度限制,每搜一次则深度+1,搜到一定深度限制还没搜到终点,则返还失败值。

四、A*算法实现(C++代码)

#include <iostream>
#include <list>
#include <vector>
#include <queue>

struct OpenPoint{
    int x;
    int y;
    int cost;                 // 耗费值
    int pred;                 // 预测值
    OpenPoint* father;        // 父节点
    OpenPoint() = default;
    OpenPoint(int pX,int pY, int endX, int endY, int c, OpenPoint* fatherp) : x(pX),y(pY),cost(c), father(fatherp) {
        //相对位移x,y取绝对值
        int relativeX = std::abs(endX - pX);
        int relativeY = std::abs(endY - pY);
        //x,y偏移值n
        int n = relativeX - relativeY;
        //预测值pred = (max–n)*14+n*10+c
        pred = std::max(relativeX, relativeY) * 14 - std::abs(n) * 4 + c;
    }
};

//比较器,用以优先队列的指针类型比较
struct OpenPointPtrCompare {
    bool operator()(OpenPoint* a, OpenPoint* b) {
        return a->pred > b->pred;
    }
};

const int width = 30;            //地图长度
const int height = 100;          //地图高度
char mapBuffer[width][height];   //地图数据
int depth = 0;                   //记录深度
const int depthLimit = 2000;     //深度限制
bool closeAndBarrierList[width][height];    //记录障碍物+关闭点的二维表
//八方的位置
int direction[8][2] = { {1,0},{0,1},{-1,0},{0,-1},{1,1},{ -1,1 },{ -1,-1 },{ 1,-1 } };
//使用最大优先队列
std::priority_queue<OpenPoint*, std::vector<OpenPoint*>, OpenPointPtrCompare> openlist;
//存储OpenPoint的内存空间
std::vector<OpenPoint> pointList = std::vector<OpenPoint>(depthLimit);

//是否在障碍物或者关闭列表
inline bool inBarrierAndCloseList(int pX,int pY) {
    if (pX < 0 || pY < 0 || pX >= width || pY >= height)
        return true;
    return closeAndBarrierList[pX][pY];
}

//创建一个开启点
inline OpenPoint* createOpenPoint(int pX,int pY,int endX,int endY, int c, OpenPoint* fatherp) {
    pointList.emplace_back(pX,pY,endX,endY, c, fatherp);
    return &pointList.back();
}

// 开启检查,检查父节点
void open(OpenPoint& pointToOpen, int endX,int endY) {
    //将父节点从openlist移除
    openlist.pop();
    //深度+1
    depth++;
    //检查p点八方的点
    for (int i = 0; i < 4; ++i)
    {
        int toOpenX = pointToOpen.x + direction[i][0];
        int toOpenY = pointToOpen.y + direction[i][1];
        if (!inBarrierAndCloseList(toOpenX,toOpenY)) {
            openlist.push(createOpenPoint(toOpenX, toOpenY, endX,endY, pointToOpen.cost + 10, &pointToOpen));
        }
    }
    for (int i = 4; i < 8; ++i)
    {
        int toOpenX = pointToOpen.x + direction[i][0];
        int toOpenY = pointToOpen.y + direction[i][1];
        if (!inBarrierAndCloseList(toOpenX, toOpenY)) {
            openlist.push(createOpenPoint(toOpenX, toOpenY, endX, endY, pointToOpen.cost + 14, &pointToOpen));
        }
    }
    //最后移入closelist
    closeAndBarrierList[pointToOpen.x][pointToOpen.y] = true;
}

//开始搜索路径
std::list<OpenPoint*> findway(int startX,int startY, int endX,int endY) {
    std::list<OpenPoint*> road;
    // 创建并开启一个父节点
    openlist.push(createOpenPoint(startX,startY, endX,endY, 0, nullptr));
    OpenPoint* toOpen = nullptr;
    //重复寻找预测和花费之和最小节点开启检查
    while (!openlist.empty())
    {
        toOpen = openlist.top();
        // 找到终点后,则停止搜索
        if (toOpen->x == endX && toOpen->y ==endY) {break;}//若超出一定深度(1000深度),则搜索失败
        if (depth >= depthLimit) {
            toOpen = nullptr;
            break;
        }
        open(*toOpen, endX,endY);
    }
    for (auto rs = toOpen; rs != nullptr; rs = rs->father) {road.push_back(rs);}
    return road;
}

//创建地图
void createMap() {
    for (int i = 0; i < width; ++i)
        for (int j = 0; j < height; ++j) {
            //五分之一概率生成障碍物,不可走
            if (rand() % 5 == 0) {
                mapBuffer[i][j] = '*';
                closeAndBarrierList[i][j] = true;
            }
            else {
                mapBuffer[i][j] = ' ';
                closeAndBarrierList[i][j] = false;
            }
        }
}

//打印地图
void printMap() {
    for (int i = 0; i < width; ++i) {
        for (int j = 0; j < height; ++j)
            std::cout << mapBuffer[i][j];
        std::cout << std::endl;
    }
    std::cout << std::endl << std::endl << std::endl;
}

int main() {
    //起点
    int beginX = 0;
    int beginY = 0;
    //终点
    int endX = 29;
    int endY = 99;
    //创建地图
    createMap();
    //保证起点和终点都不是障碍物
    mapBuffer[beginX][beginY] = mapBuffer[endX][endY] = ' ';
    closeAndBarrierList[beginX][beginY] = closeAndBarrierList[endX][endY] = false;
    //A*搜索得到一条路径
    std::list<OpenPoint*> road = findway(beginX,beginY,endX,endY);
    //将A*搜索的路径经过的点标记为'O'
    for (auto& p : road){mapBuffer[p->x][p->y] = 'O';}
    //打印走过路后的地图
    printMap();
    system("pause");
    return 0;
}

示例效果:

以上就是如何用C++实现A*寻路算法的详细内容,更多关于C++ A*寻路算法的资料请关注我们其它相关文章!

(0)

相关推荐

  • JS/HTML5游戏常用算法之路径搜索算法 A*寻路算法完整实例

    本文实例讲述了JS/HTML5游戏常用算法之路径搜索算法 A*寻路算法.分享给大家供大家参考,具体如下: 原理可参考:https://www.jb51.net/article/152744.htm 完整实例代码如下: <!DOCTYPE html> <html lang="en"> <head> <meta name="viewport" content="width=device-width, initial-s

  • Java编程实现A*算法完整代码

    前言 A*搜寻算法俗称A星算法.这是一种在图形平面上,有多个节点的路径,求出最低通过成本的算法.常用于游戏中 通过二维数组构建的一个迷宫,"%"表示墙壁,A为起点,B为终点,"#"代表障碍物,"*"代表算法计算后的路径 本文实例代码结构: % % % % % % % % o o o o o % % o o # o o % % A o # o B % % o o # o o % % o o o o o % % % % % % % % =======

  • js+ajax实现的A*游戏路径算法整理第1/2页

    作者:llinzzi 日期:2007-05-24 去年做了个小东西,一个在线WebGame,目前只实现了多人聊天,移动,拖动画面移动,场景系统等,可以当场景聊天室使用.不过扔了一年了.如图 美工由静电设计后台将由老于开发 今年想再捡起来好好做做,由于是基于坐标点的,所以移动路径非常费资源.找到了一个A*的路径算法,挺智能. 转载一些介绍[转自 http://data.gameres.com/message.asp?TopicID=25439]译者序:很久以前就知道了A*算法,但是从未认真读过相关

  • js版本A*寻路算法

    说到做游戏,必不可少的需要用到寻路算法,一般游戏里的寻路算法大多数都以A*算法为主,这里也就实现了js里采用a*寻路的程序,在51js和蓝色都开了帖. 程序是以前写的,后来也没有修正或者精简,有冗余之处大家还见谅一下. 当然,这个寻路算法也不是最优化的,像幻宇开发的"交点寻径法"也是个中精品,两者可谓各有千秋,只是如果地图很大的情况下,我们会惊讶于"交点寻径法"的迅速. use A* to find path... /* written by 百晓生 email:j

  • python 实现A*算法的示例代码

    A*作为最常用的路径搜索算法,值得我们去深刻的研究.路径规划项目.先看一下维基百科给的算法解释:https://en.wikipedia.org/wiki/A*_search_algorithm A *是最佳优先搜索它通过在解决方案的所有可能路径(目标)中搜索导致成本最小(行进距离最短,时间最短等)的问题来解决问题. ),并且在这些路径中,它首先考虑那些似乎最快速地引导到解决方案的路径.它是根据加权图制定的:从图的特定节点开始,它构造从该节点开始的路径树,一次一步地扩展路径,直到其一个路径在预定

  • C++ DFS算法实现走迷宫自动寻路

    C++ DFS算法实现走迷宫自动寻路,供大家参考,具体内容如下 深度优先搜索百度百科解释: 事实上,深度优先搜索属于图算法的一种,英文缩写为DFS即Depth First Search.其过程简要来说是对每一个可能的分支路径深入到不能再深入为止,而且每个节点只能访问一次. 运行效果: 说明: 深度优先搜索算法是在我在图的部分接触到的,后来才发现它也可以不用在图的遍历上,它是一个独立的算法,它也可以直接用在一个二维数组上. 其算法原理和实现步骤在代码中已经有了很好的体现了,这里就不再赘述. 在程序

  • Unity3D实现自动寻路

    Unity3D自动寻路,供大家参考,具体内容如下 1.首先在Unity3D创造中一个正方体,一个圆柱体和一个平面. 2.将正方体和平面设为静态(Static前的方格打勾) 3.在Window中打开AI进行导航网格烘培 4.在add component中添加Nav Mesh Agent(导航网格代理) 这里将Stopping Distance的值修改为0.5.如果使用默认值0,则最后圆柱体会与正方体重合在一起. 5.附加AI脚本给圆柱体 6.最终效果 Unity3D小白一枚,如有错误希望大佬们指正

  • Python3 A*寻路算法实现方式

    我就废话不多说了,直接上代码吧! # -*- coding: utf-8 -*- import math import random import copy import time import sys import tkinter import threading # 地图 tm = [ '############################################################', '#S............................#........

  • 如何用C++实现A*寻路算法

    目录 一.A*算法介绍 二.A*算法步骤解析 三.A*算法优化思路 3.1.openList使用优先队列(二叉堆) 3.2.障碍物列表,closeList 使用二维表(二维数组) 3.3.深度限制 四.A*算法实现(C++代码) 一.A*算法介绍 寻路,即找到一条从某个起点到某个终点的可通过路径.而因为实际情况中,起点和终点之间的直线方向往往有障碍物,便需要一个搜索的算法来解决. 有一定算法基础的同学可能知道从某个起点到某个终点通常使用深度优先搜索(DFS),DFS搜索的搜索方向一般是8个方向(

  • 赫赫大名的A*寻路算法(vb.net版本)

    在网上看到一篇A*寻路算法的译文 http://data.gameres.com/message.asp?TopicID=25439 按此原理写了以下程序 另外补充:1.此算法不是最短路径算法.        2.在实际应用中肯定还需要优化,以适合具体游戏.        3.(vb.net2005测试通过) /Files/bearhunter/6f1e1005-a5a3-4fc9-9bfe-99a615e113ed.rar 本地下载 //

  • 深入理解js A*寻路算法原理与具体实现过程

    本文实例讲述了js A*寻路算法原理与具体实现过程.分享给大家供大家参考,具体如下: 这两天研究了下 A* 寻路算法, 主要学习了这篇文章, 但这篇翻译得不是很好, 我花了很久才看明白文章中的各种指代. 特写此篇博客用来总结, 并写了寻路算法的代码, 觉得有用的同学可以看看. 另外因为图片制作起来比较麻烦, 所以我用的是原文里的图片. 当然寻路算法不止 A* 这一种, 还有递归, 非递归, 广度优先, 深度优先, 使用堆栈等等, 有兴趣的可以研究研究~~ 简易地图 如图所示简易地图, 其中绿色方

  • JS实现的A*寻路算法详解

    本文实例讲述了JS实现的A*寻路算法.分享给大家供大家参考,具体如下: 这两天在做百度前端技术学院的题目,其中有涉及到寻路相关的,于是就找来相关博客进行阅读. 看了Create Chen写的理解A*寻路算法具体过程之后,我很快就理解A*算法的原理.不得不说作者写的很好,通熟易懂,图片也做的很好,可见作者在这上面是花了心思的.如果让我写,我是写不来这么好的. 唯一的不足就是,因为我学的是js,因此最后给我的源码我是用不了的......因此才有自己写一篇的打算,方面学习js人的学习.然而前面的描述我

  • 如何用Java实现排列组合算法

    需求 我们的数据表有多个维度,任意多个维度组合后进行 group by 可能会产生一些"奇妙"的反应,由于不确定怎么组合,就需要将所有的组合都列出来进行尝试. 抽象一下就是从一个集合中取出任意元素,形成唯一的组合.如[a,b,c]可组合为[a].[b].[c].[ab].[bc].[ac].[abc]. 要求如下: 组合内的元素数大于 0 小于等于 数组大小: 组合内不能有重复元素,如 [aab] 是不符合要求的组合: 组合内元素的位置随意,即 [ab] 和 [ba] 视为同一种组合:

  • python实现A*寻路算法

    A* 算法简介 A* 算法需要维护两个数据结构:OPEN 集和 CLOSED 集.OPEN 集包含所有已搜索到的待检测节点.初始状态,OPEN集仅包含一个元素:开始节点.CLOSED集包含已检测的节点.初始状态,CLOSED集为空.每个节点还包含一个指向父节点的指针,以确定追踪关系. A* 算法会给每个搜索到的节点计算一个G+H 的和值F: F = G + H G:是从开始节点到当前节点的移动量.假设开始节点到相邻节点的移动量为1,该值会随着离开始点越来越远而增大. H:是从当前节点到目标节点的

  • 详解如何用Python实现感知器算法

    目录 一.题目 二.数学求解过程 三.感知器算法原理及步骤 四.python代码实现及结果 一.题目 二.数学求解过程 该轮迭代分类结果全部正确,判别函数为g(x)=-2x1+1 三.感知器算法原理及步骤 四.python代码实现及结果 (1)由数学求解过程可知: (2)程序运行结果 (3)绘图结果 ''' 20210610 Julyer 感知器 ''' import numpy as np import matplotlib.pyplot as plt def get_zgxl(xn, a):

随机推荐