Import of Empire 4.2.12
This commit is contained in:
commit
d8b7fdfae1
817 changed files with 126589 additions and 0 deletions
41
src/lib/as/COPYRIGHT
Normal file
41
src/lib/as/COPYRIGHT
Normal file
|
@ -0,0 +1,41 @@
|
|||
(Note that this copyright notice was changed with permission from Phil
|
||||
Lapsley to a copyright that complies with the GNU GPL. The new
|
||||
copyright is supplied here, along with the old copyright notice
|
||||
below.)
|
||||
|
||||
-----
|
||||
|
||||
A* Search - A search library used in Empire to determine paths between
|
||||
objects.
|
||||
Copyright (C) 1990-1998 Phil Lapsley
|
||||
|
||||
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
|
||||
|
||||
-----
|
||||
|
||||
Old Copyright notice follows:
|
||||
|
||||
"Copyright 1990 Phil Lapsley. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms for noncommercial
|
||||
purposes in support of BSD Empire are permitted provided that this
|
||||
notice is preserved and that due credit is given to the copyright
|
||||
holder. This software is provided ``as is'' without express or implied
|
||||
warranty. Entities interested in other distribution of this software
|
||||
should contact the copyright holder.
|
||||
|
||||
(Phil is located at phil@east.berkeley.edu)"
|
||||
|
||||
-----
|
62
src/lib/as/Makefile
Normal file
62
src/lib/as/Makefile
Normal file
|
@ -0,0 +1,62 @@
|
|||
#
|
||||
# Empire - A multi-player, client/server Internet based war game.
|
||||
# Copyright (C) 1986-2000, 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 the "LEGAL", "LICENSE", "CREDITS" and "README" files for all the
|
||||
# related information and legal notices. It is expected that any future
|
||||
# projects/authors will amend these files as needed.
|
||||
#
|
||||
# Makefile - Wolfpack, 1996
|
||||
|
||||
# Note that these could have been listed 1 per line, but I chose to just
|
||||
# stick them all together this way to shorten the file.
|
||||
|
||||
include ../../../build.conf
|
||||
include ../../make.src
|
||||
include ../../make.defs
|
||||
|
||||
LIB = $(SRCDIR)/lib/libas.a
|
||||
NTLIB = $(SRCDIR)\lib\libas.lib
|
||||
|
||||
OBJS = as_cache.o as_costcomp.o as_delete.o as_extend.o as_hash.o as_init.o \
|
||||
as_merge.o as_search.o as_stats.o as_winnow.o
|
||||
|
||||
NTOBJS = as_cache.obj as_costcomp.obj as_delete.obj as_extend.obj as_hash.obj \
|
||||
as_init.obj as_merge.obj as_search.obj as_stats.obj as_winnow.obj
|
||||
|
||||
all: $(LIB)
|
||||
|
||||
nt: $(NTLIB)
|
||||
|
||||
$(NTLIB): $(NTOBJS)
|
||||
-del /q $@
|
||||
lib /OUT:$@ /DEBUGTYPE:CV $(NTOBJS)
|
||||
|
||||
$(LIB): $(OBJS)
|
||||
rm -f $(LIB)
|
||||
ar cq $(LIB) $(OBJS)
|
||||
$(RANLIB) $(LIB)
|
||||
|
||||
clean:
|
||||
-(rm -f $(OBJS))
|
||||
-(del /q $(NTOBJS))
|
||||
|
||||
include ../../make.rules
|
||||
include Makedepend
|
300
src/lib/as/README
Normal file
300
src/lib/as/README
Normal file
|
@ -0,0 +1,300 @@
|
|||
Tue Nov 13 11:50:24 PST 1990 Phil Lapsley phil@Berkeley.EDU
|
||||
|
||||
This library implements a reasonably general version of the A* algorithm.
|
||||
Basically, A* is like an ordered search, but with a heuristic that allows
|
||||
it to make a better choices as to which path to take. The subdirectory
|
||||
"test" has example code for using A* to search a weighted cartesian matrix.
|
||||
The file "XXX//bestpath.c" has code to interface Empire to the A*
|
||||
algorithms.
|
||||
|
||||
This library is copyrighted; see the file COPYRIGHT for details.
|
||||
|
||||
COMPILATION
|
||||
|
||||
Do a "make" in this directory to make the library. Cd into "test" and
|
||||
do a "make" there to make a test program, called (ta da) "test".
|
||||
|
||||
LIBRARY USAGE
|
||||
|
||||
Pretty much all the data that the user needs to communicate to the library
|
||||
is stored in an "as_data" structure, which is created by a call to "as_init":
|
||||
|
||||
struct as_data *adp;
|
||||
|
||||
adp = as_init( ... );
|
||||
|
||||
The arguments to as_init specify a number of key things the algorithm will
|
||||
use, and these are discussed below.
|
||||
|
||||
Once you have an "as_data" structure, you can fill in its "from" and "to"
|
||||
members, which specify the coordinates of the points between which you want
|
||||
to go. The basic coordinate data structure is the "as_coord", which is
|
||||
just an "x" integer and a "y" integer. So:
|
||||
|
||||
adp->from.x = ...;
|
||||
adp->from.y = ...;
|
||||
adp->to.x = ...;
|
||||
adp->to.y = ...;
|
||||
|
||||
and then call as_search:
|
||||
|
||||
as_search(adp);
|
||||
|
||||
If the return value of as_search is 0, the algorithm found a path from
|
||||
"from" to "to". The path is stored in the "path" member of the "as_data"
|
||||
structure, and is just a linked list of coordinates ("as_coord" structs)
|
||||
from "from" to "to".
|
||||
|
||||
If the return value of as_search is -1, the algorithm couldn't find a
|
||||
path from "from" to "to". If the return is -2, a system error occurred
|
||||
(most probably malloc failed).
|
||||
|
||||
After a call to as_search, lots of malloced data 'n' stuff will be
|
||||
floating around, all pointed to by items in the "as_data" data structure.
|
||||
If you call "as_search" again (presumably with new "from" and "to"
|
||||
coordinates), the system will free these data structures and reallocate
|
||||
new ones. If you no longer want the data structures at all (i.e., you
|
||||
never intend to call "as_search" again), you can call:
|
||||
|
||||
as_delete(adp);
|
||||
|
||||
and this will free up all data associated with the as_data structure pointed
|
||||
to by adp.
|
||||
|
||||
ARGUMENTS TO AS_INIT
|
||||
|
||||
"as_init" takes eight arguments, in the following order:
|
||||
|
||||
maxneighbors The maximum number of neighboring sectors a
|
||||
coordinate can have. On a cartesian grid,
|
||||
this would be 8. On a hex map, it would be 6.
|
||||
|
||||
hashsize The size of the hash table used to determine
|
||||
if we've visited a particular coordinate.
|
||||
|
||||
hashfunc A pointer to a function that takes an
|
||||
"as_coord" as an argument and returns an
|
||||
integer that will be used as a hash value.
|
||||
If this is a NULL pointer, the as library
|
||||
will assign its own hash function that just
|
||||
adds the x and y coordinates together.
|
||||
|
||||
neighborfunc A pointer to a function that takes a coordinate
|
||||
(call it "c"), a pointer to an array of
|
||||
coordinates of length "maxneighbors", and
|
||||
a user data pointer (see below). This
|
||||
function should figure out the coordinates
|
||||
of all the neighbors of "c" and put them in
|
||||
the array cp[0] ... cp[maxneighbors-1].
|
||||
It should then return the number of neighbors
|
||||
found.
|
||||
|
||||
lbcostfunc A pointer to a function that takes two
|
||||
coordinates, call them "from" and "to",
|
||||
and a user data pointer (see below). It
|
||||
returns a double precision *LOWER BOUND*
|
||||
on the cost to get from "from" to "to".
|
||||
"from" and "to" may be separated by an
|
||||
arbitrary distance. More on this below.
|
||||
|
||||
realcostfunc A pointer to a function that takes two
|
||||
coordinates, call them "from" and "to",
|
||||
and a user data pointer (see below). It
|
||||
returns a double precision value of
|
||||
the *actual cost* to move from "from" to
|
||||
"to". Note that "from" will never be more
|
||||
than one sector away from "to".
|
||||
|
||||
seccostfunc A pointer to a function that takes two
|
||||
coordinates, call them "from" and "to",
|
||||
and a user data pointer (see below). It
|
||||
returns a double precision value that will
|
||||
be used to break ties when two nodes have
|
||||
the same lower bound to the target. An
|
||||
example will be discussed below.
|
||||
|
||||
userdata A (char *) that can be a pointer to
|
||||
any kind of data you want. This will
|
||||
be passed as the third argument to the
|
||||
neighborfunc, lbcostfunc, realcostfunc,
|
||||
and seccostfunc.
|
||||
|
||||
Look in test/cart.c to see examples of these functions for cartesian
|
||||
coordinates.
|
||||
|
||||
NOTES ON THE LOWER BOUND FUNCTION
|
||||
|
||||
"lbcostfunc" is *CRUCIAL* to the algorithm's performance. The entire
|
||||
idea behind the A* algorithm is that, when considering whether to move
|
||||
to a new coordinate, you know two things:
|
||||
|
||||
(1) how much it's cost you to get to that coordinate so far,
|
||||
|
||||
(2) a LOWER BOUND on how much it will cost to get to the
|
||||
destination from that coordinate.
|
||||
|
||||
If these two conditions are met, the algorithm will return the optimal
|
||||
path to the destination. The closer the lower bound is to the actual
|
||||
cost to get from one point to another, the quicker the algorithm will
|
||||
find this path. HOWEVER, if the lower bound is ever violated, i.e.,
|
||||
if the so-called lower bound function returns a value that is greater
|
||||
than the actual cost to get to the destination, then the algorithm will
|
||||
not necessarily find the optimal path.
|
||||
|
||||
Example:
|
||||
|
||||
Assume that we're on a cartesian matrix, and the cost to move from one point
|
||||
to another is just the distance between the two points, and that no
|
||||
other costs are involved. In this case, the lower bound function could
|
||||
be the same as the actual cost function, i.e.,
|
||||
|
||||
real cost = lower bound cost = sqrt(dx^2 + dy^2);
|
||||
|
||||
In this case, the algorithm will find the destination as quickly as possible.
|
||||
|
||||
Another example:
|
||||
|
||||
Again assume we're on a cartesian matrix, and the cost to move from
|
||||
one point to another is two things: (1) the distance between them, (2) some
|
||||
arbitrary cost function we get off of a map. E.g.,
|
||||
|
||||
X
|
||||
|
||||
0 1 2 3
|
||||
0 0 0 0 0
|
||||
1 0 0 0 0
|
||||
Y 2 0 0 2 0
|
||||
3 0 0 0 0
|
||||
|
||||
The real cost to move from (x,y) 0,0 to 1,0 is 1. That is, it's the distance
|
||||
(1) plus the value of the map at (1,0), which is 0. The real cost to move
|
||||
from (1,2) to (2,2) is 3: the distance (1) plus the map value at (2,2), which
|
||||
is 2, totaling 3.
|
||||
|
||||
In this case, the lower bound function could still be the distance between
|
||||
the two points, since this WILL NEVER BE MORE than the actual cost, and
|
||||
hence is a lower bound. I.e.,
|
||||
|
||||
real cost = sqrt(dx^2 + dy^2) + map costs
|
||||
|
||||
lower bound cost = sqrt(dx^2 + dy^2)
|
||||
|
||||
lower bound cost <= real cost for all coordinates
|
||||
|
||||
This is what the the example in the "test" directory uses.
|
||||
|
||||
A third example:
|
||||
|
||||
You could make the lower bound function always return 0. This would be
|
||||
a valid lower bound of the cost to move between any two points. In this
|
||||
case, the A* algorithm will behave like a breadth-first search, and isn't
|
||||
very much fun.
|
||||
|
||||
SECONDARY COST FUNCTION
|
||||
|
||||
The algorithm tries new coordinates in order of lowest lower-bound.
|
||||
There can be cases where the lower-bound function for two (or more)
|
||||
coordinates is the same, i.e., there is a tie. The algorithm uses
|
||||
the secondary cost function (which does NOT have to be a lower bound)
|
||||
to choose which coordinate to pick. A typical heuristic might be
|
||||
to use Euclidian distance for this secondary cost function, on the
|
||||
assumption that it's always better to move closer the destination.
|
||||
The Empire code does just this.
|
||||
|
||||
If you don't need a secondary cost function, just specify a NULL pointer
|
||||
to the seccost argument to as_init, and the routines will use 0.0 for you.
|
||||
|
||||
EMPIRE INTERFACE
|
||||
|
||||
The interface code in "XXX/bestpath.c" is a rather complicated example
|
||||
of A* interface code. What it does is based on some features of Empire
|
||||
that are explained here.
|
||||
|
||||
First, movement cost in Empire is based on a quantity called "mobility
|
||||
cost", which is a property of every sector. As we trace a path from
|
||||
source to destination, we add up mobility cost as we go. Once we're
|
||||
there, we have a total mobility cost. This is what we'd like to
|
||||
minimize.
|
||||
|
||||
Second, Empire has highways, which are zero cost movement paths. This
|
||||
hurts the A* algorithm, because it means that the lower bound cost
|
||||
function is very weak. For example, think what happens if we move from
|
||||
one side of the country to another: if the two sectors are attached via
|
||||
a highway, the cost will be very small -- in fact, it will be the cost
|
||||
to move out of the source sector, and the cost to move into the
|
||||
destination sector. If, on the other hand, the two sectors aren't
|
||||
connected by a highway, the cost could be quite large. Thus, the lower
|
||||
bound is just the cost to move out of a sector plus the cost to move
|
||||
into a sector. This is a pretty weak lower bound, and means that we
|
||||
have to explore a lot of sectors.
|
||||
|
||||
Third, the mobility costs tend to tie a lot, as explained in the
|
||||
section above on secondary cost functions. Thus, we use the Empire
|
||||
"mapdist" function as a secondary sort function to break ties. For
|
||||
example, consider the case where a sector borders two highway sectors,
|
||||
one on each side. The lower bound function will say that the two have
|
||||
equal lower bound costs, since they're both highways and cost nothing
|
||||
to move on. The secondary sort function is then used to break the tie --
|
||||
it says, "Take the one that moves you closer to the destination".
|
||||
|
||||
Fourth, all of the information we need about a sector (its mobility
|
||||
cost, who owns it, etc.) is stored in the sector file on disk. This
|
||||
means that the getsect() function to get it off disk will do a read(),
|
||||
which is VERY expensive. Because of the weak lower bound, A* ends up
|
||||
checking lots of sectors, including sectors that it's seen before.
|
||||
To avoid doing thousands of system calls per second, the bestpath.c file
|
||||
has sector caching routines that store in memory copies of every
|
||||
sector we read. (The function bp_getsect handles this). This means
|
||||
we never read a sector from disk more than once, although at the expense
|
||||
of using lots of memory.
|
||||
|
||||
THEORY OF OPERATION
|
||||
|
||||
The basic idea is as follows:
|
||||
|
||||
1. Add the start node to the head of a master queue.
|
||||
2. Is the head of the queue where we want to be? If so, stop.
|
||||
3. Make a list of all the neighbors coordinates around the
|
||||
coordinate of the head.
|
||||
|
||||
4. For each neighbor coordinate,
|
||||
|
||||
Compute the lower bound cost to the destination from here.
|
||||
|
||||
If this coordinate is already on the either the
|
||||
master queue or the "tried" queue:
|
||||
|
||||
4a. If it was on either the "master" queue or the
|
||||
"tried" queue and the one on the queue has a
|
||||
smaller lower-bound than the neighbor, ignore
|
||||
this neighbor and go on to the next neighbor.
|
||||
|
||||
4b. If it was on the "master" queue and the new
|
||||
neighbor has a smaller lower bound,
|
||||
Move the one on the queue to a different
|
||||
queue called "subsumed".
|
||||
|
||||
4c. If it was on the "tried" queue and the new
|
||||
neighbor has a smaller lower bound,
|
||||
Move the one on the "tried" queue to the
|
||||
master queue, and update its lower bound
|
||||
value to be as the new neighbor, and
|
||||
update its backpointer appropriately.
|
||||
|
||||
We now have a list of all neighbor coordinates that are either
|
||||
not on the queue already, or are cheaper than the ones on the
|
||||
queue.
|
||||
|
||||
5. Move the node at the head of the queue (the one whose neighbors
|
||||
we now have in a list) onto a different queue called "tried".
|
||||
|
||||
6. Sort this list of neighbors, and merge it into the master queue,
|
||||
keeping the master queue ordered by lower bound cost to destination.
|
||||
|
||||
7. Goto 2.
|
||||
|
||||
My algorithm does all of this, plus a little more (the above doesn't really
|
||||
mention backpointers except in passing), EXCEPT: I don't do step 4c.
|
||||
I'm not convinced that this can ever occur if the lower bound rule isn't
|
||||
broken, and I have yet to see it occur. However, if as_winnow returns -1,
|
||||
this error has occurred.
|
180
src/lib/as/as.h
Normal file
180
src/lib/as/as.h
Normal file
|
@ -0,0 +1,180 @@
|
|||
/*
|
||||
* A* Search - A search library used in Empire to determine paths between
|
||||
* objects.
|
||||
* Copyright (C) 1990-1998 Phil Lapsley
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
/*
|
||||
* A* definitions.
|
||||
*
|
||||
* @(#)as.h 1.9 11/13/90
|
||||
*/
|
||||
/*
|
||||
* 11/09/98 - Steve McClure
|
||||
* Added path list caching structures
|
||||
*/
|
||||
|
||||
#include <stdio.h> /* for FILE */
|
||||
#include "misc.h" /* for s_char */
|
||||
|
||||
/*
|
||||
* Coordinate.
|
||||
*/
|
||||
struct as_coord {
|
||||
int x, y;
|
||||
};
|
||||
|
||||
/*
|
||||
* Path, made up of a linked list of coordinates.
|
||||
*/
|
||||
struct as_path {
|
||||
struct as_coord c;
|
||||
struct as_path *next;
|
||||
};
|
||||
|
||||
/*
|
||||
* Basic node, used internally by A* algorithm.
|
||||
*/
|
||||
struct as_node {
|
||||
struct as_coord c; /* our coordinate */
|
||||
double knowncost; /* cost so far */
|
||||
double lbcost; /* lower bound on cost to dest */
|
||||
double inclbcost; /* incremental lower bound cost */
|
||||
double seccost; /* used to break ties */
|
||||
int step;
|
||||
int flags;
|
||||
struct as_node *back;
|
||||
};
|
||||
#define AS_TRIED 1 /* we've tried this node before */
|
||||
|
||||
/*
|
||||
* Linked list of nodes, used internally by A* algorithm.
|
||||
*/
|
||||
struct as_queue {
|
||||
struct as_node *np;
|
||||
struct as_queue *next;
|
||||
struct as_queue *prev;
|
||||
};
|
||||
|
||||
/*
|
||||
* Hash table entry, used to determine if we've seen a particular
|
||||
* coordinate before.
|
||||
*/
|
||||
struct as_hash {
|
||||
struct as_coord c;
|
||||
struct as_queue *qp;
|
||||
struct as_hash *next;
|
||||
};
|
||||
|
||||
/*
|
||||
* User's handle on A*, returned by as_init(). Some of the data here is
|
||||
* used by A* internals.
|
||||
*/
|
||||
struct as_data {
|
||||
int maxneighbors; /* max # of neighbors a cell can have */
|
||||
int hashsize; /* size of internal hash table */
|
||||
|
||||
int (*hash)(struct as_coord); /* hash function (coord -> int) */
|
||||
int (*neighbor)(struct as_coord, struct as_coord *, s_char *); /* function to find neighbors */
|
||||
double (*lbcost)(struct as_coord, struct as_coord, s_char *); /* function to give lower bound cost */
|
||||
double (*realcost)(struct as_coord, struct as_coord, s_char *); /* function to give real cost */
|
||||
double (*seccost)(struct as_coord, struct as_coord, s_char *); /* function to secondary cost */
|
||||
char *userdata; /* user's data, passed to callbacks */
|
||||
struct as_coord from; /* from coordinate */
|
||||
struct as_coord to; /* to coordinate */
|
||||
struct as_path *path; /* solution */
|
||||
|
||||
/* below are "private" to as_ routines */
|
||||
struct as_queue *head;
|
||||
struct as_queue *tried;
|
||||
struct as_hash **hashtab;
|
||||
struct as_queue *subsumed;
|
||||
struct as_coord *neighbor_coords;
|
||||
struct as_node **neighbor_nodes;
|
||||
};
|
||||
|
||||
/*
|
||||
* Added these for caching of paths as we stumble across them
|
||||
*/
|
||||
|
||||
struct as_topath {
|
||||
coord x;
|
||||
struct as_path *path; /* Path from holder of this list to here */
|
||||
struct as_topath *next;
|
||||
};
|
||||
|
||||
struct as_frompath {
|
||||
coord x;
|
||||
struct as_topath **tolist; /* List of nodes we have a path to */
|
||||
struct as_frompath *next;
|
||||
};
|
||||
|
||||
/*
|
||||
* Some cheezy allocation macros.
|
||||
*/
|
||||
#define AS_NEW_ARRAY(p, type, n, err) \
|
||||
(p) = (type *)calloc((n), sizeof (*(p))); \
|
||||
if ((p) == NULL) \
|
||||
return err; \
|
||||
|
||||
#define AS_NEW(p, type, err) \
|
||||
AS_NEW_ARRAY((p), type, 1, err);
|
||||
|
||||
#define AS_NEW_MALLOC(p, type, err) \
|
||||
(p) = (type *)malloc(sizeof(type)); \
|
||||
if ((p) == NULL) \
|
||||
return err; \
|
||||
|
||||
/* Functions that the user can call. */
|
||||
|
||||
extern struct as_data *
|
||||
as_init(int maxneighbors, int hashsize,
|
||||
int (*hashfunc) (struct as_coord),
|
||||
int (*neighborfunc) (struct as_coord, struct as_coord *, s_char *),
|
||||
double (*lbcostfunc) (struct as_coord, struct as_coord, s_char *),
|
||||
double (*realcostfunc) (struct as_coord, struct as_coord, s_char *),
|
||||
double (*seccostfunc) (struct as_coord, struct as_coord, s_char *),
|
||||
s_char *userdata);
|
||||
extern int as_search(struct as_data *adp);
|
||||
extern void as_delete(struct as_data *adp);
|
||||
extern void as_reset(struct as_data *adp);
|
||||
extern void as_stats(struct as_data *adp, FILE *fp);
|
||||
extern struct as_path *as_find_cachepath(coord fx,
|
||||
coord fy,
|
||||
coord tx,
|
||||
coord ty);
|
||||
|
||||
/* Functions that are "private" to algorithm */
|
||||
|
||||
extern void as_add_cachepath(struct as_data *adp);
|
||||
extern void as_clear_cachepath();
|
||||
extern void as_enable_cachepath();
|
||||
extern void as_disable_cachepath();
|
||||
|
||||
extern void as_makepath(struct as_data *adp);
|
||||
extern void as_free_path(struct as_path *pp);
|
||||
|
||||
extern int as_costcomp(struct as_node **n1, struct as_node **n2);
|
||||
extern struct as_queue *as_extend(struct as_data *adp);
|
||||
extern struct as_queue *as_merge(struct as_data *adp,
|
||||
struct as_queue *head,
|
||||
struct as_node **neighbors);
|
||||
extern struct as_queue *as_iscinq(struct as_data *adp, struct as_coord c);
|
||||
extern void as_setcinq(struct as_data *adp,
|
||||
struct as_coord c, struct as_queue *qp);
|
||||
extern void as_free_hashtab(struct as_data *adp);
|
||||
extern int as_winnow(struct as_data *adp,
|
||||
struct as_coord *coords, int ncoords);
|
196
src/lib/as/as_cache.c
Normal file
196
src/lib/as/as_cache.c
Normal file
|
@ -0,0 +1,196 @@
|
|||
/*
|
||||
* Empire - A multi-player, client/server Internet based war game.
|
||||
* Copyright (C) 1986-2000, 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 the "LEGAL", "LICENSE", "CREDITS" and "README" files for all the
|
||||
* related information and legal notices. It is expected that any 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 "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 */
|
||||
|
||||
void
|
||||
as_enable_cachepath()
|
||||
{
|
||||
as_cachepath_on = 1;
|
||||
}
|
||||
|
||||
void
|
||||
as_disable_cachepath()
|
||||
{
|
||||
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 == (struct as_frompath **)0) {
|
||||
fromhead = calloc(1, sizeof(struct as_frompath *) * WORLD_Y);
|
||||
if (fromhead == (struct as_frompath **)0)
|
||||
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 = (struct as_frompath *)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 = (struct as_topath **)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 = (struct as_topath *)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()
|
||||
{
|
||||
struct as_frompath *from, *from2;
|
||||
struct as_topath *to, *to2;
|
||||
int i, j;
|
||||
|
||||
/* Cache not used yet :) */
|
||||
if (fromhead == (struct as_frompath **)0)
|
||||
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 */
|
||||
as_free_path(to->path);
|
||||
/* Free this node */
|
||||
free((s_char *)to);
|
||||
}
|
||||
}
|
||||
/* Now, free the list of lists */
|
||||
free((s_char *)from->tolist);
|
||||
/* Save the next pointer */
|
||||
from2 = from->next;
|
||||
/* now, free this from node */
|
||||
free((s_char *)from);
|
||||
}
|
||||
}
|
||||
/* Note we don't free the fromhead here, we just zero it. That way,
|
||||
we can use it next time without mallocing int */
|
||||
bzero((s_char *)fromhead, (sizeof(struct as_frompath *) * WORLD_Y));
|
||||
}
|
||||
|
||||
struct as_path *
|
||||
as_find_cachepath(coord fx, coord fy, coord tx, coord ty)
|
||||
{
|
||||
struct as_frompath *from;
|
||||
struct as_topath *to;
|
||||
|
||||
/* Is the cache on? if not, return NULL */
|
||||
if (as_cachepath_on == 0)
|
||||
return (NULL);
|
||||
|
||||
/* Do we have any cached? */
|
||||
if (fromhead == (struct as_frompath **)0)
|
||||
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)
|
||||
return (to->path);
|
||||
}
|
||||
}
|
||||
}
|
||||
return (NULL);
|
||||
}
|
49
src/lib/as/as_costcomp.c
Normal file
49
src/lib/as/as_costcomp.c
Normal file
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* A* Search - A search library used in Empire to determine paths between
|
||||
* objects.
|
||||
* Copyright (C) 1990-1998 Phil Lapsley
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
#include "as.h"
|
||||
|
||||
#if !defined(lint) && !defined(SABER)
|
||||
static char sccsid[] = "@(#)as_costcomp.c 1.4 11/13/90";
|
||||
#endif /* not lint*/
|
||||
|
||||
/*
|
||||
* Compare the lower bound costs of two nodes. If the two nodes have
|
||||
* equal lower bound costs, sort on the secondary field.
|
||||
* Used as comparision function for qsort.
|
||||
*/
|
||||
int
|
||||
as_costcomp(struct as_node **n1, struct as_node **n2)
|
||||
{
|
||||
double diff;
|
||||
|
||||
diff = (*n1)->lbcost - (*n2)->lbcost;
|
||||
if (diff < -0.0001)
|
||||
return (-1);
|
||||
if (diff > 0.0001)
|
||||
return (1);
|
||||
|
||||
/* equal, check secondary cost */
|
||||
diff = (*n1)->seccost - (*n2)->seccost;
|
||||
if (diff < -0.0001)
|
||||
return (-1);
|
||||
if (diff > 0.0001)
|
||||
return (1);
|
||||
return (0);
|
||||
}
|
88
src/lib/as/as_delete.c
Normal file
88
src/lib/as/as_delete.c
Normal file
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* A* Search - A search library used in Empire to determine paths between
|
||||
* objects.
|
||||
* Copyright (C) 1990-1998 Phil Lapsley
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "as.h"
|
||||
|
||||
#if !defined(lint) && !defined(SABER)
|
||||
static char sccsid[] = "@(#)as_delete.c 1.5 11/13/90";
|
||||
#endif /* not lint */
|
||||
|
||||
static void as_free_queue(struct as_queue *queue);
|
||||
|
||||
/*
|
||||
* Free any dynamically allocated data stored in the as_data structure.
|
||||
*/
|
||||
void
|
||||
as_reset(struct as_data *adp)
|
||||
{
|
||||
|
||||
as_free_queue(adp->head);
|
||||
adp->head = NULL;
|
||||
as_free_queue(adp->tried);
|
||||
adp->tried = NULL;
|
||||
as_free_queue(adp->subsumed);
|
||||
adp->subsumed = NULL;
|
||||
as_free_hashtab(adp);
|
||||
as_free_path(adp->path);
|
||||
adp->path = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Free a queue (either the main, subsumed, or tried).
|
||||
*/
|
||||
static void
|
||||
as_free_queue(struct as_queue *queue)
|
||||
{
|
||||
struct as_queue *qp, *qp2;
|
||||
|
||||
for (qp = queue; qp; qp = qp2) {
|
||||
free((s_char *)qp->np);
|
||||
qp2 = qp->next;
|
||||
free((s_char *)qp);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Free a path.
|
||||
*/
|
||||
void
|
||||
as_free_path(struct as_path *pp)
|
||||
{
|
||||
struct as_path *pp2;
|
||||
|
||||
for (; pp; pp = pp2) {
|
||||
pp2 = pp->next;
|
||||
free((s_char *)pp);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Delete the as_data structure (which includes freeing its data).
|
||||
*/
|
||||
void
|
||||
as_delete(struct as_data *adp)
|
||||
{
|
||||
as_reset(adp);
|
||||
free((s_char *)adp->neighbor_coords);
|
||||
free((s_char *)adp->neighbor_nodes);
|
||||
free((s_char *)adp->hashtab);
|
||||
free((s_char *)adp);
|
||||
}
|
74
src/lib/as/as_extend.c
Normal file
74
src/lib/as/as_extend.c
Normal file
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* A* Search - A search library used in Empire to determine paths between
|
||||
* objects.
|
||||
* Copyright (C) 1990-1998 Phil Lapsley
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "as.h"
|
||||
|
||||
#if !defined(lint) && !defined(SABER)
|
||||
static char sccsid[] = "@(#)as_extend.c 1.2 11/13/90";
|
||||
#endif /* not lint */
|
||||
|
||||
/*
|
||||
* Extend the queue by neighbors. This entails getting the
|
||||
* coordinates of all the neighbors, figuring out their lower bound
|
||||
* costs, throwing away ones that are more expensive than ones we
|
||||
* already have, sorting, tand then merging into the queue.
|
||||
*/
|
||||
struct as_queue *
|
||||
as_extend(struct as_data *adp)
|
||||
{
|
||||
struct as_queue *qp;
|
||||
int i;
|
||||
struct as_queue *head;
|
||||
|
||||
head = adp->head;
|
||||
|
||||
/* Find the neighboring coordinates. */
|
||||
i = (*adp->neighbor)(head->np->c, adp->neighbor_coords, adp->userdata);
|
||||
if (i == 0)
|
||||
return (NULL);
|
||||
/*
|
||||
* Get rid of neighbors that are more costly than ones we already have,
|
||||
* and sort the rest into an array of as_nodes.
|
||||
*/
|
||||
i = as_winnow(adp, adp->neighbor_coords, i);
|
||||
if (i < 0)
|
||||
return (NULL);
|
||||
if (i > 1)
|
||||
qsort(adp->neighbor_nodes, i,
|
||||
sizeof (*adp->neighbor_nodes),
|
||||
(qsort_func_t) as_costcomp);
|
||||
|
||||
/* remove old coord from head of queue and add to list of tried */
|
||||
qp = head;
|
||||
head = head->next;
|
||||
if (head)
|
||||
head->prev = NULL;
|
||||
if (adp->tried) {
|
||||
adp->tried->prev = qp;
|
||||
qp->next = adp->tried;
|
||||
adp->tried = qp;
|
||||
} else
|
||||
adp->tried = qp;
|
||||
adp->tried->np->flags |= AS_TRIED;
|
||||
|
||||
head = as_merge(adp, head, adp->neighbor_nodes);
|
||||
return (head);
|
||||
}
|
85
src/lib/as/as_hash.c
Normal file
85
src/lib/as/as_hash.c
Normal file
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* A* Search - A search library used in Empire to determine paths between
|
||||
* objects.
|
||||
* Copyright (C) 1990-1998 Phil Lapsley
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "as.h"
|
||||
|
||||
#if !defined(lint) && !defined(SABER)
|
||||
static char sccsid[] = "@(#)as_hash.c 1.2 11/13/90";
|
||||
#endif /* not lint */
|
||||
|
||||
/*
|
||||
* Return a pointer to the as_queue structure associated with
|
||||
* this coordinate if the coordinate is in the queue.
|
||||
*/
|
||||
struct as_queue *
|
||||
as_iscinq(struct as_data *adp, struct as_coord c)
|
||||
{
|
||||
int hashval;
|
||||
struct as_hash *hp;
|
||||
|
||||
hashval = (*adp->hash)(c) % adp->hashsize;
|
||||
|
||||
for (hp = adp->hashtab[hashval]; hp; hp = hp->next)
|
||||
if (hp->c.x == c.x && hp->c.y == c.y)
|
||||
return (hp->qp);
|
||||
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the queue structure associated with this coordinate.
|
||||
*/
|
||||
void
|
||||
as_setcinq(struct as_data *adp, struct as_coord c, struct as_queue *qp)
|
||||
{
|
||||
int hashval;
|
||||
struct as_hash *hp;
|
||||
struct as_hash *new;
|
||||
|
||||
new = (struct as_hash *)malloc(sizeof(struct as_hash));
|
||||
new->c = c;
|
||||
new->qp = qp;
|
||||
|
||||
hashval = (*adp->hash)(c) % adp->hashsize;
|
||||
hp = adp->hashtab[hashval];
|
||||
|
||||
new->next = (hp) ? hp : NULL;
|
||||
adp->hashtab[hashval] = new;
|
||||
}
|
||||
|
||||
/*
|
||||
* Walk down the hash table array, freeing the chains and zeroing
|
||||
* the chain pointers.
|
||||
*/
|
||||
void
|
||||
as_free_hashtab(struct as_data *adp)
|
||||
{
|
||||
int i;
|
||||
struct as_hash *hp, *hp2;
|
||||
|
||||
for (i = 0; i < adp->hashsize; i++) {
|
||||
for (hp = adp->hashtab[i]; hp; hp = hp2) {
|
||||
hp2 = hp->next;
|
||||
free((char *)hp);
|
||||
}
|
||||
adp->hashtab[i] = NULL;
|
||||
}
|
||||
}
|
62
src/lib/as/as_init.c
Normal file
62
src/lib/as/as_init.c
Normal file
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* A* Search - A search library used in Empire to determine paths between
|
||||
* objects.
|
||||
* Copyright (C) 1990-1998 Phil Lapsley
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "as.h"
|
||||
|
||||
#if !defined(lint) && !defined(SABER)
|
||||
static char sccsid[] = "@(#)as_init.c 1.4 11/13/90";
|
||||
#endif /* not lint */
|
||||
|
||||
/*
|
||||
* Return an as_data structure with the necessary fields filled in
|
||||
* and space malloced. Return NULL if malloc fails.
|
||||
*/
|
||||
struct as_data *
|
||||
as_init(int maxneighbors,
|
||||
int hashsize,
|
||||
int (*hashfunc) (struct as_coord),
|
||||
int (*neighborfunc) (struct as_coord, struct as_coord *, s_char *),
|
||||
double (*lbcostfunc) (struct as_coord, struct as_coord, s_char *),
|
||||
double (*realcostfunc) (struct as_coord, struct as_coord, s_char *),
|
||||
double (*seccostfunc) (struct as_coord, struct as_coord, s_char *),
|
||||
s_char *userdata)
|
||||
{
|
||||
struct as_data *adp;
|
||||
|
||||
AS_NEW(adp, struct as_data, NULL);
|
||||
AS_NEW_ARRAY(adp->neighbor_coords, struct as_coord,
|
||||
maxneighbors, NULL);
|
||||
AS_NEW_ARRAY(adp->neighbor_nodes, struct as_node *,
|
||||
maxneighbors + 1, NULL);
|
||||
AS_NEW_ARRAY(adp->hashtab, struct as_hash *,
|
||||
hashsize, NULL);
|
||||
|
||||
adp->maxneighbors = maxneighbors;
|
||||
adp->hashsize = hashsize;
|
||||
adp->hash = hashfunc;
|
||||
adp->neighbor = neighborfunc;
|
||||
adp->lbcost = lbcostfunc;
|
||||
adp->realcost = realcostfunc;
|
||||
adp->seccost = seccostfunc;
|
||||
adp->userdata = userdata;
|
||||
|
||||
return (adp);
|
||||
}
|
84
src/lib/as/as_merge.c
Normal file
84
src/lib/as/as_merge.c
Normal file
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* A* Search - A search library used in Empire to determine paths between
|
||||
* objects.
|
||||
* Copyright (C) 1990-1998 Phil Lapsley
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "as.h"
|
||||
|
||||
#if !defined(lint) && !defined(SABER)
|
||||
static char sccsid[] = "@(#)as_merge.c 1.2 11/13/90";
|
||||
#endif /* not lint */
|
||||
|
||||
/*
|
||||
* Merge neighbors into queue, keeping it sorted. "neighbors" is sorted,
|
||||
* both by lower bound cost and then by secondary cost.
|
||||
*/
|
||||
struct as_queue *
|
||||
as_merge(struct as_data *adp, struct as_queue *head, struct as_node **neighbors)
|
||||
{
|
||||
struct as_queue *qp;
|
||||
struct as_queue *pp; /* previous pointer */
|
||||
struct as_queue *ip; /* insert pointer */
|
||||
struct as_node *np;
|
||||
int i;
|
||||
|
||||
qp = head;
|
||||
pp = NULL;
|
||||
for (i = 0; neighbors[i]; i++) {
|
||||
np = neighbors[i];
|
||||
/* scan until qp points to a node we should go in front of */
|
||||
while (qp && (qp->np->lbcost < np->lbcost)) {
|
||||
pp = qp;
|
||||
qp = qp->next;
|
||||
}
|
||||
/* check for equal lower bounds, and use secondary cost if = */
|
||||
if (qp && qp->np->lbcost == np->lbcost) {
|
||||
while (qp && (qp->np->lbcost == np->lbcost) &&
|
||||
(qp->np->seccost < np->seccost)) {
|
||||
pp = qp;
|
||||
qp = qp->next;
|
||||
}
|
||||
}
|
||||
AS_NEW_MALLOC(ip, struct as_queue, NULL);
|
||||
/* if there was such a node, insert us in front of it */
|
||||
if (qp) {
|
||||
ip->prev = qp->prev;
|
||||
if (ip->prev)
|
||||
ip->prev->next = ip;
|
||||
ip->next = qp;
|
||||
qp->prev = ip;
|
||||
if (qp == head)
|
||||
head = ip;
|
||||
} else { /* otherwise add us to end of queue */
|
||||
ip->next = NULL;
|
||||
ip->prev = pp;
|
||||
if (ip->prev)
|
||||
ip->prev->next = ip;
|
||||
else {
|
||||
head = ip;
|
||||
}
|
||||
pp = ip;
|
||||
}
|
||||
ip->np = np;
|
||||
as_setcinq(adp, np->c, ip);
|
||||
np->step++;
|
||||
}
|
||||
|
||||
return (head);
|
||||
}
|
149
src/lib/as/as_search.c
Normal file
149
src/lib/as/as_search.c
Normal file
|
@ -0,0 +1,149 @@
|
|||
/*
|
||||
* A* Search - A search library used in Empire to determine paths between
|
||||
* objects.
|
||||
* Copyright (C) 1990-1998 Phil Lapsley
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
/*
|
||||
* 11/09/98 - Steve McClure
|
||||
* Added path list caching structures
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "as.h"
|
||||
|
||||
#if !defined(lint) && !defined(SABER)
|
||||
static char sccsid[] = "@(#)as_search.c 1.2 11/13/90";
|
||||
#endif /* not lint */
|
||||
|
||||
/*
|
||||
* Basic A* search function. "adp" should have been initialized by
|
||||
* as_init (any previously allocated data will be freed by as_reset here),
|
||||
* and adp->from and adp->to should be set accordingly. On success,
|
||||
* returns 0, with adp->path set to a linked list of coordinates to target.
|
||||
* If we can't find target, return -1; if malloc fails, return -2.
|
||||
*/
|
||||
int
|
||||
as_search(struct as_data *adp)
|
||||
{
|
||||
int iter = 0;
|
||||
struct as_queue *head;
|
||||
struct as_node *np;
|
||||
#ifdef DEBUG
|
||||
int i;
|
||||
struct as_queue *qp;
|
||||
struct as_path *pp;
|
||||
#endif /* DEBUG */
|
||||
struct as_queue *as_extend(struct as_data *adp);
|
||||
|
||||
as_reset(adp);
|
||||
|
||||
/*
|
||||
* Jump start the queue by making first element the zero-cost
|
||||
* node where we start.
|
||||
*/
|
||||
AS_NEW_MALLOC(head, struct as_queue, -2);
|
||||
adp->head = head;
|
||||
head->next = head->prev = NULL;
|
||||
AS_NEW(np, struct as_node, -2);
|
||||
np->c = adp->from;
|
||||
head->np = np;
|
||||
as_setcinq(adp, head->np->c, adp->head);
|
||||
|
||||
for (;;) {
|
||||
iter++;
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "Iteration %d, head at %d, %d\n", iter,
|
||||
head->np->c.x, head->np->c.y);
|
||||
#endif /* DEBUG */
|
||||
|
||||
/* see if we're done, one way or another */
|
||||
if (head == NULL)
|
||||
break;
|
||||
|
||||
/* Add it to the cache */
|
||||
as_add_cachepath(adp);
|
||||
|
||||
if (head->np->c.x == adp->to.x && head->np->c.y == adp->to.y)
|
||||
break;
|
||||
|
||||
/* extend queue by neighbors */
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "\tExtending queue\n");
|
||||
#endif /* DEBUG */
|
||||
adp->head = head = as_extend(adp);
|
||||
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "queue:\n");
|
||||
i = 0;
|
||||
for (qp = head; qp; qp = qp->next) {
|
||||
fprintf(stderr, "\t%d, %d so far %f lb %f sec %f\n",
|
||||
qp->np->c.x, qp->np->c.y,
|
||||
qp->np->knowncost,
|
||||
qp->np->lbcost,
|
||||
qp->np->seccost);
|
||||
i++;
|
||||
}
|
||||
fprintf(stderr, "\tqueue len %d\n", i);
|
||||
#endif /* DEBUG */
|
||||
|
||||
}
|
||||
|
||||
if (head == NULL) {
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "Failed\n");
|
||||
#endif /* DEBUG */
|
||||
return (-1);
|
||||
}
|
||||
|
||||
as_makepath(adp);
|
||||
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "Succeeded, iter %d, cost %f!\n", iter, head->np->knowncost);
|
||||
fprintf(stderr, "Path:\n");
|
||||
for (pp = adp->path; pp; pp = pp->next) {
|
||||
fprintf(stderr, "\t%d, %d\n", pp->c.x, pp->c.y);
|
||||
}
|
||||
fprintf(stderr, "Tried queue:\n");
|
||||
for (qp = adp->tried; qp; qp = qp->next) {
|
||||
fprintf(stderr, "\t%d, %d\n", qp->np->c.x, qp->np->c.y);
|
||||
}
|
||||
fprintf(stderr, "Subsumed queue:\n");
|
||||
for (qp = adp->subsumed; qp; qp = qp->next) {
|
||||
fprintf(stderr, "\t%d, %d\n", qp->np->c.x, qp->np->c.y);
|
||||
}
|
||||
#endif /* DEBUG */
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Work backwards through the list of nodes (starting at head)
|
||||
* to produce a path.
|
||||
*/
|
||||
void
|
||||
as_makepath(struct as_data *adp)
|
||||
{
|
||||
struct as_path *pp;
|
||||
struct as_node *np;
|
||||
|
||||
for (np = adp->head->np; np; np = np->back) {
|
||||
pp = (struct as_path *)malloc(sizeof(struct as_path));
|
||||
pp->c = np->c;
|
||||
pp->next = adp->path;
|
||||
adp->path = pp;
|
||||
}
|
||||
}
|
73
src/lib/as/as_stats.c
Normal file
73
src/lib/as/as_stats.c
Normal file
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* A* Search - A search library used in Empire to determine paths between
|
||||
* objects.
|
||||
* Copyright (C) 1990-1998 Phil Lapsley
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include "as.h"
|
||||
|
||||
#if !defined(lint) && !defined(SABER)
|
||||
static char sccsid[] = "@(#)as_stats.c 1.2 11/13/90";
|
||||
#endif /* not lint */
|
||||
|
||||
/*
|
||||
* Print statistics on algorithm performance to the file pointer "fp".
|
||||
*/
|
||||
void
|
||||
as_stats(struct as_data *adp, FILE *fp)
|
||||
{
|
||||
int i;
|
||||
int j;
|
||||
int total_q;
|
||||
int total_h;
|
||||
struct as_queue *qp;
|
||||
struct as_hash *hp;
|
||||
|
||||
fprintf(fp, "Statistics:\n");
|
||||
|
||||
fprintf(fp, "queue lengths:\n");
|
||||
total_q = 0;
|
||||
total_h = 0;
|
||||
for (i = 0, qp = adp->head; qp; qp = qp->next)
|
||||
i++;
|
||||
fprintf(fp, "\tmain:\t%d\n", i);
|
||||
total_q += i;
|
||||
for (i = 0, qp = adp->tried; qp; qp = qp->next)
|
||||
i++;
|
||||
fprintf(fp, "\ttried:\t%d\n", i);
|
||||
total_q += i;
|
||||
for (i = 0, qp = adp->subsumed; qp; qp = qp->next)
|
||||
i++;
|
||||
fprintf(fp, "\tsubsumed:\t%d\n", i);
|
||||
total_q += i;
|
||||
fprintf(fp, "hash table statistics (size %d):\n", adp->hashsize);
|
||||
for (i = 0; i < adp->hashsize; i++) {
|
||||
for (j = 0, hp = adp->hashtab[i]; hp; hp = hp->next)
|
||||
j++;
|
||||
fprintf(fp, "\t%d\t%d\n", i, j);
|
||||
total_h += j;
|
||||
}
|
||||
fprintf(fp, "\ttotal\t%d\n", total_h);
|
||||
fprintf(fp, "approximate memory usage (bytes):\n");
|
||||
fprintf(fp, "\tqueues\t%d\n", (int)(total_q * sizeof (struct as_queue)));
|
||||
fprintf(fp, "\tnodes\t%d\n", (int)(total_q * sizeof (struct as_node)));
|
||||
fprintf(fp, "\thash ents\t%d\n", (int)(total_h * sizeof (struct as_hash)));
|
||||
fprintf(fp, "\ttotal\t%d\n",
|
||||
(int)(total_q * sizeof (struct as_queue) +
|
||||
total_q * sizeof (struct as_node) +
|
||||
total_h * sizeof (struct as_hash)));
|
||||
}
|
177
src/lib/as/as_winnow.c
Normal file
177
src/lib/as/as_winnow.c
Normal file
|
@ -0,0 +1,177 @@
|
|||
/*
|
||||
* A* Search - A search library used in Empire to determine paths between
|
||||
* objects.
|
||||
* Copyright (C) 1990-1998 Phil Lapsley
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "as.h"
|
||||
|
||||
#if !defined(lint) && !defined(SABER)
|
||||
static char sccsid[] = "@(#)as_winnow.c 1.8 11/13/90";
|
||||
#endif /* not lint */
|
||||
|
||||
static struct as_node *
|
||||
as_newnode(struct as_node *backp, struct as_coord c,
|
||||
double inclbcost, double lbcost, double knowncost, double seccost);
|
||||
|
||||
/*
|
||||
* Take a list of neighbor coordinates and winnow them down into
|
||||
* an interesting list of neighbor nodes. This means:
|
||||
*
|
||||
* For each neighbor,
|
||||
* Compute a lower bound on the total cost to target.
|
||||
* If this neighbor is already in our queue,
|
||||
* See if the new neighbor is cheaper.
|
||||
* If so, add it to queue and move the
|
||||
* old node to the subsumed list.
|
||||
* If not, ignore this neighbor.
|
||||
* If this neighbor isn't in the queue, add it.
|
||||
*
|
||||
*/
|
||||
int
|
||||
as_winnow(struct as_data *adp, struct as_coord *coords, int ncoords)
|
||||
{
|
||||
int i = 0;
|
||||
int fix_pointer;
|
||||
double knowncost;
|
||||
double incknowncost;
|
||||
double lbcost;
|
||||
double inclbcost;
|
||||
double seccost;
|
||||
struct as_coord *cp;
|
||||
struct as_coord *end;
|
||||
struct as_queue *qp;
|
||||
struct as_node *np;
|
||||
|
||||
for (cp = coords, end = coords + ncoords; cp < end; cp++) {
|
||||
fix_pointer = 0;
|
||||
incknowncost = (*adp->realcost)(adp->head->np->c, *cp,
|
||||
adp->userdata);
|
||||
knowncost = adp->head->np->knowncost + incknowncost;
|
||||
/*
|
||||
* If this neighbor is already in the queue, we can
|
||||
* save some time.
|
||||
*/
|
||||
qp = as_iscinq(adp, *cp);
|
||||
inclbcost = qp ? qp->np->inclbcost :
|
||||
(*adp->lbcost)(*cp, adp->to, adp->userdata);
|
||||
if (inclbcost < 0.0) /* skip bad cases */
|
||||
continue;
|
||||
lbcost = knowncost + inclbcost;
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "\tneighbor %d, %d, lbcost %f ", cp->x, cp->y, lbcost);
|
||||
#endif /* DEBUG */
|
||||
/*
|
||||
* If this neighbor is already in the queue, check to
|
||||
* see which has the lower cost. If the one already in
|
||||
* the queue is cheaper, skip this neighbor as bad. If
|
||||
* the neighbor does, delete the one in the queue.
|
||||
*/
|
||||
if (qp) {
|
||||
if (qp->np->lbcost <= lbcost) {
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "old, loses to %f\n", qp->np->lbcost);
|
||||
#endif /* DEBUG */
|
||||
continue;
|
||||
} else {
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "old, wins over %f\n", qp->np->lbcost);
|
||||
#endif /* DEBUG */
|
||||
if (qp->np->flags & AS_TRIED) {
|
||||
/* should "never happen" */
|
||||
return (0);
|
||||
}
|
||||
/*
|
||||
* The neighbor is better than a previously visited coordinate;
|
||||
* remove the old coordinate from the queue and add it to
|
||||
* the subsumed nodes queue. To get here at
|
||||
* all we can't be the head, thus qp->prev is defined.
|
||||
*/
|
||||
/* Delete from main queue */
|
||||
qp->prev->next = qp->next;
|
||||
if (qp->next)
|
||||
qp->next->prev = qp->prev;
|
||||
|
||||
/* Add to subsumed queue */
|
||||
if (adp->subsumed) {
|
||||
adp->subsumed->prev = qp;
|
||||
qp->next = adp->subsumed;
|
||||
} else {
|
||||
qp->next = NULL;
|
||||
}
|
||||
adp->subsumed = qp;
|
||||
adp->subsumed->prev = NULL;
|
||||
fix_pointer = 1;
|
||||
/*
|
||||
* At this point, the as_iscinq code may contain bogus pointer
|
||||
* refs. They'll be fixed when as_merge merges the new
|
||||
* neighbors into the main queue.
|
||||
*/
|
||||
}
|
||||
}
|
||||
#ifdef DEBUG
|
||||
else {
|
||||
fprintf(stderr, "new\n");
|
||||
}
|
||||
#endif /* DEBUG */
|
||||
|
||||
if (qp)
|
||||
seccost = qp->np->seccost;
|
||||
else
|
||||
seccost = (adp->seccost) ?
|
||||
(*adp->seccost)(*cp, adp->to, adp->userdata) :
|
||||
0.0;
|
||||
np = as_newnode(adp->head->np, *cp, inclbcost, lbcost,
|
||||
knowncost, seccost);
|
||||
if (np == NULL)
|
||||
return (0);
|
||||
if (fix_pointer) {
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "Fixing pointer for %d, %d\n", adp->subsumed->np->c.x,
|
||||
adp->subsumed->np->c.y);
|
||||
#endif
|
||||
adp->subsumed->np->back = np;
|
||||
}
|
||||
adp->neighbor_nodes[i++] = np;
|
||||
|
||||
}
|
||||
adp->neighbor_nodes[i] = NULL;
|
||||
|
||||
return (i);
|
||||
}
|
||||
|
||||
|
||||
static struct as_node *
|
||||
as_newnode(struct as_node *backp, struct as_coord c,
|
||||
double inclbcost, double lbcost, double knowncost, double seccost)
|
||||
{
|
||||
struct as_node *np;
|
||||
|
||||
/* Got an interesting coordinate; make a node for it. */
|
||||
AS_NEW_MALLOC(np, struct as_node, NULL);
|
||||
np->flags = 0;
|
||||
np->c = c;
|
||||
np->inclbcost = inclbcost;
|
||||
np->lbcost = lbcost;
|
||||
np->knowncost = knowncost;
|
||||
np->seccost = seccost;
|
||||
np->step = backp->step;
|
||||
np->back = backp;
|
||||
|
||||
return (np);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue