-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathPathfinding.cpp
273 lines (217 loc) · 6.39 KB
/
Pathfinding.cpp
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
#include <vector>
#include <unordered_map>
#include <queue>
struct GraphNode
{
// Each node points to an adjacent node
std::vector<GraphNode*> mAdjacent;
};
struct Graph
{
// Graph containing nodes
std::vector<GraphNode*> mNodes;
};
struct WeightedEdge
{
// Nodes that ar connected by this edge
struct WeightedGraphNode* mFrom;
struct WeightedGraphNode* mTo;
// Weight of the edge
float mWeight;
};
struct WeightedGraphNode
{
// Vector storing outgoing edges much like the regular
// graph node, except these have a weight
std::vector<WeightedEdge*> mEdges;
};
struct WeightedGraph
{
// weighted graph containing weighted nodes
std::vector<WeightedGraphNode*> mWeightedNodes;
};
// This unordered map contains sets of parent nodes and child nodes
using NodeToParentMap =
std::unordered_map<const GraphNode*, const GraphNode*>;
// Breadth-First Search implementation
bool BFS(const Graph& graph, const GraphNode* start,
const GraphNode* goal, NodeToParentMap& outMap)
{
// bool for identifying a path found
bool pathFound = false;
// Nodes to be considered
std::queue<const GraphNode*> q;
// Emplace first node
q.emplace(start);
// Loop for performing search
while (!q.empty())
{
// Deque a node
const GraphNode* current = q.front();
q.pop();
if (current == goal)
{
pathFound = true;
break;
}
// Enque adjacent nodes that aren't already in the queue
for (const GraphNode* node : current->mAdjacent)
{
// if the parent is null, it hasn't been enqueued
// except for the start node
const GraphNode* parent = outMap[node];
if (parent == nullptr && node != start)
{
// Enqueue this node, setting its parent
outMap[node] = current;
q.emplace(node);
}
}
}
return pathFound;
};
// Function for computing the heuristic
float ComputeHeuristic(const WeightedGraphNode* a, const WeightedGraphNode* b)
{
// fill this in later
// likely use manhattan or euclidean distances.
// Will need to get positional information from nodes
return 0.0f;
}
// GBFS scractch data struct
/*
* mParentEdge is a weighted edge poiter that points to the parent
* mHeuristic is a floating point number that represents the heuristic
* mInOpenSet identifies the node as being in the open set or not
* mInClosedSet identifies the node as being in the closed set or not
*/
struct GBFSScratch
{
const WeightedEdge* mParentEdge = nullptr;
float mHeuristic = 0.0f;
bool mInOpenSet = false;
bool mInClosedSet = false;
};
// Like in BFS we define a map containing pointers to nodes, but
// this time the value is an instance of GBFSScratch
using GBFSMap =
std::unordered_map<const WeightedGraphNode*, GBFSScratch>;
// Greedy Breadth-First Search implementation
bool GBFS(const WeightedGraph& g, const WeightedGraphNode* start,
const WeightedGraphNode* goal, GBFSMap& outMap)
{
// start by creating a vector for the open set
std::vector<const WeightedGraphNode*> openSet;
// set current node to the start node and add to the closed set
const WeightedGraphNode* current = start;
outMap[current].mInClosedSet = true;
// main loop of GBFS
do
{
// adding adjacent nodes to the open set
for (const WeightedEdge* edge : current->mEdges)
{
// get scratch data for this node
GBFSScratch& data = outMap[edge->mTo];
// Add only if it's not in the closed set
if (!data.mInClosedSet)
{
// Set the adjacent node's parent edge
data.mParentEdge = edge;
if (!data.mInOpenSet)
{
// compute heuristic for this node and
// place in open set
data.mHeuristic = ComputeHeuristic(edge->mTo, goal);
data.mInOpenSet = true;
openSet.emplace_back(edge->mTo);
}
}
}
if (openSet.empty())
{
break; // there is no path from start to goal
}
// Find the lowest cost node in the open set
auto iter = std::min_element(openSet.begin(), openSet.end(),
[&outMap](const WeightedGraphNode* a, const WeightedGraphNode* b)
{
return outMap[a].mHeuristic < outMap[b].mHeuristic;
});
// Set to current and moved from open to closed
current = *iter;
openSet.erase(iter);
outMap[current].mInOpenSet = false;
outMap[current].mInClosedSet = true;
} while (current != goal);
// return booling for path found
return (current == goal) ? true : false;
};
// Scratch data for the A* pathfinding algorithm
struct AStarScratch
{
const WeightedEdge* mParentEdge = nullptr;
float mHeuristic = 0.0f;
float mActualFromStart = 0.0f;
bool mInOpenSet = false;
bool mInClosedSet = false;
};
using AStarMap =
std::unordered_map<const WeightedGraphNode*, AStarScratch>;
// A* Search implementation
bool AStarSearch(const WeightedGraph& g, const WeightedGraphNode* start,
const WeightedGraphNode* goal, AStarMap& outMap)
{
// start by creating a vector for the open set
std::vector<const WeightedGraphNode*> openSet;
// set current node to the start node and add to the closed set
const WeightedGraphNode* current = start;
outMap[current].mInClosedSet = true;
// main loop of GBFS
do
{
// adding adjacent nodes to the open set
for (const WeightedEdge* edge : current->mEdges)
{
const WeightedGraphNode* neighbor = edge->mTo;
// get scratch data for this node
AStarScratch& data = outMap[neighbor];
// Add only if it's not in the closed set
if (!data.mInClosedSet)
{
if (!data.mInOpenSet)
{
// Not in the open set, so parent is current
data.mParentEdge = edge;
// compute heuristic for this node and
// place in open set
data.mHeuristic = ComputeHeuristic(neighbor, goal);
// Actual cost is the parent's plus cost of traversing edge
data.mActualFromStart = outMap[current].mActualFromStart +
edge->mWeight;
data.mInOpenSet = true;
openSet.emplace_back(neighbor);
}
}
}
if (openSet.empty())
{
break; // there is no path from start to goal
}
// Find the lowest cost node in the open set
auto iter = std::min_element(openSet.begin(), openSet.end(),
[&outMap](const WeightedGraphNode* a, const WeightedGraphNode* b)
{
float fOfA = outMap[a].mHeuristic + outMap[a].mActualFromStart;
float fOfB = outMap[b].mHeuristic + outMap[b].mActualFromStart;
return fOfA < fOfB;
});
// Set to current and moved from open to closed
current = *iter;
openSet.erase(iter);
outMap[current].mInOpenSet = false;
outMap[current].mInClosedSet = true;
} while (current != goal);
// return booling for path found
return (current == goal) ? true : false;
};