]> git.pond.sub.org Git - empserver/blob - src/lib/common/bestpath.c
Compute distribution paths center by center
[empserver] / src / lib / common / bestpath.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2011, Dave Pare, Jeff Bailey, Thomas Ruschak,
4  *                Ken Stevens, Steve McClure, Markus Armbruster
5  *
6  *  Empire 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 3 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, see <http://www.gnu.org/licenses/>.
18  *
19  *  ---
20  *
21  *  See files README, COPYING and CREDITS in the root of the source
22  *  tree for related information and legal notices.  It is expected
23  *  that future projects/authors will amend these files as needed.
24  *
25  *  ---
26  *
27  *  bestpath.c: Find the best path between sectors
28  *
29  *  Known contributors to this file:
30  *     Steve McClure, 1998-2000
31  *     Markus Armbruster, 2006
32  */
33
34 /*
35  * IMPORTANT: These routines are very selectively used in the server.
36  *
37  * "bestownedpath" is only used to determine paths for ships and planes.
38  *
39  * Callers should not be calling these directly anymore. They should use
40  * the "BestShipPath", "BestAirPath", "BestLandPath" and "BestDistPath"
41  * functions.  Note that those last two use the A* algorithms to find
42  * information.
43  */
44
45 #include <config.h>
46
47 #include "file.h"
48 #include "misc.h"
49 #include "nat.h"
50 #include "optlist.h"
51 #include "path.h"
52 #include "prototypes.h"
53 #include "sect.h"
54 #include "xy.h"
55
56 static int owned_and_navigable(char *, int, int, int);
57
58 #define MAXROUTE        100
59 #define valid(x,y)      ((((x) ^ (y)) & 1) == 0)
60
61 /*
62  * Ok, note that here we malloc some buffers.  BUT, we never
63  * free them.  Why, you may ask?  Because we want to allocate
64  * them based on world size which is now (or soon to be) dynamic,
65  * but we don't want to allocate each and every time, since that
66  * would be slow.  And, since world size only changes at init
67  * time, we can do this safely.
68  */
69 static unsigned short *mapbuf;
70 static unsigned short **mapindex;
71
72 /*
73  * Find passable path from X, Y to EX, EY for nation OWN.
74  * BPATH is a buffer capable of holding at least MAXROUTE characters.
75  * If BIGMAP is null, all sectors are passable (useful for flying).
76  * Else it is taken to be a bmap.
77  * Sectors owned by or allied to OWN are then passable according to
78  * the usual rules.
79  * Other sectors are assumed to be passable when BIGMAP shows '.' or
80  * nothing.
81  * Return a path if found, else a null pointer.
82  * Wart: the path isn't terminated with 'h', except when if X,Y equals
83  * EX,EY.
84  */
85 char *
86 bestownedpath(char *bpath, char *bigmap,
87               int x, int y, int ex, int ey, int own)
88 {
89     int i, j, tx, ty, markedsectors;
90     int minx, maxx, miny, maxy, scanx, scany;
91     unsigned routelen;
92
93     if (!mapbuf)
94         mapbuf = malloc(WORLD_X * WORLD_Y * sizeof(*mapbuf));
95     if (!mapbuf)
96         return NULL;
97     if (!mapindex) {
98         mapindex = malloc(WORLD_X * sizeof(*mapindex));
99         if (mapindex) {
100             /* Setup the map pointers */
101             for (i = 0; i < WORLD_X; i++)
102                 mapindex[i] = &mapbuf[WORLD_Y * i];
103         }
104     }
105     if (!mapindex)
106         return NULL;
107
108     x = XNORM(x);
109     y = YNORM(y);
110     ex = XNORM(ex);
111     ey = YNORM(ey);
112
113     if (x == ex && y == ey)
114         return "h";
115
116     if (!valid(x, y) || !valid(ex, ey))
117         return NULL;
118     if (!owned_and_navigable(bigmap, ex, ey, own))
119         return NULL;
120
121     for (i = 0; i < WORLD_X; i++)
122         for (j = 0; j < WORLD_Y; j++)
123             mapindex[i][j] = 0xFFFF;    /* clear the workspace  */
124
125     routelen = 0;               /* path length is now 0 */
126     mapindex[x][y] = 0;         /* mark starting spot   */
127     minx = x - 2;               /* set X scan bounds    */
128     maxx = x + 2;
129     miny = y - 1;               /* set Y scan bounds    */
130     maxy = y + 1;
131
132     do {
133         if (++routelen == MAXROUTE)
134             return NULL;
135         markedsectors = 0;
136         for (scanx = minx; scanx <= maxx; scanx++) {
137             x = XNORM(scanx);
138             for (scany = miny; scany <= maxy; scany++) {
139                 y = YNORM(scany);
140                 if (!valid(x, y))
141                     continue;
142                 if (((mapindex[x][y] & 0x1FFF) == routelen - 1)) {
143                     for (i = DIR_FIRST; i <= DIR_LAST; i++) {
144                         tx = x + diroff[i][0];
145                         ty = y + diroff[i][1];
146                         tx = XNORM(tx);
147                         ty = YNORM(ty);
148                         if (mapindex[tx][ty] == 0xFFFF) {
149                             if (owned_and_navigable(bigmap, tx, ty, own)) {
150                                 if (CANT_HAPPEN(i < DIR_FIRST || i > DIR_LAST))
151                                     i = DIR_STOP;
152                                 mapindex[tx][ty] =
153                                     ((i - DIR_FIRST + 1) << 13) + routelen;
154                                 markedsectors++;
155                             }
156                         }
157                         if (tx == ex && ty == ey) {
158                             bpath[routelen] = 0;
159                             while (routelen--) {
160                                 i = (mapindex[tx][ty] >> 13)
161                                     - 1 + DIR_FIRST;
162                                 bpath[routelen] = dirch[i];
163                                 tx = tx - diroff[i][0];
164                                 ty = ty - diroff[i][1];
165                                 tx = XNORM(tx);
166                                 ty = YNORM(ty);
167                             }
168                             return bpath;
169                         }
170                     }
171                 }
172             }
173         }
174         miny--;
175         maxy++;
176         minx -= 2;
177         maxx += 2;
178     } while (markedsectors);
179
180     return NULL;                /* no route possible    */
181 }
182
183 /*
184  * Return non-zero if sector X, Y is passable.
185  * If BIGMAP is null, all sectors are passable (useful for flying).
186  * Else it is taken to be a bmap.
187  * Sectors owned by or allied to OWN are checked according to the
188  * usual rules, and the result is correct.
189  * Other sectors are assumed to be passable when BIGMAP shows '.' or
190  * nothing.
191  */
192 static int
193 owned_and_navigable(char *bigmap, int x, int y, int own)
194 {
195     char mapspot;
196     struct sctstr sect;
197
198     if (!bigmap)
199         return 1;
200
201     /* Owned or allied sector?  Check the real sector.  */
202     getsect(x, y, &sect);
203     if (sect.sct_own && relations_with(sect.sct_own, own) == ALLIED) {
204         /* FIXME duplicates shp_check_nav() logic */
205         switch (dchr[sect.sct_type].d_nav) {
206         case NAVOK:
207             return 1;
208         case NAV_CANAL:
209             /* FIXME return 1 when all ships have M_CANAL */
210             return 0;
211         case NAV_02:
212             return sect.sct_effic >= 2;
213         case NAV_60:
214             return sect.sct_effic >= 60;
215         default:
216             return 0;
217         }
218     }
219
220     /* Can only check bigmap */
221     mapspot = bigmap[sect.sct_uid];
222     return mapspot == '.' || mapspot == ' ' || mapspot == 0;
223 }