src/lib/common/bestpath.c

Go to the documentation of this file.
00001 /*
00002  *  Empire - A multi-player, client/server Internet based war game.
00003  *  Copyright (C) 1986-2007, Dave Pare, Jeff Bailey, Thomas Ruschak,
00004  *                           Ken Stevens, Steve McClure
00005  *
00006  *  This program is free software; you can redistribute it and/or modify
00007  *  it under the terms of the GNU General Public License as published by
00008  *  the Free Software Foundation; either version 2 of the License, or
00009  *  (at your option) any later version.
00010  *
00011  *  This program is distributed in the hope that it will be useful,
00012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  *  GNU General Public License for more details.
00015  *
00016  *  You should have received a copy of the GNU General Public License
00017  *  along with this program; if not, write to the Free Software
00018  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00019  *
00020  *  ---
00021  *
00022  *  See files README, COPYING and CREDITS in the root of the source
00023  *  tree for related information and legal notices.  It is expected
00024  *  that future projects/authors will amend these files as needed.
00025  *
00026  *  ---
00027  *
00028  *  bestpath.c: Find the best path between sectors
00029  * 
00030  *  Known contributors to this file:
00031  *     Steve McClure, 1998-2000
00032  *     Markus Armbruster, 2006
00033  */
00034 
00035 /* 
00036  * IMPORTANT: These routines are very selectively used in the server.
00037  *
00038  * "bestownedpath" is only used to determine paths for ships and planes.
00039  * 
00040  * Callers should not be calling these directly anymore. They should use
00041  * the "BestShipPath", "BestAirPath", "BestLandPath" and "BestDistPath"
00042  * functions.  Note that those last two use the A* algorithms to find
00043  * information.
00044  */
00045 
00046 #include <config.h>
00047 
00048 #include "file.h"
00049 #include "misc.h"
00050 #include "nat.h"
00051 #include "optlist.h"
00052 #include "path.h"
00053 #include "prototypes.h"
00054 #include "sect.h"
00055 #include "xy.h"
00056 
00057 static int owned_and_navigable(char *, int, int, int);
00058 
00059 #define MAXROUTE        100
00060 #define valid(x,y)      ((((x) ^ (y)) & 1) == 0)
00061 
00062 /*
00063  * Ok, note that here we malloc some buffers.  BUT, we never
00064  * free them.  Why, you may ask?  Because we want to allocate
00065  * them based on world size which is now (or soon to be) dynamic,
00066  * but we don't want to allocate each and every time, since that
00067  * would be slow.  And, since world size only changes at init
00068  * time, we can do this safely.
00069  */
00070 static unsigned short *mapbuf;
00071 static unsigned short **mapindex;
00072 
00073 /*
00074  * Find passable path from X, Y to EX, EY for nation OWN.
00075  * BPATH is a buffer capable of holding at least MAXROUTE characters.
00076  * If BIGMAP is null, all sectors are passable (useful for flying).
00077  * Else it is taken to be a bmap.
00078  * Sectors owned by or allied to OWN are then passable according to
00079  * the usual rules.
00080  * Other sectors are assumed to be passable when BIGMAP shows '.' or
00081  * nothing.
00082  * Return a path if found, else a null pointer.
00083  * Wart: the path isn't terminated with 'h', except when if X,Y equals
00084  * EX,EY.
00085  */
00086 char *
00087 bestownedpath(char *bpath, char *bigmap,
00088               int x, int y, int ex, int ey, int own)
00089 {
00090     int i, j, tx, ty, markedsectors;
00091     int minx, maxx, miny, maxy, scanx, scany;
00092     unsigned routelen;
00093 
00094     if (!mapbuf)
00095         mapbuf = malloc(WORLD_X * WORLD_Y * sizeof(*mapbuf));
00096     if (!mapbuf)
00097         return NULL;
00098     if (!mapindex) {
00099         mapindex = malloc(WORLD_X * sizeof(*mapindex));
00100         if (mapindex) {
00101             /* Setup the map pointers */
00102             for (i = 0; i < WORLD_X; i++)
00103                 mapindex[i] = &mapbuf[WORLD_Y * i];
00104         }
00105     }
00106     if (!mapindex)
00107         return NULL;
00108 
00109     x = XNORM(x);
00110     y = YNORM(y);
00111     ex = XNORM(ex);
00112     ey = YNORM(ey);
00113 
00114     if (x == ex && y == ey)
00115         return "h";
00116 
00117     if (!valid(x, y) || !valid(ex, ey))
00118         return NULL;
00119     if (!owned_and_navigable(bigmap, ex, ey, own))
00120         return NULL;
00121 
00122     for (i = 0; i < WORLD_X; i++)
00123         for (j = 0; j < WORLD_Y; j++)
00124             mapindex[i][j] = 0xFFFF;    /* clear the workspace  */
00125 
00126     routelen = 0;               /* path length is now 0 */
00127     mapindex[x][y] = 0;         /* mark starting spot   */
00128     markedsectors = 1;          /* source sector marked */
00129     minx = x - 2;               /* set X scan bounds    */
00130     maxx = x + 2;
00131     miny = y - 1;               /* set Y scan bounds    */
00132     maxy = y + 1;
00133 
00134     do {
00135         if (++routelen == MAXROUTE)
00136             return NULL;
00137         markedsectors = 0;
00138         for (scanx = minx; scanx <= maxx; scanx++) {
00139             x = XNORM(scanx);
00140             for (scany = miny; scany <= maxy; scany++) {
00141                 y = YNORM(scany);
00142                 if (!valid(x, y))
00143                     continue;
00144                 if (((mapindex[x][y] & 0x1FFF) == routelen - 1)) {
00145                     for (i = DIR_FIRST; i <= DIR_LAST; i++) {
00146                         tx = x + diroff[i][0];
00147                         ty = y + diroff[i][1];
00148                         tx = XNORM(tx);
00149                         ty = YNORM(ty);
00150                         if (mapindex[tx][ty] == 0xFFFF) {
00151                             if (owned_and_navigable(bigmap, tx, ty, own)) {
00152                                 if (CANT_HAPPEN(i < DIR_FIRST || i > DIR_LAST))
00153                                     i = DIR_STOP;
00154                                 mapindex[tx][ty] =
00155                                     ((i - DIR_FIRST + 1) << 13) + routelen;
00156                                 markedsectors++;
00157                             }
00158                         }
00159                         if (tx == ex && ty == ey) {
00160                             bpath[routelen] = 0;
00161                             while (routelen--) {
00162                                 i = (mapindex[tx][ty] >> 13)
00163                                     - 1 + DIR_FIRST;
00164                                 bpath[routelen] = dirch[i];
00165                                 tx = tx - diroff[i][0];
00166                                 ty = ty - diroff[i][1];
00167                                 tx = XNORM(tx);
00168                                 ty = YNORM(ty);
00169                             }
00170                             return bpath;
00171                         }
00172                     }
00173                 }
00174             }
00175         }
00176         miny--;
00177         maxy++;
00178         minx -= 2;
00179         maxx += 2;
00180     } while (markedsectors);
00181 
00182     return NULL;                /* no route possible    */
00183 }
00184 
00185 /*
00186  * Return non-zero if sector X, Y is passable.
00187  * If BIGMAP is null, all sectors are passable (useful for flying).
00188  * Else it is taken to be a bmap.
00189  * Sectors owned by or allied to OWN are checked according to the
00190  * usual rules, and the result is correct.
00191  * Other sectors are assumed to be passable when BIGMAP shows '.' or
00192  * nothing.
00193  */
00194 static int
00195 owned_and_navigable(char *bigmap, int x, int y, int own)
00196 {
00197     char mapspot;
00198     struct sctstr sect;
00199 
00200     if (!bigmap)
00201         return 1;
00202 
00203     /* Owned or allied sector?  Check the real sector.  */
00204     getsect(x, y, &sect);
00205     if (sect.sct_own == own
00206         || (sect.sct_own && getrel(getnatp(sect.sct_own), own) == ALLIED)) {
00207         /* FIXME duplicates shp_check_nav() logic */
00208         switch (dchr[sect.sct_type].d_nav) {
00209         case NAVOK:
00210             return 1;
00211         case NAV_CANAL:
00212             /* FIXME return 1 when all ships have M_CANAL */
00213             return 0;
00214         case NAV_02:
00215             return sect.sct_effic >= 2;
00216         case NAV_60:
00217             return sect.sct_effic >= 60;
00218         default:
00219             return 0;
00220         }
00221     }
00222 
00223     /* Can only check bigmap */
00224     mapspot = bigmap[sctoff(x, y)];
00225     return mapspot == '.' || mapspot == ' ' || mapspot == 0;
00226 }

Generated on Fri Mar 28 11:01:13 2008 for empserver by  doxygen 1.5.2