dl
dl_astar.h
浏览该文件的文档.
1
12
13#pragma once
14
15#include <array>
16#include <vector>
17#include <map>
18#include <list>
19#include <algorithm>
20#include <cassert>
21
22#include "io/dl_log.h"
23#include "base/dl_string.h"
24
25namespace dl
26{
27
28
29 constexpr bool ASTAR_SHOW_LOG = false;
30
32template<typename T, typename COST>
33class AStar
34{
35public:
36 // 点类型
37 using AStarPoint = T;
38 // 代价类型(float、int等)
39 using AStarCost = COST;
40 using FuncCost = std::function<AStarCost(const AStarPoint&, const AStarPoint&)>;
41 using FuncLink = std::function<void(AStar*, const AStarPoint&)>;
42
43 //节点
45 {
46 public:
48 AStarNode* _parent; //父节点(为空代表起点)
49 AStarCost _cost; //前往这个节点 的代价值
50 AStarCost _total; //总代价 (cost + 估计代价)
51 bool _open; //是否在 Open表
52 bool _closed; //是否在 Close表
53 };
54
55 inline static bool AStarNodeCompare(AStarNode* n1, AStarNode* n2)
56 {
57 return n1->_total > n2->_total;
58 }
59
60 //优先队列
62 {
63 public:
65 {
66 AStarNode* node = _heap.front();
67 std::pop_heap(_heap.begin(), _heap.end(), AStarNodeCompare);
68 _heap.pop_back();
69 return node;
70 }
71 void Push(AStarNode* node)
72 {
73 _heap.push_back(node);
74 std::push_heap(_heap.begin(), _heap.end(), AStarNodeCompare);
75 }
77 {
78 for (auto iter = _heap.begin(); iter != _heap.end(); ++iter)
79 {
80 if ((*iter)->_xy == node->_xy)
81 {
82 std::push_heap(_heap.begin(), iter + 1, AStarNodeCompare);
83 return;
84 }
85 }
86 }
87 bool IsEmpty()
88 {
89 return _heap.empty();
90 }
91 void Clear()
92 {
93 _heap.clear();
94 }
95 private:
96 std::vector<AStarNode*> _heap;
97 };
98
99 inline AStarPoint AbsPoint(const AStarPoint& a, const AStarPoint& b)
100 {
101 AStarPoint ret{
102 a.x < b.x ? (b.x - a.x) : (a.x - b.x),
103 a.y < b.y ? (b.y - a.y) : (a.y - b.y)
104 };
105 return ret;
106 }
107
112 inline AStarCost AStarGetH(const AStarPoint& p0, const AStarPoint& p1)
113 {
114 constexpr AStarCost ASTAR_K0 = 10;
115 constexpr AStarCost ASTAR_K1 = 14;
116
117 AStarPoint d = AbsPoint(p0, p1);
118 return ASTAR_K0 * (d.x + d.y) - (std::min)(d.x, d.y) * (2 * ASTAR_K0 - ASTAR_K1);
119 }
120
121
131 const AStarPoint& size,
132 FuncCost func_g,
133 FuncCost func_h = nullptr,
134 FuncLink func_link = nullptr,
135 size_t max_node = 10000) :
136 _funcG{ func_g },
137 _funcH{ func_h },
138 _funcLink{ func_link },
139 _maxNode{ max_node },
140 _bDiagonal{ true },
141 _size{}
142 {
143 _arrNodeBank = new AStarNode[_maxNode];
144 _masterNode.reserve(_maxNode);
145 assert(_funcG);
146 if (_funcH == nullptr)
147 {
148 _funcH = std::bind(&AStar::AStarGetH, this, std::placeholders::_1, std::placeholders::_2);
149 }
150 }
151
152 void SetSize(const AStarPoint& size)
153 {
154 _size = size;
155 }
156
166 bool Search(const AStarPoint& source, const AStarPoint& target, bool target_near,
167 std::list<AStarPoint>& list_path,
168 std::list<AStarPoint>* fail_path = nullptr)
169 {
170 if (ASTAR_SHOW_LOG)
171 log_msg("{} -> {}", source, target);
172 //清空
173 _bankCur = 0;
174 _masterNode.clear();
175 _queueOpen.Clear();
176 //起点插入Open表
177 AStarNode* node_start = _get_node(source);
178 node_start->_open = true;
179 node_start->_closed = false;
180 node_start->_cost = 0;
181 node_start->_total = _funcH(source, target);
182 node_start->_parent = NULL;
183 _queueOpen.Push(node_start);
184
185 _nodeBest = nullptr;
186 _xyTarget = target;
187 while (!_queueOpen.IsEmpty())
188 {
189 //取出 消耗值 最小的节点
190 _nodeBest = _queueOpen.Pop();
191 if (ASTAR_SHOW_LOG)
192 log_info("当前最佳节点为:{}", _nodeBest->_xy);
193 //是目标节点
194 if (_nodeBest->_xy == target
195 || (target_near && _is_link_node(_nodeBest->_xy, target)))
196 {
197 //构造路径(包含首尾节点)
198 list_path.clear();
199 while (_nodeBest)
200 {
201 list_path.push_front(_nodeBest->_xy);
202 _nodeBest = _nodeBest->_parent;
203 }
204 return true;
205 }
206
207 //由于_check_connecting 会增加节点,所以提前判断,并预留一些
208 if (_bankCur + 32 >= _maxNode)
209 {
210 if (fail_path)
211 {
212 //构造路径(包含首尾节点)
213 fail_path->clear();
214 while (_nodeBest)
215 {
216 fail_path->push_front(_nodeBest->_xy);
217 _nodeBest = _nodeBest->_parent;
218 }
219 }
220 return false;
221 }
222 //图的连接
223 if (_funcLink)
224 _funcLink(this, _nodeBest->_xy);
225 else
226 _link_node(_nodeBest->_xy);
227
228 //
229 _nodeBest->_closed = true;
230 }
231
232 if (fail_path)
233 {
234 //构造路径(包含首尾节点)
235 while (_nodeBest)
236 {
237 fail_path->push_front(_nodeBest->_xy);
238 _nodeBest = _nodeBest->_parent;
239 }
240 }
241 return false;
242 }
243
244
250 void AddNode(const AStarPoint& xy, AStarCost k_cost)
251 {
252 if (_nodeBest->_parent == nullptr ||
253 _nodeBest->_parent->_xy != xy)
254 {
255 // 代价
256 AStarCost g = _funcG(_nodeBest->_xy, xy);
257 // 如果代价 等于0,说明此节点不可通行
258 if (g == 0)
259 return;
260
261 AStarNode* actual_node = _get_node(xy);
262
263 // 不在close表
264 if (actual_node->_closed == false)
265 {
266 if (actual_node->_open)
267 {
268 // 上一个点代价 + 移到自己的代价
269 AStarCost new_cost = _nodeBest->_cost + g * k_cost;
270 // 总代价,自己代价 + 估计代价
271 AStarCost new_total = new_cost + _funcH(xy, _xyTarget);
272
273 //新的 代价更小 才刷新
274 if (new_total < actual_node->_total)
275 {
276 if (ASTAR_SHOW_LOG)
277 log_info("更新节点:{},{}->{}", actual_node->_xy, actual_node->_cost, new_cost);
278
279 actual_node->_parent = _nodeBest;
280 actual_node->_cost = new_cost;
281 actual_node->_total = new_total;
282 _queueOpen.UpdateNode(actual_node);
283 }
284 }
285 else
286 {//不在open表,则加入
287 actual_node->_parent = _nodeBest;
288 actual_node->_cost = _nodeBest->_cost + g * k_cost;
289 actual_node->_total = actual_node->_cost + _funcH(xy, _xyTarget);
290
291 _queueOpen.Push(actual_node);
292 actual_node->_open = true;
293
294 if (ASTAR_SHOW_LOG)
295 log_info("增加节点:{},{}", actual_node->_xy, actual_node->_cost);
296 }
297 }
298 }
299 }
300
304 void SetLinkDiagonal(bool diagonal)
305 {
306 assert(_funcLink == nullptr);
307 _bDiagonal = diagonal;
308 }
309
313 void SetFuncLink(FuncLink func_link)
314 {
315 _funcLink = func_link;
316 }
317
318
320 {
321 delete[] _arrNodeBank;
322 }
323private:
324 AStarNode* _get_node(const AStarPoint& xy)
325 {
326 //如果在 主列表 直接返回,如果没有则从节点银行构造一个
327 uint64_t index = ((uint64_t)xy.y << 32) | (uint64_t)xy.x;
328
329 AStarNode*& node = _masterNode[index];
330
331 if (node)
332 {
333 return node;
334 }
335 else
336 {
337 //此处会越界
338 if (_bankCur >= _maxNode)
339 {
340 //cout << "寻路的节点超过上限!" <<;
341 return nullptr;
342 }
343
344 node = &_arrNodeBank[_bankCur++];
345 node->_xy = xy;
346 node->_open = false;
347 node->_closed = false;
348 return node;
349 }
350 }
354 void _link_node(const AStarPoint& p)
355 {
356 constexpr AStarCost ASTAR_K0 = 10;
357 constexpr AStarCost ASTAR_K1 = 14;
358
359 if (_size.x == 0)
360 {
361 AddNode({ p.x + 1, p.y }, ASTAR_K0);
362 AddNode({ p.x - 1, p.y }, ASTAR_K0);
363 AddNode({ p.x, p.y + 1 }, ASTAR_K0);
364 AddNode({ p.x, p.y - 1 }, ASTAR_K0);
365 if (_bDiagonal)
366 {
367 AddNode({ p.x + 1, p.y + 1 }, ASTAR_K1);
368 AddNode({ p.x + 1, p.y - 1 }, ASTAR_K1);
369 AddNode({ p.x - 1, p.y - 1 }, ASTAR_K1);
370 AddNode({ p.x - 1, p.y + 1 }, ASTAR_K1);
371 }
372 }
373 else
374 {
375 if (p.x + 1 < _size.w)
376 AddNode({ p.x + 1, p.y }, ASTAR_K0);
377 if (p.x > 0)
378 AddNode({ p.x - 1, p.y }, ASTAR_K0);
379 if (p.y + 1 < _size.h)
380 AddNode({ p.x, p.y + 1 }, ASTAR_K0);
381 if (p.y > 0)
382 AddNode({ p.x, p.y - 1}, ASTAR_K0);
383
384 if (_bDiagonal)
385 {
386 if (p.x + 1 < _size.w && p.y + 1 < _size.h)
387 AddNode({ p.x + 1, p.y + 1 }, ASTAR_K1);
388 if (p.x + 1 < _size.w && p.y > 0)
389 AddNode({ p.x + 1, p.y - 1 }, ASTAR_K1);
390 if (p.x > 0 && p.y > 0)
391 AddNode({ p.x - 1, p.y - 1 }, ASTAR_K1);
392 if (p.x > 0 && p.y + 1 < _size.h)
393 AddNode({ p.x - 1, p.y + 1 }, ASTAR_K1);
394 }
395 }
396 }
400 bool _is_link_node(const AStarPoint& p0, const AStarPoint& p1)
401 {
402 AStarPoint p = AbsPoint(p0, p1);
403 if (_bDiagonal)
404 {
405 return p.x <= 1 && p.y <= 1;
406 }
407 else
408 return p.x + p.y <= 1;
409 }
410
411 // 主列表{y, x}
412 std::unordered_map<uint64_t, AStarNode*>_masterNode;
413 // Open表
414 AStarPriorityQueue _queueOpen;
415 // 节点银行
416 AStarNode* _arrNodeBank;
417 // 每次搜索前置0
418 size_t _bankCur;
419 // 遍历节点上限(也是节点银行大小)
420 size_t _maxNode;
421
422 // 是否可斜穿
423 bool _bDiagonal;
424 // 不为0则检查越界
425 AStarPoint _size;
426
427 //回调函数
428 FuncCost _funcG;
429 FuncCost _funcH;
430 FuncLink _funcLink;
431
432 //临时缓存
433 AStarNode* _nodeBest;
434 AStarPoint _xyTarget;
435};
436}
void UpdateNode(AStarNode *node)
void Push(AStarNode *node)
AStarCost AStarGetH(const AStarPoint &p0, const AStarPoint &p1)
默认获取H方法
void AddNode(const AStarPoint &xy, AStarCost k_cost)
添加节点(在func_link回调函数使用)
AStarPoint AbsPoint(const AStarPoint &a, const AStarPoint &b)
void SetFuncLink(FuncLink func_link)
修改连接函数
std::function< void(AStar *, const AStarPoint &)> FuncLink
static bool AStarNodeCompare(AStarNode *n1, AStarNode *n2)
AStar(const AStarPoint &size, FuncCost func_g, FuncCost func_h=nullptr, FuncLink func_link=nullptr, size_t max_node=10000)
定制一个A星寻路
std::function< AStarCost(const AStarPoint &, const AStarPoint &)> FuncCost
bool Search(const AStarPoint &source, const AStarPoint &target, bool target_near, std::list< AStarPoint > &list_path, std::list< AStarPoint > *fail_path=nullptr)
搜索
void SetSize(const AStarPoint &size)
void SetLinkDiagonal(bool diagonal)
设置是否对角线可走(使用默认连接函数才有效)
日志系统
#define log_msg(...)
#define log_info(...)
字符串相关
constexpr bool ASTAR_SHOW_LOG