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