/*
 *  Empire - A multi-player, client/server Internet based war game.
 *  Copyright (C) 1986-2021, 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.
 *
 *  ---
 *
 *  fairland.c: Create a nice, new world
 *
 *  Known contributors to this file:
 *     Ken Stevens, 1995
 *     Steve McClure, 1998
 *     Markus Armbruster, 2004-2020
 */

/*
 * How fairland works
 *
 * 1. Place capitals
 *
 * Place the capitals on the torus in such a way so as to maximize
 * their distances from one another.  This uses the perturbation
 * technique of calculus of variations.
 *
 * 2. Grow start islands ("continents")
 *
 * For all continents, add the first sector at the capital's location,
 * and the second right to it.  These are the capital sectors.  Then
 * add one sector to each continent in turn, until they have the
 * specified size.
 *
 * Growth uses weighted random sampling to pick one sector from the
 * set of adjacent sea sectors that aren't too close to another
 * continent.  Growth operates in spiking mode with a chance given by
 * the spike percentage.  When "spiking", a sector's weight increases
 * with number of adjacent sea sectors.  This directs the growth away
 * from land, resulting in spikes.  When not spiking, the weight
 * increases with the number of adjacent land sectors.  This makes the
 * island more rounded.
 *
 * If growing fails due to lack of room, start over.  If it fails too
 * many times, give up and terminate unsuccessfully.
 *
 * 3. Place and grow additional islands
 *
 * Each continent has a "sphere of influence": the set of sectors
 * closer to it than to any other continent.  Each island is entirely
 * in one such sphere, and each sphere contains the same number of
 * islands with the same sizes.
 *
 * First, split the specified number of island sectors per continent
 * randomly into the island sizes.  Sort by size so that larger
 * islands are grown before smaller ones, to give the large ones the
 * best chance to grow to their planned size.
 *
 * Then place one island's first sector into each sphere, using
 * weighted random sampling with weights favoring sectors away from
 * land and other spheres.  Add one sector to each island in turn,
 * until they have the intended size.  Repeat until the specified
 * number of islands has been grown.
 *
 * If placement fails due to lack of room, start over, just like for
 * continents.
 *
 * Growing works as for continents, except the minimum distance for
 * additional islands applies, and growing simply stops when any of
 * the islands being grown lacks the room to grow further.  The number
 * of sectors not grown carries over to the next island size.
 *
 * 4. Compute elevation
 *
 * First, use a simple random hill algorithm to assign raw elevations:
 * initialize elevation to zero, then randomly raise circular hills on
 * land / lower circular depressions at sea.  Their size and height
 * depends on the distance to the coast.
 *
 * Then, elevate islands one after the other.
 *
 * Set the capitals' elevation to a fixed value.  Process the
 * remaining sectors in order of increasing raw elevation, first
 * non-mountains, then mountains.  Non-mountain elevation starts at 1,
 * and increases linearly to just below "high" elevation.  Mountain
 * elevation starts at "high" elevation, and increases linearly.
 *
 * This gives islands of the same size the same set of elevations.
 * Larger islands get more and taller mountains.
 *
 * Finally, elevate sea: normalize the raw elevations to [-127:-1].
 *
 * 5. Set resources
 *
 * Sector resources are simple functions of elevation.  You can alter
 * iron_conf[], gold_conf[], fert_conf[], oil_conf[], and uran_conf[]
 * to customize them.
 */

#include <config.h>

#include <assert.h>
#include <errno.h>
#include <limits.h>
#include <stdarg.h>
#include <stdio.h>
#include <unistd.h>
#include "chance.h"
#include "optlist.h"
#include "path.h"
#include "prototypes.h"
#include "sect.h"
#include "version.h"
#include "xy.h"

/*
 * Number of retries when growing land fails
 */
#define NUMTRIES 10

/* do not change these defines */
#define LANDMIN		1	/* plate altitude for normal land */
#define PLATMIN		36	/* plate altitude for plateau */
#define HIGHMIN		98	/* plate altitude for mountains */

/*
 * Resource configuration

 * Resources are determined by elevation.  The map from elevation to
 * resource is defined as a linear interpolation of resource data
 * points (elev, res) defined in the tables below.  Elevations range
 * from -127 to 127, and resource values from 0 to 100.
 */

struct resource_point {
    int elev, res;
};

struct resource_point iron_conf[] = {
    { -127, 0 },
    { 21, 0 },
    { 85, 100 },
    { HIGHMIN - 1, 100 },
    { HIGHMIN , 0 },
    { 127, 0 } };

struct resource_point gold_conf[] = {
    { -127, 0 },
    { 35, 0 },
    { HIGHMIN - 1, 80 },
    { HIGHMIN, 80 },
    { 127, 85 } };

struct resource_point fert_conf[] = {
    { -127, 100 },
    { -59, 100 },
    { LANDMIN - 1, 41 },
    { LANDMIN, 100 },
    { 10, 100 },
    { 56, 0 },
    { 127, 0 } };

struct resource_point oil_conf[] = {
    { -127, 100 },
    { -49, 100 },
    { LANDMIN - 1, 2 },
    { LANDMIN, 100 },
    { 6, 100 },
    { 34, 0 },
    { 127, 0 } };

struct resource_point uran_conf[] = {
    { -127, 0 },
    { 55, 0 },
    { 90, 100 },
    { 97, 100 },
    { 98, 0 },
    { 127, 0 } };

/*
 * Program arguments and options
 */
static char *program_name;
static int nc, sc;		/* number and size of continents */
static int ni, is;		/* number and size of islands */
#define DEFAULT_SPIKE 10
static int sp = DEFAULT_SPIKE;	/* spike percentage */
#define DEFAULT_MOUNTAIN 0
static int pm = DEFAULT_MOUNTAIN; /* mountain percentage */
#define DEFAULT_CONTDIST 2
static int di = DEFAULT_CONTDIST; /* min. distance between continents */
#define DEFAULT_ISLDIST 1
static int id = DEFAULT_ISLDIST;  /* ... continents and islands */
/* don't let the islands crash into each other.
   1 = don't merge, 0 = merge. */
static int DISTINCT_ISLANDS = 1;
static int quiet;
#define DEFAULT_OUTFILE_NAME "newcap_script"
static const char *outfile = DEFAULT_OUTFILE_NAME;

#define STABLE_CYCLE 4		/* stability required for perturbed capitals */
#define DRIFT_BEFORE_CHECK ((WORLD_X + WORLD_Y)/2)
#define DRIFT_MAX ((WORLD_X + WORLD_Y)*2)

#define new_x(newx) (((newx) + WORLD_X) % WORLD_X)
#define new_y(newy) (((newy) + WORLD_Y) % WORLD_Y)

struct xy {
    coord x, y;
};

/*
 * Capital locations
 * The i-th capital is at cap[i].
 */
static struct xy *cap;

/*
 * Island sizes
 * isecs[i] is the size of the i-th island.
 */
static int *isecs;

/*
 * Island sectors
 * The i-th island's j-th sector is at sect[i][j].
 */
struct xy **sect;

/*
 * Island at x, y
 * own[XYOFFSET(x, y)] is x,y's island number, -1 if water.
 */
static short *own;

/*
 * Adjacent land sectors
 * adj_land[XYOFFSET(x, y)] bit d is set exactly when the sector next
 * to x, y in direction d is land.
 */
static unsigned char *adj_land;

/*
 * Elevation at x,y
 * elev[XYOFFSET(x, y)] is x,y's elevation.
 */
static short *elev;

/*
 * Exclusive zones
 * Each island is surrounded by an exclusive zone where only it may
 * grow.  The width of the zone depends on minimum distances.
 * While growing continents, it is @di sectors wide.
 * While growing additional islands, it is @id sectors wide.
 * DISTINCT_ISLANDS nullifies the exclusive zone then.
 * xzone[XYOFFSET(x, y)] is -1 when the sector is in no exclusive
 * zone, a (non-negative) island number when it is in that island's
 * exclusive zone and no other, and -2 when it is in multiple
 * exclusive zones.
 */
static short *xzone;

/*
 * Set of sectors seen already
 * Increment @cur_seen to empty the set of sectors seen, set
 * seen[XYOFFSET(x, y)] to @cur_seen to add x,y to the set.
 */
static unsigned *seen;
static unsigned cur_seen;

/*
 * Closest continent and "distance"
 * closest[XYOFFSET(x, y)] is the closest continent's number.
 * distance[] is complicated; see init_spheres_of_influence() and
 * init_distance_to_coast().
 */
static natid *closest;
static unsigned short *distance;

/*
 * Queue for breadth-first search
 */
static int *bfs_queue;
static int bfs_queue_head, bfs_queue_tail;

static const char *numletter =
    "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";

static void print_vars(void);
static void qprint(const char * const fmt, ...)
    ATTRIBUTE((format (printf, 1, 2)));
static void help(char *);
static void usage(void);
static void parse_args(int argc, char *argv[]);
static void allocate_memory(void);
static void init(void);
static int drift(void);
static int stable(int);
static void drift_capital(int);
static int grow_continents(void);
static int grow_islands(void);
static void create_elevations(void);
static void elevate_prep(void);
static void elevate_land(void);
static void elevate_sea(void);
static void write_sects(void);
static void output(void);
static int write_newcap_script(void);

/* Debugging aids: */
void print_own_map(void);
void print_xzone_map(void);
void print_closest_map(void);
void print_distance_map(void);
void print_elev_map(void);


int
main(int argc, char *argv[])
{
    int opt;
    char *config_file = NULL;
    int try, done;
    unsigned rnd_seed = 0;
    int seed_set = 0;

    program_name = argv[0];

    while ((opt = getopt(argc, argv, "e:hiqR:s:v")) != EOF) {
	switch (opt) {
	case 'e':
	    config_file = optarg;
	    break;
	case 'i':
	    DISTINCT_ISLANDS = 0;
	    break;
	case 'q':
	    quiet = 1;
	    break;
	case 'R':
	    rnd_seed = strtoul(optarg, NULL, 10);
	    seed_set = 1;
	    break;
	case 's':
	    outfile = optarg;
	    break;
	case 'h':
	    usage();
	    exit(0);
	case 'v':
	    printf("%s\n\n%s", version, legal);
	    exit(0);
	default:
	    help(NULL);
	    exit(1);
	}
    }

    if (!seed_set)
	rnd_seed = pick_seed();
    seed_prng(rnd_seed);
    empfile_init();
    if (emp_config(config_file) < 0)
	exit(1);
    empfile_fixup();

    parse_args(argc - optind, argv + optind);

    allocate_memory();
    print_vars();

    qprint("\n        #*# ...fairland rips open a rift in the datumplane... #*#\n\n");
    qprint("seed is %u\n", rnd_seed);
    try = 0;
    do {
	init();
	if (try)
	    qprint("\ntry #%d (out of %d)...\n", try + 1, NUMTRIES);
	qprint("placing capitals...\n");
	if (!drift())
	    qprint("unstable drift\n");
	qprint("growing continents...\n");
	done = grow_continents();
	if (!done)
	    continue;
	qprint("growing islands:");
	done = grow_islands();
    } while (!done && ++try < NUMTRIES);
    if (!done) {
	fprintf(stderr, "%s: world not large enough for this much land\n",
		program_name);
	exit(1);
    }
    qprint("elevating land...\n");
    create_elevations();

    qprint("writing to sectors file...\n");
    if (!write_newcap_script())
	exit(1);
    if (chdir(gamedir)) {
	fprintf(stderr, "%s: can't chdir to %s (%s)\n",
		program_name, gamedir, strerror(errno));
	exit(1);
    }
    if (!ef_open(EF_SECTOR, EFF_MEM | EFF_NOTIME))
	exit(1);
    write_sects();
    if (!ef_close(EF_SECTOR))
	exit(1);

    output();
    qprint("\n\nA script for adding all the countries can be found in \"%s\".\n",
	   outfile);
    exit(0);
}

static void
print_vars(void)
{
    if (quiet)
	return;
    puts("Creating a planet with:\n");
    printf("%d continents\n", nc);
    printf("continent size: %d\n", sc);
    printf("number of islands: %d\n", ni);
    printf("average size of islands: %d\n", is);
    printf("spike: %d%%\n", sp);
    printf("%d%% of land is mountain (each continent will have %d mountains)\n",
	   pm, (pm * sc) / 100);
    printf("minimum distance between continents: %d\n", di);
    printf("minimum distance from islands to continents: %d\n", id);
    printf("World dimensions: %dx%d\n", WORLD_X, WORLD_Y);
}

static void
qprint(const char *const fmt, ...)
{
    va_list ap;

    if (!quiet) {
	va_start(ap, fmt);
	vfprintf(stdout, fmt, ap);
	va_end(ap);
    }
}

static void
help(char *complaint)
{
    if (complaint)
	fprintf(stderr, "%s: %s\n", program_name, complaint);
    fprintf(stderr, "Try -h for help.\n");
}

static void
usage(void)
{
    printf("Usage: %s [OPTION]... NC SC [NI] [IS] [SP] [PM] [DI] [ID]\n"
	   "  -e CONFIG-FILE  configuration file\n"
	   "                  (default %s)\n"
	   "  -i              islands may merge\n"
	   "  -q              quiet\n"
	   "  -R SEED         seed for random number generator\n"
	   "  -s SCRIPT       name of script to create (default %s)\n"
	   "  -h              display this help and exit\n"
	   "  -v              display version information and exit\n"
	   "  NC              number of continents\n"
	   "  SC              continent size\n"
	   "  NI              number of islands (default NC)\n"
	   "  IS              average island size (default SC/2)\n"
	   "  SP              spike percentage: 0 = round, 100 = snake (default %d)\n"
	   "  PM              percentage of land that is mountain (default %d)\n"
	   "  DI              minimum distance between continents (default %d)\n"
	   "  ID              minimum distance from islands to continents (default %d)\n",
	   program_name, dflt_econfig, DEFAULT_OUTFILE_NAME,
	   DEFAULT_SPIKE, DEFAULT_MOUNTAIN, DEFAULT_CONTDIST, DEFAULT_ISLDIST);
}

static void
parse_args(int argc, char *argv[])
{
    int dist_max = mapdist(0, 0, WORLD_X / 2, WORLD_Y / 2);

    if (argc < 2) {
	help("missing arguments");
	exit(1);
    }
    if (argc > 8) {
	help("too many arguments");
	exit(1);
    }
    nc = atoi(argv[0]);
    if (nc < 1) {
	fprintf(stderr, "%s: number of continents must be > 0\n",
		program_name);
	exit(1);
    }

    sc = atoi(argv[1]);
    if (sc < 2) {
	fprintf(stderr, "%s: size of continents must be > 1\n",
		program_name);
	exit(1);
    }

    ni = nc;
    is = sc / 2;

    if (argc > 2)
	ni = atoi(argv[2]);
    if (ni < 0) {
	fprintf(stderr, "%s: number of islands must be >= 0\n",
		program_name);
	exit(1);
    }
    if (ni % nc) {
	fprintf(stderr, "%s: number of islands must be a multiple of"
		" the number of continents\n",
		program_name);
	exit(1);
    }

    if (argc > 3)
	is = atoi(argv[3]);
    if (is < 1) {
	fprintf(stderr, "%s: size of islands must be > 0\n",
		program_name);
	exit(1);
    }

    if (argc > 4)
	sp = atoi(argv[4]);
    if (sp < 0 || sp > 100) {
	fprintf(stderr,
		"%s: spike percentage must be between 0 and 100\n",
		program_name);
	exit(1);
    }

    if (argc > 5)
	pm = atoi(argv[5]);
    if (pm < 0 || pm > 100) {
	fprintf(stderr,
		"%s: mountain percentage must be between 0 and 100\n",
		program_name);
	exit(1);
    }

    if (argc > 6)
	di = atoi(argv[6]);
    if (di < 0) {
	fprintf(stderr, "%s: distance between continents must be >= 0\n",
		program_name);
	exit(1);
    }
    if (di > dist_max) {
	fprintf(stderr, "%s: distance between continents too large\n",
		program_name);
	exit(1);
    }

    if (argc > 7)
	id = atoi(argv[7]);
    if (id < 0) {
	fprintf(stderr,
		"%s: distance from islands to continents must be >= 0\n",
		program_name);
	exit(1);
    }
    if (id > dist_max) {
	fprintf(stderr,
		"%s: distance from islands to continents too large\n",
		program_name);
	exit(1);
    }
}


/*
 * Variable initialization
 */

static void
allocate_memory(void)
{
    int i;

    cap = malloc(nc * sizeof(*cap));
    own = malloc(WORLD_SZ() * sizeof(*own));
    adj_land = malloc(WORLD_SZ() * sizeof(*adj_land));
    elev = calloc(WORLD_SZ(), sizeof(*elev));
    xzone = malloc(WORLD_SZ() * sizeof(*xzone));
    seen = calloc(WORLD_SZ(), sizeof(*seen));
    closest = malloc(WORLD_SZ() * sizeof(*closest));
    distance = malloc(WORLD_SZ() * sizeof(*distance));
    bfs_queue = malloc(WORLD_SZ() * sizeof(*bfs_queue));
    isecs = calloc(nc + ni, sizeof(int));
    sect = malloc((nc + ni) * sizeof(*sect));
    for (i = 0; i < nc; i++)
	sect[i] = malloc(sc * sizeof(**sect));
    for (i = nc; i < nc + ni; i++)
	sect[i] = malloc(is * 2 * sizeof(**sect));
}

static void
init(void)
{
    int i;

    for (i = 0; i < WORLD_SZ(); i++)
	own[i] = -1;
    memset(adj_land, 0, WORLD_SZ() * sizeof(*adj_land));
}


/*
 * Drift the capitals until they are as far away from each other as possible
 */

/*
 * How isolated is capital @j at @newx,@newy?
 * Return the distance to the closest other capital.
 */
static int
iso(int j, int newx, int newy)
{
    int d = INT_MAX;
    int i, md;

    for (i = 0; i < nc; ++i) {
	if (i == j)
	    continue;
	md = mapdist(cap[i].x, cap[i].y, newx, newy);
	if (md < d)
	    d = md;
    }

    return d;
}

/*
 * Drift the capitals
 * Return 1 for a stable drift, 0 for an unstable one.
 */
static int
drift(void)
{
    int turns, i;

    for (i = 0; i < nc; i++) {
	cap[i].y = (2 * i) / WORLD_X;
	cap[i].x = (2 * i) % WORLD_X + cap[i].y % 2;
	if (cap[i].y >= WORLD_Y) {
	    fprintf(stderr,
		    "%s: world not big enough for all the continents\n",
		    program_name);
	    exit(1);
	}
    }

    for (turns = 0; turns < DRIFT_MAX; ++turns) {
	if (stable(turns))
	    return 1;
	for (i = 0; i < nc; ++i)
	    drift_capital(i);
    }
    return 0;
}

/*
 * Has the drift stabilized?
 * @turns is the number of turns so far.
 */
static int
stable(int turns)
{
    static int mc[STABLE_CYCLE];
    int i, isod, d = 0, stab = 1;

    if (!turns) {
	for (i = 0; i < STABLE_CYCLE; i++)
	    mc[i] = i;
    }

    if (turns <= DRIFT_BEFORE_CHECK)
	return 0;

    for (i = 0; i < nc; ++i) {
	isod = iso(i, cap[i].x, cap[i].y);
	if (isod > d)
	    d = isod;
    }

    for (i = 0; i < STABLE_CYCLE; ++i)
	if (d != mc[i])
	    stab = 0;

    mc[turns % STABLE_CYCLE] = d;
    return stab;
}

/*
 * Drift capital @j.
 * Move it to an adjacent sector where it is at least as isolated.
*/
static void
drift_capital(int j)
{
    int dir, i, newx, newy;

    dir = DIR_L + roll0(6);
    for (i = 0; i < 6; i++) {
	if (dir > DIR_LAST)
	    dir -= 6;
	newx = new_x(cap[j].x + diroff[dir][0]);
	newy = new_y(cap[j].y + diroff[dir][1]);
	dir++;
	if (iso(j, newx, newy) >= iso(j, cap[j].x, cap[j].y)) {
	    cap[j].x = newx;
	    cap[j].y = newy;
	    return;
	}
    }
}


/*
 * Grow land
 */

static int
is_coastal(int x, int y)
{
    return adj_land[XYOFFSET(x, y)]
	!= (1u << (DIR_LAST + 1)) - (1u << DIR_FIRST);
}

struct hexagon_iter {
    int dir, i, n;
};

/*
 * Start iterating around @x0,@y0 at distance @d.
 * Set *x,*y to coordinates of the first sector.
 */
static inline void
hexagon_first(struct hexagon_iter *iter, int x0, int y0, int n,
	      int *x, int *y)
{
    *x = new_x(x0 - 2 * n);
    *y = y0;
    iter->dir = DIR_FIRST;
    iter->i = 0;
    iter->n = n;
}

/*
 * Continue iteration started with hexagon_first().
 * Set *x,*y to coordinates of the next sector.
 * Return whether we're back at the first sector, i.e. iteration is
 * complete.
 */
static inline int
hexagon_next(struct hexagon_iter *iter, int *x, int *y)
{
    *x = new_x(*x + diroff[iter->dir][0]);
    *y = new_y(*y + diroff[iter->dir][1]);
    iter->i++;
    if (iter->i == iter->n) {
	iter->i = 0;
	iter->dir++;
    }
    return iter->dir <= DIR_LAST;
}

/*
 * Is @x,@y in no exclusive zone other than perhaps @c's?
 */
static int
xzone_ok(int c, int x, int y)
{
    int off = XYOFFSET(x, y);

    return xzone[off] == c || xzone[off] == -1;
}

/*
 * Add sectors within distance @dist of @x,@y to @c's exclusive zone.
 */
static void
xzone_around_sector(int c, int x, int y, int dist)
{
    int d, x1, y1, off;
    struct hexagon_iter hexit;

    assert(xzone_ok(c, x, y));

    xzone[XYOFFSET(x, y)] = c;
    for (d = 1; d <= dist; d++) {
	hexagon_first(&hexit, x, y, d, &x1, &y1);
	do {
	    off = XYOFFSET(x1, y1);
	    if (xzone[off] == -1)
		xzone[off] = c;
	    else if (xzone[off] != c)
		xzone[off] = -2;
	} while (hexagon_next(&hexit, &x1, &y1));
    }
}

/*
 * Add sectors within distance @dist to island @c's exclusive zone.
 */
static void
xzone_around_island(int c, int dist)
{
    int i;

    for (i = 0; i < isecs[c]; i++)
	xzone_around_sector(c, sect[c][i].x, sect[c][i].y, dist);
}

/*
 * Initialize exclusive zones around @n islands.
 */
static void
xzone_init(int n)
{
    int i, c;

    for (i = 0; i < WORLD_SZ(); i++)
	xzone[i] = -1;

    for (c = 0; c < n; c++)
	xzone_around_island(c, id);
}

/*
 * Initialize breadth-first search.
 */
static void
bfs_init(void)
{
    int i;

    for (i = 0; i < WORLD_SZ(); i++) {
	closest[i] = -1;
	distance[i] = USHRT_MAX;
    }

    bfs_queue_head = bfs_queue_tail = 0;
}

/*
 * Add sector @x,@y to the BFS queue.
 * It's closest to @c, with distance @dist.
 */
static void
bfs_enqueue(int c, int x, int y, int dist)
{
    int off = XYOFFSET(x, y);

    assert(dist < distance[off]);
    closest[off] = c;
    distance[off] = dist;
    bfs_queue[bfs_queue_tail] = off;
    bfs_queue_tail++;
    if (bfs_queue_tail >= WORLD_SZ())
	bfs_queue_tail = 0;
    assert(bfs_queue_tail != bfs_queue_head);
}

/*
 * Search breadth-first until the queue is empty.
 */
static void
bfs_run_queue(void)
{
    int off, dist, i, noff, nx, ny;
    coord x, y;

    while (bfs_queue_head != bfs_queue_tail) {
	off = bfs_queue[bfs_queue_head];
	bfs_queue_head++;
	if (bfs_queue_head >= WORLD_SZ())
	    bfs_queue_head = 0;
	dist = distance[off] + 1;
	sctoff2xy(&x, &y, off);
	for (i = DIR_FIRST; i <= DIR_LAST; i++) {
	    nx = new_x(x + diroff[i][0]);
	    ny = new_y(y + diroff[i][1]);
	    noff = XYOFFSET(nx, ny);
	    if (dist < distance[noff]) {
		bfs_enqueue(closest[off], nx, ny, dist);
	    } else if (distance[noff] == dist) {
		if (closest[off] != closest[noff])
		    closest[noff] = (natid)-1;
	    } else
		assert(distance[noff] < dist);
	}
    }
}

/*
 * Add island @c's coastal sectors to the BFS queue, with distance 0.
 */
static void
bfs_enqueue_island(int c)
{
    int i;

    for (i = 0; i < isecs[c]; i++) {
	if (is_coastal(sect[c][i].x, sect[c][i].y))
	    bfs_enqueue(c, sect[c][i].x, sect[c][i].y, 0);
    }
}

/*
 * Enqueue spheres of influence borders for breadth-first search.
 */
static void
bfs_enqueue_border(void)
{
    int x, y, off, dir, nx, ny, noff;

    for (y = 0; y < WORLD_Y; y++) {
	for (x = y % 2; x < WORLD_X; x += 2) {
	    off = XYOFFSET(x, y);
	    if (distance[off] <= id + 1)
		continue;
	    if (closest[off] == (natid)-1)
		continue;
	    for (dir = DIR_FIRST; dir <= DIR_LAST; dir++) {
		nx = new_x(x + diroff[dir][0]);
		ny = new_y(y + diroff[dir][1]);
		noff = XYOFFSET(nx, ny);
		if (closest[noff] != closest[off]) {
		    bfs_enqueue(closest[off], x, y, id + 1);
		    break;
		}
	    }
	}
    }
}

/*
 * Compute spheres of influence
 * A continent's sphere of influence is the set of sectors closer to
 * it than to any other continent.
 * Set closest[XYOFFSET(x, y)] to the closest continent's number,
 * -1 if no single continent is closest.
 * Set distance[XYOFFSET(x, y)] to the minimum of the distance to the
 * closest coastal land sector and the distance to just outside the
 * sphere of influence plus @id.  For sea sectors within a continent's
 * sphere of influence, distance[off] - id is the distance to the
 * border of the area where additional islands can be placed.
 */
static void
init_spheres_of_influence(void)
{
    int c;

    bfs_init();
    for (c = 0; c < nc; c++)
	bfs_enqueue_island(c);
    bfs_run_queue();
    bfs_enqueue_border();
    bfs_run_queue();
}

/*
 * Precompute distance to coast
 * Set distance[XYOFFSET(x, y)] to the distance to the closest coastal
 * land sector.
 * Set closest[XYOFFSET(x, y)] to the closest continent's number,
 * -1 if no single continent is closest.
 */
static void
init_distance_to_coast(void)
{
    int c;

    bfs_init();
    for (c = 0; c < nc + ni; c++)
	bfs_enqueue_island(c);
    bfs_run_queue();
}

/*
 * Is @x,@y in the same sphere of influence as island @c?
 * Always true when @c is a continent.
 */
static int
is_in_sphere(int c, int x, int y)
{
    return c < nc || closest[XYOFFSET(x, y)] == c % nc;
}

/*
 * Can island @c grow at @x,@y?
 */
static int
can_grow_at(int c, int x, int y)
{
    return own[XYOFFSET(x, y)] == -1 && xzone_ok(c, x, y)
	&& is_in_sphere(c, x, y);
}

static void
adj_land_update(int x, int y)
{
    int is_land = own[XYOFFSET(x, y)] != -1;
    int dir, nx, ny, noff;

    for (dir = DIR_FIRST; dir <= DIR_LAST; dir++) {
	nx = new_x(x + diroff[dir][0]);
	ny = new_y(y + diroff[dir][1]);
	noff = XYOFFSET(nx, ny);
	if (is_land)
	    adj_land[noff] |= 1u << DIR_BACK(dir);
	else
	    adj_land[noff] &= ~(1u << DIR_BACK(dir));
    }
}

static void
add_sector(int c, int x, int y)
{
    int off = XYOFFSET(x, y);

    assert(own[off] == -1);
    xzone_around_sector(c, x, y, c < nc ? di : DISTINCT_ISLANDS ? id : 0);
    sect[c][isecs[c]].x = x;
    sect[c][isecs[c]].y = y;
    isecs[c]++;
    own[off] = c;
    adj_land_update(x, y);
}

static int
grow_weight(int c, int x, int y, int spike)
{
    int n, b;

    /*
     * #Land neighbors is #bits set in adj_land[].
     * Count them Brian Kernighan's way.
     */
    n = 0;
    for (b = adj_land[XYOFFSET(x, y)]; b; b &= b - 1)
	n++;
    assert(n > 0 && n < 7);

    if (spike)
	return (6 - n) * (6 - n);

    return n * n * n;
}

static int
grow_one_sector(int c)
{
    int spike = roll0(100) < sp;
    int wsum, newx, newy, i, x, y, off, dir, nx, ny, noff, w;

    assert(cur_seen < UINT_MAX);
    cur_seen++;
    wsum = 0;
    newx = newy = -1;

    for (i = 0; i < isecs[c]; i++) {
	x = sect[c][i].x;
	y = sect[c][i].y;
	off = XYOFFSET(x, y);

	for (dir = DIR_FIRST; dir <= DIR_LAST; dir++) {
	    if (adj_land[off] & (1u << dir))
		continue;
	    nx = new_x(x + diroff[dir][0]);
	    ny = new_y(y + diroff[dir][1]);
	    noff = XYOFFSET(nx, ny);
	    if (seen[noff] == cur_seen)
		continue;
	    assert(seen[noff] < cur_seen);
	    seen[noff] = cur_seen;
	    if (!can_grow_at(c, nx, ny))
		continue;
	    w = grow_weight(c, nx, ny, spike);
	    assert(wsum < INT_MAX - w);
	    wsum += w;
	    if (roll0(wsum) < w) {
		newx = nx;
		newy = ny;
	    }
	}
    }

    if (!wsum)
	return 0;

    add_sector(c, newx, newy);
    return 1;
}

/*
 * Grow the continents.
 * Return 1 on success, 0 on error.
 */
static int
grow_continents(void)
{
    int done = 1;
    int c, secs;

    xzone_init(0);

    for (c = 0; c < nc; ++c) {
	isecs[c] = 0;
	if (!can_grow_at(c, cap[c].x, cap[c].y)
	    || !can_grow_at(c, new_x(cap[c].x + 2), cap[c].y)) {
	    done = 0;
	    continue;
	}
	add_sector(c, cap[c].x, cap[c].y);
	add_sector(c, new_x(cap[c].x + 2), cap[c].y);
    }

    if (!done) {
	qprint("No room for continents\n");
	return 0;
    }

    for (secs = 2; secs < sc && done; secs++) {
	for (c = 0; c < nc; ++c) {
	    if (!grow_one_sector(c))
		done = 0;
	}
    }

    if (!done)
	qprint("Only managed to grow %d out of %d sectors.\n",
	       secs - 1, sc);
    return done;
}

/*
 * Place additional island @c's first sector.
 * Return 1 on success, 0 on error.
 */
static int
place_island(int c, int isiz)
{
    int n, x, y, d, w, newx, newy;

    n = 0;

    for (y = 0; y < WORLD_Y; y++) {
	for (x = y % 2; x < WORLD_X; x += 2) {
	    if (can_grow_at(c, x, y)) {
		d = distance[XYOFFSET(x, y)];
		assert(d > id);
		w = (d - id) * (d - id);
		n += MIN(w, (isiz + 2) / 3);
		if (roll0(n) < w) {
		    newx = x;
		    newy = y;
		}
	    }
	}
    }

    if (n)
	add_sector(c, newx, newy);
    return n;
}

static int
int_cmp(const void *a, const void *b)
{
    return *(int *)b - *(int *)a;
}

static int *
size_islands(void)
{
    int n = ni / nc;
    int *isiz = malloc(n * sizeof(*isiz));
    int r0, r1, i;

    isiz[0] = n * is;
    r1 = roll0(is);
    for (i = 1; i < n; i++) {
	r0 = r1;
	r1 = roll0(is);
	isiz[i] = is + r1 - r0;
	isiz[0] -= isiz[i];
    }

    qsort(isiz, n, sizeof(*isiz), int_cmp);
    return isiz;
}

/*
 * Grow the additional islands.
 * Return 1 on success, 0 on error.
 */
static int
grow_islands(void)
{
    int *island_size = size_islands();
    int xzone_valid = 0;
    int carry = 0;
    int i, j, c, done, secs, isiz, x, y;

    init_spheres_of_influence();

    for (i = 0; i < ni / nc; i++) {
	c = nc + i * nc;

	if (!xzone_valid)
	    xzone_init(c);

	carry += island_size[i];
	isiz = MIN(2 * is, carry);

	for (j = 0; j < nc; j++) {
	    isecs[c + j] = 0;
	    if (!place_island(c + j, isiz)) {
		qprint("\nNo room for island #%d\n", c - nc + j + 1);
		free(island_size);
		return 0;
	    }
	}

	done = 1;
	for (secs = 1; secs < isiz && done; secs++) {
	    for (j = 0; j < nc; j++) {
		if (!grow_one_sector(c + j))
		    done = 0;
	    }
	}

	if (!done) {
	    secs--;
	    for (j = 0; j < nc; j++) {
		if (isecs[c + j] != secs) {
		    isecs[c + j]--;
		    assert(isecs[c + j] == secs);
		    x = sect[c + j][secs].x;
		    y = sect[c + j][secs].y;
		    own[XYOFFSET(x, y)] = -1;
		    adj_land_update(x, y);
		}
	    }
	    xzone_valid = 0;
	}

	for (j = 0; j < nc; j++)
	    qprint(" %d(%d)", c - nc + j + 1, isecs[c + j]);

	carry -= secs;
    }

    free(island_size);
    qprint("\n");

    if (carry)
	qprint("Only managed to grow %d out of %d island sectors.\n",
	       is * ni - carry * nc, is * ni);

    return 1;
}


/*
 * Create elevations
 */

static void
create_elevations(void)
{
    elevate_prep();
    elevate_land();
    elevate_sea();
}

static int
elev_cmp(const void *p, const void *q)
{
    int a = *(int *)p;
    int b = *(int *)q;
    int delev = elev[a] - elev[b];

    return delev ? delev : a - b;
}

static void
elevate_prep(void)
{
    int n = WORLD_SZ() * 8;
    int off0, r, sign, elevation, d, x1, y1, off1;
    coord x0, y0;
    struct hexagon_iter hexit;

    init_distance_to_coast();

    while (n > 0) {
	off0 = roll0(WORLD_SZ());
	sctoff2xy(&x0, &y0, off0);
	if (own[off0] == -1) {
	    r = roll(MIN(3, distance[off0]));
	    sign = -1;
	} else {
	    r = roll(MIN(3, distance[off0]) + 1);
	    sign = 1;
	}
	elevation = elev[off0] + sign * r * r;
	elev[off0] = LIMIT_TO(elevation, SHRT_MIN, SHRT_MAX);
	n--;
	for (d = 1; d < r; d++) {
	    hexagon_first(&hexit, x0, y0, d, &x1, &y1);
	    do {
		off1 = XYOFFSET(x1, y1);
		elevation = elev[off1] + sign * (r * r - d * d);
		elev[off1] = LIMIT_TO(elevation, SHRT_MIN, SHRT_MAX);
		n--;
	    } while (hexagon_next(&hexit, &x1, &y1));
	}
    }
}

static void
elevate_land(void)
{
    int *off = malloc(MAX(sc, is * 2) * sizeof(*off));
    int max_nm = (pm * MAX(sc, is * 2)) / 100;
    int c, nm, i0, n, i;
    double elevation, delta;

    for (c = 0; c < nc + ni; c++) {
	nm = (pm * isecs[c]) / 100;
	i0 = c < nc ? 2 : 0;
	n = isecs[c] - i0;
	for (i = 0; i < i0; i++)
	    elev[XYOFFSET(sect[c][i].x, sect[c][i].y)] = PLATMIN;
	for (i = 0; i < n; i++)
	    off[i] = XYOFFSET(sect[c][i0 + i].x, sect[c][i0 + i].y);
	qsort(off, n, sizeof(*off), elev_cmp);
	delta = (double)(HIGHMIN - LANDMIN - 1) / (n - nm - 1);
	elevation = LANDMIN;
	for (i = 0; i < n - nm; i++) {
	    elev[off[i]] = (int)(elevation + 0.5);
	    elevation += delta;
	}
	elevation = HIGHMIN;
	delta = (127.0 - HIGHMIN) / max_nm;
	for (; i < n; i++) {
	    elevation += delta;
	    elev[off[i]] = (int)(elevation + 0.5);
	}
    }

    free(off);
}

static void
elevate_sea(void)
{
    int i, min;

    min = 0;
    for (i = 0; i < WORLD_SZ(); i++) {
	if (elev[i] < min)
	    min = elev[i];
    }

    for (i = 0; i < WORLD_SZ(); i++) {
	if (elev[i] < 0)
	    elev[i] = -1 - 126 * elev[i] / min;
    }
}

static int
elev_to_sct_type(int elevation)
{
    if (elevation < LANDMIN)
	return SCT_WATER;
    if (elevation < HIGHMIN)
	return SCT_RURAL;
    return SCT_MOUNT;
}


/*
 * Add resources
 */

/*
 * Map elevation @elev to a resource value according to @conf.
 * This is a linear interpolation on the data points in @conf.
 */
static int
elev_to_resource(int elev, struct resource_point conf[])
{
    int i, elev1, elev2, delev, res1, res2, dres;

    for (i = 1; elev > conf[i].elev; i++) ;
    assert(conf[i - 1].elev <= elev);

    elev1 = conf[i - 1].elev;
    elev2 = conf[i].elev;
    delev = elev2 - elev1;
    res1 = conf[i - 1].res;
    res2 = conf[i].res;
    dres = res2 - res1;
    return (int)(res1 + (double)((elev - elev1) * dres) / delev);
}

static void
add_resources(struct sctstr *sct)
{
    sct->sct_min = elev_to_resource(sct->sct_elev, iron_conf);
    sct->sct_gmin = elev_to_resource(sct->sct_elev, gold_conf);
    sct->sct_fertil = elev_to_resource(sct->sct_elev, fert_conf);
    sct->sct_oil = elev_to_resource(sct->sct_elev, oil_conf);
    sct->sct_uran = elev_to_resource(sct->sct_elev, uran_conf);
}


/*
 * Designate the sectors
 */

static void
write_sects(void)
{
    struct sctstr *sp;
    int i;

    for (i = 0; i < WORLD_SZ(); i++) {
	sp = getsectid(i);
	sp->sct_elev = elev[i];
	sp->sct_type = elev_to_sct_type(sp->sct_elev);
	sp->sct_newtype = sp->sct_type;
	sp->sct_dterr = own[i] + 1;
	sp->sct_coastal = is_coastal(sp->sct_x, sp->sct_y);
	add_resources(sp);
    }
}


/*
 * Print a picture of the map
 */

static void
output(void)
{
    int sx, sy, x, y, off, c, type;

    if (quiet == 0) {
	for (sy = -WORLD_Y / 2; sy < WORLD_Y / 2; sy++) {
	    y = YNORM(sy);
	    puts("");
	    if (y % 2)
		printf(" ");
	    for (sx = -WORLD_X / 2 + y % 2; sx < WORLD_X / 2; sx += 2) {
		x = XNORM(sx);
		off = XYOFFSET(x, y);
		c = own[off];
		type = elev_to_sct_type(elev[off]);
		if (type == SCT_WATER)
		    printf(". ");
		else if (type == SCT_MOUNT)
		    printf("^ ");
		else if (c >= nc)
		    printf("%% ");
		else {
		    assert(0 <= c && c < nc);
		    if ((x == cap[c].x || x == new_x(cap[c].x + 2))
			&& y == cap[c].y)
			printf("%c ", numletter[c % 62]);
		    else
			printf("# ");
		}
	    }
	}
    }
}

/*
 * Print a map to help visualize own[].
 * This is for debugging.
 */
void
print_own_map(void)
{
    int sx, sy, x, y, off;

    for (sy = -WORLD_Y / 2; sy < WORLD_Y / 2; sy++) {
	y = YNORM(sy);
	printf("%4d ", sy);
	for (sx = -WORLD_X / 2; sx < WORLD_X / 2; sx++) {
	    x = XNORM(sx);
	    off = XYOFFSET(x, y);
	    if ((x + y) & 1)
		putchar(' ');
	    else if (own[off] == -1)
		putchar('.');
	    else
		putchar(numletter[own[off] % 62]);
	}
	putchar('\n');
    }
}

/*
 * Print a map to help visualize elev[].
 * This is for debugging.  It expects the terminal to understand
 * 24-bit color escape sequences \e[48;2;$red;$green;$blue;m.
 */
void
print_elev_map(void)
{
    int sx, sy, x, y, off, sat;

    for (sy = -WORLD_Y / 2; sy < WORLD_Y / 2; sy++) {
	y = YNORM(sy);
	printf("%4d ", sy);
	for (sx = -WORLD_X / 2; sx < WORLD_X / 2; sx++) {
	    x = XNORM(sx);
	    off = XYOFFSET(x, y);
	    if ((x + y) & 1)
		putchar(' ');
	    else if (!elev[off])
		putchar(' ');
	    else if (elev[off] < 0) {
		sat = 256 + elev[off] * 2;
		printf("\033[48;2;%d;%d;%dm \033[0m", sat, sat, 255);
	    } else if (elev[off] < HIGHMIN / 2) {
		sat = (HIGHMIN / 2 - elev[off]) * 4;
		printf("\033[48;2;%d;%d;%dm \033[0m", sat, 255, sat);
	    } else if (elev[off] < HIGHMIN) {
		sat = 128 + (HIGHMIN - elev[off]) * 2;
		printf("\033[48;2;%d;%d;%dm \033[0m", sat, sat / 2, sat / 4);
	    } else {
		sat = 128 + (elev[off] - HIGHMIN) * 2;
		printf("\033[48;2;%d;%d;%dm^\033[0m", sat, sat, sat);
	    }
	}
	putchar('\n');
    }
}

/*
 * Print a map to help visualize xzone[].
 * This is for debugging.
 */
void
print_xzone_map(void)
{
    int sx, sy, x, y, off;

    for (sy = -WORLD_Y / 2; sy < WORLD_Y / 2; sy++) {
	y = YNORM(sy);
	printf("%4d ", sy);
	for (sx = -WORLD_X / 2; sx < WORLD_X / 2; sx++) {
	    x = XNORM(sx);
	    off = XYOFFSET(x, y);
	    if ((x + y) & 1)
		putchar(' ');
	    else if (own[off] >= 0)
		putchar('-');
	    else if (xzone[off] >= 0)
		putchar(numletter[xzone[off] % 62]);
	    else {
		assert(own[off] == -1);
		putchar(xzone[off] == -1 ? '.' : '!');
	    }
	}
	putchar('\n');
    }
}

/*
 * Print a map to help visualize closest[].
 * This is for debugging.
 */
void
print_closest_map(void)
{
    int sx, sy, x, y, off;

    for (sy = -WORLD_Y / 2; sy < WORLD_Y / 2; sy++) {
	y = YNORM(sy);
	printf("%4d ", sy);
	for (sx = -WORLD_X / 2; sx < WORLD_X / 2; sx++) {
	    x = XNORM(sx);
	    off = XYOFFSET(x, y);
	    if ((x + y) & 1)
		putchar(' ');
	    else if (closest[off] == (natid)-1)
		putchar('.');
	    else if (!distance[off]) {
		assert(closest[off] == own[off]);
		putchar('-');
	    } else {
		putchar(numletter[closest[off] % 62]);
	    }
	}
	printf("\n");
    }
}

void
print_distance_map(void)
{
    int sx, sy, x, y, off;

    for (sy = -WORLD_Y / 2; sy < WORLD_Y / 2; sy++) {
	y = YNORM(sy);
	printf("%4d ", sy);
	for (sx = -WORLD_X / 2; sx < WORLD_X / 2; sx++) {
	    x = XNORM(sx);
	    off = XYOFFSET(x, y);
	    if ((x + y) & 1)
		putchar(' ');
	    else if (closest[off] == (natid)-1)
		putchar('.');
	    else if (!distance[off]) {
		assert(closest[off] == own[off]);
		putchar('-');
	    } else {
		putchar(numletter[distance[off] % 62]);
	    }
	}
	printf("\n");
    }
}


/*
 * Write a script for placing capitals
 */

static int
write_newcap_script(void)
{
    int c;
    FILE *script = fopen(outfile, "w");

    if (!script) {
	fprintf(stderr, "%s: unable to write to %s (%s)\n",
		program_name, outfile, strerror(errno));
	return 0;
    }

    for (c = 0; c < nc; ++c) {
	fprintf(script, "add %d %d %d p\n", c + 1, c + 1, c + 1);
	fprintf(script, "newcap %d %d,%d\n", c + 1, cap[c].x, cap[c].y);
    }
    fprintf(script, "add %d visitor visitor v\n", c + 1);
    fclose(script);
    return 1;
}
