1 /*
2 Copyright (c) 2012, Sean Heber. All rights reserved.
3 Copyright (c) 2013, Cong Xu. All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
7
8 1. Redistributions of source code must retain the above copyright
9 notice, this list of conditions and the following disclaimer.
10
11 2. Redistributions in binary form must reproduce the above copyright notice,
12 this list of conditions and the following disclaimer in the documentation
13 and/or other materials provided with the distribution.
14
15 3. Neither the name of Sean Heber nor the names of its contributors may
16 be used to endorse or promote products derived from this software without
17 specific prior written permission.
18
19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
20 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 DISCLAIMED. IN NO EVENT SHALL SEAN HEBER BE LIABLE FOR ANY DIRECT,
23 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
24 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
27 OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
28 ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31 #include "AStar.h"
32 #include <math.h>
33 #include <stdint.h>
34 #include <string.h>
35
36 #include "sys_specifics.h"
37 #include "utils.h"
38
39 struct __ASNeighborList {
40 const ASPathNodeSource *source;
41 size_t capacity;
42 size_t count;
43 float *costs;
44 void *nodeKeys;
45 };
46
47 struct __ASPath {
48 size_t nodeSize;
49 size_t count;
50 float cost;
51 int8_t nodeKeys[1];
52 };
53
54 typedef struct {
55 unsigned isClosed:1;
56 unsigned isOpen:1;
57 unsigned isGoal:1;
58 unsigned hasParent:1;
59 unsigned hasEstimatedCost:1;
60 float estimatedCost;
61 float cost;
62 size_t openIndex;
63 size_t parentIndex;
64 int8_t nodeKey[1];
65 } NodeRecord;
66
67 struct __VisitedNodes {
68 const ASPathNodeSource *source;
69 void *context;
70 size_t nodeRecordsCapacity;
71 size_t nodeRecordsCount;
72 void *nodeRecords;
73 size_t *nodeRecordsIndex; // array of nodeRecords indexes, kept sorted by nodeRecords[i]->nodeKey using source->nodeComparator
74 size_t openNodesCapacity;
75 size_t openNodesCount;
76 size_t *openNodes; // binary heap of nodeRecords indexes, sorted by the nodeRecords[i]->rank
77 };
78 typedef struct __VisitedNodes *VisitedNodes;
79
80 typedef struct {
81 VisitedNodes nodes;
82 int index;
83 } Node;
84
85 static const Node NodeNull = {NULL, -1};
86
87 /********************************************/
88
VisitedNodesCreate(const ASPathNodeSource * source,void * context)89 static VisitedNodes VisitedNodesCreate(const ASPathNodeSource *source, void *context)
90 {
91 VisitedNodes nodes;
92 CCALLOC(nodes, sizeof(struct __VisitedNodes));
93 nodes->source = source;
94 nodes->context = context;
95 return nodes;
96 }
97
VisitedNodesDestroy(VisitedNodes visitedNodes)98 static void VisitedNodesDestroy(VisitedNodes visitedNodes)
99 {
100 CFREE(visitedNodes->nodeRecordsIndex);
101 CFREE(visitedNodes->nodeRecords);
102 CFREE(visitedNodes->openNodes);
103 CFREE(visitedNodes);
104 }
105
NodeIsNull(Node n)106 static int NodeIsNull(Node n)
107 {
108 return (n.nodes == NodeNull.nodes) && (n.index == NodeNull.index);
109 }
110
NodeMake(VisitedNodes nodes,size_t idx)111 static Node NodeMake(VisitedNodes nodes, size_t idx)
112 {
113 Node n;
114 memset(&n, 0, sizeof n);
115 n.nodes = nodes;
116 n.index = (int)idx;
117 return n;
118 }
119
NodeGetRecord(Node node)120 static NodeRecord *NodeGetRecord(Node node)
121 {
122 return (NodeRecord *)((char *)node.nodes->nodeRecords + (node.index * (node.nodes->source->nodeSize + sizeof(NodeRecord))));
123 }
124
GetNodeKey(Node node)125 static void *GetNodeKey(Node node)
126 {
127 return NodeGetRecord(node)->nodeKey;
128 }
129
NodeIsInOpenSet(Node n)130 static int NodeIsInOpenSet(Node n)
131 {
132 return NodeGetRecord(n)->isOpen;
133 }
134
NodeIsInClosedSet(Node n)135 static int NodeIsInClosedSet(Node n)
136 {
137 return NodeGetRecord(n)->isClosed;
138 }
139
RemoveNodeFromClosedSet(Node n)140 static void RemoveNodeFromClosedSet(Node n)
141 {
142 NodeGetRecord(n)->isClosed = 0;
143 }
144
AddNodeToClosedSet(Node n)145 static void AddNodeToClosedSet(Node n)
146 {
147 NodeGetRecord(n)->isClosed = 1;
148 }
149
GetNodeRank(Node n)150 static float GetNodeRank(Node n)
151 {
152 NodeRecord *record = NodeGetRecord(n);
153 return record->estimatedCost + record->cost;
154 }
155
GetNodeCost(Node n)156 static float GetNodeCost(Node n)
157 {
158 return NodeGetRecord(n)->cost;
159 }
160
SetNodeEstimatedCost(Node n,float estimatedCost)161 static void SetNodeEstimatedCost(Node n, float estimatedCost)
162 {
163 NodeRecord *record = NodeGetRecord(n);
164 record->estimatedCost = estimatedCost;
165 record->hasEstimatedCost = 1;
166 }
167
NodeHasEstimatedCost(Node n)168 static int NodeHasEstimatedCost(Node n)
169 {
170 return NodeGetRecord(n)->hasEstimatedCost;
171 }
172
SetNodeIsGoal(Node n)173 static void SetNodeIsGoal(Node n)
174 {
175 if (!NodeIsNull(n)) {
176 NodeGetRecord(n)->isGoal = 1;
177 }
178 }
179
NodeIsGoal(Node n)180 static int NodeIsGoal(Node n)
181 {
182 return !NodeIsNull(n) && NodeGetRecord(n)->isGoal;
183 }
184
GetParentNode(Node n)185 static Node GetParentNode(Node n)
186 {
187 NodeRecord *record = NodeGetRecord(n);
188 if (record->hasParent) {
189 return NodeMake(n.nodes, record->parentIndex);
190 } else {
191 return NodeNull;
192 }
193 }
194
NodeRankCompare(Node n1,Node n2)195 static int NodeRankCompare(Node n1, Node n2)
196 {
197 const float rank1 = GetNodeRank(n1);
198 const float rank2 = GetNodeRank(n2);
199 if (rank1 < rank2) {
200 return -1;
201 } else if (rank1 > rank2) {
202 return 1;
203 } else {
204 return 0;
205 }
206 }
207
GetPathCostHeuristic(Node a,Node b)208 static float GetPathCostHeuristic(Node a, Node b)
209 {
210 if (a.nodes->source->pathCostHeuristic && !NodeIsNull(a) && !NodeIsNull(b)) {
211 return a.nodes->source->pathCostHeuristic(GetNodeKey(a), GetNodeKey(b), a.nodes->context);
212 } else {
213 return 0;
214 }
215 }
216
NodeKeyCompare(Node node,void * nodeKey)217 static int NodeKeyCompare(Node node, void *nodeKey)
218 {
219 if (node.nodes->source->nodeComparator) {
220 return node.nodes->source->nodeComparator(GetNodeKey(node), nodeKey, node.nodes->context);
221 } else {
222 return memcmp(GetNodeKey(node), nodeKey, node.nodes->source->nodeSize);
223 }
224 }
225
GetNode(VisitedNodes nodes,void * nodeKey)226 static Node GetNode(VisitedNodes nodes, void *nodeKey)
227 {
228 size_t first;
229 Node node;
230 NodeRecord *record;
231 if (!nodeKey) {
232 return NodeNull;
233 }
234
235 // looks it up in the index, if it's not found it inserts a new record in the sorted index and the nodeRecords array and returns a reference to it
236 first = 0;
237
238 if (nodes->nodeRecordsCount > 0) {
239 size_t last = nodes->nodeRecordsCount-1;
240
241 while (first <= last) {
242 const size_t mid = (first + last) / 2;
243 const int comp = NodeKeyCompare(NodeMake(nodes, nodes->nodeRecordsIndex[mid]), nodeKey);
244
245 if (comp < 0) {
246 first = mid + 1;
247 } else if (comp > 0 && mid > 0) {
248 last = mid - 1;
249 } else if (comp > 0) {
250 break;
251 } else {
252 return NodeMake(nodes, nodes->nodeRecordsIndex[mid]);
253 }
254 }
255 }
256
257 if (nodes->nodeRecordsCount == nodes->nodeRecordsCapacity) {
258 nodes->nodeRecordsCapacity = 1 + (nodes->nodeRecordsCapacity * 2);
259 CREALLOC(nodes->nodeRecords, nodes->nodeRecordsCapacity * (sizeof(NodeRecord) + nodes->source->nodeSize));
260 CREALLOC(nodes->nodeRecordsIndex, nodes->nodeRecordsCapacity * sizeof(size_t));
261 }
262
263 node = NodeMake(nodes, nodes->nodeRecordsCount);
264 nodes->nodeRecordsCount++;
265
266 memmove(&nodes->nodeRecordsIndex[first+1], &nodes->nodeRecordsIndex[first], (nodes->nodeRecordsCapacity - first - 1) * sizeof(size_t));
267 nodes->nodeRecordsIndex[first] = node.index;
268
269 record = NodeGetRecord(node);
270 memset(record, 0, sizeof(NodeRecord));
271 memcpy(record->nodeKey, nodeKey, nodes->source->nodeSize);
272
273 return node;
274 }
275
SwapOpenSetNodesAtIndexes(VisitedNodes nodes,size_t index1,size_t index2)276 static void SwapOpenSetNodesAtIndexes(VisitedNodes nodes, size_t index1, size_t index2)
277 {
278 if (index1 != index2) {
279 NodeRecord *record1 = NodeGetRecord(NodeMake(nodes, nodes->openNodes[index1]));
280 NodeRecord *record2 = NodeGetRecord(NodeMake(nodes, nodes->openNodes[index2]));
281
282 const size_t tempOpenIndex = record1->openIndex;
283 const size_t tempNodeIndex = nodes->openNodes[index1];
284 record1->openIndex = record2->openIndex;
285 record2->openIndex = tempOpenIndex;
286
287 nodes->openNodes[index1] = nodes->openNodes[index2];
288 nodes->openNodes[index2] = tempNodeIndex;
289 }
290 }
291
DidRemoveFromOpenSetAtIndex(VisitedNodes nodes,size_t idx)292 static void DidRemoveFromOpenSetAtIndex(
293 VisitedNodes nodes, size_t idx)
294 {
295 size_t smallestIndex = idx;
296
297 do {
298 size_t leftIndex;
299 size_t rightIndex;
300 if (smallestIndex != idx)
301 {
302 SwapOpenSetNodesAtIndexes(nodes, smallestIndex, idx);
303 idx = smallestIndex;
304 }
305
306 leftIndex = (2 * idx) + 1;
307 rightIndex = (2 * idx) + 2;
308
309 if (leftIndex < nodes->openNodesCount && NodeRankCompare(NodeMake(nodes, nodes->openNodes[leftIndex]), NodeMake(nodes, nodes->openNodes[smallestIndex])) < 0) {
310 smallestIndex = leftIndex;
311 }
312
313 if (rightIndex < nodes->openNodesCount && NodeRankCompare(NodeMake(nodes, nodes->openNodes[rightIndex]), NodeMake(nodes, nodes->openNodes[smallestIndex])) < 0) {
314 smallestIndex = rightIndex;
315 }
316 } while (smallestIndex != idx);
317 }
318
RemoveNodeFromOpenSet(Node n)319 static void RemoveNodeFromOpenSet(Node n)
320 {
321 NodeRecord *record = NodeGetRecord(n);
322
323 if (record->isOpen)
324 {
325 const size_t idx = record->openIndex;
326 record->isOpen = 0;
327 n.nodes->openNodesCount--;
328
329 SwapOpenSetNodesAtIndexes(n.nodes, idx, n.nodes->openNodesCount);
330 DidRemoveFromOpenSetAtIndex(n.nodes, idx);
331 }
332 }
333
DidInsertIntoOpenSetAtIndex(VisitedNodes nodes,size_t idx)334 static void DidInsertIntoOpenSetAtIndex(VisitedNodes nodes, size_t idx)
335 {
336 while (idx > 0)
337 {
338 const size_t parentIndex = (size_t)floorf((float)(idx-1) / 2);
339
340 if (NodeRankCompare(NodeMake(nodes, nodes->openNodes[parentIndex]), NodeMake(nodes, nodes->openNodes[idx])) < 0)
341 {
342 break;
343 }
344 else
345 {
346 SwapOpenSetNodesAtIndexes(nodes, parentIndex, idx);
347 idx = parentIndex;
348 }
349 }
350 }
351
AddNodeToOpenSet(Node n,float cost,Node parent)352 static void AddNodeToOpenSet(Node n, float cost, Node parent)
353 {
354 NodeRecord *record = NodeGetRecord(n);
355 const size_t openIndex = n.nodes->openNodesCount;
356
357 if (!NodeIsNull(parent)) {
358 record->hasParent = 1;
359 record->parentIndex = parent.index;
360 } else {
361 record->hasParent = 0;
362 }
363
364 if (n.nodes->openNodesCount == n.nodes->openNodesCapacity) {
365 n.nodes->openNodesCapacity = 1 + (n.nodes->openNodesCapacity * 2);
366 CREALLOC(n.nodes->openNodes, n.nodes->openNodesCapacity * sizeof(size_t));
367 }
368
369 n.nodes->openNodes[openIndex] = n.index;
370 n.nodes->openNodesCount++;
371
372 record->openIndex = openIndex;
373 record->isOpen = 1;
374 record->cost = cost;
375
376 DidInsertIntoOpenSetAtIndex(n.nodes, openIndex);
377 }
378
HasOpenNode(VisitedNodes nodes)379 static int HasOpenNode(VisitedNodes nodes)
380 {
381 return nodes->openNodesCount > 0;
382 }
383
GetOpenNode(VisitedNodes nodes)384 static Node GetOpenNode(VisitedNodes nodes)
385 {
386 return NodeMake(nodes, nodes->openNodes[0]);
387 }
388
NeighborListCreate(const ASPathNodeSource * source)389 static ASNeighborList NeighborListCreate(const ASPathNodeSource *source)
390 {
391 ASNeighborList list;
392 CCALLOC(list, sizeof(struct __ASNeighborList));
393 list->source = source;
394 return list;
395 }
396
NeighborListDestroy(ASNeighborList list)397 static void NeighborListDestroy(ASNeighborList list)
398 {
399 CFREE(list->costs);
400 CFREE(list->nodeKeys);
401 CFREE(list);
402 }
403
NeighborListGetEdgeCost(ASNeighborList list,size_t idx)404 static float NeighborListGetEdgeCost(ASNeighborList list, size_t idx)
405 {
406 return list->costs[idx];
407 }
408
NeighborListGetNodeKey(ASNeighborList list,size_t idx)409 static void *NeighborListGetNodeKey(ASNeighborList list, size_t idx)
410 {
411 return (char *)list->nodeKeys + (idx * list->source->nodeSize);
412 }
413
414 /********************************************/
415
ASNeighborListAdd(ASNeighborList list,void * node,float edgeCost)416 void ASNeighborListAdd(ASNeighborList list, void *node, float edgeCost)
417 {
418 if (list->count == list->capacity) {
419 list->capacity = 1 + (list->capacity * 2);
420 CREALLOC(list->costs, sizeof(float) * list->capacity);
421 CREALLOC(list->nodeKeys, list->source->nodeSize * list->capacity);
422 }
423 list->costs[list->count] = edgeCost;
424 memcpy((char *)list->nodeKeys + (list->count * list->source->nodeSize), node, list->source->nodeSize);
425 list->count++;
426 }
427
ASPathCreate(const ASPathNodeSource * source,void * context,void * startNodeKey,void * goalNodeKey)428 ASPath ASPathCreate(const ASPathNodeSource *source, void *context, void *startNodeKey, void *goalNodeKey)
429 {
430 VisitedNodes visitedNodes;
431 ASNeighborList neighborList;
432 Node current;
433 Node goalNode;
434 ASPath path = NULL;
435 if (!startNodeKey || !source || !source->nodeNeighbors || source->nodeSize == 0) {
436 return NULL;
437 }
438
439 visitedNodes = VisitedNodesCreate(source, context);
440 neighborList = NeighborListCreate(source);
441 current = GetNode(visitedNodes, startNodeKey);
442 goalNode = GetNode(visitedNodes, goalNodeKey);
443
444 // mark the goal node as the goal
445 SetNodeIsGoal(goalNode);
446
447 // set the starting node's estimate cost to the goal and add it to the open set
448 SetNodeEstimatedCost(current, GetPathCostHeuristic(current, goalNode));
449 AddNodeToOpenSet(current, 0, NodeNull);
450
451 // perform the A* algorithm
452 while (HasOpenNode(visitedNodes) && !NodeIsGoal((current = GetOpenNode(visitedNodes)))) {
453 size_t n;
454 if (source->earlyExit) {
455 const int shouldExit = source->earlyExit(visitedNodes->nodeRecordsCount, GetNodeKey(current), goalNodeKey, context);
456
457 if (shouldExit > 0) {
458 SetNodeIsGoal(current);
459 break;
460 } else if (shouldExit < 0) {
461 break;
462 }
463 }
464
465 RemoveNodeFromOpenSet(current);
466 AddNodeToClosedSet(current);
467
468 neighborList->count = 0;
469 source->nodeNeighbors(neighborList, GetNodeKey(current), context);
470
471 for (n=0; n<neighborList->count; n++) {
472 const float cost = GetNodeCost(current) + NeighborListGetEdgeCost(neighborList, n);
473 Node neighbor = GetNode(visitedNodes, NeighborListGetNodeKey(neighborList, n));
474
475 if (!NodeHasEstimatedCost(neighbor)) {
476 SetNodeEstimatedCost(neighbor, GetPathCostHeuristic(neighbor, goalNode));
477 }
478
479 if (NodeIsInOpenSet(neighbor) && cost < GetNodeCost(neighbor)) {
480 RemoveNodeFromOpenSet(neighbor);
481 }
482
483 if (NodeIsInClosedSet(neighbor) && cost < GetNodeCost(neighbor)) {
484 RemoveNodeFromClosedSet(neighbor);
485 }
486
487 if (!NodeIsInOpenSet(neighbor) && !NodeIsInClosedSet(neighbor)) {
488 AddNodeToOpenSet(neighbor, cost, current);
489 }
490 }
491 }
492
493 if (NodeIsNull(goalNode)) {
494 SetNodeIsGoal(current);
495 }
496
497 if (NodeIsGoal(current)) {
498 size_t count = 0;
499 Node n = current;
500 size_t i;
501
502 while (!NodeIsNull(n)) {
503 count++;
504 n = GetParentNode(n);
505 }
506
507 CMALLOC(path, sizeof(struct __ASPath) + (count * source->nodeSize));
508 path->nodeSize = source->nodeSize;
509 path->count = count;
510 path->cost = GetNodeCost(current);
511
512 n = current;
513 for (i=count; i>0; i--) {
514 memcpy(path->nodeKeys + ((i - 1) * source->nodeSize), GetNodeKey(n), source->nodeSize);
515 n = GetParentNode(n);
516 }
517 }
518
519 NeighborListDestroy(neighborList);
520 VisitedNodesDestroy(visitedNodes);
521
522 return path;
523 }
524
ASPathDestroy(ASPath path)525 void ASPathDestroy(ASPath path)
526 {
527 CFREE(path);
528 }
529
ASPathCopy(ASPath path)530 ASPath ASPathCopy(ASPath path)
531 {
532 if (path) {
533 const size_t size = sizeof(struct __ASPath) + (path->count * path->nodeSize);
534 ASPath newPath;
535 CMALLOC(newPath, size);
536 memcpy(newPath, path, size);
537 return newPath;
538 } else {
539 return NULL;
540 }
541 }
542
ASPathGetCount(ASPath path)543 size_t ASPathGetCount(ASPath path)
544 {
545 return path? path->count : 0;
546 }
547
ASPathGetNode(ASPath path,size_t idx)548 void *ASPathGetNode(ASPath path, size_t idx)
549 {
550 return (path && idx < path->count)? (path->nodeKeys + (idx * path->nodeSize)) : NULL;
551 }
552