as_clear_cachepath() now prints cache hits, misses, number of entries, and memory use to stderr, when compiled with AS_STATS defined.
238 lines
6.6 KiB
C
238 lines
6.6 KiB
C
/*
|
|
* Empire - A multi-player, client/server Internet based war game.
|
|
* Copyright (C) 1986-2010, Dave Pare, Jeff Bailey, Thomas Ruschak,
|
|
* Ken Stevens, Steve McClure
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*
|
|
* ---
|
|
*
|
|
* See files README, COPYING and CREDITS in the root of the source
|
|
* tree for related information and legal notices. It is expected
|
|
* that future projects/authors will amend these files as needed.
|
|
*
|
|
* ---
|
|
*
|
|
* as_cache.c: Routines used to create/delete caches of A* paths.
|
|
*
|
|
* Known contributors to this file:
|
|
* Steve McClure, 1998
|
|
*/
|
|
|
|
#include <config.h>
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "as.h"
|
|
#include "optlist.h"
|
|
|
|
/* The way this works is interesting. :) */
|
|
/* We keep a pointer to a list of pointers. The index into this list
|
|
* is the y coordinate of the from sector. This member points to a list
|
|
* of from sectors on that y coordinate. So, we march that list checking
|
|
* the x value to find the from x,y we want. */
|
|
/* Once we find the from x,y, that node has the same type of pointer to
|
|
* a list of pointers. The index into this list is the y coordinate of
|
|
* the to sector. This member points to a list of to sectors on that y
|
|
* coordinate. So, we march that list checking the x value to find the
|
|
* to x,y we want. */
|
|
/* These lists are dynamically created since the world is dynamically sized. */
|
|
/* See, I told you it was interesting. :) */
|
|
|
|
static struct as_frompath **fromhead = (struct as_frompath **)0;
|
|
|
|
/* Note that we only want to cache during updates. Other times, it
|
|
* probably doesn't make much sense, but can be done. */
|
|
|
|
static int as_cachepath_on = 0; /* Default to off */
|
|
|
|
#ifdef AS_STATS
|
|
unsigned as_cache_tries, as_cache_hits;
|
|
#define as_cache_try() ((void)as_cache_tries++)
|
|
#define as_cache_hit() ((void)as_cache_hits++)
|
|
#else
|
|
#define as_cache_try() ((void)0)
|
|
#define as_cache_hit() ((void)0)
|
|
#endif
|
|
|
|
void
|
|
as_enable_cachepath(void)
|
|
{
|
|
as_cachepath_on = 1;
|
|
}
|
|
|
|
void
|
|
as_disable_cachepath(void)
|
|
{
|
|
as_cachepath_on = 0;
|
|
}
|
|
|
|
/* Note we want these to be as fast as possible */
|
|
|
|
void
|
|
as_add_cachepath(struct as_data *adp)
|
|
{
|
|
struct as_frompath *from;
|
|
struct as_topath *to = (struct as_topath *)0;
|
|
struct as_node *np;
|
|
|
|
/* Don't do anything if we aren't cacheing these */
|
|
if (as_cachepath_on == 0)
|
|
return;
|
|
|
|
/* Note we will only allocate this once. Afterwards, we just keep
|
|
* zeroing it since it's rather small and we don't need to re-allocate
|
|
* each time. */
|
|
if (fromhead == NULL) {
|
|
fromhead = calloc(1, sizeof(struct as_frompath *) * WORLD_Y);
|
|
if (fromhead == NULL)
|
|
return;
|
|
}
|
|
|
|
np = adp->head->np;
|
|
for (from = fromhead[adp->from.y]; from; from = from->next)
|
|
if (from->x == adp->from.x)
|
|
break;
|
|
if (from) {
|
|
for (to = from->tolist[np->c.y]; to; to = to->next) {
|
|
if (to->x == np->c.x) {
|
|
/* It is already here! Don't bother adding it again */
|
|
return;
|
|
}
|
|
}
|
|
} else {
|
|
/* We must make a new one of these */
|
|
from = malloc(sizeof(struct as_frompath));
|
|
if (from == NULL)
|
|
return;
|
|
/* And set some stuff */
|
|
from->x = adp->from.x;
|
|
/* Here we malloc a whole bunch of tolist pointers. */
|
|
from->tolist = calloc(1, sizeof(struct as_topath *) * WORLD_Y);
|
|
/* Now, add from to the global list */
|
|
from->next = fromhead[adp->from.y];
|
|
fromhead[adp->from.y] = from;
|
|
}
|
|
if (!to) {
|
|
/* We must make a new one */
|
|
to = malloc(sizeof(struct as_topath));
|
|
/* We can't, sorry */
|
|
if (to == NULL)
|
|
return;
|
|
/* Now set some stuff */
|
|
to->x = np->c.x;
|
|
/* Now add it to the list we are in */
|
|
to->next = from->tolist[np->c.y];
|
|
from->tolist[np->c.y] = to;
|
|
}
|
|
/* Now, make the path */
|
|
as_makepath(adp);
|
|
/* Now, take the path */
|
|
to->path = adp->path;
|
|
/* And clear the path in the adp */
|
|
adp->path = NULL;
|
|
}
|
|
|
|
void
|
|
as_clear_cachepath(void)
|
|
{
|
|
struct as_frompath *from, *from2;
|
|
struct as_topath *to, *to2;
|
|
int i, j;
|
|
#ifdef AS_STATS
|
|
size_t index_sz = 0;
|
|
unsigned index_nb = 0, paths_nb = 0, paths = 0;
|
|
#define stats_index(sz) ((void)(index_sz += (sz), index_nb++))
|
|
#else
|
|
#define stats_index(sz) ((void)0)
|
|
#endif
|
|
|
|
/* Cache not used yet :) */
|
|
if (fromhead == NULL)
|
|
return;
|
|
|
|
for (j = 0; j < WORLD_Y; j++) {
|
|
for (from = fromhead[j]; from; from = from2) {
|
|
for (i = 0; i < WORLD_Y; i++) {
|
|
for (to = from->tolist[i]; to; to = to2) {
|
|
to2 = to->next;
|
|
/* Free this path */
|
|
#ifdef AS_STATS
|
|
{
|
|
struct as_path *pp;
|
|
for (pp = to->path; pp; pp = pp->next)
|
|
paths_nb++;
|
|
paths++;
|
|
}
|
|
#endif
|
|
as_free_path(to->path);
|
|
/* Free this node */
|
|
free(to);
|
|
stats_index(sizeof(*to));
|
|
}
|
|
}
|
|
/* Now, free the list of lists */
|
|
free(from->tolist);
|
|
stats_index(WORLD_Y * sizeof(*from->tolist));
|
|
/* Save the next pointer */
|
|
from2 = from->next;
|
|
/* now, free this from node */
|
|
free(from);
|
|
stats_index(sizeof(*from));
|
|
}
|
|
}
|
|
/* Note we don't free the fromhead here, we just zero it. That way,
|
|
we can use it next time without mallocing int */
|
|
memset(fromhead, 0, (sizeof(struct as_frompath *) * WORLD_Y));
|
|
stats_index(WORLD_Y * sizeof(*fromhead));
|
|
#ifdef AS_STATS
|
|
fprintf(stderr, "as_cache %u searches, %u hits, %u entries,"
|
|
" index %zu bytes %u blocks, paths %zu bytes %u blocks\n",
|
|
as_cache_tries, as_cache_hits,
|
|
paths,
|
|
index_sz, index_nb,
|
|
paths_nb * sizeof(struct as_path), paths_nb);
|
|
as_cache_hits = as_cache_tries = 0;
|
|
#endif
|
|
}
|
|
|
|
struct as_path *
|
|
as_find_cachepath(coord fx, coord fy, coord tx, coord ty)
|
|
{
|
|
struct as_frompath *from;
|
|
struct as_topath *to;
|
|
|
|
as_cache_try();
|
|
/* Is the cache on? if not, return NULL */
|
|
if (as_cachepath_on == 0)
|
|
return NULL;
|
|
|
|
/* Do we have any cached? */
|
|
if (fromhead == NULL)
|
|
return NULL;
|
|
|
|
/* Yes! */
|
|
for (from = fromhead[fy]; from; from = from->next) {
|
|
if (from->x == fx) {
|
|
for (to = from->tolist[ty]; to; to = to->next) {
|
|
if (to->x == tx) {
|
|
as_cache_hit();
|
|
return to->path;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|