]> git.pond.sub.org Git - empserver/commitdiff
Merge branch 'pathfind' into pathfind-test
authorMarkus Armbruster <armbru@pond.sub.org>
Tue, 12 Apr 2011 20:07:10 +0000 (22:07 +0200)
committerMarkus Armbruster <armbru@pond.sub.org>
Tue, 12 Apr 2011 20:17:21 +0000 (22:17 +0200)
Conflicts:
src/lib/as/as_cache.c
src/lib/as/as_stats.c
src/lib/common/path.c

Modified:
modified:   include/path.h
modified:   include/prototypes.h
modified:   src/lib/update/finish.c

include/path.h
src/lib/common/path.c
src/lib/common/pathfind.c [new file with mode: 0644]
src/lib/subs/move.c
src/lib/update/finish.c

index e0ce6fce231fe20aa3d5b204be5cff14126cd4b4..d1f2d4e5c1bcd2e93657a3a393572d8bf353b9ee 100644 (file)
  *  path.h: Definitions for directions, paths, etc.
  *
  *  Known contributors to this file:
- *
+ *     Markus Armbruster, 2005-2011
  */
 
 #ifndef PATH_H
 #define PATH_H
 
+#include <stddef.h>
 #include "types.h"
 
        /* direction indices */
@@ -51,6 +52,8 @@
 #define MOB_MOVE       0
 #define MOB_MARCH      1
 #define MOB_RAIL       2
+#define MOB_SAIL       3
+#define MOB_FLY                4
 
 enum p_mode {                  /* How to find path to destination */
     P_NONE,                    /* don't */
@@ -67,6 +70,20 @@ extern char *routech[DIR_LAST+1];
 /* src/lib/common/bestpath.c */
 extern char *bestownedpath(char *, char *, int, int, int, int, int);
 
+/* src/lib/common/findpath.c */
+extern void path_find_from(coord, coord, natid, int);
+extern double path_find_to(coord, coord);
+extern double path_find(coord, coord, coord, coord, natid, int);
+extern size_t path_find_route(char *, size_t, coord, coord, coord, coord);
+#ifdef PATH_FIND_DEBUG
+extern void path_find_visualize(coord, coord, coord, coord);
+#endif
+#ifdef PATH_FIND_STATS
+extern void path_find_print_stats(void);
+#else
+#define path_find_print_stats() ((void)0)
+#endif
+
 /* src/lib/common/path.c */
 extern void bp_enable_cachepath(void);
 extern void bp_disable_cachepath(void);
index 10e1eb0abc9e3da14dea0ffec5626a170330fac3..e901d48509b2678019fe40f1344e878656676e98 100644 (file)
  *
  *  ---
  *
- *  path.c: Empire/A* Interface code.
+ *  path.c: Path finding interface code
  *
  *  Known contributors to this file:
  *     Phil Lapsley, 1991
  *     Dave Pare, 1991
  *     Thomas Ruschak, 1993
  *     Steve McClure, 1997
+ *     Markus Armbruster, 2011
  */
 
 /*
 
 #include <config.h>
 
-#include <stdio.h>
+#include <string.h>
 #include "../as/as.h"
 #include "file.h"
-#include "misc.h"
 #include "optlist.h"
 #include "path.h"
 #include "prototypes.h"
 #include "sect.h"
 #include "xy.h"
 
+#ifdef USE_PATH_FIND
+void
+bp_enable_cachepath(void)
+{
+}
+
+void
+bp_disable_cachepath(void)
+{
+}
+
+void
+bp_clear_cachepath(void)
+{
+}
+
+char *
+BestLandPath(char *path,
+            struct sctstr *from,
+            struct sctstr *to, double *cost, int mob_type)
+{
+    double c;
+    size_t len;
+
+    *cost = 0.0;
+    *path = 0;
+
+    /*
+     * Note: passing from->sct_own for actor is funny, but works: its
+     * only effect is to confine the search to that nation's land.  It
+     * doesn't affect mobility costs.  The real actor is different for
+     * marching in allied land, and passing it would break path
+     * finding there.
+     */
+    c = path_find(from->sct_x, from->sct_y, to->sct_x, to->sct_y,
+                 from->sct_own, mob_type);
+    if (c < 0)
+       return NULL;
+    len = path_find_route(path, 1024,
+                         from->sct_x, from->sct_y,
+                         to->sct_x, to->sct_y);
+    if (len + 1 >= 1024)
+       return NULL;
+    strcpy(path + len, "h");
+    *cost = c;
+    return path;
+}
+
+char *
+BestDistPath(char *path,
+            struct sctstr *from,
+            struct sctstr *to, double *cost)
+{
+    return BestLandPath(path, from, to, cost, MOB_MOVE);
+}
+
+char *
+BestShipPath(char *path, int fx, int fy, int tx, int ty, int owner)
+{
+    size_t len;
+
+    if (path_find(fx, fy, tx, ty, owner, MOB_SAIL) < 0)
+       return NULL;
+    len = path_find_route(path, 100, fx, fy, tx, ty);
+    if (len >= 100)
+       return NULL;
+    if (len == 0)
+       strcpy(path, "h");
+    return path;
+}
+
+char *
+BestAirPath(char *path, int fx, int fy, int tx, int ty)
+{
+    size_t len;
+
+    if (path_find(fx, fy, tx, ty, 0, MOB_FLY) < 0)
+       return NULL;
+    len = path_find_route(path, 100, fx, fy, tx, ty);
+    if (len >= 100)
+       return NULL;
+    if (len == 0)
+       strcpy(path, "h");
+    return path;
+}
+#else  /* !USE_PATH_FIND */
 #define BP_ASHASHSIZE  128     /* A* queue hash table size */
 #define BP_NEIGHBORS   6       /* max number of neighbors */
 
@@ -397,3 +483,4 @@ BestAirPath(char *path, int fx, int fy, int tx, int ty)
 {
     return bestownedpath(path, NULL, fx, fy, tx, ty, -1);
 }
+#endif /* !USE_PATH_FIND */
diff --git a/src/lib/common/pathfind.c b/src/lib/common/pathfind.c
new file mode 100644 (file)
index 0000000..bd5cb12
--- /dev/null
@@ -0,0 +1,660 @@
+/*
+ *  Empire - A multi-player, client/server Internet based war game.
+ *  Copyright (C) 1986-2011, Dave Pare, Jeff Bailey, Thomas Ruschak,
+ *                Ken Stevens, Steve McClure, Markus Armbruster
+ *
+ *  Empire 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ *  ---
+ *
+ *  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.
+ *
+ *  ---
+ *
+ *  pathfind.c: Find cheapest paths
+ *
+ *  Known contributors to this file:
+ *     Markus Armbruster, 2011
+ */
+
+#include <config.h>
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "file.h"
+#include "nat.h"
+#include "optlist.h"
+#include "path.h"
+#include "sect.h"
+
+#ifdef PATH_FIND_DEBUG
+#define DPRINTF(fmt, ...) ((void)printf(fmt , ## __VA_ARGS__))
+#else
+#define DPRINTF(fmt, ...) ((void)0)
+#endif
+
+static char *bufrotate(char *buf, size_t bufsz, size_t i);
+
+/*
+ * Dijkstra's algorithm.  Refer to your graph algorithm textbook for
+ * how it works.  Implementation is specialized to hex maps.
+ *
+ * Define PATH_FIND_STATS for performance statistics on stdout.
+ */
+
+/*
+ * Array of sector data, indexed by sector uid
+ *
+ * We need to store a few values per sector visited by the path
+ * search.  An array is the stupidest data structure that could
+ * possibly work.
+ *
+ * Extra benefit: it works really well for distribution in a
+ * continental game, where we visit most sectors.  That's our most
+ * demanding use of path search, and its performance has noticable
+ * impact on the update.
+ *
+ * Island game distribution is much less demanding.  The array may not
+ * be the best choice here, but it's plainly good enough.  Same for
+ * path searches outside the update.
+ */
+
+struct pf_map {
+    /*
+     * visit < pf_visit      : unvisited, remaining members invalid
+     * visit == pf_visit     : open, dir & cost tentative, heapi used
+     * visit == pf_visit + 1 : closed, dir & cost final, heapi unused
+     */
+    unsigned short visit;
+    signed char dir;           /* cheapest direction to source */
+    int heapi;                 /* index in heap, valid if open */
+    double cost;               /* cost from source */
+};
+
+static unsigned short pf_visit;
+static struct pf_map *pf_map;
+
+/*
+ * Binary heap, cost priority queue of all open sectors
+ *
+ * Again, we use the stupidest data structure that could possibly
+ * work: an array.  And we make it so large it can hold *all* sectors.
+ * In practice, we need much less space, but a tighter upper bound is
+ * not obvious to me right now.
+ */
+
+struct pf_heap {
+    int uid;                   /* sector uid and */
+    coord x, y;                        /* coordinates, uid == XYOFFSET(x, y) */
+    double cost;               /* cost from source */
+};
+
+static int pf_nheap;           /* #entries in pf_nheap[] */
+static struct pf_heap *pf_heap;
+
+/*
+ * Source and costs
+ */
+static coord pf_sx, pf_sy;
+static int pf_suid;
+static natid pf_actor;
+static double (*pf_sct_cost)(natid, int);
+
+/*
+ * Performance statistics
+ */
+#ifdef PATH_FIND_STATS
+static unsigned pf_nsearch, pf_nsource, pf_nopen, pf_nclose;
+static unsigned pf_nheap_max, pf_noway;
+static double pf_sumcost;
+#define STAT_INC(v) ((void)((v)++))
+#define STAT_INCBY(v, i) ((void)((v) += i))
+#define STAT_HIMARK(v, h) ((void)((v) < (h) ? (v) = (h) : (h)))
+#else  /* !PATH_FIND_STATS */
+#define STAT_INC(v) ((void)0)
+#define STAT_INCBY(v, i) ((void)0)
+#define STAT_HIMARK(v, h) ((void)0)
+#endif /* !PATH_FIND_STATS */
+
+#ifndef NDEBUG                 /* silence "not used" warning */
+/* Is sector with uid UID open?  */
+static int
+pf_is_open(int uid)
+{
+    return pf_map[uid].visit == pf_visit;
+}
+#endif
+
+/* Is sector with uid UID closed?  */
+static int
+pf_is_closed(int uid)
+{
+    /*
+     * optimization: check > pf_visit instead of == pf_visit + 1
+     * works because pf_map[uid].visit <= pf_visit + 1
+     */
+    return pf_map[uid].visit > pf_visit;
+}
+
+/* Is sector with uid UID unvisited?  */
+static int
+pf_is_unvisited(int uid)
+{
+    return pf_map[uid].visit < pf_visit;
+}
+
+#ifdef PATH_FIND_DEBUG
+static void
+pf_check(void)
+{
+    int i, uid, c;
+
+    for (i = 0; i < pf_nheap; i++) {
+       uid = pf_heap[i].uid;
+       assert(0 <= uid && uid < WORLD_SZ());
+       assert(pf_map[uid].heapi == i);
+       assert(pf_map[uid].visit == pf_visit);
+       assert(pf_map[uid].cost <= pf_heap[i].cost);
+       c = 2 * i + 1;
+       assert(c >= pf_nheap || pf_heap[i].cost <= pf_heap[c].cost);
+       c++;
+       assert(c >= pf_nheap || pf_heap[i].cost <= pf_heap[c].cost);
+    }
+
+    for (uid = 0; uid < WORLD_SZ(); uid++) {
+       assert(pf_map[uid].visit <= pf_visit + 1);
+       if (pf_map[uid].visit == pf_visit) {
+           i = pf_map[uid].heapi;
+           assert(0 <= i && i < pf_nheap && pf_heap[i].uid == uid);
+       }
+    }
+}
+#else
+#define pf_check() ((void)0)
+#endif
+
+/* Swap pf_heap's I-th and J-th elements.  */
+static void
+pf_heap_swap(int i, int j)
+{
+    struct pf_heap tmp;
+
+    assert(0 <= i && i < pf_nheap);
+    assert(0 <= j && j < pf_nheap);
+    tmp = pf_heap[i];
+    pf_heap[i] = pf_heap[j];
+    pf_heap[j] = tmp;
+    pf_map[pf_heap[i].uid].heapi = i;
+    pf_map[pf_heap[j].uid].heapi = j;
+}
+
+/* Restore heap property after N-th element's cost increased.  */
+static void
+pf_sift_down(int n)
+{
+    int r, c;
+
+    assert(0 <= n && n < pf_nheap);
+    for (r = n; (c = 2 * r + 1) < pf_nheap; r = c) {
+       if (c + 1 < pf_nheap && pf_heap[c].cost > pf_heap[c + 1].cost)
+           c++;
+       if (pf_heap[r].cost < pf_heap[c].cost)
+           break;
+       pf_heap_swap(r, c);
+    }
+}
+
+/* Restore heap property after N-th element's cost decreased.  */
+static void
+pf_sift_up(int n)
+{
+    int c, p;
+
+    assert(0 <= n && n < pf_nheap);
+    for (c = n; (p = (c - 1) / 2), c > 0; c = p) {
+       if (pf_heap[p].cost < pf_heap[c].cost)
+           break;
+       pf_heap_swap(p, c);
+    }
+}
+
+/*
+ * Open the unvisited sector X,Y.
+ * UID is sector uid, it equals XYOFFSET(X,Y).
+ * Cheapest path from source comes from direction DIR and has cost COST.
+ */
+static void
+pf_open(int uid, coord x, coord y, int dir, double cost)
+{
+    int i;
+
+    STAT_INC(pf_nopen);
+    i = pf_nheap++;
+    STAT_HIMARK(pf_nheap_max, (unsigned)pf_nheap);
+    DPRINTF("pf: open %d,%d %g %c %d\n", x, y, cost, dirch[dir], i);
+    assert(pf_is_unvisited(uid));
+    pf_map[uid].visit = pf_visit;
+    pf_map[uid].dir = dir;
+    pf_map[uid].heapi = i;
+    pf_map[uid].cost = cost;
+    pf_heap[i].uid = uid;
+    pf_heap[i].x = x;
+    pf_heap[i].y = y;
+    pf_heap[i].cost = cost;
+
+    pf_sift_up(i);
+    pf_check();
+}
+
+/*
+ * Close the sector at the top of the heap.
+ */
+static void
+pf_close(void)
+{
+    int uid = pf_heap[0].uid;
+
+    STAT_INC(pf_nclose);
+    DPRINTF("pf: close %d,%d %d\n", pf_heap[0].x, pf_heap[0].y, pf_nheap);
+    assert(pf_is_open(uid));
+    if (--pf_nheap) {
+       pf_heap[0] = pf_heap[pf_nheap];
+       pf_map[pf_heap[0].uid].heapi = 0;
+       pf_sift_down(0);
+    }
+    pf_map[uid].visit = pf_visit + 1;
+    pf_check();
+}
+
+/* silence "not used" warning */
+#ifdef PATH_FIND_DEBUG
+/*
+ * Return cost from source to sector with uid UID.
+ * It must be visited, i.e. open or closed.
+ */
+static double
+pf_cost(int uid)
+{
+    assert(!pf_is_unvisited(uid));
+    return pf_map[uid].cost;
+}
+#endif
+
+static coord
+x_in_dir(coord x, int dir)
+{
+    int xx;
+
+    assert(0 <= x && x < WORLD_X);
+    assert(0 <= dir && dir <= DIR_LAST);
+    xx = x + diroff[dir][0];
+    if (xx < 0)
+       return xx + WORLD_X;
+    if (xx >= WORLD_X)
+       return xx - WORLD_X;
+    return xx;
+}
+
+static coord
+y_in_dir(coord y, int dir)
+{
+    int yy;
+
+    assert(0 <= y && y < WORLD_Y);
+    assert(0 <= dir && dir <= DIR_LAST);
+    yy = y + diroff[dir][1];
+    if (yy < 0)
+       return yy + WORLD_Y;
+    if (yy >= WORLD_Y)
+       return yy - WORLD_Y;
+    return yy;
+}
+
+static int
+rev_dir(int dir)
+{
+    assert(DIR_FIRST <= dir && dir <= DIR_LAST);
+    return dir >= DIR_FIRST + 3 ? dir - 3 : dir + 3;
+}
+
+/*
+ * Set the current source and cost function.
+ * SX,SY is the source.
+ * The cost to enter the sector with uid u is COST(ACTOR, u).
+ * Negative value means the sector can't be entered.
+ */
+static void
+pf_set_source(coord sx, coord sy, natid actor, double (*cost)(natid, int))
+{
+    STAT_INC(pf_nsource);
+    DPRINTF("pf: source %d,%d\n", sx, sy);
+    pf_sx = sx;
+    pf_sy = sy;
+    pf_suid = XYOFFSET(sx, sy);
+    pf_actor = actor;
+    pf_sct_cost = cost;
+
+    if (!pf_map) {
+       pf_map = calloc(WORLD_SZ(), sizeof(*pf_map));
+       pf_heap = malloc(WORLD_SZ() * sizeof(*pf_heap));
+       pf_visit = 1;
+    } else if ((unsigned short)(pf_visit + 3) < pf_visit) {
+       DPRINTF("pf: visit wrap-around\n");
+       memset(pf_map, 0, WORLD_SZ() * sizeof(*pf_map));
+       pf_visit = 1;
+    } else
+       pf_visit += 2;
+
+    pf_nheap = 0;
+
+    pf_open(pf_suid, pf_sx, pf_sy, DIR_STOP, 0.0);
+}
+
+/*
+ * Find cheapest path from current source to DX,DY, return its cost.
+ */
+double
+path_find_to(coord dx, coord dy)
+{
+    int duid;
+    int uid, nuid, i;
+    double cost, c1;
+    coord x, y, nx, ny;
+
+    STAT_INC(pf_nsearch);
+    DPRINTF("pf: dest %d,%d\n", dx, dy);
+    duid = XYOFFSET(dx, dy);
+    if (pf_is_closed(duid)) {
+       DPRINTF("pf: done old %g\n", pf_map[duid].cost);
+       STAT_INCBY(pf_sumcost, pf_map[duid].cost);
+       return pf_map[duid].cost;
+    }
+
+    while (pf_nheap > 0 && (uid = pf_heap[0].uid) != duid) {
+       x = pf_heap[0].x;
+       y = pf_heap[0].y;
+       cost = pf_heap[0].cost;
+       pf_close();
+
+       for (i = 0; i < 6; i++) { /* for all neighbors */
+           nx = x_in_dir(x, DIR_FIRST + i);
+           ny = y_in_dir(y, DIR_FIRST + i);
+           nuid = XYOFFSET(nx, ny);
+           /*
+            * Cost to enter NX,NY doesn't depend on direction of
+            * entry.  This X,Y is at least as expensive as any
+            * previous one.  Therefore, cost to go to NX,NY via X,Y
+            * is at least as high as any previously found route.
+            * Skip neighbors that have a route already.
+            */
+           if (!pf_is_unvisited(nuid))
+               continue;
+           c1 = pf_sct_cost(pf_actor, nuid);
+           if (c1 < 0)
+               continue;
+           pf_open(nuid, nx, ny, DIR_FIRST + i, cost + c1);
+       }
+    }
+
+    DPRINTF("pf: done new %g\n", !pf_nheap ? -1.0 : pf_map[duid].cost);
+    if (!pf_nheap) {
+       STAT_INC(pf_noway);
+       return -1.0;
+    }
+    STAT_INCBY(pf_sumcost, pf_map[duid].cost);
+    return pf_map[duid].cost;
+}
+
+/*
+ * Write route from SX,SY to DX,DY to BUF[BUFSIZ], return its length.
+ * If the route is longer than BUFSIZ-1 characters, it's truncated.
+ * You must compute path cost first, with path_find_to().
+ * SX,SY must be on a shortest path from the current source to DX,DY.
+ */
+size_t
+path_find_route(char *buf, size_t bufsz,
+               coord sx, coord sy, coord dx, coord dy)
+{
+    coord x, y;
+    size_t i, len;
+    int suid, uid, d;
+
+    suid = XYOFFSET(sx, sy);
+    assert(bufsz > 0 && !pf_is_unvisited(suid));
+
+    i = bufsz;
+    buf[--i] = 0;
+    len = 0;
+
+    x = dx;
+    y = dy;
+    for (;;) {
+       DPRINTF("pf: %d,%d %.*s%.*s\n",
+               x, y,
+               (int)(bufsz - i), buf + i,
+               len >= bufsz ? (int)i : 0, buf);
+       uid = XYOFFSET(x, y);
+       assert(!pf_is_unvisited(uid));
+       d = pf_map[uid].dir;
+       if (d == DIR_STOP || uid == suid)
+           break;
+       if (!i)
+           i = bufsz;
+       buf[--i] = dirch[d];
+       len++;
+       x = x_in_dir(x, rev_dir(d));
+       y = y_in_dir(y, rev_dir(d));
+    }
+
+    assert(x == sx && y == sy);
+    if (len >= bufsz)
+       bufrotate(buf, bufsz, i);
+    else {
+       assert(i + len < bufsz);
+       memmove(buf, buf + i, len + 1);
+    }
+    return len;
+}
+
+/*
+ * Rotate BUF[BUFSZ] to put BUF[I] into BUF[0], and zero-terminate.
+ */
+static char *
+bufrotate(char *buf, size_t bufsz, size_t i)
+{
+    char tmp[64];
+    size_t n;
+
+    while (i) {
+       n = MIN(i, sizeof(tmp));
+       memcpy(tmp, buf, n);
+       memcpy(buf, buf + n, bufsz - n);
+       memcpy(buf + bufsz - n, tmp, n);
+       i -= n;
+    }
+    buf[bufsz - 1] = 0;
+    return buf;
+}
+
+#ifdef PATH_FIND_DEBUG
+void
+path_find_visualize(coord sx, coord sy, coord dx, coord dy)
+{
+    int uid;
+    int xmin, xmax, ymin, ymax, x, y, odd, ch;
+    double c, u, cost;
+    char buf[1024];
+
+    assert(pf_cost(XYOFFSET(sx, sy)) == 0.0);
+    c = pf_cost(XYOFFSET(dx, dy));
+    u = c / 10.0;
+
+    /* find bounding box */
+    xmin = xmax = 0;
+    ymin = ymax = 0;
+    for (y = -WORLD_Y / 2; y < WORLD_Y / 2; y++) {
+       odd = ((sx + -WORLD_X / 2) ^ (sy + y)) & 1;
+       for (x = -WORLD_X / 2 + odd; x < WORLD_X / 2; x += 2) {
+           uid = XYOFFSET(XNORM(sx + x), YNORM(sy + y));
+           if (pf_is_unvisited(uid))
+               continue;
+           if (xmin > x)
+               xmin = x;
+           if (xmax < x)
+               xmax = x;
+           if (ymin > y)
+               ymin = y;
+           if (ymax < y)
+               ymax = y;
+       }
+    }
+    printf("bbox %d:%d,%d:%d origin %d,%d\n",
+          xmin, xmax, ymin, ymax, sx, sy);
+
+    for (y = ymin; y <= ymax; y++) {
+       odd = ((sx + xmin) ^ (sy + y)) & 1;
+       if (odd)
+           printf(" ");
+       for (x = xmin + odd; x <= xmax; x += 2) {
+           uid = XYOFFSET(XNORM(sx + x), YNORM(sy + y));
+           if (pf_is_unvisited(uid))
+               ch = ' ';
+           else if (uid == XYOFFSET(dx, dy))
+               ch = 'D';
+           else if (uid == XYOFFSET(sx, sy))
+               ch = 'S';
+           else {
+               cost = pf_cost(uid);
+               ch = cost > c ? '+' : '0' + (int)(10 * (cost / c));
+           }
+           printf(" %c", ch);
+       }
+       printf("\n");
+    }
+    path_find_route(buf, sizeof(buf), sx, sy, dx, dy);
+    printf("%s %g\n", buf, pf_cost(XYOFFSET(dx, dy)));
+}
+#endif
+
+#ifdef PATH_FIND_STATS
+void
+path_find_print_stats(void)
+{
+    printf("pathfind %u searches, %u sources, %u opened, %u closed,"
+          " %u heap max, %zu bytes, %u noway, %g avg cost\n",
+          pf_nsearch, pf_nsource, pf_nopen, pf_nclose,
+          pf_nheap_max,
+          (WORLD_SZ() * (sizeof(*pf_map) + sizeof(*pf_heap))),
+          pf_noway, pf_nsearch ? pf_sumcost / pf_nsearch : 0.0);
+}
+#endif
+
+/*
+ * Empire interface glue
+ */
+
+static double
+cost_land(natid actor, int uid, int mobtype)
+{
+    /*
+     * Non-negative cost must not depend on ACTOR, see BestLandPath().
+     */
+    struct sctstr *sp = (void *)empfile[EF_SECTOR].cache;
+
+    if (sp[uid].sct_own != actor)
+       return -1.0;
+    return sector_mcost(&sp[uid], mobtype);
+}
+
+static double
+cost_move(natid actor, int uid)
+{
+    return cost_land(actor, uid, MOB_MOVE);
+}
+
+static double
+cost_march(natid actor, int uid)
+{
+    return cost_land(actor, uid, MOB_MARCH);
+}
+
+static double
+cost_rail(natid actor, int uid)
+{
+    return cost_land(actor, uid, MOB_RAIL);
+}
+
+static double
+cost_sail(natid actor, int uid)
+{
+    struct sctstr *sp = (void *)empfile[EF_SECTOR].cache;
+    natid sctown = sp[uid].sct_own;
+    char *bmap;
+
+    if (sctown && relations_with(sctown, actor) == ALLIED) {
+       /* FIXME duplicates shp_check_nav() logic */
+       switch (dchr[sp[uid].sct_type].d_nav) {
+       case NAVOK:
+           return 1.0;
+       case NAV_CANAL:
+           /* FIXME return 1.0 when all ships have M_CANAL */
+           return -1.0;
+       case NAV_02:
+           return sp[uid].sct_effic >= 2 ? 1.0 : -1.0;
+       case NAV_60:
+           return sp[uid].sct_effic >= 60 ? 1.0 : -1.0;
+       default:
+           return -1.0;
+       }
+    }
+
+    bmap = ef_ptr(EF_BMAP, actor);
+    return bmap[uid] == '.' || bmap[uid] == ' ' || bmap[uid] == 0
+       ? 1.0 : -1.0;
+}
+
+static double
+cost_fly(natid actor, int uid)
+{
+    return 1.0;
+}
+
+static double (*cost_tab[])(natid, int) = {
+    cost_move, cost_march, cost_rail, cost_sail, cost_fly
+};
+
+/*
+ * Start finding paths from SX,SY.
+ * Use mobility costs for ACTOR and MOBTYPE.
+ */
+void
+path_find_from(coord sx, coord sy, natid actor, int mobtype)
+{
+    pf_set_source(sx, sy, actor, cost_tab[mobtype]);
+}
+
+/*
+ * Find cheapest path from SX,SY to DX,DY, return its mobility cost.
+ * Use mobility costs for ACTOR and MOBTYPE.
+ */
+double
+path_find(coord sx, coord sy, coord dx, coord dy, natid actor, int mobtype)
+{
+    pf_set_source(sx, sy, actor, cost_tab[mobtype]);
+    return path_find_to(dx, dy);
+}
index 5ae391766cc474aab2c24e411f16518aa573c6e7..57abc754388357f26a291ef6dff92d78ce9694fe 100644 (file)
@@ -63,8 +63,8 @@ move_ground(struct sctstr *start, struct sctstr *end,
     int intcost;
     int takedam = *dam;
     int out = 0;
-    char bpath[512];
-    char buf2[512];
+    char bpath[1024];
+    char buf2[1024];
     char prompt[128];
     char buf[1024];
 
index b621db5474b601d7a2a71cc164492b0f1ea874a5..9da4a8873feb58440b52674717c7bc572982bfb0 100644 (file)
@@ -122,14 +122,73 @@ finish_sects(int etu)
 
 }
 
+#ifdef USE_PATH_FIND
+static int
+distcmp(const void *p, const void *q)
+{
+    int a = *(int *)p;
+    int b = *(int *)q;
+    struct sctstr *sp = (void *)empfile[EF_SECTOR].cache;
+    int d;
+
+    d = sp[b].sct_dist_y - sp[a].sct_dist_y;
+    if (d)
+       return d;
+    d = sp[b].sct_dist_x - sp[a].sct_dist_x;
+    if (d)
+       return d;
+    return b - a;
+}
+#endif
+
 static void
 assemble_dist_paths(double *import_cost)
 {
-    char *path;
-    double d;
     struct sctstr *sp;
     struct sctstr *dist;
     int n;
+#ifdef USE_PATH_FIND
+    static int *job;
+    int uid, i;
+    coord dx = 1, dy = 0;      /* invalid */
+
+    if (!job)
+       job = malloc(WORLD_SZ() * sizeof(*job));
+
+    n = 0;
+    for (uid = 0; NULL != (sp = getsectid(uid)); uid++) {
+       import_cost[uid] = -1;
+       if (sp->sct_dist_x == sp->sct_x && sp->sct_dist_y == sp->sct_y)
+           continue;
+       job[n++] = uid;
+    }
+
+#ifdef PATH_FIND_STATS
+    printf("dist path reuse %zu bytes, %d/%d used\n",
+          WORLD_SZ() * sizeof(*job), n, WORLD_SZ());
+#endif
+
+    qsort(job, n, sizeof(*job), distcmp);
+
+    for (i = 0; i < n; i++) {
+       uid = job[i];
+       sp = getsectid(uid);
+       dist = getsectp(sp->sct_dist_x, sp->sct_dist_y);
+       if (CANT_HAPPEN(!dist))
+           continue;
+       if (sp->sct_own != dist->sct_own)
+           continue;
+       if (sp->sct_dist_x != dx || sp->sct_dist_y != dy) {
+           dx = sp->sct_dist_x;
+           dy = sp->sct_dist_y;
+           path_find_from(dx, dy, dist->sct_own, MOB_MOVE);
+       }
+       import_cost[uid] = path_find_to(sp->sct_x, sp->sct_y);
+    }
+    path_find_print_stats();
+#else  /* !USE_PATH_FIND */
+    char *path;
+    double d;
     char buf[512];
 
     for (n = 0; NULL != (sp = getsectid(n)); n++) {
@@ -148,4 +207,5 @@ assemble_dist_paths(double *import_cost)
        if (path)
            import_cost[n] = d;
     }
+#endif /* !USE_PATH_FIND */
 }