/* * 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 #include #include #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; }