Deep RTS
JPS.h
Go to the documentation of this file.
1#pragma once
2
3/*
4Public domain Jump Point Search implementation -- very fast pathfinding for uniform cost grids.
5Scroll down for compile config, usage tips, example code.
6
7License:
8 Public domain, WTFPL, CC0 or your favorite permissive license; whatever is available in your country.
9
10Dependencies:
11 libc (stdlib.h, math.h) by default, change defines below to use your own functions. (realloc(), free(), sqrt())
12 Compiles as C++98, does not require C++11 nor the STL.
13 Does not throw exceptions, works without RTTI, does not contain any virtual methods.
14
15Thread safety:
16 No global state. Searcher instances are not thread-safe. Grid template class is up to you.
17 If your grid access is read-only while pathfinding you may have many threads compute paths at the same time,
18 each with its own Searcher instance.
19
20Background:
21 If you want to generate paths on a map with the following properties:
22 - You have a 2D grid (exactly two dimensions!), where each tile has exactly 8 neighbors (up, down, left, right + diagonals)
23 - There is no "cost" -- a tile is either walkable, or not.
24 then you may want to avoid full fledged A* and go for Jump Point Search (this lib).
25 JPS is usually much faster than plain old A*, as long as your tile traversability check function is fast.
26
27Origin:
28 https://github.com/fgenesis/tinypile/blob/master/jps.hh
29
30Based on my older implementation:
31 https://github.com/fgenesis/jps/blob/master/JPS.h
32 (For changes compared to that version go to the end of this file)
33
34Inspired by:
35 http://users.cecs.anu.edu.au/~dharabor/data/papers/harabor-grastien-aaai11.pdf (The original paper)
36 https://github.com/Yonaba/Jumper
37 https://github.com/qiao/PathFinding.js
38
39Usage:
40
41 Define a class that overloads `operator()(x, y) const`, returning a value that can be treated as boolean.
42 You are responsible for bounds checking!
43 You want your operator() to be as fast and small as possible, as it will be called a LOT.
44 Ask your compiler to force-inline it if possible.
45
46// --- Begin example code ---
47
48struct MyGrid
49{
50 inline bool operator()(unsigned x, unsigned y) const // coordinates must be unsigned; method must be const
51 {
52 if(x < width && y < height) // Unsigned will wrap if < 0
53 ... return true if terrain at (x, y) is walkable.
54 // return false if terrain is not walkable or out-of-bounds.
55 }
56 unsigned width, height;
57};
58
59// Then you can retrieve a path:
60
61MyGrid grid(... set grid width, height, map data, whatever);
62
63const unsigned step = 0; // 0 compresses the path as much as possible and only records waypoints.
64 // Set this to 1 if you want a detailed single-step path
65 // (e.g. if you plan to further mangle the path yourself),
66 // or any other higher value to output every Nth position.
67 // (Waypoints are always output regardless of the step size.)
68
69JPS::PathVector path; // The resulting path will go here.
70 // You may also use std::vector or whatever, as long as your vector type
71 // has push_back(), begin(), end(), resize() methods (same semantics as std::vector).
72 // Note that the path will NOT include the starting position!
73 // --> If called with start == end it will report that a path has been found,
74 // but the resulting path vector will be empty!
75
76// Single-call interface:
77// (Further remarks about this function can be found near the bottom of this file.)
78// Note that the path vector is NOT cleared! New path points are appended at the end.
79bool found = JPS::findPath(path, grid, startx, starty, endx, endy, step);
80
81
82// --- Alternatively, if you want more control & efficiency for repeated pathfinding runs: ---
83
84// Use a Searcher instance (can be a class member, on the stack, ...)
85// Make sure the passed grid reference stays valid throughout the searcher's lifetime.
86// If you need control over memory allocation, you may pass an extra pointer that will be
87// forwarded to your own JPS_realloc & JPS_free if you've set those. Otherwise it's ignored.
88JPS::Searcher<MyGrid> search(grid, userPtr = NULL);
89
90// build path incrementally from waypoints:
91JPS::Position a, b, c, d = <...>; // set some waypoints
92if (search.findPath(path, a, b)
93 && search.findPath(path, b, c)
94 && search.findPath(path, c, d))
95{
96 // found path: a->b->c->d
97}
98
99// keep re-using existing pathfinder instance
100while(whatever)
101{
102 // Set startx, starty, endx, endy = <...>
103 if(!search.findPath(path, JPS::Pos(startx, starty), JPS::Pos(endx, endy), step))
104 {
105 // ...handle failure...
106 }
107}
108
109// If necessary, you may free internal memory -- this is never required; neither for performance, nor for correct function.
110// If you do pathfinding after freeing memory, it'll allocate new memory.
111// Note that freeing memory aborts any incremental search currently ongoing.
112search.freeMemory();
113
114// If you need to know how much memory is internally allocated by a searcher:
115unsigned bytes = search.getTotalMemoryInUse();
116
117
118// -------------------------------
119// --- Incremental pathfinding ---
120// -------------------------------
121
122Calling JPS::findPath() or Searcher<>::findPath() always computes an entire path or returns failure.
123If the path is long or costly and you have a tight CPU budget per frame you may want to perform pathfinding incrementally,
124stretched over multiple frames.
125
126First, call
127 ### JPS_Result res = search.findPathInit(Position start, Position end) ###
128Don't forget to check the return value, as it may return:
129- JPS_NO_PATH if one or both of the points are obstructed
130- JPS_EMPTY_PATH if the points are equal and not obstructed
131- JPS_FOUND_PATH if the initial greedy heuristic could find a path quickly.
132- JPS_OUT_OF_MEMORY if... well yeah.
133If it returns JPS_NEED_MORE_STEPS then the next part can start.
134
135Repeatedly call
136 ### JPS_Result res = search.findPathStep(int limit) ###
137until it returns JPS_NO_PATH or JPS_FOUND_PATH, or JPS_OUT_OF_MEMORY.
138For consistency, you will want to ensure that the grid does not change between subsequent calls;
139if the grid changes, parts of the path may go through a now obstructed area or may be no longer optimal.
140If limit is 0, it will perform the pathfinding in one go. Values > 0 pause the search
141as soon as possible after the number of steps was exceeded, returning NEED_MORE_STEPS.
142Use search.getStepsDone() after some test runs to find a good value for the limit.
143
144After getting JPS_FOUND_PATH, generate the actual path points via
145 ### JPS_Result res = search.findPathFinish(PathVector& path, unsigned step = 0) ###
146As described above, path points are appended, and granularity can be adjusted with the step parameter.
147Returns JPS_FOUND_PATH if the path was successfully built and appended to the path vector.
148Returns JPS_NO_PATH if the pathfinding did not finish or generating the path failed.
149May return JPS_OUT_OF_MEMORY if the path vector must be resized but fails to allocate.
150
151If findPathInit() or findPathStep() return JPS_OUT_OF_MEMORY, the current searcher progress becomes undefined.
152To recover, free some memory elsewhere and call findPathInit() to try again.
153
154If findPathFinish() returns out-of-memory but previous steps finished successfully,
155then the found path is still valid for generating the path vector.
156In that case you may call findPathFinish() again after making some memory available.
157
158If you do not worry about memory, treat JPS_OUT_OF_MEMORY as if JPS_NO_PATH was returned.
159
160You may pass JPS::PathVector, std::vector, or your own to findPathFinish().
161Note that if the path vector type you pass throws exceptions in case of allocation failures (std::vector does, for example),
162you'll get that exception, and the path vector will be in whatever state it was in when the last element was successfully inserted.
163If no exception is thrown (ie. you used JPS::PathVector) then the failure cases do not modify the path vector.
164
165You may abort a search anytime by starting a new one via findPathInit(), calling freeMemory(), or by destroying the searcher instance.
166Aborting or starting a search resets the values returned by .getStepsDone() and .getNodesExpanded() to 0.
167
168*/
169
170// ============================
171// ====== COMPILE CONFIG ======
172// ============================
173
174
175// If you want to avoid sqrt() or floats in general, define this.
176// Turns out in some testing this was ~12% faster, so it's the default.
177#define JPS_NO_FLOAT
178
179
180// ------------------------------------------------
181
182#include <stddef.h> // for size_t (needed for operator new)
183
184// Assertions
185#ifndef JPS_ASSERT
186# ifdef _DEBUG
187# include <assert.h>
188# define JPS_ASSERT(cond) assert(cond)
189# else
190# define JPS_ASSERT(cond)
191# endif
192#endif
193
194// The default allocator uses realloc(), free(). Change if necessary.
195// You will get the user pointer that you passed to findPath() or the Searcher ctor.
196#if !defined(JPS_realloc) || !defined(JPS_free)
197# include <stdlib.h> // for realloc, free
198# ifndef JPS_realloc
199# define JPS_realloc(p, newsize, oldsize, user) realloc(p, newsize)
200# endif
201# ifndef JPS_free
202# define JPS_free(p, oldsize, user) free(p)
203# endif
204#endif
205
206#ifdef JPS_NO_FLOAT
207#define JPS_HEURISTIC_ACCURATE(a, b) (Heuristic::Chebyshev(a, b))
208#else
209# ifndef JPS_sqrt
210// for Euclidean heuristic.
211# include <math.h>
212# define JPS_sqrt(x) sqrtf(float(x)) // float cast here avoids a warning about implicit int->float cast
213# endif
214#endif
215
216// Which heuristics to use.
217// Basic property: Distance estimate, returns values >= 0. Smaller is better.
218// The accurate heuristic should always return guesses less or equal than the estimate heuristic,
219// otherwise the resulting paths may not be optimal.
220// (The rule of thumb is that the estimate is fast but can overestimate)
221// For the implementation of heuristics, scroll down.
222#ifndef JPS_HEURISTIC_ACCURATE
223#define JPS_HEURISTIC_ACCURATE(a, b) (Heuristic::Euclidean(a, b))
224#endif
225
226#ifndef JPS_HEURISTIC_ESTIMATE
227#define JPS_HEURISTIC_ESTIMATE(a, b) (Heuristic::Manhattan(a, b))
228#endif
229
230
231// --- Data types ---
232namespace JPS {
233
234// unsigned integer type wide enough to store a position on one grid axis.
235// Note that on x86, u32 is actually faster than u16.
236 typedef unsigned PosType;
237
238// Result of heuristics. can also be (unsigned) int but using float by default since that's what sqrtf() returns
239// and we don't need to cast float->int that way. Change if you use integer-only heuristics.
240// (Euclidean heuristic using sqrt() works fine even if cast to int. Your choice really.)
241#ifdef JPS_NO_FLOAT
242 typedef int ScoreType;
243#else
244 typedef float ScoreType;
245#endif
246
247// Size type; used internally for vectors and the like. You can set this to size_t if you want, but 32 bits is more than enough.
248 typedef unsigned SizeT;
249
250} // end namespace JPS
251
252
253// ================================
254// ====== COMPILE CONFIG END ======
255// ================================
256// ----------------------------------------------------------------------------------------
257
258typedef unsigned JPS_Flags;
260{
261 // No special behavior
263
264 // If this is defined, disable the greedy direct-short-path check that avoids the large area scanning that JPS does.
265 // This is just a performance tweak. May save a lot of CPU when constantly re-planning short paths without obstacles
266 // (e.g. an entity follows close behind another).
267 // Does not change optimality of results. If you perform your own line-of-sight checks
268 // before starting a pathfinding run you can disable greedy since checking twice isn't needed,
269 // but otherwise it's better to leave it enabled.
271
272 // If this is set, use standard A* instead of JPS (e.g. if you want to compare performance in your scenario).
273 // In most cases this will be MUCH slower, but might be beneficial if your grid lookup
274 // is slow (aka worse than O(1) or more than a few inlined instructions),
275 // as it avoids the large area scans that the JPS algorithm does.
276 // (Also increases memory usage as each checked position is expanded into a node.)
278
279 // Don't check whether start position is walkable.
280 // This makes the start position always walkable, even if the map data say otherwise.
282
283 // Don't check whether end position is walkable.
285};
286
288{
295
296// operator new() without #include <new>
297// Unfortunately the standard mandates the use of size_t, so we need stddef.h the very least.
298// Trick via https://github.com/ocornut/imgui
299// "Defining a custom placement new() with a dummy parameter allows us to bypass including <new>
300// which on some platforms complains when user has disabled exceptions."
302inline void* operator new(size_t, JPS__NewDummy, void* ptr) { return ptr; }
303inline void operator delete(void*, JPS__NewDummy, void*) {}
304#define JPS_PLACEMENT_NEW(p) new(JPS__NewDummy(), p)
305
306
307namespace JPS {
308
309 struct Position
310 {
312
313 inline bool operator==(const Position& p) const
314 {
315 return x == p.x && y == p.y;
316 }
317 inline bool operator!=(const Position& p) const
318 {
319 return x != p.x || y != p.y;
320 }
321
322 inline bool isValid() const { return x != PosType(-1); }
323 };
324
325// The invalid position. Used internally to mark non-walkable points.
326 static const Position npos = {PosType(-1), PosType(-1)};
327 static const SizeT noidx = SizeT(-1);
328
329// ctor function to keep Position a real POD struct.
330 inline static Position Pos(PosType x, PosType y)
331 {
332 Position p;
333 p.x = x;
334 p.y = y;
335 return p;
336 }
337
338 template<typename T> inline static T Max(T a, T b) { return a < b ? b : a; }
339 template<typename T> inline static T Min(T a, T b) { return a < b ? a : b; }
340 template<typename T> inline static T Abs(T a) { return a < T(0) ? -a : a; }
341 template<typename T> inline static int Sgn(T val) { return (T(0) < val) - (val < T(0)); }
342
343
344// Heuristics. Add new ones if you need them.
345 namespace Heuristic
346 {
347 inline ScoreType Manhattan(const Position& a, const Position& b)
348 {
349 const int dx = Abs(int(a.x - b.x));
350 const int dy = Abs(int(a.y - b.y));
351 return static_cast<ScoreType>(dx + dy);
352 }
353
354 inline ScoreType Chebyshev(const Position& a, const Position& b)
355 {
356 const int dx = Abs(int(a.x - b.x));
357 const int dy = Abs(int(a.y - b.y));
358 return static_cast<ScoreType>(Max(dx, dy));
359 }
360#ifdef JPS_sqrt
361 inline ScoreType Euclidean(const Position& a, const Position& b)
362 {
363 const int dx = (int(a.x - b.x));
364 const int dy = (int(a.y - b.y));
365 return static_cast<ScoreType>(JPS_sqrt(dx*dx + dy*dy));
366 }
367#endif
368 } // end namespace heuristic
369
370
371
372// --- Begin infrastructure, data structures ---
373
374 namespace Internal {
375
376// Never allocated outside of a PodVec<Node> --> All nodes are linearly adjacent in memory.
377 struct Node
378 {
379 ScoreType f, g; // heuristic distances
381 int parentOffs; // no parent if 0
382 unsigned _flags;
383
384 inline int hasParent() const { return parentOffs; }
385 inline void setOpen() { _flags |= 1; }
386 inline void setClosed() { _flags |= 2; }
387 inline unsigned isOpen() const { return _flags & 1; }
388 inline unsigned isClosed() const { return _flags & 2; }
389
390 // We know nodes are allocated sequentially in memory, so this is fine.
391 inline Node& getParent() { JPS_ASSERT(parentOffs); return this[parentOffs]; }
392 inline const Node& getParent() const { JPS_ASSERT(parentOffs); return this[parentOffs]; }
393 inline const Node *getParentOpt() const { return parentOffs ? this + parentOffs : 0; }
394 inline void setParent(const Node& p) { JPS_ASSERT(&p != this); parentOffs = static_cast<int>(&p - this); }
395 };
396
397 template<typename T>
398 class PodVec
399 {
400 public:
401 PodVec(void *user = 0)
402 : _data(0), used(0), cap(0), _user(user)
403 {}
405 inline void clear()
406 {
407 used = 0;
408 }
409 void dealloc()
410 {
411 JPS_free(_data, cap * sizeof(T), _user);
412 _data = 0;
413 used = 0;
414 cap = 0;
415 }
416 T *alloc()
417 {
418 T *e = 0;
419 if(used < cap || _grow())
420 {
421 e = _data + used;
422 ++used;
423 }
424 return e;
425 }
426 inline void push_back(const T& e)
427 {
428 if(T *dst = alloc()) // yes, this silently fails when OOM. this is handled internally.
429 *dst = e;
430 }
431 inline void pop_back() { JPS_ASSERT(used); --used; }
432 inline T& back() { JPS_ASSERT(used); return _data[used-1]; }
433 inline SizeT size() const { return used; }
434 inline bool empty() const { return !used; }
435 inline T *data() { return _data; }
436 inline const T *data() const { return _data; }
437 inline T& operator[](size_t idx) const { JPS_ASSERT(idx < used); return _data[idx]; }
438 inline SizeT getindex(const T *e) const
439 {
440 JPS_ASSERT(e && _data <= e && e < _data + used);
441 return static_cast<SizeT>(e - _data);
442 }
443
444 void *_reserve(SizeT newcap) // for internal use
445 {
446 return cap < newcap ? _grow(newcap) : _data;
447 }
448 void resize(SizeT sz)
449 {
450 if(_reserve(sz))
451 used = sz;
452 }
454 {
455 return cap * sizeof(T);
456 }
457
458 // minimal iterator interface
459 typedef T* iterator;
460 typedef const T* const_iterator;
462 typedef T value_type;
463 inline iterator begin() { return data(); }
464 inline iterator end() { return data() + size(); }
465 inline const_iterator cbegin() const { return data(); }
466 inline const_iterator cend() const { return data() + size(); }
467
468 private:
469 void *_grow(SizeT newcap)
470 {
471 void *p = JPS_realloc(_data, newcap * sizeof(T), cap * sizeof(T), _user);
472 if(p)
473 {
474 _data = (T*)p;
475 cap = newcap;
476 }
477 return p;
478 }
479 void * _grow()
480 {
481 const SizeT newcap = cap + (cap / 2) + 32;
482 return _grow(newcap);
483 }
484 T *_data;
485 SizeT used, cap;
486
487 public:
488 void * const _user;
489
490 private:
491 // forbid ops
492 PodVec<T>& operator=(const PodVec<T>&);
493 PodVec(const PodVec<T>&);
494 };
495
496 template<typename T>
497 inline static void Swap(T& a, T& b)
498 {
499 const T tmp = a;
500 a = b;
501 b = tmp;
502 }
503
504 template<typename IT>
505 inline static void Reverse(IT first, IT last)
506 {
507 while((first != last) && (first != --last))
508 {
509 Swap(*first, *last);
510 ++first;
511 }
512 }
513
515
516
518 {
519 private:
520 static const unsigned LOAD_FACTOR = 8; // estimate: {CPU cache line size (64)} / sizeof(HashLoc)
521 static const unsigned INITIAL_BUCKETS = 16; // must be > 1 and power of 2
522
523 struct HashLoc
524 {
525 unsigned hash2; // for early-out check only
526 SizeT idx; // index in central storage
527 };
528 typedef PodVec<HashLoc> Bucket;
529
530 // hash function to determine bucket. only uses lower few bits. should jumble lower bits nicely.
531 static inline unsigned Hash(PosType x, PosType y)
532 {
533 return x ^ y;
534 }
535
536 // hash function designed to lose as little data as possible. for early-out checks. all bits used.
537 static inline unsigned Hash2(PosType x, PosType y)
538 {
539 return (y << 16) ^ x;
540 }
541
542 public:
543
544 NodeMap(Storage& storage)
545 : _storageRef(storage), _buckets(storage._user)
546 {}
547
548 void dealloc()
549 {
550 for(SizeT i = 0; i < _buckets.size(); ++i)
551 _buckets[i].~Bucket();
552 _buckets.dealloc();
553 }
554 void clear()
555 {
556 // clear the buckets, but *not* the bucket vector
557 for(SizeT i = 0; i < _buckets.size(); ++i)
558 _buckets[i].clear();
559 }
560
562 {
563 const unsigned h = Hash(x, y);
564 const unsigned h2 = Hash2(x, y);
565 const SizeT ksz = _buckets.size(); // known to be power-of-2
566 Bucket *b = 0; // MSVC /W4 complains that this was uninitialized and used, so we init it...
567 if (ksz)
568 {
569 b = &_buckets[h & (ksz - 1)];
570 const SizeT bsz = b->size();
571 const HashLoc * const bdata = b->data();
572 for (SizeT i = 0; i < bsz; ++i)
573 {
574 // this is the only place where HashLoc::hash2 is used; it *could*be removed, which means:
575 // - twice as much space for indexes per cache line
576 // - but also higher chances for a cache miss because for each entry in the bucket we still need to check the node's X/Y coords,
577 // and we'll likely end up in a random location in RAM for each node.
578 // Quick benchmarking showed that *with* the hash2 check it's almost immeasurably (less than 1%) faster.
579 if (bdata[i].hash2 == h2)
580 {
581 Node &n = _storageRef[bdata[i].idx];
582 if(n.pos.x == x && n.pos.y == y)
583 return &n;
584 }
585 }
586 }
587
588 // enlarge hashmap if necessary; fix bucket if so
589 SizeT newbsz = _enlarge();
590 if(newbsz > 1)
591 b = &_buckets[h & (newbsz - 1)];
592 else if(newbsz == 1) // error case
593 return 0;
594
595 HashLoc *loc = b->alloc(); // ... see above. b is always initialized here. when ksz==0, _enlarge() will do its initial allocation, so it can never return 0.
596
597 if(!loc)
598 return 0;
599
600 loc->hash2 = h2;
601 loc->idx = _storageRef.size();
602
603 // no node at (x, y), create new one
604 Node *n = _storageRef.alloc();
605 if(n)
606 {
607 n->f = 0;
608 n->g = 0;
609 n->pos.x = x;
610 n->pos.y = y;
611 n->parentOffs = 0;
612 n->_flags = 0;
613 }
614 return n;
615 }
616
618 {
619 SizeT sum = _buckets._getMemSize();
620 for(Buckets::const_iterator it = _buckets.cbegin(); it != _buckets.cend(); ++it)
621 sum += it->_getMemSize();
622 return sum;
623 }
624
625 private:
626
627 // return values: 0 = nothing to do; 1 = error; >1: internal storage was enlarged to this many buckets
628 SizeT _enlarge()
629 {
630 const SizeT n = _storageRef.size();
631 const SizeT oldsz = _buckets.size();
632 if (n < oldsz * LOAD_FACTOR)
633 return 0;
634
635 // pre-allocate bucket storage that we're going to use
636 const SizeT newsz = oldsz ? oldsz * 2 : INITIAL_BUCKETS; // stays power of 2
637
638 if(!_buckets._reserve(newsz))
639 return 0; // early out if realloc fails; this not a problem and we can continue.
640
641 // forget everything
642 for(SizeT i = 0; i < oldsz; ++i)
643 _buckets[i].clear();
644
645 // resize and init
646 for(SizeT i = oldsz; i < newsz; ++i)
647 {
648 void *p = _buckets.alloc(); // can't fail since the space was reserved
650 }
651
652 const SizeT mask = _buckets.size() - 1;
653 for(SizeT i = 0; i < n; ++i)
654 {
655 const Position p = _storageRef[i].pos;
656 HashLoc *loc = _buckets[Hash(p.x, p.y) & mask].alloc();
657 if(!loc)
658 return 1; // error case
659
660 loc->hash2 = Hash2(p.x, p.y);
661 loc->idx = i;
662 }
663 return newsz;
664 }
665
666 Storage& _storageRef;
667 typedef PodVec<Bucket> Buckets;
668 Buckets _buckets;
669 };
670
672 {
673 private:
674 const Storage& _storageRef;
675 PodVec<SizeT> idxHeap;
676
677 public:
678
679 OpenList(const Storage& storage)
680 : _storageRef(storage), idxHeap(storage._user)
681 {}
682
683
684 inline void pushNode(Node *n)
685 {
686 _heapPushIdx(_storageRef.getindex(n));
687 }
688
689 inline Node& popNode()
690 {
691 return _storageRef[_popIdx()];
692 }
693
694 // re-heapify after node changed its order
695 inline void fixNode(const Node& n)
696 {
697 const unsigned ni = _storageRef.getindex(&n);
698 const unsigned sz = idxHeap.size();
699 unsigned *p = idxHeap.data();
700 for(unsigned i = 0; i < sz; ++i) // TODO: if this ever becomes a perf bottleneck: make it so that each node knows its heap index
701 if(p[i] == ni)
702 {
703 _fixIdx(i);
704 return;
705 }
706 JPS_ASSERT(false); // expect node to be found
707 }
708
709 inline void dealloc() { idxHeap.dealloc(); }
710 inline void clear() { idxHeap.clear(); }
711 inline bool empty() const { return idxHeap.empty(); }
712
713 inline SizeT _getMemSize() const
714 {
715 return idxHeap._getMemSize();
716 }
717
718 private:
719
720 inline bool _heapLess(SizeT a, SizeT b)
721 {
722 return _storageRef[idxHeap[a]].f > _storageRef[idxHeap[b]].f;
723 }
724
725 inline bool _heapLessIdx(SizeT a, SizeT idx)
726 {
727 return _storageRef[idxHeap[a]].f > _storageRef[idx].f;
728 }
729
730 void _percolateUp(SizeT i)
731 {
732 const SizeT idx = idxHeap[i];
733 SizeT p;
734 goto start;
735 do
736 {
737 idxHeap[i] = idxHeap[p]; // parent is smaller, move it down
738 i = p; // continue with parent
739 start:
740 p = (i - 1) >> 1;
741 }
742 while(i && _heapLessIdx(p, idx));
743 idxHeap[i] = idx; // found correct place for idx
744 }
745
746 void _percolateDown(SizeT i)
747 {
748 const SizeT idx = idxHeap[i];
749 const SizeT sz = idxHeap.size();
750 SizeT child;
751 goto start;
752 do
753 {
754 // pick right sibling if exists and larger or equal
755 if(child + 1 < sz && !_heapLess(child+1, child))
756 ++child;
757 idxHeap[i] = idxHeap[child];
758 i = child;
759 start:
760 child = (i << 1) + 1;
761 }
762 while(child < sz);
763 idxHeap[i] = idx;
764 _percolateUp(i);
765 }
766
767 void _heapPushIdx(SizeT idx)
768 {
769 SizeT i = idxHeap.size();
770 idxHeap.push_back(idx);
771 _percolateUp(i);
772 }
773
774 SizeT _popIdx()
775 {
776 SizeT sz = idxHeap.size();
777 JPS_ASSERT(sz);
778 const SizeT root = idxHeap[0];
779 idxHeap[0] = idxHeap[--sz];
780 idxHeap.pop_back();
781 if(sz > 1)
782 _percolateDown(0);
783 return root;
784 }
785
786 // re-heapify node at index i
787 inline void _fixIdx(SizeT i)
788 {
789 _percolateDown(i);
790 _percolateUp(i);
791 }
792 };
793
794#undef JPS_PLACEMENT_NEW
795
796// --- End infrastructure, data structures ---
797
798// All those things that don't depend on template parameters...
800 {
801 protected:
805
811
812
813 SearcherBase(void *user)
814 : storage(user)
815 , open(storage)
817 , endPos(npos), endNodeIdx(noidx)
818 , flags(0)
819 , stepsRemain(0), stepsDone(0)
820 {}
821
822 void clear()
823 {
824 open.clear();
825 nodemap.clear();
826 storage.clear();
827 endNodeIdx = noidx;
828 stepsDone = 0;
829 }
830
831 void _expandNode(const Position jp, Node& jn, const Node& parent)
832 {
833 JPS_ASSERT(jn.pos == jp);
834 ScoreType extraG = JPS_HEURISTIC_ACCURATE(jp, parent.pos);
835 ScoreType newG = parent.g + extraG;
836 if(!jn.isOpen() || newG < jn.g)
837 {
838 jn.g = newG;
839 jn.f = jn.g + JPS_HEURISTIC_ESTIMATE(jp, endPos);
840 jn.setParent(parent);
841 if(!jn.isOpen())
842 {
843 open.pushNode(&jn);
844 jn.setOpen();
845 }
846 else
847 open.fixNode(jn);
848 }
849 }
850
851 public:
852
853 template <typename PV>
854 JPS_Result generatePath(PV& path, unsigned step) const;
855
857 {
858 open.dealloc();
861 endNodeIdx = noidx;
862 }
863
864 // --- Statistics ---
865
866 inline SizeT getStepsDone() const { return stepsDone; }
867 inline SizeT getNodesExpanded() const { return storage.size(); }
868
870 {
871 return storage._getMemSize()
873 + open._getMemSize();
874 }
875 };
876
877 template <typename GRID> class Searcher : public SearcherBase
878 {
879 public:
880 Searcher(const GRID& g, void *user = 0)
881 : SearcherBase(user), grid(g)
882 {}
883
884 // single-call
885 template<typename PV>
886 bool findPath(PV& path, Position start, Position end, unsigned step, JPS_Flags flags = JPS_Flag_Default);
887
888 // incremental pathfinding
890 JPS_Result findPathStep(int limit);
891 // generate path after one was found
892 template<typename PV>
893 JPS_Result findPathFinish(PV& path, unsigned step) const;
894
895 private:
896
897 const GRID& grid;
898
899 Node *getNode(const Position& pos);
900 bool identifySuccessors(const Node& n);
901
902 bool findPathGreedy(Node *start, Node *end);
903
904 unsigned findNeighborsAStar(const Node& n, Position *wptr);
905
906 unsigned findNeighborsJPS(const Node& n, Position *wptr) const;
907 Position jumpP(const Position& p, const Position& src);
908 Position jumpD(Position p, int dx, int dy);
909 Position jumpX(Position p, int dx);
910 Position jumpY(Position p, int dy);
911
912 // forbid any ops
913 Searcher& operator=(const Searcher<GRID>&);
914 Searcher(const Searcher<GRID>&);
915 };
916
917
918// -----------------------------------------------------------------------
919
920 template<typename PV> JPS_Result SearcherBase::generatePath(PV& path, unsigned step) const
921 {
922 if(endNodeIdx == noidx)
923 return JPS_NO_PATH;
924 const SizeT offset = path.size();
925 SizeT added = 0;
926 const Node& endNode = storage[endNodeIdx];
927 const Node *next = &endNode;
928 if(!next->hasParent())
929 return JPS_NO_PATH;
930 if(step)
931 {
932 const Node *prev = endNode.getParentOpt();
933 if(!prev)
934 return JPS_NO_PATH;
935 do
936 {
937 const unsigned x = next->pos.x, y = next->pos.y;
938 int dx = int(prev->pos.x - x);
939 int dy = int(prev->pos.y - y);
940 const int adx = Abs(dx);
941 const int ady = Abs(dy);
942 JPS_ASSERT(!dx || !dy || adx == ady); // known to be straight, if diagonal
943 const int steps = Max(adx, ady);
944 dx = int(step) * Sgn(dx);
945 dy = int(step) * Sgn(dy);
946 int dxa = 0, dya = 0;
947 for(int i = 0; i < steps; i += step)
948 {
949 path.push_back(Pos(x+dxa, y+dya));
950 ++added;
951 dxa += dx;
952 dya += dy;
953 }
954 next = prev;
955 prev = prev->getParentOpt();
956 }
957 while (prev);
958 }
959 else
960 {
961 do
962 {
963 JPS_ASSERT(next != &next->getParent());
964 path.push_back(next->pos);
965 ++added;
966 next = &next->getParent();
967 }
968 while (next->hasParent());
969 }
970
971 // JPS::PathVector silently discards push_back() when memory allocation fails;
972 // detect that case and roll back.
973 if(path.size() != offset + added)
974 {
975 path.resize(offset);
976 return JPS_OUT_OF_MEMORY;
977 }
978
979 // Nodes were traversed backwards, fix that
980 Reverse(path.begin() + offset, path.end());
981 return JPS_FOUND_PATH;
982 }
983
984//-----------------------------------------
985
986 template <typename GRID> inline Node *Searcher<GRID>::getNode(const Position& pos)
987 {
988 JPS_ASSERT(grid(pos.x, pos.y));
989 return nodemap(pos.x, pos.y);
990 }
991
992 template <typename GRID> Position Searcher<GRID>::jumpP(const Position &p, const Position& src)
993 {
994 JPS_ASSERT(grid(p.x, p.y));
995
996 int dx = int(p.x - src.x);
997 int dy = int(p.y - src.y);
998 JPS_ASSERT(dx || dy);
999
1000 if(dx && dy)
1001 return jumpD(p, dx, dy);
1002 else if(dx)
1003 return jumpX(p, dx);
1004 else if(dy)
1005 return jumpY(p, dy);
1006
1007 // not reached
1008 JPS_ASSERT(false);
1009 return npos;
1010 }
1011
1012 template <typename GRID> Position Searcher<GRID>::jumpD(Position p, int dx, int dy)
1013 {
1014 JPS_ASSERT(grid(p.x, p.y));
1015 JPS_ASSERT(dx && dy);
1016
1017 const Position endpos = endPos;
1018 unsigned steps = 0;
1019
1020 while(true)
1021 {
1022 if(p == endpos)
1023 break;
1024
1025 ++steps;
1026 const PosType x = p.x;
1027 const PosType y = p.y;
1028
1029 if( (grid(x-dx, y+dy) && !grid(x-dx, y)) || (grid(x+dx, y-dy) && !grid(x, y-dy)) )
1030 break;
1031
1032 const bool gdx = !!grid(x+dx, y);
1033 const bool gdy = !!grid(x, y+dy);
1034
1035 if(gdx && jumpX(Pos(x+dx, y), dx).isValid())
1036 break;
1037
1038 if(gdy && jumpY(Pos(x, y+dy), dy).isValid())
1039 break;
1040
1041 if((gdx || gdy) && grid(x+dx, y+dy))
1042 {
1043 p.x += dx;
1044 p.y += dy;
1045 }
1046 else
1047 {
1048 p = npos;
1049 break;
1050 }
1051 }
1052 stepsDone += steps;
1053 stepsRemain -= steps;
1054 return p;
1055 }
1056
1057 template <typename GRID> inline Position Searcher<GRID>::jumpX(Position p, int dx)
1058 {
1059 JPS_ASSERT(dx);
1060 JPS_ASSERT(grid(p.x, p.y));
1061
1062 const PosType y = p.y;
1063 const Position endpos = endPos;
1064 unsigned steps = 0;
1065
1066 unsigned a = ~((!!grid(p.x, y+1)) | ((!!grid(p.x, y-1)) << 1));
1067
1068 while(true)
1069 {
1070 const unsigned xx = p.x + dx;
1071 const unsigned b = (!!grid(xx, y+1)) | ((!!grid(xx, y-1)) << 1);
1072
1073 if((b & a) || p == endpos)
1074 break;
1075 if(!grid(xx, y))
1076 {
1077 p = npos;
1078 break;
1079 }
1080
1081 p.x += dx;
1082 a = ~b;
1083 ++steps;
1084 }
1085
1086 stepsDone += steps;
1087 stepsRemain -= steps;
1088 return p;
1089 }
1090
1091 template <typename GRID> inline Position Searcher<GRID>::jumpY(Position p, int dy)
1092 {
1093 JPS_ASSERT(dy);
1094 JPS_ASSERT(grid(p.x, p.y));
1095
1096 const PosType x = p.x;
1097 const Position endpos = endPos;
1098 unsigned steps = 0;
1099
1100 unsigned a = ~((!!grid(x+1, p.y)) | ((!!grid(x-1, p.y)) << 1));
1101
1102 while(true)
1103 {
1104 const unsigned yy = p.y + dy;
1105 const unsigned b = (!!grid(x+1, yy)) | ((!!grid(x-1, yy)) << 1);
1106
1107 if((a & b) || p == endpos)
1108 break;
1109 if(!grid(x, yy))
1110 {
1111 p = npos;
1112 break;
1113 }
1114
1115 p.y += dy;
1116 a = ~b;
1117 ++steps;
1118 }
1119
1120 stepsDone += steps;
1121 stepsRemain -= steps;
1122 return p;
1123 }
1124
1125#define JPS_CHECKGRID(dx, dy) (grid(x+(dx), y+(dy)))
1126#define JPS_ADDPOS(dx, dy) do { *w++ = Pos(x+(dx), y+(dy)); } while(0)
1127#define JPS_ADDPOS_CHECK(dx, dy) do { if(JPS_CHECKGRID(dx, dy)) JPS_ADDPOS(dx, dy); } while(0)
1128#define JPS_ADDPOS_NO_TUNNEL(dx, dy) do { if(grid(x+(dx),y) || grid(x,y+(dy))) JPS_ADDPOS_CHECK(dx, dy); } while(0)
1129
1130 template <typename GRID> unsigned Searcher<GRID>::findNeighborsJPS(const Node& n, Position *wptr) const
1131 {
1132 Position *w = wptr;
1133 const unsigned x = n.pos.x;
1134 const unsigned y = n.pos.y;
1135
1136 if(!n.hasParent())
1137 {
1138 // straight moves
1139 JPS_ADDPOS_CHECK(-1, 0);
1140 JPS_ADDPOS_CHECK(0, -1);
1141 JPS_ADDPOS_CHECK(0, 1);
1142 JPS_ADDPOS_CHECK(1, 0);
1143
1144 // diagonal moves + prevent tunneling
1145 JPS_ADDPOS_NO_TUNNEL(-1, -1);
1146 JPS_ADDPOS_NO_TUNNEL(-1, 1);
1147 JPS_ADDPOS_NO_TUNNEL(1, -1);
1149
1150 return unsigned(w - wptr);
1151 }
1152 const Node& p = n.getParent();
1153 // jump directions (both -1, 0, or 1)
1154 const int dx = Sgn<int>(x - p.pos.x);
1155 const int dy = Sgn<int>(y - p.pos.y);
1156
1157 if(dx && dy)
1158 {
1159 // diagonal
1160 // natural neighbors
1161 const bool walkX = !!grid(x+dx, y);
1162 if(walkX)
1163 *w++ = Pos(x+dx, y);
1164 const bool walkY = !!grid(x, y+dy);
1165 if(walkY)
1166 *w++ = Pos(x, y+dy);
1167
1168 if(walkX || walkY)
1169 JPS_ADDPOS_CHECK(dx, dy);
1170
1171 // forced neighbors
1172 if(walkY && !JPS_CHECKGRID(-dx,0))
1173 JPS_ADDPOS_CHECK(-dx, dy);
1174
1175 if(walkX && !JPS_CHECKGRID(0,-dy))
1176 JPS_ADDPOS_CHECK(dx, -dy);
1177 }
1178 else if(dx)
1179 {
1180 // along X axis
1181 if(JPS_CHECKGRID(dx, 0))
1182 {
1183 JPS_ADDPOS(dx, 0);
1184
1185 // Forced neighbors (+ prevent tunneling)
1186 if(!JPS_CHECKGRID(0, 1))
1187 JPS_ADDPOS_CHECK(dx, 1);
1188 if(!JPS_CHECKGRID(0,-1))
1189 JPS_ADDPOS_CHECK(dx,-1);
1190 }
1191 }
1192 else if(dy)
1193 {
1194 // along Y axis
1195 if(JPS_CHECKGRID(0, dy))
1196 {
1197 JPS_ADDPOS(0, dy);
1198
1199 // Forced neighbors (+ prevent tunneling)
1200 if(!JPS_CHECKGRID(1, 0))
1201 JPS_ADDPOS_CHECK(1, dy);
1202 if(!JPS_CHECKGRID(-1, 0))
1203 JPS_ADDPOS_CHECK(-1,dy);
1204 }
1205 }
1206
1207 return unsigned(w - wptr);
1208 }
1209
1210//-------------- Plain old A* search ----------------
1211 template <typename GRID> unsigned Searcher<GRID>::findNeighborsAStar(const Node& n, Position *wptr)
1212 {
1213 Position *w = wptr;
1214 const int x = n.pos.x;
1215 const int y = n.pos.y;
1216 const int d = 1;
1217 JPS_ADDPOS_NO_TUNNEL(-d, -d);
1218 JPS_ADDPOS_CHECK ( 0, -d);
1219 JPS_ADDPOS_NO_TUNNEL(+d, -d);
1220 JPS_ADDPOS_CHECK (-d, 0);
1221 JPS_ADDPOS_CHECK (+d, 0);
1222 JPS_ADDPOS_NO_TUNNEL(-d, +d);
1223 JPS_ADDPOS_CHECK ( 0, +d);
1224 JPS_ADDPOS_NO_TUNNEL(+d, +d);
1225 stepsDone += 8;
1226 return unsigned(w - wptr);
1227 }
1228
1229//-------------------------------------------------
1230#undef JPS_ADDPOS
1231#undef JPS_ADDPOS_CHECK
1232#undef JPS_ADDPOS_NO_TUNNEL
1233#undef JPS_CHECKGRID
1234
1235
1236 template <typename GRID> bool Searcher<GRID>::identifySuccessors(const Node& n_)
1237 {
1238 const SizeT nidx = storage.getindex(&n_);
1239 const Position np = n_.pos;
1240 Position buf[8];
1241
1242 const int num = (flags & JPS_Flag_AStarOnly)
1243 ? findNeighborsAStar(n_, &buf[0])
1244 : findNeighborsJPS(n_, &buf[0]);
1245
1246 for(int i = num-1; i >= 0; --i)
1247 {
1248 // Invariant: A node is only a valid neighbor if the corresponding grid position is walkable (asserted in jumpP)
1249 Position jp;
1250 if(flags & JPS_Flag_AStarOnly)
1251 jp = buf[i];
1252 else
1253 {
1254 jp = jumpP(buf[i], np);
1255 if(!jp.isValid())
1256 continue;
1257 }
1258 // Now that the grid position is definitely a valid jump point, we have to create the actual node.
1259 Node *jn = getNode(jp); // this might realloc the storage
1260 if(!jn)
1261 return false; // out of memory
1262
1263 Node& n = storage[nidx]; // get valid ref in case we realloc'd
1264 JPS_ASSERT(jn != &n);
1265 if(!jn->isClosed())
1266 _expandNode(jp, *jn, n);
1267 }
1268 return true;
1269 }
1270
1271 template <typename GRID> template<typename PV> bool Searcher<GRID>::findPath(PV& path, Position start, Position end, unsigned step, JPS_Flags flags)
1272 {
1273 JPS_Result res = findPathInit(start, end, flags);
1274
1275 // If this is true, the resulting path is empty (findPathFinish() would fail, so this needs to be checked before)
1276 if(res == JPS_EMPTY_PATH)
1277 return true;
1278
1279 while(true)
1280 {
1281 switch(res)
1282 {
1284 res = findPathStep(0);
1285 break; // the switch
1286
1287 case JPS_FOUND_PATH:
1288 return findPathFinish(path, step) == JPS_FOUND_PATH;
1289
1290 case JPS_EMPTY_PATH:
1291 JPS_ASSERT(false); // can't happen
1292 // fall through
1293 case JPS_NO_PATH:
1294 case JPS_OUT_OF_MEMORY:
1295 return false;
1296 }
1297 }
1298 }
1299
1300 template <typename GRID> JPS_Result Searcher<GRID>::findPathInit(Position start, Position end, JPS_Flags flags)
1301 {
1302 // This just resets a few counters; container memory isn't touched
1303 this->clear();
1304
1305 this->flags = flags;
1306 endPos = end;
1307
1308 // FIXME: check this
1309 if(start == end && !(flags & (JPS_Flag_NoStartCheck|JPS_Flag_NoEndCheck)))
1310 {
1311 // There is only a path if this single position is walkable.
1312 // But since the starting position is omitted in the output, there is nothing to do here.
1313 return grid(end.x, end.y) ? JPS_EMPTY_PATH : JPS_NO_PATH;
1314 }
1315
1316 if(!(flags & JPS_Flag_NoStartCheck))
1317 if(!grid(start.x, start.y))
1318 return JPS_NO_PATH;
1319
1320 if(!(flags & JPS_Flag_NoEndCheck))
1321 if(!grid(end.x, end.y))
1322 return JPS_NO_PATH;
1323
1324 Node *endNode = getNode(end); // this might realloc the internal storage...
1325 if(!endNode)
1326 return JPS_OUT_OF_MEMORY;
1327 endNodeIdx = storage.getindex(endNode); // .. so we keep this for later
1328
1329 Node *startNode = getNode(start); // this might also realloc
1330 if(!startNode)
1331 return JPS_OUT_OF_MEMORY;
1332 endNode = &storage[endNodeIdx]; // startNode is valid, make sure that endNode is valid too in case we reallocated
1333
1334 if(!(flags & JPS_Flag_NoGreedy))
1335 {
1336 // Try the quick way out first
1337 if(findPathGreedy(startNode, endNode))
1338 return JPS_FOUND_PATH;
1339 }
1340
1341 open.pushNode(startNode);
1342
1343 return JPS_NEED_MORE_STEPS;
1344 }
1345
1346 template <typename GRID> JPS_Result Searcher<GRID>::findPathStep(int limit)
1347 {
1348 stepsRemain = limit;
1349 do
1350 {
1351 if(open.empty())
1352 return JPS_NO_PATH;
1353 Node& n = open.popNode();
1354 n.setClosed();
1355 if(n.pos == endPos)
1356 return JPS_FOUND_PATH;
1357 if(!identifySuccessors(n))
1358 return JPS_OUT_OF_MEMORY;
1359 }
1360 while(stepsRemain >= 0);
1361 return JPS_NEED_MORE_STEPS;
1362 }
1363
1364 template<typename GRID> template<typename PV> JPS_Result Searcher<GRID>::findPathFinish(PV& path, unsigned step) const
1365 {
1366 return this->generatePath(path, step);
1367 }
1368
1369 template<typename GRID> bool Searcher<GRID>::findPathGreedy(Node *n, Node *endnode)
1370 {
1371 Position midpos = npos;
1372 PosType x = n->pos.x;
1373 PosType y = n->pos.y;
1374 const Position endpos = endnode->pos;
1375
1376 JPS_ASSERT(x != endpos.x || y != endpos.y); // must not be called when start==end
1377 JPS_ASSERT(n != endnode);
1378
1379 int dx = int(endpos.x - x);
1380 int dy = int(endpos.y - y);
1381 const int adx = Abs(dx);
1382 const int ady = Abs(dy);
1383 dx = Sgn(dx);
1384 dy = Sgn(dy);
1385
1386 // go diagonally first
1387 if(x != endpos.x && y != endpos.y)
1388 {
1389 JPS_ASSERT(dx && dy);
1390 const int minlen = Min(adx, ady);
1391 const PosType tx = x + dx * minlen;
1392 while(x != tx)
1393 {
1394 if(grid(x, y) && (grid(x+dx, y) || grid(x, y+dy))) // prevent tunneling as well
1395 {
1396 x += dx;
1397 y += dy;
1398 }
1399 else
1400 return false;
1401 }
1402
1403 if(!grid(x, y))
1404 return false;
1405
1406 midpos = Pos(x, y);
1407 }
1408
1409 // at this point, we're aligned to at least one axis
1410 JPS_ASSERT(x == endpos.x || y == endpos.y);
1411
1412 if(!(x == endpos.x && y == endpos.y))
1413 {
1414 while(x != endpos.x)
1415 if(!grid(x += dx, y))
1416 return false;
1417
1418 while(y != endpos.y)
1419 if(!grid(x, y += dy))
1420 return false;
1421
1422 JPS_ASSERT(x == endpos.x && y == endpos.y);
1423 }
1424
1425 if(midpos.isValid())
1426 {
1427 const unsigned nidx = storage.getindex(n);
1428 Node *mid = getNode(midpos); // this might invalidate n, endnode
1429 if(!mid)
1430 return false;
1431 n = &storage[nidx]; // reload pointers
1432 endnode = &storage[endNodeIdx];
1433 JPS_ASSERT(mid && mid != n);
1434 mid->setParent(*n);
1435 if(mid != endnode)
1436 endnode->setParent(*mid);
1437 }
1438 else
1439 endnode->setParent(*n);
1440
1441 return true;
1442 }
1443
1444#undef JPS_ASSERT
1445#undef JPS_realloc
1446#undef JPS_free
1447#undef JPS_sqrt
1448#undef JPS_HEURISTIC_ACCURATE
1449#undef JPS_HEURISTIC_ESTIMATE
1450
1451
1452 } // end namespace Internal
1453
1454 using Internal::Searcher;
1455
1457
1458// Single-call convenience function. For efficiency, do NOT use this if you need to compute paths repeatedly.
1459//
1460// Returns: 0 if failed or no path could be found, otherwise number of steps taken.
1461//
1462// path: If the function returns success, the path is appended to this vector.
1463// The path does NOT contain the starting position, i.e. if start and end are the same,
1464// the resulting path has no elements.
1465// The vector does not have to be empty. The function does not clear it;
1466// instead, the new path positions are appended at the end.
1467// This allows building a path incrementally.
1468//
1469// grid: Functor, expected to overload operator()(x, y), return true if position is walkable, false if not.
1470//
1471// step: If 0, only return waypoints.
1472// If 1, create exhaustive step-by-step path.
1473// If N, put in one position for N blocks travelled, or when a waypoint is hit.
1474// All returned points are guaranteed to be on a straight line (vertically, horizontally, or diagonally),
1475// and there is no obstruction between any two consecutive points.
1476// Note that this parameter does NOT influence the pathfinding in any way;
1477// it only controls the coarseness of the output path.
1478 template <typename GRID, typename PV>
1479 SizeT findPath(PV& path, const GRID& grid, PosType startx, PosType starty, PosType endx, PosType endy,
1480 unsigned step = 0, // optional
1482 void *user = 0) // memory allocation userdata
1483 {
1484 Searcher<GRID> search(grid, user);
1485 if(!search.findPath(path, Pos(startx, starty), Pos(endx, endy), step, flags))
1486 return 0;
1487 const SizeT done = search.getStepsDone();
1488 return done + !done; // report at least 1 step; as 0 would indicate failure
1489 }
1490
1491} // end namespace JPS
1492
1493
1494/*
1495Changes compared to the older JPS.h at https://github.com/fgenesis/jps:
1496
1497- Explicitly freeing memory is no longer necessary. The freeMemory() method is still there
1498 and does its thing (drop all internal storage), but you never have to call it explicitly.
1499 Unlike the old version, there will be no performance degradation if you don't free memory every now and then.
1500 Actually it'll be slightly slower if you free memory and pathfind again for the first time,
1501 as it has to re-allocate internal data structures.
1502
1503- Searcher::getNodesExpanded() is now reset to 0 upon starting a search.
1504
1505- Added optional JPS_Flags parameter to pathfind (-init) functions to control search
1506 behavior. Compile-time #defines are gone.
1507
1508- Removed skip parameter. Imho that one just added confusion and no real benefit.
1509 If you want it back for some reason: poke me, open an issue, whatever.
1510
1511- Renamed JPS::Result to JPS_Result. Enum values gained JPS_ prefix, so JPS::NO_PATH is now JPS_NO_PATH, and so on.
1512
1513- Added one more JPS_Result value: JPS_OUT_OF_MEMORY. See info block at the top how to handle this.
1514
1515- Changed signature of Searcher<>::findPathFinish() to return JPS_Result (was bool).
1516 This is more in line with the other 2 methods, as it can now return JPS_OUT_OF_MEMORY.
1517
1518- Changed signature of JPS::findPath(). Nonzero return is still success. Pointers to output stats are gone.
1519 Use a Searcher instance if you need the details.
1520
1521- This version no longer depends on the C++ STL: <algorithm>, <vector>, <map>, operator new(), all gone.
1522 Makes things more memory- and cache-friendly, and quite a bit faster, too.
1523
1524- The canonical file name is now "jps.hh" instead of "JPS.h"
1525*/
1526
1527
1528/*
1529TODO:
1530- make int -> DirType
1531- make possible to call findPathStep()/findPathFinish() even when JPS_EMPTY_PATH was returned on init (simplifies switch-case)
1532- make node know its heap index
1533- optional diagonals (make runtime param)
1534*/
JPS_Flags_
Definition: JPS.h:260
@ JPS_Flag_AStarOnly
Definition: JPS.h:277
@ JPS_Flag_Default
Definition: JPS.h:262
@ JPS_Flag_NoGreedy
Definition: JPS.h:270
@ JPS_Flag_NoStartCheck
Definition: JPS.h:281
@ JPS_Flag_NoEndCheck
Definition: JPS.h:284
#define JPS_ADDPOS_NO_TUNNEL(dx, dy)
Definition: JPS.h:1128
#define JPS_HEURISTIC_ACCURATE(a, b)
Definition: JPS.h:207
#define JPS_PLACEMENT_NEW(p)
Definition: JPS.h:304
#define JPS_ADDPOS(dx, dy)
Definition: JPS.h:1126
unsigned JPS_Flags
Definition: JPS.h:258
#define JPS_ADDPOS_CHECK(dx, dy)
Definition: JPS.h:1127
JPS_Result
Definition: JPS.h:288
@ JPS_EMPTY_PATH
Definition: JPS.h:292
@ JPS_NEED_MORE_STEPS
Definition: JPS.h:291
@ JPS_NO_PATH
Definition: JPS.h:289
@ JPS_OUT_OF_MEMORY
Definition: JPS.h:293
@ JPS_FOUND_PATH
Definition: JPS.h:290
#define JPS_HEURISTIC_ESTIMATE(a, b)
Definition: JPS.h:227
#define JPS_free(p, oldsize, user)
Definition: JPS.h:202
#define JPS_realloc(p, newsize, oldsize, user)
Definition: JPS.h:199
#define JPS_CHECKGRID(dx, dy)
Definition: JPS.h:1125
#define JPS_ASSERT(cond)
Definition: JPS.h:190
Definition: JPS.h:518
NodeMap(Storage &storage)
Definition: JPS.h:544
void dealloc()
Definition: JPS.h:548
SizeT _getMemSize() const
Definition: JPS.h:617
Node * operator()(PosType x, PosType y)
Definition: JPS.h:561
void clear()
Definition: JPS.h:554
Definition: JPS.h:672
void pushNode(Node *n)
Definition: JPS.h:684
SizeT _getMemSize() const
Definition: JPS.h:713
OpenList(const Storage &storage)
Definition: JPS.h:679
bool empty() const
Definition: JPS.h:711
void fixNode(const Node &n)
Definition: JPS.h:695
Node & popNode()
Definition: JPS.h:689
void dealloc()
Definition: JPS.h:709
void clear()
Definition: JPS.h:710
Definition: JPS.h:399
bool empty() const
Definition: JPS.h:434
void clear()
Definition: JPS.h:405
void pop_back()
Definition: JPS.h:431
T value_type
Definition: JPS.h:462
void dealloc()
Definition: JPS.h:409
SizeT size() const
Definition: JPS.h:433
const_iterator cbegin() const
Definition: JPS.h:465
T & operator[](size_t idx) const
Definition: JPS.h:437
void resize(SizeT sz)
Definition: JPS.h:448
const_iterator cend() const
Definition: JPS.h:466
iterator end()
Definition: JPS.h:464
SizeT getindex(const T *e) const
Definition: JPS.h:438
void * _reserve(SizeT newcap)
Definition: JPS.h:444
T * data()
Definition: JPS.h:435
void *const _user
Definition: JPS.h:488
T & back()
Definition: JPS.h:432
SizeT _getMemSize() const
Definition: JPS.h:453
T * alloc()
Definition: JPS.h:416
const T * data() const
Definition: JPS.h:436
PodVec(void *user=0)
Definition: JPS.h:401
const T * const_iterator
Definition: JPS.h:460
T * iterator
Definition: JPS.h:459
~PodVec()
Definition: JPS.h:404
void push_back(const T &e)
Definition: JPS.h:426
SizeT size_type
Definition: JPS.h:461
iterator begin()
Definition: JPS.h:463
Definition: JPS.h:800
JPS_Flags flags
Definition: JPS.h:808
OpenList open
Definition: JPS.h:803
void clear()
Definition: JPS.h:822
SizeT getTotalMemoryInUse() const
Definition: JPS.h:869
void freeMemory()
Definition: JPS.h:856
Position endPos
Definition: JPS.h:806
SizeT stepsDone
Definition: JPS.h:810
SizeT getStepsDone() const
Definition: JPS.h:866
SizeT getNodesExpanded() const
Definition: JPS.h:867
SearcherBase(void *user)
Definition: JPS.h:813
void _expandNode(const Position jp, Node &jn, const Node &parent)
Definition: JPS.h:831
JPS_Result generatePath(PV &path, unsigned step) const
Definition: JPS.h:920
int stepsRemain
Definition: JPS.h:809
SizeT endNodeIdx
Definition: JPS.h:807
Storage storage
Definition: JPS.h:802
NodeMap nodemap
Definition: JPS.h:804
Definition: JPS.h:878
bool findPath(PV &path, Position start, Position end, unsigned step, JPS_Flags flags=JPS_Flag_Default)
Definition: JPS.h:1271
JPS_Result findPathStep(int limit)
Definition: JPS.h:1346
JPS_Result findPathFinish(PV &path, unsigned step) const
Definition: JPS.h:1364
JPS_Result findPathInit(Position start, Position end, JPS_Flags flags=JPS_Flag_Default)
Definition: JPS.h:1300
Searcher(const GRID &g, void *user=0)
Definition: JPS.h:880
ScoreType Manhattan(const Position &a, const Position &b)
Definition: JPS.h:347
ScoreType Chebyshev(const Position &a, const Position &b)
Definition: JPS.h:354
PodVec< Node > Storage
Definition: JPS.h:514
Definition: JPS.h:232
unsigned SizeT
Definition: JPS.h:248
unsigned PosType
Definition: JPS.h:236
Internal::PodVec< Position > PathVector
Definition: JPS.h:1456
int ScoreType
Definition: JPS.h:242
SizeT findPath(PV &path, const GRID &grid, PosType startx, PosType starty, PosType endx, PosType endy, unsigned step=0, JPS_Flags flags=JPS_Flag_Default, void *user=0)
Definition: JPS.h:1479
Definition: JPS.h:378
void setClosed()
Definition: JPS.h:386
Position pos
Definition: JPS.h:380
void setParent(const Node &p)
Definition: JPS.h:394
const Node * getParentOpt() const
Definition: JPS.h:393
int parentOffs
Definition: JPS.h:381
unsigned isClosed() const
Definition: JPS.h:388
Node & getParent()
Definition: JPS.h:391
const Node & getParent() const
Definition: JPS.h:392
int hasParent() const
Definition: JPS.h:384
ScoreType f
Definition: JPS.h:379
unsigned _flags
Definition: JPS.h:382
unsigned isOpen() const
Definition: JPS.h:387
void setOpen()
Definition: JPS.h:385
ScoreType g
Definition: JPS.h:379
Definition: JPS.h:310
bool operator==(const Position &p) const
Definition: JPS.h:313
PosType y
Definition: JPS.h:311
bool isValid() const
Definition: JPS.h:322
bool operator!=(const Position &p) const
Definition: JPS.h:317
PosType x
Definition: JPS.h:311
Definition: JPS.h:301
Definition: Position.h:8
int y
Definition: Position.h:13
int x
Definition: Position.h:10