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 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288
|
/*
* Copyright (c) 2000-2003 Lee Thomason (www.grinninglizard.com)
* Grinning Lizard Utilities.
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any
* damages arising from the use of this software.
*
* Permission is granted to anyone to use this software for any
* purpose, including commercial applications, and to alter it and
* redistribute it freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must
* not claim that you wrote the original software. If you use this
* software in a product, an acknowledgment in the product documentation
* would be appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and
* must not be misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source
* distribution.
*/
/*
* Updated and changed by Tournesol (on the TA Spring client).
* May (currenty) only be used to compile KAI (AI for TA Spring).
* Take care when you use it!
*
* This notice may not be removed or altered from any source
*
* As of 12 march, 2007, the following applies instead of the above notice:
* (Tournesol gave permission to change it per e-mail)
*
* "All parts of the code in this file that are made by 'Tournesol' are
* released under GPL licence."
*
* --Tobi Vollebregt
*/
/*
* @mainpage MicroPather
*
* MicroPather is a path finder and A* solver (astar or a-star) written in platform independent
* C++ that can be easily integrated into existing code. MicroPather focuses on being a path
* finding engine for video games but is a generic A* solver. MicroPather is open source, with
* a license suitable for open source or commercial use.
*
* An overview of using MicroPather is in the <A HREF="../readme.htm">readme</A> or
* on the Grinning Lizard website: http://grinninglizard.com/micropather/
*/
#ifndef GRINNINGLIZARD_MICROPATHER_INCLUDED
#define GRINNINGLIZARD_MICROPATHER_INCLUDED
#include <vector>
#include "Defines.h"
#ifdef _DEBUG
#ifndef DEBUG
#define DEBUG
#endif
#endif
#define FLT_BIG (MY_FLT_MAX / 2.0)
struct AIClasses;
/*
* USE_LIST and USE_BINARY_HASH change the some of details the pather algorithms. They
* are set optimally in my experiments, but different applications may benefit
* from a different combination of these #defines.
*/
// #define USE_LIST
// #define USE_BINARY_HASH
namespace NSMicroPather {
/*
* A pure abstract class used to define a set of callbacks.
* The client application inherits from
* this class, and the methods will be called when MicroPather::Solve() is invoked.
*
* The notion of a "state" is very important. It must have the following properties:
* - Unique
* - Unchanging (unless MicroPather::Reset() is called)
*
* If the client application represents states as objects, then the state is usually
* just the object cast to a void*. If the client application sees states as numerical
* values, (x, y) for example, then state is an encoding of these values. MicroPather
* never interprets or modifies the value of state.
*/
class Graph {
public:
// KLOOTNOTE: declaring a pure virtual destructor here results in
// Spring load-time error when AI compiled with gcc and link-time
// error when AI compiled with mingw32
// virtual ~Graph() = 0;
virtual ~Graph() {}
/*
* This function is only used in DEBUG mode - it dumps output to stdout. Since void*
* aren't really human readable, normally you print out some concise info (like "(1,2)")
* without an ending newline.
* @note If you are using other grinning lizard utilities, you should use GLOUTPUT for output.
*/
// virtual void PrintStateInfo(void* state) = 0;
// virtual void PrintData(string s) = 0;
};
class PathNode {
// trashy trick to get rid of compiler warning because this class has a private constructor and destructor
// (it can never be "new" or created on the stack, only by special allocators)
friend class none;
public:
void Init(unsigned _frame, float _costFromStart, PathNode* _parent) {
costFromStart = _costFromStart;
totalCost = _costFromStart;
parent = _parent;
frame = _frame;
#ifdef USE_BINARY_HASH
right = 0;
#endif
#ifdef USE_LIST
next = 0;
prev = 0;
#endif
inOpen = 0;
inClosed = 0;
isEndNode = 0;
}
inline void Reuse(unsigned _frame) {
costFromStart = (FLT_BIG / 2.0f);
parent = 0;
frame = _frame;
inOpen = 0;
inClosed = 0;
}
int myIndex;
float costFromStart; // exact
float totalCost; // could be a function, but save some math.
PathNode* parent; // the parent is used to reconstruct the path
// Binary tree, where the 'state' is what is being compared.
// Also used as a "next" pointer for memory layout.
#ifdef USE_BINARY_HASH
PathNode* right;
#endif
#ifdef USE_LIST
PathNode* next, *prev;
#endif
unsigned inOpen:1;
unsigned inClosed:1;
unsigned isEndNode:1; // this must be cleared by the call that sets it
unsigned frame:16; // this might be set to more that 16
#ifdef USE_LIST
void Unlink() {
next -> prev = prev;
prev -> next = next;
next = prev = 0;
}
void AddBefore(PathNode* addThis) {
addThis -> next = this;
addThis -> prev = prev;
prev -> next = addThis;
prev = addThis;
}
#ifdef DEBUG
void CheckList() {
assert(totalCost == FLT_BIG);
for (PathNode* it = next; it != this; it = it -> next) {
assert(it -> prev == this || it -> totalCost >= it -> prev -> totalCost);
assert(it -> totalCost <= it -> next -> totalCost);
}
}
#endif
#endif
private:
PathNode();
~PathNode();
};
// create a MicroPather object to solve for a best path
class MicroPather {
friend class NSMicroPather::PathNode;
public:
enum {
SOLVED,
NO_SOLUTION,
START_END_SAME,
};
/*
* Construct the pather, passing a pointer to the object that implements the Graph callbacks.
*
* @param graph The "map" that implements the Graph callbacks.
* @param allocate The block size that the node cache is allocated from. In some
* cases setting this parameter will improve the perfomance of the pather.
* - If you have a small map, for example the size of a chessboard, set allocate
* to be the number of states + 1. 65 for a chessboard. This will allow
* MicroPather to used a fixed amount of memory.
* - If your map is large, something like 1/4 the number of possible
* states is good. For example, Lilith3D normally has about 16000
* states, so 'allocate' should be about 4000.
*/
MicroPather(Graph* graph, AIClasses* ai, unsigned allocate);
~MicroPather();
AIClasses* ai;
/*
* Solve for the path from start to end.
*
* @param startState Input, the starting state for the path.
* @param endState Input, the ending state for the path.
* @param path Output, a vector of states that define the path. Empty if not found.
* @param totalCost Output, the cost of the path, if found.
* @return Success or failure, expressed as SOLVED, NO_SOLUTION, or START_END_SAME.
*/
int Solve(void* startState, void* endState, std::vector<void*>* path, float* totalCost);
// Should not be called unless there is danger for frame overflow (16bit atm)
void Reset();
/**
* Return the "checksum" of the last path returned by Solve(). Useful for debugging,
* and a quick way to see if 2 paths are the same.
*/
unsigned Checksum() {
return checksum;
}
// Tournesol's stuff
unsigned int* lockUpCount;
bool* canMoveArray;
float* costArray;
int mapSizeX;
int mapSizeY;
int offsets[8];
int xEndNode, yEndNode;
bool hasStartedARun;
void SetMapData(bool* canMoveArray, float* costArray, int mapSizeX, int mapSizeY);
int FindBestPathToPointOnRadius(void* startNode, void* endNode, std::vector<void*>* path, float* cost, int radius);
int FindBestPathToAnyGivenPoint(void* startNode, std::vector<void*> endNodes, std::vector<void*>* path, float* cost);
private:
void GoalReached(PathNode* node, void* start, void* end, std::vector<void*> *path);
void FixStartEndNode(void** startNode, void** endNode);
float LeastCostEstimateLocal(int nodeStartIndex);
void FixNode(void** Node);
// allocates the node array, don't call more than once
PathNode* AllocatePathNode();
const unsigned ALLOCATE; // how big a block of pathnodes to allocate at once
const unsigned BLOCKSIZE; // how many useable pathnodes are in a block
Graph* graph;
PathNode* pathNodeMem; // pointer to root of PathNode blocks
PathNode* pathNodeMemForFree; // pointer to root of PathNode blocks
PathNode** heapArrayMem; // pointer to root of a PathNode pointer array
unsigned availMem; // # PathNodes available in the current block
unsigned pathNodeCount; // the # of PathNodes in use
unsigned frame; // incremented with every solve, used to determine if cached data needs to be refreshed
unsigned checksum; // the checksum of the last successful "Solve".
};
}
#endif
|