ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/include/micropather.h
Revision: 1.1
Committed: Thu May 24 00:24:11 2007 UTC (17 years ago) by root
Content type: text/plain
Branch: MAIN
CVS Tags: rel-2_1
Log Message:
I like micropather so much without ever having used it that I included it.

This is not to be used directly, it should be integrated into the map management code.

File Contents

# Content
1 /*
2 Copyright (c) 2000-2003 Lee Thomason (www.grinninglizard.com)
3 Grinning Lizard Utilities.
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any
7 damages arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any
10 purpose, including commercial applications, and to alter it and
11 redistribute it freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must
14 not claim that you wrote the original software. If you use this
15 software in a product, an acknowledgment in the product documentation
16 would be appreciated but is not required.
17
18 2. Altered source versions must be plainly marked as such, and
19 must not be misrepresented as being the original software.
20
21 3. This notice may not be removed or altered from any source
22 distribution.
23 */
24
25
26 #ifndef GRINNINGLIZARD_MICROPATHER_INCLUDED
27 #define GRINNINGLIZARD_MICROPATHER_INCLUDED
28
29 /** @mainpage MicroPather
30
31 MicroPather is a path finder and A* solver (astar or a-star) written in platform independent
32 C++ that can be easily integrated into existing code. MicroPather focuses on being a path
33 finding engine for video games but is a generic A* solver. MicroPather is open source, with
34 a license suitable for open source or commercial use.
35
36 An overview of using MicroPather is in the <A HREF="../readme.htm">readme</A> or
37 on the Grinning Lizard website: http://grinninglizard.com/micropather/
38 */
39
40 #include <vector>
41 #include <list>
42 #include <assert.h>
43 #include <float.h>
44
45 #ifdef _DEBUG
46 #ifndef DEBUG
47 #define DEBUG
48 #endif
49 #endif
50
51 #ifndef GRINLIZ_TYPES_INCLUDED
52 #if defined(_MSC_VER) && (_MSC_VER >= 1400 )
53 #include <stdlib.h>
54 typedef uintptr_t UPTR;
55 #elif defined (__GNUC__) && (__GNUC__ >= 3 )
56 #include <stdlib.h>
57 typedef uintptr_t UPTR;
58 #else
59 // Assume not 64 bit pointers. Get a new compiler.
60 typedef unsigned UPTR;
61 #endif
62 #endif
63
64 /* USE_LIST and USE_BINARY_HASH change the some of details the pather algorithms. They
65 are set optimally in my experiments, but different applications may benefit
66 from a different combination of these #defines.
67 */
68 #define USE_LIST
69 //#define USE_BINARY_HASH
70
71 namespace micropather
72 {
73 const float FLT_BIG = FLT_MAX / 2.0f;
74
75 /**
76 Used to pass the cost of states from the cliet application to MicroPather. This
77 structure is copied in a vector.
78
79 @sa AdjacentCost
80 */
81 struct StateCost
82 {
83 void* state; ///< The state as a void*
84 float cost; ///< The cost to the state. Use FLT_MAX for infinite cost.
85 };
86
87
88 /**
89 A pure abstract class used to define a set of callbacks.
90 The client application inherits from
91 this class, and the methods will be called when MicroPather::Solve() is invoked.
92
93 The notion of a "state" is very important. It must have the following properties:
94 - Unique
95 - Unchanging (unless MicroPather::Reset() is called)
96
97 If the client application represents states as objects, then the state is usually
98 just the object cast to a void*. If the client application sees states as numerical
99 values, (x,y) for example, then state is an encoding of these values. MicroPather
100 never interprets or modifies the value of state.
101 */
102 class Graph
103 {
104 public:
105 virtual ~Graph() {}
106
107 /**
108 Return the least possible cost between 2 states. For example, if your pathfinding
109 is based on distance, this is simply the straight distance between 2 points on the
110 map. If you pathfinding is based on minimum time, it is the minimal travel time
111 between 2 points given the best possible terrain.
112 */
113 virtual float LeastCostEstimate( void* stateStart, void* stateEnd ) = 0;
114
115 /**
116 Return the exact cost from the given state to all its neighboring states. This
117 may be called multiple times, or cached by the solver. It *must* return the same
118 exact values for every call to MicroPather::Solve(). It should generally be a simple,
119 fast function with no callbacks into the pather.
120 */
121 virtual void AdjacentCost( void* state, std::vector< micropather::StateCost > *adjacent ) = 0;
122
123 /**
124 This function is only used in DEBUG mode - it dumps output to stdout. Since void*
125 aren't really human readable, normally you print out some concise info (like "(1,2)")
126 without an ending newline.
127
128 @note If you are using other grinning lizard utilities, you should use GLOUTPUT for output.
129 */
130 virtual void PrintStateInfo( void* state ) = 0;
131 };
132
133
134 class PathNode;
135
136 struct NodeCost
137 {
138 PathNode* node;
139 float cost;
140 };
141
142
143 class PathNode
144 {
145 friend class none; // Trashy trick to get rid of compiler warning, because
146 // this class has a private constructor and destructor -
147 // it can never be "new" or created on the stack, only
148 // by special allocators.
149 public:
150 void Init( unsigned _frame,
151 void* _state,
152 float _costFromStart,
153 float _estToGoal,
154 PathNode* _parent )
155 {
156 state = _state;
157 costFromStart = _costFromStart;
158 estToGoal = _estToGoal;
159 totalCost = _costFromStart + _estToGoal;
160 parent = _parent;
161 frame = _frame;
162 numAdjacent = -1;
163 left = 0;
164 #ifdef USE_BINARY_HASH
165 right = 0;
166 #endif
167 #ifdef USE_LIST
168 next = 0;
169 prev = 0;
170 #endif
171 inOpen = 0;
172 inClosed = 0;
173 }
174
175 void Reuse( unsigned _frame, float _costFromStart, float _estToGoal, PathNode* _parent )
176 {
177 costFromStart = _costFromStart;
178 estToGoal = _estToGoal;
179 parent = _parent;
180 frame = _frame;
181
182 inOpen = 0;
183 inClosed = 0;
184 }
185
186 void *state; // the client state
187 float costFromStart; // exact
188 float estToGoal; // estimated
189 float totalCost; // could be a function, but save some math.
190 PathNode* parent; // the parent is used to reconstruct the path
191 unsigned frame; // unique id for this path, so the solver can distinguish
192 // correct from stale values
193
194 enum {
195 /*
196 @enum MAX_CACHE. If you want to optimize "down deep" this is the way
197 to go. MAX_CACHE determines the number of adjacent nodes cached by
198 MicroPather. If your nodes generally have 8 or 3 neighbors (common cases)
199 changing this may increase performance, sometimes dramatically.
200 */
201 MAX_CACHE = 4
202 };
203 // If there are 4 or less adjacent states, they will be cached as *nodes*.
204 NodeCost adjacent[ MAX_CACHE ];
205 int numAdjacent; // -1 is unknown & needs to be queried
206 // 0-4 adjacent is known & in 'adjacent'
207 // 5+ known, but has to be queried from Graph
208
209 PathNode* left; // Binary tree, where the 'state' is what is being compared.
210 // Also used as a "next" pointer for memory layout.
211 #ifdef USE_BINARY_HASH
212 PathNode* right;
213 #endif
214 #ifdef USE_LIST
215 PathNode *next, *prev;
216 #endif
217
218 unsigned inOpen:1;
219 unsigned inClosed:1;
220
221 #ifdef USE_LIST
222 void Unlink() {
223 next->prev = prev;
224 prev->next = next;
225 next = prev = 0;
226 }
227 void AddBefore( PathNode* addThis ) {
228 addThis->next = this;
229 addThis->prev = prev;
230 prev->next = addThis;
231 prev = addThis;
232 }
233 #ifdef DEBUG
234 void CheckList()
235 {
236 assert( totalCost == FLT_MAX );
237 for( PathNode* it = next; it != this; it=it->next ) {
238 assert( it->prev == this || it->totalCost >= it->prev->totalCost );
239 assert( it->totalCost <= it->next->totalCost );
240 }
241 }
242 #endif
243 #endif
244
245 private:
246 PathNode();
247 ~PathNode();
248 PathNode( const PathNode& );
249 void operator=( const PathNode& );
250 };
251
252
253 /**
254 Create a MicroPather object to solve for a best path. Detailed usage notes are
255 on the main page.
256 */
257 class MicroPather
258 {
259 friend class micropather::PathNode;
260
261 public:
262 enum
263 {
264 SOLVED,
265 NO_SOLUTION,
266 START_END_SAME,
267 };
268
269 /**
270 Construct the pather, passing a pointer to the object that implements
271 the Graph callbacks.
272
273 @param graph The "map" that implements the Graph callbacks.
274 @param allocate The block size that the node cache is allocated from. In some
275 cases setting this parameter will improve the perfomance of
276 the pather.
277 - If you have a small map, for example the size of a chessboard, set allocate
278 to be the number of states+1. 65 for a chessboard. This will allow
279 MicroPather to used a fixed amount of memory.
280 - If your map is large, something like 1/4 the number of possible
281 states is good. For example, Lilith3D normally has about 16,000
282 states, so 'allocate' should be about 4000.
283 */
284 MicroPather( Graph* graph, unsigned allocate = 250 );
285 ~MicroPather();
286
287 /**
288 Solve for the path from start to end.
289
290 @param startState Input, the starting state for the path.
291 @param endState Input, the ending state for the path.
292 @param path Output, a vector of states that define the path. Empty if not found.
293 @param totalCost Output, the cost of the path, if found.
294 @return Success or failure, expressed as SOLVED, NO_SOLUTION, or START_END_SAME.
295 */
296 int Solve( void* startState, void* endState, std::vector< void* >* path, float* totalCost );
297
298 /// Should be called whenever the cost between states or the connection between states changes.
299 void Reset();
300
301 /* Deprecated. Too complex for the practical value.
302 @warning It is very easy to "miss" a state that needs to be changed and break the pather.
303
304 As described on the main page, it is possible to change the costs (but not the connections)
305 of states without calling Reset(). Be sure that all effected states are passed to
306 StateCostChange.
307 */
308 //void StateCostChange( void* state );
309
310 /**
311 Return the "checksum" of the last path returned by Solve(). Useful for debugging,
312 and a quick way to see if 2 paths are the same.
313 */
314 UPTR Checksum() { return checksum; }
315
316 void DumpHashTable();
317
318 // Debugging function to return all states that were used
319 // by the last "solve"
320 void StatesInPool( std::vector< void* >* stateVec );
321
322 private:
323 MicroPather( const MicroPather& ); // undefined and unsupported
324 void operator=( const MicroPather ); // undefined and unsupported
325
326 void GoalReached( PathNode* node, void* start, void* end, std::vector< void* > *path );
327 void RecPathNodeWalk( PathNode* node, int* count );
328
329 // Allocates and returns memory for a new, unititialized node.
330 PathNode* AllocatePathNode();
331
332 // Returns a path node that is ready to go.
333 // Strictly speaking, the name is somewhat misleading, as it
334 // may be reused from the cache, but it will be ready for use regardless.
335 PathNode* NewPathNode( void* state, float costFromStart, float estToEnd, PathNode* parent );
336
337 // Finds the node for state, or returns null.
338 // A node that doesn't have a correct "frame" will never be found.
339 PathNode* FindPathNode( void* state, bool findStale = false );
340
341 NodeCost* GetNodeNeighbors( PathNode* node, std::vector< StateCost >* neighbor, std::vector< NodeCost >* neighborNode );
342
343 #ifdef DEBUG
344 void DumpStats();
345 #endif
346
347 enum {
348 #ifdef DEBUG_PATH
349 HASH_SIZE = 16, // In debug mode, stress the allocators more than in release.
350 // The HASH_SIZE must be a power of 2.
351 #else
352 HASH_SIZE = 256,
353 #endif
354 };
355
356 unsigned Hash( void* voidval ) {
357 UPTR val = (UPTR)voidval;
358 return (unsigned)((HASH_SIZE-1) & ( val + (val>>8) + (val>>16) + (val>>24) ));
359 }
360
361 const unsigned ALLOCATE; // how big a block of pathnodes to allocate at once
362 const unsigned BLOCKSIZE; // how many useable pathnodes are in a block
363
364 Graph* graph;
365 PathNode* hashTable[HASH_SIZE]; // a fixed hashtable used to "jumpstart" a binary search
366 PathNode* pathNodeMem; // pointer to root of PathNode blocks
367 unsigned availMem; // # PathNodes available in the current block
368 unsigned pathNodeCount; // the # of PathNodes in use
369 unsigned frame; // incremented with every solve, used to determine
370 // if cached data needs to be refreshed
371 UPTR checksum; // the checksum of the last successful "Solve".
372
373 std::vector< StateCost > neighborVec; // really local vars to Solve, but put here to reduce memory allocation
374 std::vector< NodeCost > neighborNodeVec; // really local vars to Solve, but put here to reduce memory allocation
375 };
376
377 inline PathNode* MicroPather::FindPathNode( void* state, bool findStale )
378 {
379 unsigned key = Hash( state );
380
381 PathNode* root = hashTable[key];
382 #ifdef USE_BINARY_HASH
383 while ( root ) {
384 if ( root->state == state ) {
385 if ( findStale || root->frame == frame )
386 return root;
387 else
388 return 0;
389 }
390 else if ( state > root->state ) {
391 root = root->right;
392 }
393 else {
394 assert( state < root->state );
395 root = root->left;
396 }
397 }
398 #else
399 while( root ) {
400 if ( root->state == state ) {
401 if ( findStale || root->frame == frame )
402 return root;
403 else
404 return 0;
405 }
406 else {
407 root = root->left;
408 }
409 }
410 #endif
411 return 0;
412 }
413 }; // namespace grinliz
414
415 #endif
416