Skip to content

Commit 68fc609

Browse files
implemented the A* pathfinding algorithm
1 parent 14981b4 commit 68fc609

File tree

2 files changed

+149
-21
lines changed

2 files changed

+149
-21
lines changed

Main.cpp

+7
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,13 @@ void playerController()
6767
// place starting cell
6868
pathFinder->setValAt(mousePos, GridValue::START);
6969
}
70+
else if (Keyboard::isKeyPressed(Keyboard::Key::Tab))
71+
{
72+
if (!pathFinder->drawShortestPath(window))
73+
{
74+
cout << "missing start/end" << endl;
75+
}
76+
}
7077
}
7178

7279
int main()

PathFinder.hpp

+142-21
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "GridCellStates.hpp"
88
#include <vector>
99
#include <unordered_set>
10+
#include <algorithm>
1011

1112
using namespace std;
1213
using namespace sf;
@@ -30,25 +31,37 @@ class PathFinder
3031
{
3132
return gCost + hCost;
3233
}
33-
};
3434

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+
}
3940

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+
};
4247

4348
// custom hashing for GridNodes
4449
struct NodeHash
4550
{
46-
size_t operator()(const GridNode& node) const
51+
size_t operator() (const GridNode* node) const
4752
{
48-
return node.gCost + node.hCost + (int)node.val;
53+
return node->gCost + node->hCost + (int)node->val;
4954
}
5055
};
5156

57+
private:
58+
// private instance variables
59+
Grid<GridNode> *grid;
60+
int outlineThickness;
61+
62+
Vector2i* startPos;
63+
Vector2i* endPos;
64+
5265
int getDistance(GridNode* node1, GridNode* node2);
5366

5467
public:
@@ -66,11 +79,14 @@ class PathFinder
6679

6780
bool setValAt(Vector2i pos, GridValue val);
6881

69-
GridNode* getShortestPath();
82+
vector<GridNode *> *getShortestPath();
83+
84+
bool drawShortestPath(RenderWindow* window);
7085

7186
private:
7287
void initializeNodes();
7388

89+
vector<GridNode*> *retracePath(GridNode* startNode, GridNode* endNode);
7490
};
7591

7692
/// <summary>
@@ -272,17 +288,65 @@ bool PathFinder::setValAt(Vector2i pos, GridValue val)
272288
return setValAt(gridPos.x, gridPos.y, val);
273289
}
274290

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>
275297
int PathFinder::getDistance(GridNode* node1, GridNode* node2)
276298
{
299+
// cost of moves
300+
const int DIAGONAL_COST = 14;
301+
const int NORMAL_COST = 10;
302+
303+
// distance between the two nodes
277304
int xDist = abs(node1->gridPos.x - node2->gridPos.x);
278305
int yDist = abs(node1->gridPos.y - node2->gridPos.y);
279306

307+
// find the number of diagonal and normal moves
280308
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;
282336
}
283337

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()
285344
{
345+
if (startPos == NULL || endPos == NULL)
346+
{
347+
return NULL;
348+
}
349+
286350
GridNode* startNode = grid->getValueAt(startPos->x, startPos->y);
287351
GridNode* endNode = grid->getValueAt(endPos->x, endPos->y);
288352

@@ -298,12 +362,12 @@ PathFinder::GridNode* PathFinder::getShortestPath()
298362
int pos = 0;
299363
for (int i = 1; i < openList.size(); i++)
300364
{
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))
305369
{
306-
lowestCostNode = currNode;
370+
lowestCostNode = currOpenNode;
307371
pos = i;
308372
}
309373
}
@@ -314,26 +378,83 @@ PathFinder::GridNode* PathFinder::getShortestPath()
314378
// add lowest cost node to closed set
315379
closedSet.insert(lowestCostNode);
316380

381+
// check to see if lowestCostNode is the end node
382+
if (lowestCostNode == endNode)
383+
{
384+
return retracePath(startNode, endNode);
385+
}
386+
317387
Vector2i lowestCostPos = lowestCostNode->gridPos;
318388
vector<GridNode*>* neighbours = grid->getNeighbours(lowestCostPos.x, lowestCostPos.y);
319389
for (int i = 0; i < neighbours->size(); i++)
320390
{
321-
GridNode* currNode = (*neighbours)[i];
391+
GridNode* currNeighbour = (*neighbours)[i];
322392

323-
if (currNode->val == GridValue::OCCUPIED
324-
|| closedSet.find(currNode) != closedSet.end())
393+
if (currNeighbour->val == GridValue::OCCUPIED
394+
|| closedSet.find(currNeighbour) != closedSet.end())
325395
{
326396
// go to next neighbour if this node is either occupied
327397
// or is in the closed set
328398
continue;
329399
}
330400

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+
}
332421
}
333422

334423
}
335424
}
336425

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+
337458

338459
#endif // !PATH_FINDER_H
339460

0 commit comments

Comments
 (0)