Merge branch 'pathfind' into pathfind-test
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
This commit is contained in:
commit
d8bef8e806
5 changed files with 832 additions and 8 deletions
|
@ -27,12 +27,13 @@
|
|||
* 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);
|
||||
|
|
|
@ -24,13 +24,14 @@
|
|||
*
|
||||
* ---
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
/*
|
||||
|
@ -47,16 +48,101 @@
|
|||
|
||||
#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 */
|
||||
|
|
660
src/lib/common/pathfind.c
Normal file
660
src/lib/common/pathfind.c
Normal file
|
@ -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);
|
||||
}
|
|
@ -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];
|
||||
|
||||
|
|
|
@ -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 */
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue