]> git.pond.sub.org Git - empserver/blobdiff - src/util/fairland.c
fairland: Drop try_to_grow() parameter @extra_dist
[empserver] / src / util / fairland.c
index 92516f33292dcae467f249c1637406ee353e9116..ca125371027416b4e2ea2a5c7eae9f117ec2cd1a 100644 (file)
 
 #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"
@@ -145,13 +147,13 @@ 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;                 /* spike percentage */
+static int sp = DEFAULT_SPIKE; /* spike percentage */
 #define DEFAULT_MOUNTAIN 0
-static int pm;                 /* mountain percentage */
+static int pm = DEFAULT_MOUNTAIN; /* mountain percentage */
 #define DEFAULT_CONTDIST 2
-static int di;                 /* min. distance between continents */
+static int di = DEFAULT_CONTDIST; /* min. distance between continents */
 #define DEFAULT_ISLDIST 1
-static int id;                 /* ... continents and islands */
+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;
@@ -160,7 +162,7 @@ static int quiet;
 static const char *outfile = DEFAULT_OUTFILE_NAME;
 
 #define STABLE_CYCLE 4         /* stability required for perterbed capitals */
-#define INFINITY       999     /* a number which means "BIG" */
+#define INFINITE_ELEVATION 999
 
 /* these defines prevent infinite loops:
 */
@@ -177,28 +179,35 @@ static const char *outfile = DEFAULT_OUTFILE_NAME;
 #define new_x(newx) (((newx) + WORLD_X) % WORLD_X)
 #define new_y(newy) (((newy) + WORLD_Y) % WORLD_Y)
 
-static int secs;               /* number of sectors grown */
 static int ctot;               /* total number of continents and islands grown */
 static int *isecs;             /* array of how large each island is */
 
 static int *capx, *capy;       /* location of the nc capitals */
-static int *mc, mcc;           /* array and counter used for stability
-                                  check when perturbing */
-static int spike;              /* are we spiking? */
-static int mind;               /* the final distance between capitals that
-                                  we achieved */
 static int dirx[] = { -2, -1, 1, 2, 1, -1 }; /* gyujnb */
 static int diry[] = { 0, -1, -1, 0, 1, 1 };
 
 static int **own;              /* owner of the sector.  -1 means water */
+
+/*
+ * 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;
+
 static int **elev;             /* elevation of the sectors */
 static int **sectx, **secty;   /* the sectors for each continent */
 static int **sectc;            /* which sectors are on the coast? */
-static int *vector;            /* used for measuring distances */
 static int *weight;            /* used for placing mountains */
 static int *dsea, *dmoun;      /* the dist to the ocean and mountain */
-static int fl_status;          /* is anything wrong? */
-#define STATUS_NO_ROOM 1       /* there was no room to grow */
+
 #define NUMTRIES 10            /* keep trying to grow this many times */
 
 static const char *numletter =
@@ -210,15 +219,14 @@ static void parse_args(int argc, char *argv[]);
 static void allocate_memory(void);
 static void init(void);
 static int drift(void);
-static void grow_continents(void);
+static int grow_continents(void);
 static void create_elevations(void);
 static void write_sects(void);
 static void output(void);
 static int write_newcap_script(void);
-static int stable(void);
+static int stable(int);
 static void elevate_land(void);
 static void elevate_sea(void);
-static int map_symbol(int x, int y);
 static void set_coastal_flags(void);
 
 static void print_vars(void);
@@ -227,6 +235,7 @@ static void grow_islands(void);
 
 /* Debugging aids: */
 void print_own_map(void);
+void print_xzone_map(void);
 void print_elev_map(void);
 
 /****************************************************************************
@@ -238,7 +247,7 @@ main(int argc, char *argv[])
 {
     int opt;
     char *config_file = NULL;
-    int i = 0;
+    int try, done;
     unsigned rnd_seed = 0;
     int seed_set = 0;
 
@@ -273,7 +282,6 @@ main(int argc, char *argv[])
            exit(1);
        }
     }
-    parse_args(argc - optind, argv + optind);
 
     if (!seed_set)
        rnd_seed = pick_seed();
@@ -283,43 +291,45 @@ main(int argc, char *argv[])
        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 (i)
-           qprint("\ntry #%d (out of %d)...\n", i + 1, NUMTRIES);
+       if (try)
+           qprint("\ntry #%d (out of %d)...\n", try + 1, NUMTRIES);
        qprint("placing capitals...\n");
        if (!drift())
-           qprint("fairland: unstable drift -- try increasing DRIFT_MAX\n");
+           qprint("unstable drift\n");
        qprint("growing continents...\n");
-       grow_continents();
-    } while (fl_status && ++i < NUMTRIES);
-    if (fl_status) {
-       fputs("ERROR: World not large enough to hold continents\n",
-             stderr);
+       done = grow_continents();
+    } while (!done && ++try < NUMTRIES);
+    if (!done) {
+       fprintf(stderr, "%s: world not large enough to hold continents\n",
+               program_name);
        exit(1);
     }
     qprint("growing islands:");
     grow_islands();
     qprint("\nelevating land...\n");
     create_elevations();
-    qprint("designating sectors...\n");
-    qprint("adding resources...\n");
+
+    qprint("writing to sectors file...\n");
     if (!write_newcap_script())
        exit(1);
-
     if (chdir(gamedir)) {
-       fprintf(stderr, "Can't chdir to %s (%s)\n", gamedir, strerror(errno));
-       exit(EXIT_FAILURE);
+       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();
-    qprint("writing to sectors file...\n");
     if (!ef_close(EF_SECTOR))
        exit(1);
 
@@ -347,19 +357,6 @@ print_vars(void)
     printf("World dimensions: %dx%d\n", WORLD_X, WORLD_Y);
 }
 
-static int
-my_sqrt(int n)
-{
-    int i;
-
-    for (i = 1; i * i < n * 10000; ++i) ;
-    return (i + 50) / 100;
-}
-
-/****************************************************************************
-  PARSE COMMAND LINE ARGUMENTS
-****************************************************************************/
-
 static void
 help(char *complaint)
 {
@@ -395,6 +392,8 @@ usage(void)
 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);
@@ -405,72 +404,82 @@ parse_args(int argc, char *argv[])
     }
     nc = atoi(argv[0]);
     if (nc < 1) {
-       puts("fairland: error -- number of continents must be > 0");
+       fprintf(stderr, "%s: number of continents must be > 0\n",
+               program_name);
        exit(1);
     }
 
     sc = atoi(argv[1]);
-    if (sc < 1) {
-       puts("fairland: error -- size of continents must be > 0");
+    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]);
-    else
-       ni = nc;
+    if (ni < 0) {
+       fprintf(stderr, "%s: number of islands must be >= 0\n",
+               program_name);
+       exit(1);
+    }
 
     if (argc > 3)
        is = atoi(argv[3]);
-    else
-       is = sc / 2;
-    if (is < 1)
-       is = 1;
+    if (is < 1) {
+       fprintf(stderr, "%s: size of islands must be > 0\n",
+               program_name);
+       exit(1);
+    }
 
     if (argc > 4)
        sp = atoi(argv[4]);
-    else
-       sp = DEFAULT_SPIKE;
-    sp = LIMIT_TO(sp, 0, 100);
+    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]);
-    else
-       pm = DEFAULT_MOUNTAIN;
-    if (pm < 0)
-       pm = 0;
+    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]);
-    else
-       di = DEFAULT_CONTDIST;
-
     if (di < 0) {
-       puts("fairland: error -- distance between continents must be >= 0");
+       fprintf(stderr, "%s: distance between continents must be >= 0\n",
+               program_name);
        exit(1);
     }
-    if (di > WORLD_X / 2 || di > WORLD_Y / 2) {
-       puts("fairland: error -- distance between continents too large");
+    if (di > dist_max) {
+       fprintf(stderr, "%s: distance between continents too large\n",
+               program_name);
        exit(1);
     }
 
     if (argc > 7)
        id = atoi(argv[7]);
-    else
-       id = DEFAULT_ISLDIST;
     if (id < 0) {
-       puts("fairland: error -- distance from islands to continents must be >= 0");
+       fprintf(stderr,
+               "%s: distance from islands to continents must be >= 0\n",
+               program_name);
        exit(1);
     }
-    if (id > WORLD_X || id > WORLD_Y) {
-       puts("fairland: error -- distance from islands to continents too large");
+    if (id > dist_max) {
+       fprintf(stderr,
+               "%s: distance from islands to continents too large\n",
+               program_name);
        exit(1);
     }
-    if (nc * sc + nc * my_sqrt(sc) * 2 * (di + 1) > WORLD_X * WORLD_Y) {
-       puts("fairland: warning -- world might be too small to fit continents.");
-       puts("arguments should satisfy:");
-       puts("nc*sc*sc + nc*sqrt(sc)*2*(di+1) < WORLD_X * WORLD_Y");
-    }
 }
 
 /****************************************************************************
@@ -484,9 +493,8 @@ allocate_memory(void)
 
     capx = calloc(nc, sizeof(int));
     capy = calloc(nc, sizeof(int));
-    vector = calloc(WORLD_X + WORLD_Y, sizeof(int));
-    mc = calloc(STABLE_CYCLE, sizeof(int));
     own = calloc(WORLD_X, sizeof(int *));
+    xzone = malloc(WORLD_SZ() * sizeof(*xzone));
     elev = calloc(WORLD_X, sizeof(int *));
     for (i = 0; i < WORLD_X; ++i) {
        own[i] = calloc(WORLD_Y, sizeof(int));
@@ -515,45 +523,28 @@ allocate_memory(void)
 static void
 init(void)
 {
-    int i, j, xx = 0, yy = 0;
-
-    mcc = 0;
-    fl_status = 0;
+    int i, j;
 
     for (i = 0; i < WORLD_X; ++i) {
        for (j = 0; j < WORLD_Y; ++j) {
            own[i][j] = -1;
-           elev[i][j] = -INFINITY;
        }
     }
-
-    for (i = 0; i < nc; ++i) {
-       if (xx >= WORLD_X) {
-           ++yy;
-           xx = yy % 2;
-           if (yy == WORLD_Y) {
-               puts("fairland error: world not big enough for all the continents.\n");
-               exit(1);
-           }
-       }
-       capx[i] = xx;
-       capy[i] = yy;
-       xx += 2;
-    }
-    for (i = 0; i < STABLE_CYCLE; ++i)
-       mc[i] = i;
 }
 
 /****************************************************************************
   DRIFT THE CAPITALS UNTIL THEY ARE AS FAR AWAY FROM EACH OTHER AS POSSIBLE
 ****************************************************************************/
 
-/* How isolated is capital j?
-*/
+/*
+ * 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 i, md, d = WORLD_X + WORLD_Y;
+    int d = INT_MAX;
+    int i, md;
 
     for (i = 0; i < nc; ++i) {
        if (i == j)
@@ -566,15 +557,28 @@ iso(int j, int newx, int newy)
     return d;
 }
 
-/* Drift all the capitals
-*/
+/*
+ * Drift the capitals
+ * Return 1 for a stable drift, 0 for an unstable one.
+ */
 static int
 drift(void)
 {
-    int i, turns;
+    int turns, i;
+
+    for (i = 0; i < nc; i++) {
+       capy[i] = (2 * i) / WORLD_X;
+       capx[i] = (2 * i) % WORLD_X + capy[i] % 2;
+       if (capy[i] >= 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 (turns > DRIFT_BEFORE_CHECK && (mind = stable()))
+       if (stable(turns))
            return 1;
        for (i = 0; i < nc; ++i)
            fl_move(i);
@@ -582,25 +586,36 @@ drift(void)
     return 0;
 }
 
-/* Check to see if we have stabilized--can we stop drifting the capitals?
-*/
-
+/*
+ * Has the drift stabilized?
+ * @turns is the number of turns so far.
+ */
 static int
-stable(void)
+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, capx[i], capy[i]);
        if (isod > d)
            d = isod;
     }
+
     for (i = 0; i < STABLE_CYCLE; ++i)
        if (d != mc[i])
            stab = 0;
-    mc[mcc] = d;
-    mcc = (mcc + 1) % STABLE_CYCLE;
-    return stab ? d : 0;
+
+    mc[turns % STABLE_CYCLE] = d;
+    return stab;
 }
 
 /* This routine does the actual drifting
@@ -634,7 +649,7 @@ find_coast(int c)
 {
     int i, j;
 
-    for (i = 0; i < secs; ++i) {
+    for (i = 0; i < isecs[c]; ++i) {
        sectc[c][i] = 0;
        for (j = 0; j < 6; ++j)
            if (own[new_x(sectx[c][i] + dirx[j])][new_y(secty[c][i] + diry[j])] == -1)
@@ -642,50 +657,133 @@ find_coast(int c)
     }
 }
 
-/* Used for measuring distances
-*/
-static int
-next_vector(int n)
+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)
 {
-    int i;
+    *x = new_x(x0 - 2 * n);
+    *y = y0;
+    iter->dir = DIR_FIRST;
+    iter->i = 0;
+    iter->n = n;
+}
 
-    if (n == 1) {
-       vector[0] += 1;
-       vector[0] %= 6;
-       return vector[0];
+/*
+ * 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++;
     }
-    for (i = 1; i < n && vector[i] == vector[i - 1]; ++i) ;
-    vector[i - 1] += 1;
-    vector[i - 1] %= 6;
-    return i > 1 || vector[0] > 0;
+    return iter->dir <= DIR_LAST;
 }
 
-/* Test to see if we're allowed to grow there: the arguments di and id
-*/
+/*
+ * Is @x,@y in no exclusive zone other than perhaps @c's?
+ */
 static int
-try_to_grow(int c, int newx, int newy, int d)
+xzone_ok(int c, int x, int y)
 {
-    int i, j, px, py;
+    int off = XYOFFSET(x, y);
 
-    for (i = 1; i <= d; ++i) {
-       for (j = 0; j < i; ++j)
-           vector[j] = 0;
+    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 {
-           px = newx;
-           py = newy;
-           for (j = 0; j < i; ++j) {
-               px = new_x(px + dirx[vector[j]]);
-               py = new_y(py + diry[vector[j]]);
-           }
-           if (own[px][py] != -1 &&
-               own[px][py] != c &&
-               (DISTINCT_ISLANDS || own[px][py] < nc))
-               return 0;
-       } while (next_vector(i));
+           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));
     }
-    sectx[c][secs] = newx;
-    secty[c][secs] = newy;
-    own[newx][newy] = c;
+}
+
+/*
+ * 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, sectx[c][i], secty[c][i], 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);
+}
+
+/*
+ * Can island @c grow at @x,@y?
+ */
+static int
+can_grow_at(int c, int x, int y)
+{
+    return own[x][y] == -1 && xzone_ok(c, x, y);
+}
+
+static void
+add_sector(int c, int x, int y)
+{
+    assert(own[x][y] == -1);
+    xzone_around_sector(c, x, y, c < nc ? di : DISTINCT_ISLANDS ? id : 0);
+    sectx[c][isecs[c]] = x;
+    secty[c][isecs[c]] = y;
+    isecs[c]++;
+    own[x][y] = c;
+}
+
+static int
+try_to_grow(int c, int newx, int newy)
+{
+    if (!can_grow_at(c, newx, newy))
+       return 0;
+
+    add_sector(c, newx, newy);
     return 1;
 }
 
@@ -697,7 +795,7 @@ next_coast(int c, int x, int y, int *xp, int *yp)
 {
     int i, nx, ny, wat = 0;
 
-    if (secs == 1) {
+    if (isecs[c] == 1) {
        *xp = x;
        *yp = y;
        return;
@@ -720,8 +818,9 @@ next_coast(int c, int x, int y, int *xp, int *yp)
 */
 
 static int
-new_try(int c)
+new_try(int c, int spike)
 {
+    int secs = isecs[c];
     int i, starti;
 
     if (secs == 1) {
@@ -746,10 +845,10 @@ new_try(int c)
 static int
 grow_one_sector(int c)
 {
+    int spike = roll0(100) < sp;
     int done, coast_search, try1, x, y, newx, newy, i, n, sx, sy;
 
-    spike = roll0(100) < sp;
-    if ((try1 = new_try(c)) == -1)
+    if ((try1 = new_try(c, spike)) == -1)
        return 0;
     x = sx = sectx[c][try1];
     y = sy = secty[c][try1];
@@ -760,94 +859,101 @@ grow_one_sector(int c)
            for (i = roll0(6), n = 0; n < 12 && !done; i = (i + 1) % 6, ++n) {
                newx = new_x(x + dirx[i]);
                newy = new_y(y + diry[i]);
-               if (own[newx][newy] == -1 &&
-                   (n > 5 ||
-                    (own[new_x(x+dirx[(i+5)%6])][new_y(y+diry[(i+5)%6])] == -1 &&
-                     own[new_x(x+dirx[(i+1)%6])][new_y(y+diry[(i+1)%6])] == -1)))
-                   if (try_to_grow(c, newx, newy, c < nc ? di : id))
+               if (n > 5 ||
+                   (own[new_x(x+dirx[(i+5)%6])][new_y(y+diry[(i+5)%6])] == -1 &&
+                    own[new_x(x+dirx[(i+1)%6])][new_y(y+diry[(i+1)%6])] == -1))
+                   if (try_to_grow(c, newx, newy))
                        done = 1;
            }
        } else
            for (i = roll0(6), n = 0; n < 6 && !done; i = (i + 1) % 6, ++n) {
                newx = new_x(x + dirx[i]);
                newy = new_y(y + diry[i]);
-               if (own[newx][newy] == -1)
-                   if (try_to_grow(c, newx, newy, c < nc ? di : id))
-                       done = 1;
+               if (try_to_grow(c, newx, newy))
+                   done = 1;
            }
        next_coast(c, x, y, &x, &y);
        ++coast_search;
     } while (!done && coast_search < COAST_SEARCH_MAX &&
-            (secs == 1 || x != sx || y != sy));
-    if (!done && c < nc) {
-       qprint("fairland: error -- continent %c had no room to grow!\n",
-              numletter[c % 62]);
-       fl_status |= STATUS_NO_ROOM;
-    }
+            (isecs[c] == 1 || x != sx || y != sy));
     return done;
 }
 
-/* Grow all the continents
-*/
-static void
+/*
+ * Grow the continents.
+ * Return 1 on success, 0 on error.
+ */
+static int
 grow_continents(void)
 {
-    int c;
+    int done = 1;
+    int c, secs;
+
+    ctot = 0;
+    xzone_init(0);
 
     for (c = 0; c < nc; ++c) {
-       sectx[c][0] = capx[c];
-       secty[c][0] = capy[c];
-       own[sectx[c][0]][secty[c][0]] = c;
-       sectx[c][1] = new_x(capx[c] + 2);
-       secty[c][1] = capy[c];
-       own[sectx[c][1]][secty[c][1]] = c;
+       isecs[c] = 0;
+       if (!try_to_grow(c, capx[c], capy[c])
+           || !try_to_grow(c, new_x(capx[c] + 2), capy[c])) {
+           done = 0;
+           continue;
+       }
     }
 
-    for (secs = 2; secs < sc && !fl_status; ++secs) {
+    if (!done) {
+       qprint("No room for continents\n");
+       return 0;
+    }
+
+    for (secs = 2; secs < sc && done; secs++) {
        for (c = 0; c < nc; ++c) {
            find_coast(c);
-           grow_one_sector(c);
+           if (!grow_one_sector(c))
+               done = 0;
        }
     }
+
     for (c = 0; c < nc; ++c)
        find_coast(c);
 
-    if (fl_status)
-       qprint("Only managed to grow %d out of %d sectors.\n", secs, sc);
+    if (!done)
+       qprint("Only managed to grow %d out of %d sectors.\n",
+              secs - 1, sc);
     ctot = nc;
+    return done;
 }
 
 /****************************************************************************
   GROW THE ISLANDS
 ****************************************************************************/
 
-/* Choose a place to start growing an island from
-*/
+/*
+ * Place additional island @c's first sector.
+ * Return 1 on success, 0 on error.
+ */
 static int
-place_island(int c, int *xp, int *yp)
+place_island(int c)
 {
-    int d, sx, sy;
-    int ssy = roll0(WORLD_Y);
-    int ssx = new_x(roll0(WORLD_X / 2) * 2 + ssy % 2);
-
-    if (ssx > WORLD_X - 2)
-       ssx = new_x(ssx + 2);
-    for (d = di + id; d >= id; --d) {
-       sx = ssx;
-       sy = ssy;
-       *xp = new_x(sx + 2);
-       for (*yp = sy; *xp != sx || *yp != sy; *xp += 2) {
-           if (*xp >= WORLD_X) {
-               *yp = new_y(*yp + 1);
-               *xp = *yp % 2;
-               if (*xp == sx && *yp == sy)
-                   break;
+    int n, x, y, 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)) {
+               n++;
+               if (!roll0(n)) {
+                   newx = x;
+                   newy = y;
+               }
            }
-           if (own[*xp][*yp] == -1 && try_to_grow(c, *xp, *yp, d))
-               return 1;
        }
     }
-    return 0;
+
+    if (n)
+       add_sector(c, newx, newy);
+    return n;
 }
 
 /* Grow all the islands
@@ -856,22 +962,34 @@ place_island(int c, int *xp, int *yp)
 static void
 grow_islands(void)
 {
-    int c, x, y, isiz;
+    int stunted_islands = 0;
+    int c, secs, isiz;
+
+    xzone_init(nc);
 
     for (c = nc; c < nc + ni; ++c) {
-       secs = 0;
-       if (!place_island(c, &x, &y))
-           return;
+       if (!place_island(c)) {
+           qprint("\nNo room for island #%d", c - nc + 1);
+           break;
+       }
+
        isiz = roll(is) + roll0(is);
-       do {
-           ++secs;
+       for (secs = 1; secs < isiz; secs++) {
            find_coast(c);
-       } while (secs < isiz && grow_one_sector(c));
+           if (!grow_one_sector(c)) {
+               stunted_islands++;
+               break;
+           }
+       }
+
        find_coast(c);
        qprint(" %d(%d)", c - nc + 1, secs);
-       isecs[c] = secs;
        ctot++;
     }
+
+    if (stunted_islands)
+       qprint("\n%d stunted island%s",
+              stunted_islands, splur(stunted_islands));
 }
 
 /****************************************************************************
@@ -880,6 +998,12 @@ grow_islands(void)
 static void
 create_elevations(void)
 {
+    int i, j;
+
+    for (i = 0; i < WORLD_X; i++) {
+       for (j = 0; j < WORLD_Y; j++)
+           elev[i][j] = -INFINITE_ELEVATION;
+    }
     elevate_land();
     elevate_sea();
 }
@@ -890,18 +1014,12 @@ create_elevations(void)
 static int
 distance_to_what(int x, int y, int flag)
 {
-    int j, d, px, py;
+    int d, px, py;
+    struct hexagon_iter hexit;
 
     for (d = 1; d < 5; ++d) {
-       for (j = 0; j < d; ++j)
-           vector[j] = 0;
+       hexagon_first(&hexit, x, y, d, &px, &py);
        do {
-           px = x;
-           py = y;
-           for (j = 0; j < d; ++j) {
-               px = new_x(px + dirx[vector[j]]);
-               py = new_y(py + diry[vector[j]]);
-           }
            switch (flag) {
            case 0:             /* distance to sea */
                if (own[px][py] == -1)
@@ -912,11 +1030,11 @@ distance_to_what(int x, int y, int flag)
                    return d;
                break;
            case 2:             /* distance to mountain */
-               if (elev[px][py] == INFINITY)
+               if (elev[px][py] == INFINITE_ELEVATION)
                    return d;
                break;
            }
-       } while (next_vector(d));
+       } while (hexagon_next(&hexit, &px, &py));
     }
     return d;
 }
@@ -935,7 +1053,7 @@ elevate_land(void)
 
     for (c = 0; c < ctot; ++c) {
        total = 0;
-       ns = (c < nc) ? sc : isecs[c];
+       ns = isecs[c];
        nm = (pm * ns) / 100;
 
 /* Place the mountains */
@@ -950,13 +1068,13 @@ elevate_land(void)
             ++mountain_search) {
            r = roll0(total);
            for (i = 0; i < ns; ++i)
-               if (r < weight[i] && ELEV == -INFINITY &&
+               if (r < weight[i] && ELEV == -INFINITE_ELEVATION &&
                    (c >= nc ||
                     ((!(capx[c] == sectx[c][i] &&
                         capy[c] == secty[c][i])) &&
                      (!(new_x(capx[c] + 2) == sectx[c][i] &&
                         capy[c] == secty[c][i]))))) {
-                   ELEV = INFINITY;
+                   ELEV = INFINITE_ELEVATION;
                    break;
                }
            --k;
@@ -968,17 +1086,18 @@ elevate_land(void)
            dmoun[i] = distance_to_mountain();
        dk = (ns - nm - ((c < nc) ? 3 : 1) > 0) ?
          (100 * (HIGHMIN - LANDMIN)) / (ns - nm - ((c < nc) ? 3 : 1)) :
-         100 * INFINITY;
+         100 * INFINITE_ELEVATION;
        for (k = 100 * (HIGHMIN - 1);; k -= dk) {
-           highest = -INFINITY;
+           highest = 0;
            where = -1;
            for (i = 0; i < ns; ++i) {
-               if (ELEV != INFINITY &&
+               if (ELEV == -INFINITE_ELEVATION &&
                    (c >= nc || ((!(capx[c] == sectx[c][i] &&
                                    capy[c] == secty[c][i])) &&
                                 (!(new_x(capx[c] + 2) == sectx[c][i] &&
                                    capy[c] == secty[c][i]))))) {
                    h = 3 * (5 - dmoun[i]) + dsea[i];
+                   assert(h > 0);
                    if (h > highest) {
                        highest = h;
                        where = i;
@@ -993,14 +1112,12 @@ elevate_land(void)
            if (newk < LANDMIN)
                newk = LANDMIN;
            elev[sectx[c][where]][secty[c][where]] = newk;
-           dsea[where] = -INFINITY;
-           dmoun[where] = INFINITY;
        }
 
 /* Elevate the mountains and capitals */
 
        for (i = 0; i < ns; ++i) {
-           if (ELEV == INFINITY) {
+           if (ELEV == INFINITE_ELEVATION) {
                if (dsea[i] == 1)
                    ELEV = HILLMIN + roll0(PLATMIN - HILLMIN);
                else
@@ -1024,12 +1141,26 @@ elevate_sea(void)
 
     for (y = 0; y < WORLD_Y; ++y) {
        for (x = y % 2; x < WORLD_X; x += 2) {
-           if (elev[x][y] == -INFINITY)
+           if (elev[x][y] == -INFINITE_ELEVATION)
                elev[x][y] = -roll(distance_to_land() * 20 + 27);
        }
     }
 }
 
+static int
+elev_to_sct_type(int elevation)
+{
+    if (elevation < LANDMIN)
+       return SCT_WATER;
+    if (elevation < HILLMIN)
+       return SCT_RURAL;
+    if (elevation < PLATMIN)
+       return SCT_MOUNT;
+    if (elevation < HIGHMIN)
+       return SCT_RURAL;
+    return SCT_MOUNT;
+}
+
 /****************************************************************************
   ADD THE RESOURCES
 ****************************************************************************/
@@ -1115,23 +1246,13 @@ static void
 write_sects(void)
 {
     struct sctstr *sct;
-    int x, y, total;
+    int x, y;
 
     for (y = 0; y < WORLD_Y; y++) {
        for (x = y % 2; x < WORLD_X; x += 2) {
            sct = getsectp(x, y);
-           total = elev[x][y];
-           if (total < LANDMIN) {
-               sct->sct_type = SCT_WATER;
-           } else if (total < HILLMIN)
-               sct->sct_type = SCT_RURAL;
-           else if (total < PLATMIN)
-               sct->sct_type = SCT_MOUNT;
-           else if (total < HIGHMIN)
-               sct->sct_type = SCT_RURAL;
-           else
-               sct->sct_type = SCT_MOUNT;
-           sct->sct_elev = total;
+           sct->sct_elev = elev[x][y];
+           sct->sct_type = elev_to_sct_type(elev[x][y]);
            sct->sct_newtype = sct->sct_type;
            sct->sct_dterr = own[sct->sct_x][y] + 1;
            add_resources(sct);
@@ -1146,7 +1267,7 @@ write_sects(void)
 static void
 output(void)
 {
-    int sx, sy, x, y;
+    int sx, sy, x, y, c, type;
 
     if (quiet == 0) {
        for (sy = -WORLD_Y / 2; sy < WORLD_Y / 2; sy++) {
@@ -1156,31 +1277,27 @@ output(void)
                printf(" ");
            for (sx = -WORLD_X / 2 + y % 2; sx < WORLD_X / 2; sx += 2) {
                x = XNORM(sx);
-               if (own[x][y] == -1)
+               c = own[x][y];
+               type = elev_to_sct_type(elev[x][y]);
+               if (type == SCT_WATER)
                    printf(". ");
+               else if (type == SCT_MOUNT)
+                   printf("^ ");
+               else if (c >= nc)
+                   printf("%% ");
                else {
-                   printf("%c ", map_symbol(x, y));
+                   assert(0 <= c && c < nc);
+                   if ((x == capx[c] || x == new_x(capx[c] + 2))
+                       && y == capy[c])
+                       printf("%c ", numletter[c % 62]);
+                   else
+                       printf("# ");
                }
            }
        }
     }
 }
 
-static int
-map_symbol(int x, int y)
-{
-    int c;
-
-    for (c = 0; c < nc; ++c)
-       if ((x == capx[c] && y == capy[c])
-           || (x == new_x(capx[c] + 2) && y == capy[c]))
-           return numletter[own[x][y] % 62];
-    if ((elev[x][y] >= HILLMIN && elev[x][y] < PLATMIN)
-       || elev[x][y] >= HIGHMIN)
-       return '^';
-    return own[x][y] >= nc ? '%' : '#';
-}
-
 /*
  * Print a map to help visualize own[][].
  * This is for debugging.
@@ -1243,6 +1360,37 @@ print_elev_map(void)
     }
 }
 
+/*
+ * 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[x][y] >= 0)
+               putchar('-');
+           else if (xzone[off] >= 0)
+               putchar(numletter[xzone[off] % 62]);
+           else {
+               assert(own[x][y] == -1);
+               putchar(xzone[off] == -1 ? '.' : '!');
+           }
+       }
+       putchar('\n');
+    }
+}
+
+
 /***************************************************************************
   WRITE A SCRIPT FOR PLACING CAPITALS
 ****************************************************************************/
@@ -1253,7 +1401,8 @@ write_newcap_script(void)
     FILE *script = fopen(outfile, "w");
 
     if (!script) {
-       printf("fairland: error, unable to write to %s.\n", outfile);
+       fprintf(stderr, "%s: unable to write to %s (%s)\n",
+               program_name, outfile, strerror(errno));
        return 0;
     }
 
@@ -1284,14 +1433,7 @@ set_coastal_flags(void)
     int i, j;
     struct sctstr *sp;
 
-    qprint("setting coastal flags...\n");
-    for (i = 0; i < nc; ++i) {
-       for (j = 0; j < sc; j++) {
-           sp = getsectp(sectx[i][j], secty[i][j]);
-           sp->sct_coastal = sectc[i][j];
-       }
-    }
-    for (i = nc; i < nc + ni; ++i) {
+    for (i = 0; i < nc + ni; ++i) {
        for (j = 0; j < isecs[i]; j++) {
            sp = getsectp(sectx[i][j], secty[i][j]);
            sp->sct_coastal = sectc[i][j];