]> git.pond.sub.org Git - empserver/blob - src/lib/common/path.c
[DO_EFF_MEM_CHECKING]: Dead for some ten years. Bury.
[empserver] / src / lib / common / path.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2004, Dave Pare, Jeff Bailey, Thomas Ruschak,
4  *                           Ken Stevens, Steve McClure
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  *  ---
21  *
22  *  See the "LEGAL", "LICENSE", "CREDITS" and "README" files for all the
23  *  related information and legal notices. It is expected that any future
24  *  projects/authors will amend these files as needed.
25  *
26  *  ---
27  *
28  *  path.c: Empire/A* Interface code.
29  *          Define AS_STATS for A* statistics.
30  * 
31  *  Known contributors to this file:
32  *     Phil Lapsley, 1991
33  *     Dave Pare, 1991
34  *     Thomas Ruschak, 1993
35  *     Steve McClure, 1997
36  */
37
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include "../as/as.h"
41 #include "misc.h"
42 #include "path.h"
43 #include "xy.h"
44 #include "sect.h"
45 #include "file.h"
46 #include "common.h"
47 #include "gen.h"
48 #include "optlist.h"
49
50 #define BP_ASHASHSIZE   128     /* A* queue hash table size */
51 #define BP_NEIGHBORS    6       /* max number of neighbors */
52
53 struct bestp {
54     int sctcache_hits;
55     int sctcache_misses;
56     int bp_mobtype;
57     struct as_data *adp;
58 };
59
60 static int bp_path(struct as_path *pp, s_char *buf);
61 static int bp_neighbors(struct as_coord c, struct as_coord *cp,
62                         s_char *pp);
63 static double bp_lbcost(struct as_coord from, struct as_coord to,
64                         s_char *pp);
65 static double bp_realcost(struct as_coord from, struct as_coord to,
66                           s_char *pp);
67 static double bp_seccost(struct as_coord from, struct as_coord to,
68                          s_char *pp);
69 static int bp_coord_hash(struct as_coord c);
70
71 struct empfile *ep;
72
73 /* We use this for caching neighbors.  It never changes except
74  * at reboot time (maybe) so we never need to free it */
75 struct sctstr **neighsects = (struct sctstr **)0;
76
77 static s_char *
78 bp_init(void)
79 {
80     struct bestp *bp;
81
82     ep = &empfile[EF_SECTOR];
83
84     bp = (struct bestp *)malloc(sizeof(*bp));
85     memset(bp, 0, sizeof(*bp));
86     bp->adp = as_init(BP_NEIGHBORS, BP_ASHASHSIZE, bp_coord_hash,
87                       bp_neighbors, bp_lbcost, bp_realcost,
88                       bp_seccost, (s_char *)bp);
89
90     if (bp->adp == NULL)
91         return NULL;
92
93     if (neighsects == (struct sctstr **)0)
94         neighsects = (struct sctstr **)calloc(1, (sizeof(struct sctstr *) *
95                                                   ((WORLD_X * WORLD_Y) /
96                                                    2) * 6));
97
98     return (s_char *)bp;
99 }
100
101 /*
102  * Find the best path from sector to to sector, and put the Empire movement
103  * string in path.  Return 0 on success, -1 on error.
104  */
105 static int
106 best_path(struct sctstr *from, struct sctstr *to, s_char *path,
107           int mob_type)
108 {
109     static struct bestp *mybestpath;
110     struct as_data *adp;
111     struct as_path *ap;
112
113     if (mybestpath == 0)
114         mybestpath = (struct bestp *)bp_init();
115     adp = mybestpath->adp;
116     ap = as_find_cachepath(from->sct_x, from->sct_y, to->sct_x, to->sct_y);
117     if (ap == NULL) {
118         adp->from.x = from->sct_x;
119         adp->from.y = from->sct_y;
120         adp->to.x = to->sct_x;
121         adp->to.y = to->sct_y;
122         mybestpath->bp_mobtype = mob_type;
123
124         if (as_search(adp) < 0)
125             return -1;
126         ap = adp->path;
127     }
128
129     if (bp_path(ap, path) < 0)
130         return -1;
131
132 #ifdef AS_STATS
133     as_stats(adp, stderr);
134 #endif /* AS_STATS */
135 #ifdef BP_STATS
136     fprintf(stderr, "best path %s\n", path);
137     fprintf(stderr, "cache hits/misses: %d/%d\n",
138             bp->sctcache_hits, bp->sctcache_misses);
139 #endif /* BP_STATS */
140     return 0;
141 }
142
143 /*
144  * Translate an A* path into an empire movement string.  Return 0 on
145  * success, -1 on failure.
146  */
147 static int
148 bp_path(struct as_path *pp, s_char *buf)
149 {
150     struct as_path *np;
151     s_char *cp = buf;
152     int dx, dy;
153     int n;
154
155     np = pp->next;
156     while (np) {
157         dx = np->c.x - pp->c.x;
158         /* deal with wraparound from non-neg coords */
159         if (dx < -2)
160             dx += WORLD_X;
161         else if (dx > 2)
162             dx -= WORLD_X;
163         dy = np->c.y - pp->c.y;
164         if (dy < -1)
165             dy += WORLD_Y;
166         else if (dy > 1)
167             dy -= WORLD_Y;
168         for (n = 1; n <= 6; n++)
169             if (dx == diroff[n][0] && dy == diroff[n][1])
170                 break;
171         if (n > 6)
172             return -1;
173
174         *cp++ = dirch[n];
175         pp = np;
176         np = np->next;
177     }
178     *cp = '\0';
179     return 0;
180 }
181
182 /*
183  * Find coords neighboring this sector; return number of such
184  * coords, and coordinartes themselves in an array pointed
185  * to by *cpp.
186  * XXX need to check ownership, sector types, etc.
187  */
188 static int
189 bp_neighbors(struct as_coord c, struct as_coord *cp, s_char *pp)
190 {
191     coord x, y;
192     coord nx, ny;
193     int n = 0, q;
194     struct sctstr *sp, *from, **ssp;
195     /* Six pointers, just in case our cache isn't there */
196     struct sctstr *tsp[] = { 0, 0, 0, 0, 0, 0 };
197     int sx, sy, offset;
198
199     x = c.x;
200     y = c.y;
201     sx = XNORM(x);
202     sy = YNORM(y);
203     offset = (sy * WORLD_X + sx) / 2;
204     from = (struct sctstr *)(ep->cache + ep->size * offset);
205
206     if (neighsects == (struct sctstr **)0)
207         ssp = (struct sctstr **)&tsp[0];
208     else
209         ssp = (struct sctstr **)&neighsects[offset * 6];
210     for (q = 1; q <= 6; q++, ssp++) {
211         if (*ssp == (struct sctstr *)0) {
212             /* We haven't cached this neighbor yet */
213             nx = x + diroff[q][0];
214             ny = y + diroff[q][1];
215             sx = XNORM(nx);
216             sy = YNORM(ny);
217             offset = (sy * WORLD_X + sx) / 2;
218             sp = (struct sctstr *)(ep->cache + ep->size * offset);
219             *ssp = sp;
220         } else {
221             sp = *ssp;
222             sx = XNORM(sp->sct_x);
223             sy = YNORM(sp->sct_y);
224         }
225         /* No need to calculate cost each time, just make sure we can
226            move through it.  We calculate it later. */
227         if (dchr[sp->sct_type].d_mcst == 0)
228             continue;
229         if (sp->sct_own != from->sct_own)
230             continue;
231         cp[n].x = sx;
232         cp[n].y = sy;
233         n++;
234     }
235     return (n);
236 }
237
238 /*
239  * Compute a lower-bound on the cost from "from" to "to".
240  */
241 /*ARGSUSED*/
242 static double
243 bp_lbcost(struct as_coord from, struct as_coord to, s_char *pp)
244 {
245     struct bestp *bp = (struct bestp *)pp;
246     struct sctstr *ts;
247     float cost;
248     int x, y, sx, sy, offset;
249
250     x = to.x;
251     y = to.y;
252     sx = XNORM(x);
253     sy = YNORM(y);
254     offset = (sy * WORLD_X + sx) / 2;
255     ts = (struct sctstr *)(ep->cache + ep->size * offset);
256     cost = sector_mcost(ts, bp->bp_mobtype);
257     return (cost);
258 }
259
260 /*
261  * Compute the real cost to move from "from" to "to".
262  */
263 static double
264 bp_realcost(struct as_coord from, struct as_coord to, s_char *pp)
265 {
266     return (bp_lbcost(from, to, pp));
267 }
268
269 /*
270  * Tie breaker secondary metric (only used when lower bound costs
271  * are equal).
272  */
273 /*ARGSUSED*/
274 static double
275 bp_seccost(struct as_coord from, struct as_coord to, s_char *pp)
276 {
277     return ((double)mapdist((coord)from.x, (coord)from.y,
278                             (coord)to.x, (coord)to.y));
279 }
280
281 /*
282  * Hash a coordinate into an integer.
283  */
284 static int
285 bp_coord_hash(struct as_coord c)
286 {
287     return ((abs(c.x) + 1) << 3) ^ abs(c.y);
288 }
289
290 void
291 bp_enable_cachepath(void)
292 {
293     as_enable_cachepath();
294 }
295
296 void
297 bp_disable_cachepath(void)
298 {
299     as_disable_cachepath();
300 }
301
302 void
303 bp_clear_cachepath(void)
304 {
305     as_clear_cachepath();
306 }
307
308 double
309 pathcost(struct sctstr *start, s_char *path, int mob_type)
310 {
311     register int o;
312     register int cx, cy;
313     double cost = 0.0;
314     struct sctstr *sp;
315     int sx, sy, offset;
316
317     cx = start->sct_x;
318     cy = start->sct_y;
319
320     while (*path) {
321         if (*path == 'h') {
322             path++;
323             continue;
324         }
325         o = dirindex[(int)((*path) - 'a')];
326         cx += diroff[o][0];
327         cy += diroff[o][1];
328         sx = XNORM(cx);
329         sy = YNORM(cy);
330         offset = (sy * WORLD_X + sx) / 2;
331         sp = (struct sctstr *)(ep->cache + ep->size * offset);
332         cost += sector_mcost(sp, mob_type);
333         path++;
334     }
335
336     return cost;
337 }
338
339 s_char *
340 BestDistPath(s_char *path,
341              struct sctstr *from,
342              struct sctstr *to, double *cost, int mob_type)
343 {
344     return BestLandPath(path, from, to, cost, mob_type);
345 }
346
347 s_char *
348 BestLandPath(s_char *path,
349              struct sctstr *from,
350              struct sctstr *to, double *cost, int mob_type)
351 {
352     int length;
353
354     *path = 0;
355     *cost = 0.0;
356     if (best_path(from, to, path, mob_type) < 0)
357         return (s_char *)0;
358     *cost = pathcost(from, path, mob_type);
359     length = strlen(path);
360     path[length] = 'h';
361     path[length + 1] = '\0';
362     return path;
363 }
364
365 s_char *
366 BestShipPath(s_char *path, int fx, int fy, int tx, int ty, int owner)
367 {
368     s_char *map;
369
370     /* need to make sector database available to bestpath */
371     map = ef_ptr(EF_BMAP, owner);
372
373     return (bestownedpath(path, map, fx, fy, tx, ty, ".=h", owner));
374 }
375
376 s_char *
377 BestAirPath(s_char *path, int fx, int fy, int tx, int ty)
378 {
379     return (bestownedpath(path, 0, fx, fy, tx, ty, "", -1));
380     /*    return (bestpath(path, fx, fy, tx, ty, "")); */
381 }