]> git.pond.sub.org Git - empserver/blobdiff - src/util/fairland.c
fairland: Precompute "exclusive zone" for speed
[empserver] / src / util / fairland.c
index 7f58e0254217dc76a47b897c7c7ef1e92b9bfcf4..4787719d7cd8da5c94fbe9b1d66f31cf43db4c69 100644 (file)
@@ -162,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:
 */
@@ -187,6 +187,21 @@ 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? */
@@ -220,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);
 
 /****************************************************************************
@@ -478,6 +494,7 @@ allocate_memory(void)
     capx = calloc(nc, sizeof(int));
     capy = calloc(nc, 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));
@@ -678,19 +695,89 @@ hexagon_next(struct hexagon_iter *iter, int *x, int *y)
     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
+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, 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
-try_to_grow(int c, int newx, int newy, int d)
+can_grow_at(int c, int x, int y)
 {
+    return own[x][y] == -1 && xzone_ok(c, x, y);
+}
+
+static int
+try_to_grow(int c, int newx, int newy, int extra_dist)
+{
+    int d = c < nc ? di : id;
     int i, px, py;
     struct hexagon_iter hexit;
 
-    if (own[newx][newy] != -1)
+    if (!can_grow_at(c, newx, newy))
        return 0;
 
-    for (i = 1; i <= d; ++i) {
-       hexagon_first(&hexit, newx, newy, i, &px, &py);
+    for (i = 1; i <= extra_dist; i++) {
+       hexagon_first(&hexit, newx, newy, d + i, &px, &py);
        do {
            if (own[px][py] != -1 &&
                own[px][py] != c &&
@@ -698,6 +785,9 @@ try_to_grow(int c, int newx, int newy, int d)
                return 0;
        } while (hexagon_next(&hexit, &px, &py));
     }
+
+    xzone_around_sector(c, newx, newy,
+                       c < nc ? di : DISTINCT_ISLANDS ? id : 0);
     sectx[c][isecs[c]] = newx;
     secty[c][isecs[c]] = newy;
     isecs[c]++;
@@ -780,14 +870,14 @@ grow_one_sector(int c)
                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, c < nc ? di : id))
+                   if (try_to_grow(c, newx, newy, 0))
                        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 (try_to_grow(c, newx, newy, c < nc ? di : id))
+               if (try_to_grow(c, newx, newy, 0))
                    done = 1;
            }
        next_coast(c, x, y, &x, &y);
@@ -807,10 +897,13 @@ grow_continents(void)
     int done = 1;
     int c, secs;
 
+    ctot = 0;
+    xzone_init(0);
+
     for (c = 0; c < nc; ++c) {
        isecs[c] = 0;
-       if (!try_to_grow(c, capx[c], capy[c], di)
-           || !try_to_grow(c, new_x(capx[c] + 2), capy[c], di)) {
+       if (!try_to_grow(c, capx[c], capy[c], 0)
+           || !try_to_grow(c, new_x(capx[c] + 2), capy[c], 0)) {
            done = 0;
            continue;
        }
@@ -854,7 +947,7 @@ place_island(int c, int *xp, int *yp)
 
     if (ssx > WORLD_X - 2)
        ssx = new_x(ssx + 2);
-    for (d = di + id; d >= id; --d) {
+    for (d = di; d >= 0; --d) {
        sx = ssx;
        sy = ssy;
        *xp = new_x(sx + 2);
@@ -881,6 +974,8 @@ grow_islands(void)
     int stunted_islands = 0;
     int c, secs, x, y, isiz;
 
+    xzone_init(nc);
+
     for (c = nc; c < nc + ni; ++c) {
        if (!place_island(c, &x, &y)) {
            qprint("\nNo room for island #%d", c - nc + 1);
@@ -916,7 +1011,7 @@ create_elevations(void)
 
     for (i = 0; i < WORLD_X; i++) {
        for (j = 0; j < WORLD_Y; j++)
-           elev[i][j] = -INFINITY;
+           elev[i][j] = -INFINITE_ELEVATION;
     }
     elevate_land();
     elevate_sea();
@@ -944,7 +1039,7 @@ 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;
            }
@@ -982,13 +1077,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;
@@ -1000,17 +1095,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;
@@ -1025,14 +1121,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
@@ -1056,7 +1150,7 @@ 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);
        }
     }
@@ -1275,6 +1369,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
 ****************************************************************************/