A*寻路算法C++简单实现
搜索区域
开始寻路
- 1.从起点A开始, 把它作为待处理的方格存入一个"开启列表", 开启列表就是一个等待检查方格的列表.
- 2.寻找起点A周围可以到达的方格, 将它们放入"开启列表", 并设置它们的"父方格"为A.
- 3.从"开启列表"中删除起点 A, 并将起点 A 加入"关闭列表", "关闭列表"中存放的都是不需要再次检查的方格
H 表示从指定的方格移动到终点 B 的预计耗费 (H 有很多计算方法, 本文代码使用简单的欧几里得距离计算方法).
- 4.把它从 "开启列表" 中删除, 并放到 "关闭列表" 中.
- 5.检查它所有相邻并且可以到达 (障碍物和 "关闭列表" 的方格都不考虑) 的方格. 如果这些方格还不在 "开启列表" 里的话, 将它们加入 "开启列表", 计算这些方格的 G, H 和 F 值各是多少, 并设置它们的 "父方格" 为 C.
- 6.如果某个相邻方格 D 已经在 "开启列表" 里了,
检查如果用新的路径 (就是经过C 的路径) 到达它的话, G值是否会更低一些, 如果新的G值更低, 那就把它的 "父方格" 改为目前选中的方格
C, 然后重新计算它的 F 值和 G 值 (H 值不需要重新计算, 因为对于每个方块, H 值是不变的). 如果新的 G 值比较高, 就说明经过
C 再到达 D 不是一个明智的选择, 因为它需要更远的路, 这时我们什么也不做.
中删除, 并把它加入 "关闭列表". 它右边上下三个都是墙, 所以不考虑它们. 它左边是起始方块, 已经加入到 "关闭列表" 了, 也不考虑.
所以它周围的候选方块就只剩下 4 个. 让我们来看看 C 下面的那个格子, 它目前的 G 是14, 如果通过 C 到达它的话, G将会是 10
+ 10, 这比 14 要大, 因此我们什么也不做.
输出路径
算法伪码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
把起始格添加到 "开启列表" do { 寻找开启列表中F值最低的格子, 我们称它为当前格. 把它切换到关闭列表. 对当前格相邻的8格中的每一个 if (它不可通过 || 已经在 "关闭列表" 中) { 什么也不做. } if (它不在开启列表中) { 把它添加进 "开启列表" , 把当前格作为这一格的父节点, 计算这一格的 FGH if (它已经在开启列表中) { if (用G值为参考检查新的路径是否更好, 更低的G值意味着更好的路径) { 把这一格的父节点改成当前格, 并且重新计算这一格的 GF 值. } } while ( 目标格已经在 "开启列表" , 这时候路径被找到) 如果开启列表已经空了, 说明路径不存在. 最后从目标格开始, 沿着每一格的父节点移动直到回到起始格, 这就是路径. |
C++实现代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
#pragma once /* //A*算法对象类 */ #include <vector> #include <list> const int kCost1=10; //直移一格消耗 const int kCost2=14; //斜移一格消耗 struct Point { int x,y; //点坐标,这里为了方便按照C++的数组来计算,x代表横排,y代表竖列 int F,G,H; //F=G+H Point *parent; //parent的坐标,这里没有用指针,从而简化代码 Point( int _x, int _y):x(_x),y(_y),F(0),G(0),H(0),parent(NULL) //变量初始化 { } }; class Astar { public : void InitAstar(std::vector<std::vector< int >> &_maze); std::list<Point *> GetPath(Point &startPoint,Point &endPoint, bool isIgnoreCorner); private : Point *findPath(Point &startPoint,Point &endPoint, bool isIgnoreCorner); std::vector<Point *> getSurroundPoints( const Point *point, bool isIgnoreCorner) const ; bool isCanreach( const Point *point, const Point *target, bool isIgnoreCorner) const ; //判断某点是否可以用于下一步判断 Point *isInList( const std::list<Point *> &list, const Point *point) const ; //判断开启/关闭列表中是否包含某点 Point *getLeastFpoint(); //从开启列表中返回F值最小的节点 //计算FGH值 int calcG(Point *temp_start,Point *point); int calcH(Point *point,Point *end); int calcF(Point *point); private : std::vector<std::vector< int >> maze; std::list<Point *> openList; //开启列表 std::list<Point *> closeList; //关闭列表 }; |
Astar.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
|
#include <math.h> #include "Astar.h" void Astar::InitAstar(std::vector<std::vector< int >> &_maze) { maze=_maze; } int Astar::calcG(Point *temp_start,Point *point) { int extraG=( abs (point->x-temp_start->x)+ abs (point->y-temp_start->y))==1?kCost1:kCost2; int parentG=point->parent==NULL?0:point->parent->G; //如果是初始节点,则其父节点是空 return parentG+extraG; } int Astar::calcH(Point *point,Point *end) { //用简单的欧几里得距离计算H,这个H的计算是关键,还有很多算法,没深入研究^_^ return sqrt (( double )(end->x-point->x)*( double )(end->x-point->x)+( double )(end->y-point->y)*( double )(end->y-point->y))*kCost1; } int Astar::calcF(Point *point) { return point->G+point->H; } Point *Astar::getLeastFpoint() { if (!openList.empty()) { auto resPoint=openList.front(); for ( auto &point:openList) if (point->F<resPoint->F) resPoint=point; return resPoint; } return NULL; } Point *Astar::findPath(Point &startPoint,Point &endPoint, bool isIgnoreCorner) { openList.push_back( new Point(startPoint.x,startPoint.y)); //置入起点,拷贝开辟一个节点,内外隔离 while (!openList.empty()) { auto curPoint=getLeastFpoint(); //找到F值最小的点 openList. remove (curPoint); //从开启列表中删除 closeList.push_back(curPoint); //放到关闭列表 //1,找到当前周围八个格中可以通过的格子 auto surroundPoints=getSurroundPoints(curPoint,isIgnoreCorner); for ( auto &target:surroundPoints) { //2,对某一个格子,如果它不在开启列表中,加入到开启列表,设置当前格为其父节点,计算F G H if (!isInList(openList,target)) { target->parent=curPoint; target->G=calcG(curPoint,target); target->H=calcH(target,&endPoint); target->F=calcF(target); openList.push_back(target); } //3,对某一个格子,它在开启列表中,计算G值, 如果比原来的大, 就什么都不做, 否则设置它的父节点为当前点,并更新G和F else { int tempG=calcG(curPoint,target); if (tempG<target->G) { target->parent=curPoint; target->G=tempG; target->F=calcF(target); } } Point *resPoint=isInList(openList,&endPoint); if (resPoint) return resPoint; //返回列表里的节点指针,不要用原来传入的endpoint指针,因为发生了深拷贝 } } return NULL; } std::list<Point *> Astar::GetPath(Point &startPoint,Point &endPoint, bool isIgnoreCorner) { Point *result=findPath(startPoint,endPoint,isIgnoreCorner); std::list<Point *> path; //返回路径,如果没找到路径,返回空链表 while (result) { path.push_front(result); result=result->parent; } return path; } Point *Astar::isInList( const std::list<Point *> &list, const Point *point) const { //判断某个节点是否在列表中,这里不能比较指针,因为每次加入列表是新开辟的节点,只能比较坐标 for ( auto p:list) if (p->x==point->x&&p->y==point->y) return p; return NULL; } bool Astar::isCanreach( const Point *point, const Point *target, bool isIgnoreCorner) const { if (target->x<0||target->x>maze.size()-1 ||target->y<0&&target->y>maze[0].size()-1 ||maze[target->x][target->y]==1 ||target->x==point->x&&target->y==point->y ||isInList(closeList,target)) //如果点与当前节点重合、超出地图、是障碍物、或者在关闭列表中,返回false return false ; else { if ( abs (point->x-target->x)+ abs (point->y-target->y)==1) //非斜角可以 return true ; else { //斜对角要判断是否绊住 if (maze[point->x][target->y]==0&&maze[target->x][point->y]==0) return true ; else return isIgnoreCorner; } } } std::vector<Point *> Astar::getSurroundPoints( const Point *point, bool isIgnoreCorner) const { std::vector<Point *> surroundPoints; for ( int x=point->x-1;x<=point->x+1;x++) for ( int y=point->y-1;y<=point->y+1;y++) if (isCanreach(point, new Point(x,y),isIgnoreCorner)) surroundPoints.push_back( new Point(x,y)); return surroundPoints; } |
main.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
#include <iostream> #include "Astar.h" using namespace std; int main() { //初始化地图,用二维矩阵代表地图,1表示障碍物,0表示可通 vector<vector< int >> maze={ {1,1,1,1,1,1,1,1,1,1,1,1}, {1,0,0,1,1,0,1,0,0,0,0,1}, {1,0,0,1,1,0,0,0,0,0,0,1}, {1,0,0,0,0,0,1,0,0,1,1,1}, {1,1,1,0,0,0,0,0,1,1,0,1}, {1,1,0,1,0,0,0,0,0,0,0,1}, {1,0,1,0,0,0,0,1,0,0,0,1}, {1,1,1,1,1,1,1,1,1,1,1,1} }; Astar astar; astar.InitAstar(maze); //设置起始和结束点 Point start(1,1); Point end(6,10); //A*算法找寻路径 list<Point *> path=astar.GetPath(start,end, false ); //打印 for ( auto &p:path) cout<< '(' <<p->x<< ',' <<p->y<< ')' <<endl; system ( "pause" ); return 0; |
A*寻路算法C++简单实现的更多相关文章
- [转] A*寻路算法C++简单实现
参考文章: http://www.policyalmanac.org/games/aStarTutorial.htm 这是英文原文<A*入门>,最经典的讲解,有demo演示 http: ...
- 【Android】基于A星寻路算法的简单迷宫应用
简介 基于[漫画算法-小灰的算法之旅]上的A星寻路算法,开发的一个Demo.目前实现后退.重新载入.路径提示.地图刷新等功能.没有做太多的性能优化,算是深化对A星寻路算法的理解. 界面预览: 初始化: ...
- 基于Unity的A星寻路算法(绝对简单完整版本)
前言 在上一篇文章,介绍了网格地图的实现方式,基于该文章,我们来实现一个A星寻路的算法,最终实现的效果为: 项目源码已上传Github:AStarNavigate 在阅读本篇文章,如果你对于里面提到的 ...
- 用简单直白的方式讲解A星寻路算法原理
很多游戏特别是rts,rpg类游戏,都需要用到寻路.寻路算法有深度优先搜索(DFS),广度优先搜索(BFS),A星算法等,而A星算法是一种具备启发性策略的算法,效率是几种算法中最高的,因此也成为游戏中 ...
- A星寻路算法介绍
你是否在做一款游戏的时候想创造一些怪兽或者游戏主角,让它们移动到特定的位置,避开墙壁和障碍物呢? 如果是的话,请看这篇教程,我们会展示如何使用A星寻路算法来实现它! 在网上已经有很多篇关于A星寻路算法 ...
- A*寻路算法的探寻与改良(三)
A*寻路算法的探寻与改良(三) by:田宇轩 第三分:这部分内容基于树.查找算法等对A*算法的执行效率进行了改良,想了解细 ...
- A*寻路算法的探寻与改良(二)
A*寻路算法的探寻与改良(二) by:田宇轩 第二部分:这部分内容主要是使用C语言编程实现A*, ...
- [笔记]A*寻路算法初探
写在开始之前 最近突然对各路游戏的寻路算法很感兴趣,于是去学习了下游戏里的AI们是如何寻路的.网上相关内容很多,但同时有些说法也不一,制作自己的A* 算法时也有因不同的说法而困惑.整理多方资料并自己实 ...
- A星寻路算法(A* Search Algorithm)
你是否在做一款游戏的时候想创造一些怪兽或者游戏主角,让它们移动到特定的位置,避开墙壁和障碍物呢? 如果是的话,请看这篇教程,我们会展示如何使用A星寻路算法来实现它! 在网上已经有很多篇关于A星寻路算法 ...
随机推荐
- PHP发送公众号模板消息
<?php /* * 模板消息发送,电脑端测试时需要手动填写openid * 微信端会自动获取当前openid发送无需填写 */ header("Content-type: text/ ...
- OC学习篇之---@class关键字的作用以及#include和#import的区别
前一篇文章说到了OC中类的三大特性:http://blog.csdn.net/jiangwei0910410003/article/details/41707161今天我们来看一下在学习OC的过程中遇 ...
- mybatis中Parameter index out of range (2 > number of parameters, which is 1).
${name} 是不带单引号的,而#{name} 是带单引号的
- 《Java技术》 第二次作业
java第二次作业 (一)学习总结 1.学习使用Eclipse关联jdk源代码,查看String类的equals()方法,截图,并学习其实现方法.举例说明equals方法和==的区别. 在Eclips ...
- Linux / Unix Command: rz
yum install lrzsz Most communications programs invoke rz and sz automatically. You can also connect ...
- CookieUtil.java
package util; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.n ...
- RegionServer Splitting Implementation:regionServer 分裂过程分析
图片: RegionServer Split Process The RegionServer decides locally to split the region, and prepares th ...
- Linux的各个发行版本(一)
三大流派 1.Slackware SUSE Linux Enterprise Server (SLES) OpenSuse桌面 2.debian 迄今为止最遵循GNU规范的Linux系统 Ubuntu ...
- CTU OPEN 2017 Punching Power /// 最大独立集
题目大意: 给定n 给定n个机器的位置 要求任意两个机器间的距离至少为1.3米 求最多能选择多少个机器 至少为1.3米 说明若是位于上下左右一步的得放就不行 将机器编号 将不能同时存在的机器连边 此时 ...
- 原生js星星评分源码
html: <div id="fiveStars"> <div>到场时间:<img v-for="(star,index) in stars ...