Skip to content

Commit ea2c862

Browse files
committed
added capability for bots to reuse paths stored in memory instead of constantly recomputing, causing significant performance impact
1 parent 0ef0aa5 commit ea2c862

File tree

7 files changed

+147
-30
lines changed

7 files changed

+147
-30
lines changed

src/ai/AStar.c

Lines changed: 125 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,40 +3,43 @@
33
#include "ai_local.h"
44

55
//==========================================
6-
//
7-
//
6+
// Related data:
7+
// pLinks - links or connections between nodes loaded from file that are used for pathfinding (determining distance/cost between nodes)
8+
// nodes - data structures loaded from file that have map coordinates of nodes
89
//==========================================
910

10-
static int alist[MAX_NODES]; //list contains all studied nodes, Open and Closed together
11-
static int alist_numNodes;
11+
static int alist[MAX_NODES]; //list contains indexes (node numbers) of all studied nodes, Open and Closed together
12+
static int alist_numNodes; //number of nodes in list
1213

1314
enum {
14-
NOLIST,
15-
OPENLIST,
16-
CLOSEDLIST
15+
NOLIST, //unprocessed nodes to be added to OPENLIST
16+
OPENLIST, //nodes to explore and then move to CLOSEDLIST
17+
CLOSEDLIST //nodes explored--stop when end (target/destination) node is added
1718
};
1819

1920
typedef struct
2021
{
21-
int parent;
22-
int G;
23-
int H;
22+
int parent; //previous link in path chain (start<-end)
23+
int G; //(pLink) distance between current node and the start node
24+
int H; //heuristic - estimated distance from the current node to the end node
2425

25-
int list;
26+
int list; //which list node is currently in, i.e. NOLIST/OPENLIST/CLOSEDLIST
2627

2728
} astarnode_t;
2829

29-
astarnode_t astarnodes[MAX_NODES];
30+
astarnode_t astarnodes[MAX_NODES]; //working list of nodes being processed by A* algo
31+
32+
// note: Apath is populated by AStar_ListsToPath and this is copied to (astarpath_t)path to determine the cost of path traversal via AI_FindCost
33+
static int Apath[MAX_NODES]; //array of node#s (node indexes) from start to end
34+
static int Apath_numNodes; //number of nodes in path
3035

31-
static int Apath[MAX_NODES];
32-
static int Apath_numNodes;
3336
//==========================================
3437
//
3538
//
3639
//==========================================
37-
static int originNode;
38-
static int goalNode;
39-
static int currentNode;
40+
static int originNode; //starting node closest to origin of search and beginning of path
41+
static int goalNode; //ending node closest to destination
42+
static int currentNode; //current node being processed by A* algo
4043

4144
int ValidLinksMask;
4245
#define DEFAULT_MOVETYPES_MASK (LINK_MOVE|LINK_STAIRS|LINK_FALL|LINK_WATER|LINK_WATERJUMP|LINK_JUMPPAD|LINK_PLATFORM|LINK_TELEPORT);
@@ -78,6 +81,11 @@ int AStar_nodeIsInOpen( int node )
7881
return 0;
7982
}
8083

84+
//==========================================
85+
// Reset values of astarnodes, Apath, and alist
86+
// Moves all nodes to NOLIST
87+
// Run before a new path is generated
88+
//==========================================
8189
static void AStar_InitLists (void)
8290
{
8391
int i;
@@ -224,14 +232,20 @@ static int AStar_FindInOpen_BestF ( void )
224232
best = node;
225233
}
226234
}
227-
//printf("BEST:%i\n", best);
235+
//gi.dprintf("BEST:%i\n", best);
228236
return best;
229237
}
230238

239+
//==========================================
240+
// Called after A* path has been computed on astarnodes[]
241+
// Works backwards from end (goalNode) and copies parent node indices to Apath[]
242+
// Apath[] will then contain a list of node#s (indexes) for the path from start to end
243+
//==========================================
231244
static void AStar_ListsToPath ( void )
232245
{
233246
int count = 0;
234247
int cur = goalNode;
248+
qboolean Spath_added = false;//GHz
235249

236250
while ( cur != originNode )
237251
{
@@ -244,9 +258,91 @@ static void AStar_ListsToPath ( void )
244258
{
245259
Apath[count] = cur;
246260
Apath_numNodes++;
261+
//GHz
262+
// save to list to speed up future searches
263+
if (Spath_numNodes < MAX_SPATH)
264+
{
265+
Spath[Spath_numNodes].path[count] = cur;
266+
Spath[Spath_numNodes].numNodes++;
267+
Spath_added = true;
268+
}
269+
//GHz
247270
count--;
248271
cur = astarnodes[cur].parent;
249272
}
273+
//GHz
274+
if (Spath_added)
275+
Spath_numNodes++;
276+
//GHz
277+
}
278+
279+
// copies saved path (Spath.path) data to Apath
280+
// Spath_index is the index in Spath with the data you want copied to Apath
281+
// path_index_start is the starting index in Spath.path with the data you want copied
282+
// path_index_end is the ending index in Spath.path
283+
// path_index_start and path_index_end tell the function which subset of the path you want copied
284+
static void AStar_UseSavedPath(int Spath_index, int path_index_start, int path_index_end)
285+
{
286+
int i, cur = 0, len = abs(path_index_end - path_index_start) + 1;
287+
288+
if (path_index_start > path_index_end)
289+
{
290+
// starting index is higher than ending index, so copy backwards from this index
291+
cur = path_index_start;
292+
for (i = 0;i < len;i++)
293+
{
294+
Apath[i] = Spath[Spath_index].path[cur];
295+
Apath_numNodes++;
296+
cur--;
297+
}
298+
}
299+
else
300+
{
301+
cur = path_index_start;
302+
for (i = 0;i < len;i++)
303+
{
304+
Apath[i] = Spath[Spath_index].path[cur];
305+
Apath_numNodes++;
306+
cur++;
307+
}
308+
}
309+
//gi.dprintf("AStar_UseSavedPath\n");
310+
}
311+
312+
// searches saved path array for a path that contains start and end nodes
313+
// if found, it copies the indexes to Apath and returns true
314+
static qboolean AStar_FindSavedPath(int start_node, int end_node)
315+
{
316+
int i, j;
317+
318+
for (i = 0;i < Spath_numNodes;i++)
319+
{
320+
int start_index = 0, end_index = 0;
321+
qboolean found_start = false, found_end = false;
322+
for (j = 0;j < Spath[i].numNodes;j++)
323+
{
324+
if (Spath[i].path[j] == start_node)
325+
{
326+
start_index = j;
327+
found_start = true;
328+
}
329+
else if (Spath[i].path[j] == end_node)
330+
{
331+
end_index = j;
332+
found_end = true;
333+
}
334+
if (found_start && found_end
335+
&& end_index > start_index) //GHz: reverse order is disabled for now because jump links would be impossible to do backwards!
336+
{
337+
//if (start_index != 0)
338+
// gi.dprintf("saved path found in subset of data!\n");
339+
//gi.dprintf("saved path found! index: %d, start: %d end: %d %d->%d\n", i, start_index, end_index, start_node, end_node);
340+
AStar_UseSavedPath(i, start_index, end_index);
341+
return true;
342+
}
343+
}
344+
}
345+
return false;
250346
}
251347

252348
static int AStar_FillLists ( void )
@@ -265,25 +361,35 @@ static int AStar_FillLists ( void )
265361

266362
int AStar_ResolvePath ( int n1, int n2, int movetypes )
267363
{
364+
//int i=0;//GHz
268365
ValidLinksMask = movetypes;
366+
269367
if ( !ValidLinksMask )
270368
ValidLinksMask = DEFAULT_MOVETYPES_MASK;
271369

272370
AStar_InitLists();
273371

372+
if (AStar_FindSavedPath(n1, n2))//GHz
373+
return 1;
374+
274375
originNode = n1;
275376
goalNode = n2;
276377
currentNode = originNode;
277378

278379
while ( !AStar_nodeIsInOpen(goalNode) )
279380
{
381+
//i++;//GHz
280382
if( !AStar_FillLists() )
281383
return 0; //failed
282384
}
283385

284386
AStar_ListsToPath();
285387

286-
// printf("RESULT:\n Origin:%i\n Goal:%i\n numNodes:%i\n FirstInPath:%i\n LastInPath:%i\n", originNode, goalNode, Apath_numNodes, Apath[0], Apath[Apath_numNodes-1]);
388+
//GHz: debugging A* perf issues
389+
//gi.dprintf("RESULT:\n Origin:%i\n Goal:%i\n numNodes:%i\n FirstInPath:%i\n LastInPath:%i\n", originNode, goalNode, Apath_numNodes, Apath[0], Apath[Apath_numNodes-1]);
390+
//gi.dprintf("AStar_FillLists() ran %d times\n", i);
391+
//gi.dprintf("Spath[%d]: start:%d end:%d length:%d\n",
392+
// Spath_numNodes-1, Spath[Spath_numNodes-1].path[0], Spath[Spath_numNodes-1].path[Spath[Spath_numNodes - 1].numNodes-1], Spath[Spath_numNodes-1].numNodes);
287393

288394
return 1;
289395
}

src/ai/ai_class_dmbot.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -421,7 +421,7 @@ qboolean BOT_DMclass_FindEnemy(edict_t* self)
421421
if (self->ai.status.playersWeights[i] == 0)
422422
continue;
423423

424-
if (!visible1(self, AIEnemies[i]))
424+
if (!visible(self, AIEnemies[i]))
425425
continue;
426426
if (!infront(self, AIEnemies[i]))
427427
continue;

src/ai/ai_dropnodes.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ in NO WAY supported by Steve Yeager.
2727
#include "ai_local.h"
2828

2929

30-
nav_plink_t pLinks[MAX_NODES]; // pLinks array
30+
nav_plink_t pLinks[MAX_NODES]; // pLinks array - links or connections between nodes; used for pathfinding (determining distance/cost between nodes)
3131
nav_node_t nodes[MAX_NODES]; // nodes array
3232
ai_navigation_t nav;
3333

@@ -87,7 +87,7 @@ int AI_AddNode( vec3_t origin, int flagsmask )
8787
nodes[nav.num_nodes].flags = flagsmask;
8888
nodes[nav.num_nodes].flags |= AI_FlagsForNode( nodes[nav.num_nodes].origin, player.ent );
8989

90-
//Com_Printf("Dropped Node\n");
90+
//gi.dprintf("Dropped Node\n");
9191
//if( sv_cheats->value )
9292
// Com_Printf("Dropped Node\n");
9393

src/ai/ai_main.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -368,7 +368,8 @@ void AI_PickShortRangeGoal(edict_t *self)
368368
return;
369369

370370
// Missile detection code
371-
if(strcmp(target->classname,"rocket")==0 || strcmp(target->classname,"grenade")==0)
371+
if((strcmp(target->classname,"rocket")==0 || strcmp(target->classname,"grenade")==0) && target->owner
372+
&& target->owner->inuse && !OnSameTeam(self, target->owner)) // GHz: don't get angry at teammates
372373
{
373374
//if player who shoot is a potential enemy
374375
if (self->ai.status.playersWeights[target->owner->s.number-1])

src/ai/ai_nodes.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -898,6 +898,8 @@ void AI_InitNavigationData(void)
898898
nav.num_nodes = 0;
899899
memset( nodes, 0, sizeof(nav_node_t) * MAX_NODES );
900900
memset( pLinks, 0, sizeof(nav_plink_t) * MAX_NODES );
901+
memset( Spath, 0, sizeof(spath_t) * MAX_SPATH );//GHz
902+
Spath_numNodes = 0;//GHz
901903

902904
//Load nodes from file
903905
nav.loaded = AI_LoadPLKFile( level.mapname );

src/ai/ai_nodes_shared.h

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -78,17 +78,17 @@ in NO WAY supported by Steve Yeager.
7878

7979
#define LINK_INVALID 0x00001000
8080

81+
#define MAX_SPATH 1024 //GHz: maximum number of previous paths to store in memory
8182

8283
typedef struct nav_plink_s
8384
{
84-
int numLinks;
85-
int nodes[NODES_MAX_PLINKS];
86-
int dist[NODES_MAX_PLINKS];
87-
int moveType[NODES_MAX_PLINKS];
85+
int numLinks; // number of links between adjacent nodes
86+
int nodes[NODES_MAX_PLINKS]; // adjacent node indexes
87+
int dist[NODES_MAX_PLINKS]; // distance to adjacent nodes
88+
int moveType[NODES_MAX_PLINKS]; // move type between adjacent nodes
8889

8990
} nav_plink_t;
9091

91-
9292
typedef struct nav_node_s
9393
{
9494
vec3_t origin;
@@ -97,3 +97,11 @@ typedef struct nav_node_s
9797

9898
} nav_node_t;
9999

100+
typedef struct
101+
{
102+
int numNodes;
103+
int path[MAX_NODES];
104+
} spath_t;
105+
106+
spath_t Spath[MAX_SPATH]; //GHz: searchable list of previously computed paths, used to speed up pathfinding
107+
int Spath_numNodes; //GHz: number of saved paths

src/ai/bot_spawn.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,7 @@ void BOT_DMClass_JoinGame (edict_t *ent, char *team_name)
304304
char *s;
305305
//int rnd = CLASS_PALADIN;
306306

307-
gi.dprintf("BOT_DMClass_JoinGame\n");
307+
//gi.dprintf("BOT_DMClass_JoinGame\n");
308308

309309
if ( !BOT_JoinCTFTeam(ent, team_name) )
310310
gi.bprintf (PRINT_HIGH, "[BOT] %s joined the game.\n",
@@ -363,7 +363,7 @@ void BOT_DMClass_JoinGame (edict_t *ent, char *team_name)
363363
//==========================================
364364
void BOT_StartAsSpectator (edict_t *ent)
365365
{
366-
gi.dprintf("BOT_StartAsSpectator\n");
366+
//gi.dprintf("BOT_StartAsSpectator\n");
367367

368368
// start as 'observer'
369369
ent->movetype = MOVETYPE_NOCLIP;

0 commit comments

Comments
 (0)