7
7
#include " GridCellStates.hpp"
8
8
#include < vector>
9
9
#include < unordered_set>
10
+ #include < algorithm>
10
11
11
12
using namespace std ;
12
13
using namespace sf ;
@@ -30,25 +31,37 @@ class PathFinder
30
31
{
31
32
return gCost + hCost;
32
33
}
33
- };
34
34
35
- private:
36
- // private instance variables
37
- Grid<GridNode> *grid;
38
- int outlineThickness;
35
+ // override equals operator
36
+ bool operator == (const GridNode& other) const
37
+ {
38
+ return gridPos == other.gridPos ;
39
+ }
39
40
40
- Vector2i* startPos;
41
- Vector2i* endPos;
41
+ // override not equals operator
42
+ bool operator != (const GridNode& other) const
43
+ {
44
+ return gridPos != other.gridPos ;
45
+ }
46
+ };
42
47
43
48
// custom hashing for GridNodes
44
49
struct NodeHash
45
50
{
46
- size_t operator ()(const GridNode& node) const
51
+ size_t operator () (const GridNode* node) const
47
52
{
48
- return node. gCost + node. hCost + (int )node. val ;
53
+ return node-> gCost + node-> hCost + (int )node-> val ;
49
54
}
50
55
};
51
56
57
+ private:
58
+ // private instance variables
59
+ Grid<GridNode> *grid;
60
+ int outlineThickness;
61
+
62
+ Vector2i* startPos;
63
+ Vector2i* endPos;
64
+
52
65
int getDistance (GridNode* node1, GridNode* node2);
53
66
54
67
public:
@@ -66,11 +79,14 @@ class PathFinder
66
79
67
80
bool setValAt (Vector2i pos, GridValue val);
68
81
69
- GridNode* getShortestPath ();
82
+ vector<GridNode *> *getShortestPath ();
83
+
84
+ bool drawShortestPath (RenderWindow* window);
70
85
71
86
private:
72
87
void initializeNodes ();
73
88
89
+ vector<GridNode*> *retracePath (GridNode* startNode, GridNode* endNode);
74
90
};
75
91
76
92
// / <summary>
@@ -272,17 +288,65 @@ bool PathFinder::setValAt(Vector2i pos, GridValue val)
272
288
return setValAt (gridPos.x , gridPos.y , val);
273
289
}
274
290
291
+ // / <summary>
292
+ // / Find the distance between two nodes. Distance is given as a cost
293
+ // / </summary>
294
+ // / <param name="node1">a grid node</param>
295
+ // / <param name="node2">a grid onde</param>
296
+ // / <returns>the distance between two nodes given as a cost</returns>
275
297
int PathFinder::getDistance (GridNode* node1, GridNode* node2)
276
298
{
299
+ // cost of moves
300
+ const int DIAGONAL_COST = 14 ;
301
+ const int NORMAL_COST = 10 ;
302
+
303
+ // distance between the two nodes
277
304
int xDist = abs (node1->gridPos .x - node2->gridPos .x );
278
305
int yDist = abs (node1->gridPos .y - node2->gridPos .y );
279
306
307
+ // find the number of diagonal and normal moves
280
308
int numOfDiagonals = min (xDist, yDist);
281
- int numOfRegMoves = abs (xDist - yDist);
309
+ int numOfNormMoves = abs (xDist - yDist);
310
+
311
+ return DIAGONAL_COST * numOfDiagonals + NORMAL_COST * numOfNormMoves;
312
+ }
313
+
314
+ // / <summary>
315
+ // / Retrace the path from the end node to the start node
316
+ // / </summary>
317
+ // / <param name="endNode">the end node in the grid</param>
318
+ // / <param name="startNode">the starting node in the grid</param>
319
+ // / <returns>the path from the end node to the start node as a vector of nodes
320
+ // / starting with the starting node</returns>
321
+ vector<PathFinder::GridNode*> *PathFinder::retracePath (GridNode *startNode, GridNode *endNode)
322
+ {
323
+ vector<GridNode*>* path = new vector<GridNode*>();
324
+ GridNode* currNode = endNode;
325
+ // traverse the path backwards starting from the end node
326
+ // and add every node in the path to the path vector
327
+ while (currNode != startNode)
328
+ {
329
+ path->push_back (currNode);
330
+ currNode = currNode->parentNode ;
331
+ }
332
+ // reverse the path vector
333
+ reverse (path->begin (), path->end ());
334
+
335
+ return path;
282
336
}
283
337
284
- PathFinder::GridNode* PathFinder::getShortestPath ()
338
+ // / <summary>
339
+ // / Get the shortest path from the start and end positions
340
+ // / </summary>
341
+ // / <returns>the shortest path from the start and end positions if
342
+ // / there are start and end positions, otherwise return NULL</returns>
343
+ vector<PathFinder::GridNode *> *PathFinder::getShortestPath ()
285
344
{
345
+ if (startPos == NULL || endPos == NULL )
346
+ {
347
+ return NULL ;
348
+ }
349
+
286
350
GridNode* startNode = grid->getValueAt (startPos->x , startPos->y );
287
351
GridNode* endNode = grid->getValueAt (endPos->x , endPos->y );
288
352
@@ -298,12 +362,12 @@ PathFinder::GridNode* PathFinder::getShortestPath()
298
362
int pos = 0 ;
299
363
for (int i = 1 ; i < openList.size (); i++)
300
364
{
301
- GridNode* currNode = openList[i];
302
- if (currNode ->fCost () < lowestCostNode->fCost ()
303
- || (currNode ->fCost () == lowestCostNode->fCost ()
304
- && currNode ->hCost <= lowestCostNode->hCost ))
365
+ GridNode* currOpenNode = openList[i];
366
+ if (currOpenNode ->fCost () < lowestCostNode->fCost ()
367
+ || (currOpenNode ->fCost () == lowestCostNode->fCost ()
368
+ && currOpenNode ->hCost <= lowestCostNode->hCost ))
305
369
{
306
- lowestCostNode = currNode ;
370
+ lowestCostNode = currOpenNode ;
307
371
pos = i;
308
372
}
309
373
}
@@ -314,26 +378,83 @@ PathFinder::GridNode* PathFinder::getShortestPath()
314
378
// add lowest cost node to closed set
315
379
closedSet.insert (lowestCostNode);
316
380
381
+ // check to see if lowestCostNode is the end node
382
+ if (lowestCostNode == endNode)
383
+ {
384
+ return retracePath (startNode, endNode);
385
+ }
386
+
317
387
Vector2i lowestCostPos = lowestCostNode->gridPos ;
318
388
vector<GridNode*>* neighbours = grid->getNeighbours (lowestCostPos.x , lowestCostPos.y );
319
389
for (int i = 0 ; i < neighbours->size (); i++)
320
390
{
321
- GridNode* currNode = (*neighbours)[i];
391
+ GridNode* currNeighbour = (*neighbours)[i];
322
392
323
- if (currNode ->val == GridValue::OCCUPIED
324
- || closedSet.find (currNode ) != closedSet.end ())
393
+ if (currNeighbour ->val == GridValue::OCCUPIED
394
+ || closedSet.find (currNeighbour ) != closedSet.end ())
325
395
{
326
396
// go to next neighbour if this node is either occupied
327
397
// or is in the closed set
328
398
continue ;
329
399
}
330
400
331
-
401
+ int newMovementCostToNeighbour =
402
+ lowestCostNode->gCost + getDistance (lowestCostNode, currNeighbour);
403
+ // check to see if the new cost is less than the current cost
404
+ // or if the current neightbour is not in the open list
405
+ bool isInOpenSet = find (openList.begin (), openList.end (), currNeighbour)
406
+ != openList.end ();
407
+ if (newMovementCostToNeighbour < currNeighbour->gCost
408
+ || !isInOpenSet)
409
+ {
410
+ // set g and h costs of neighbour
411
+ currNeighbour->gCost = newMovementCostToNeighbour;
412
+ currNeighbour->hCost = getDistance (currNeighbour, endNode);
413
+ // set parent of neighbour to the lowestCostNode
414
+ currNeighbour->parentNode = lowestCostNode;
415
+ // add neighbour to open list if it is not in it
416
+ if (!isInOpenSet)
417
+ {
418
+ openList.push_back (currNeighbour);
419
+ }
420
+ }
332
421
}
333
422
334
423
}
335
424
}
336
425
426
+ bool PathFinder::drawShortestPath (RenderWindow* window)
427
+ {
428
+ int cellSize = grid->getCellSize ();
429
+
430
+ vector<GridNode*> *path = getShortestPath ();
431
+ if (path == NULL )
432
+ {
433
+ return false ;
434
+ }
435
+
436
+ Color pathColor = Color::Green;
437
+
438
+ // draw the path
439
+ for (int i = 0 ; i < path->size (); i++)
440
+ {
441
+ GridNode *currNode = (*path)[i];
442
+ Vector2i currPos = currNode->gridPos ;
443
+
444
+ RectangleShape square (Vector2f (cellSize, cellSize));
445
+ Vector2f pos = grid->gridToScreen (currPos.x , currPos.y );
446
+ square.setPosition (pos);
447
+ square.setOutlineThickness (outlineThickness);
448
+ square.setFillColor (pathColor);
449
+ square.setOutlineColor (Color::White);
450
+
451
+ window->draw (square);
452
+ }
453
+
454
+ delete (path);
455
+ return true ;
456
+ }
457
+
337
458
338
459
#endif // !PATH_FINDER_H
339
460
0 commit comments