forked from wangchen/Programming-Game-AI-by-Example-src
-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathHandyGraphFunctions.h
340 lines (284 loc) · 10.9 KB
/
HandyGraphFunctions.h
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
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
#ifndef GRAPH_FUNCS
#define GRAPH_FUNCS
//-----------------------------------------------------------------------------
//
// Name: HandyGraphFunctions.h
//
// Author: Mat Buckland (www.ai-junkie.com)
//
// Desc: As the name implies, some useful functions you can use with your
// graphs.
// For the function templates, make sure your graph interface complies
// with the SparseGraph class
//-----------------------------------------------------------------------------
#include <iostream>
#include "misc/Cgdi.h"
#include "misc/utils.h"
#include "misc/Stream_Utility_Functions.h"
#include "Graph/GraphAlgorithms.h"
#include "Graph/AStarHeuristicPolicies.h"
//--------------------------- ValidNeighbour -----------------------------
//
// returns true if x,y is a valid position in the map
//------------------------------------------------------------------------
bool ValidNeighbour(int x, int y, int NumCellsX, int NumCellsY)
{
return !((x < 0) || (x >= NumCellsX) || (y < 0) || (y >= NumCellsY));
}
//------------ GraphHelper_AddAllNeighboursToGridNode ------------------
//
// use to add he eight neighboring edges of a graph node that
// is positioned in a grid layout
//------------------------------------------------------------------------
template <class graph_type>
void GraphHelper_AddAllNeighboursToGridNode(graph_type& graph,
int row,
int col,
int NumCellsX,
int NumCellsY)
{
for (int i=-1; i<2; ++i)
{
for (int j=-1; j<2; ++j)
{
int nodeX = col+j;
int nodeY = row+i;
//skip if equal to this node
if ( (i == 0) && (j==0) ) continue;
//check to see if this is a valid neighbour
if (ValidNeighbour(nodeX, nodeY, NumCellsX, NumCellsY))
{
//calculate the distance to this node
Vector2D PosNode = graph.GetNode(row*NumCellsX+col).Pos();
Vector2D PosNeighbour = graph.GetNode(nodeY*NumCellsX+nodeX).Pos();
double dist = PosNode.Distance(PosNeighbour);
//this neighbour is okay so it can be added
graph_type::EdgeType NewEdge(row*NumCellsX+col,
nodeY*NumCellsX+nodeX,
dist);
graph.AddEdge(NewEdge);
//if graph is not a diagraph then an edge needs to be added going
//in the other direction
if (!graph.isDigraph())
{
graph_type::EdgeType NewEdge(nodeY*NumCellsX+nodeX,
row*NumCellsX+col,
dist);
graph.AddEdge(NewEdge);
}
}
}
}
}
//--------------------------- GraphHelper_CreateGrid --------------------------
//
// creates a graph based on a grid layout. This function requires the
// dimensions of the environment and the number of cells required horizontally
// and vertically
//-----------------------------------------------------------------------------
template <class graph_type>
void GraphHelper_CreateGrid(graph_type& graph,
int cySize,
int cxSize,
int NumCellsY,
int NumCellsX)
{
//need some temporaries to help calculate each node center
double CellWidth = (double)cySize / (double)NumCellsX;
double CellHeight = (double)cxSize / (double)NumCellsY;
double midX = CellWidth/2;
double midY = CellHeight/2;
//first create all the nodes
for (int row=0; row<NumCellsY; ++row)
{
for (int col=0; col<NumCellsX; ++col)
{
graph.AddNode(NavGraphNode<>(graph.GetNextFreeNodeIndex(),
Vector2D(midX + (col*CellWidth),
midY + (row*CellHeight))));
}
}
//now to calculate the edges. (A position in a 2d array [x][y] is the
//same as [y*NumCellsX + x] in a 1d array). Each cell has up to eight
//neighbours.
for (row=0; row<NumCellsY; ++row)
{
for (int col=0; col<NumCellsX; ++col)
{
GraphHelper_AddAllNeighboursToGridNode(graph, row, col, NumCellsX, NumCellsY);
}
}
}
//--------------------------- GraphHelper_DrawUsingGDI ------------------------
//
// draws a graph using the GDI
//-----------------------------------------------------------------------------
template <class graph_type>
void GraphHelper_DrawUsingGDI(const graph_type& graph, int color, bool DrawNodeIDs = false)
{
//just return if the graph has no nodes
if (graph.NumNodes() == 0) return;
gdi->SetPenColor(color);
//draw the nodes
graph_type::ConstNodeIterator NodeItr(graph);
for (const graph_type::NodeType* pN=NodeItr.begin();
!NodeItr.end();
pN=NodeItr.next())
{
gdi->Circle(pN->Pos(), 2);
if (DrawNodeIDs)
{
gdi->TextColor(200,200,200);
gdi->TextAtPos((int)pN->Pos().x+5, (int)pN->Pos().y-5, ttos(pN->Index()));
}
graph_type::ConstEdgeIterator EdgeItr(graph, pN->Index());
for (const graph_type::EdgeType* pE=EdgeItr.begin();
!EdgeItr.end();
pE=EdgeItr.next())
{
gdi->Line(pN->Pos(), graph.GetNode(pE->To()).Pos());
}
}
}
//--------------------------- WeightNavGraphNodeEdges -------------------------
//
// Given a cost value and an index to a valid node this function examines
// all a node's edges, calculates their length, and multiplies
// the value with the weight. Useful for setting terrain costs.
//------------------------------------------------------------------------
template <class graph_type>
void WeightNavGraphNodeEdges(graph_type& graph, int node, double weight)
{
//make sure the node is present
assert(node < graph.NumNodes());
//set the cost for each edge
graph_type::ConstEdgeIterator ConstEdgeItr(graph, node);
for (const graph_type::EdgeType* pE=ConstEdgeItr.begin();
!ConstEdgeItr.end();
pE=ConstEdgeItr.next())
{
//calculate the distance between nodes
double dist = Vec2DDistance(graph.GetNode(pE->From()).Pos(),
graph.GetNode(pE->To()).Pos());
//set the cost of this edge
graph.SetEdgeCost(pE->From(), pE->To(), dist * weight);
//if not a digraph, set the cost of the parallel edge to be the same
if (!graph.isDigraph())
{
graph.SetEdgeCost(pE->To(), pE->From(), dist * weight);
}
}
}
//----------------------- CreateAllPairsTable ---------------------------------
//
// creates a lookup table encoding the shortest path info between each node
// in a graph to every other
//-----------------------------------------------------------------------------
template <class graph_type>
std::vector<std::vector<int> > CreateAllPairsTable(const graph_type& G)
{
enum {no_path = -1};
std::vector<int> row(G.NumNodes(), no_path);
std::vector<std::vector<int> > ShortestPaths(G.NumNodes(), row);
for (int source=0; source<G.NumNodes(); ++source)
{
//calculate the SPT for this node
Graph_SearchDijkstra<graph_type> search(G, source);
std::vector<const graph_type::EdgeType*> spt = search.GetSPT();
//now we have the SPT it's easy to work backwards through it to find
//the shortest paths from each node to this source node
for (int target = 0; target<G.NumNodes(); ++target)
{
//if the source node is the same as the target just set to target
if (source == target)
{
ShortestPaths[source][target] = target;
}
else
{
int nd = target;
while ((nd != source) && (spt[nd] != 0))
{
ShortestPaths[spt[nd]->From][target]= nd;
nd = spt[nd]->From;
}
}
}//next target node
}//next source node
return ShortestPaths;
}
//----------------------- CreateAllPairsCostsTable -------------------------------
//
// creates a lookup table of the cost associated from traveling from one
// node to every other
//-----------------------------------------------------------------------------
template <class graph_type>
std::vector<std::vector<double> > CreateAllPairsCostsTable(const graph_type& G)
{
//create a two dimensional vector
std::vector<double> row(G.NumNodes(), 0.0);
std::vector<std::vector<double> > PathCosts(G.NumNodes(), row);
for (int source=0; source<G.NumNodes(); ++source)
{
//do the search
Graph_SearchDijkstra<graph_type> search(G, source);
//iterate through every node in the graph and grab the cost to travel to
//that node
for (int target = 0; target<G.NumNodes(); ++target)
{
if (source != target)
{
PathCosts[source][target]= search.GetCostToNode(target);
}
}//next target node
}//next source node
return PathCosts;
}
//---------------------- CalculateAverageGraphEdgeLength ----------------------
//
// determines the average length of the edges in a navgraph (using the
// distance between the source & target node positions (not the cost of the
// edge as represented in the graph, which may account for all sorts of
// other factors such as terrain type, gradients etc)
//------------------------------------------------------------------------------
template <class graph_type>
double CalculateAverageGraphEdgeLength(const graph_type& G)
{
double TotalLength = 0;
int NumEdgesCounted = 0;
graph_type::ConstNodeIterator NodeItr(G);
const graph_type::NodeType* pN;
for (pN = NodeItr.begin(); !NodeItr.end(); pN=NodeItr.next())
{
graph_type::ConstEdgeIterator EdgeItr(G, pN->Index());
for (const graph_type::EdgeType* pE = EdgeItr.begin(); !EdgeItr.end(); pE=EdgeItr.next())
{
//increment edge counter
++NumEdgesCounted;
//add length of edge to total length
TotalLength += Vec2DDistance(G.GetNode(pE->From()).Pos(), G.GetNode(pE->To()).Pos());
}
}
return TotalLength / (double)NumEdgesCounted;
}
//----------------------------- GetCostliestGraphEdge -------------------
//
// returns the cost of the costliest edge in the graph
//-----------------------------------------------------------------------------
template <class graph_type>
double GetCostliestGraphEdge(const graph_type& G)
{
double greatest = MinDouble;
graph_type::ConstNodeIterator NodeItr(G);
const graph_type::NodeType* pN;
for (pN = NodeItr.begin(); !NodeItr.end(); pN=NodeItr.next())
{
graph_type::ConstEdgeIterator EdgeItr(G, pN->Index());
for (const graph_type::EdgeType* pE = EdgeItr.begin(); !EdgeItr.end(); pE=EdgeItr.next())
{
if (pE->Cost() > greatest)greatest = pE->Cost();
}
}
return greatest;
}
#endif