src/util/fairland.c

Go to the documentation of this file.
00001 /*
00002  *  Empire - A multi-player, client/server Internet based war game.
00003  *  Copyright (C) 1986-2007, Dave Pare, Jeff Bailey, Thomas Ruschak,
00004  *                           Ken Stevens, Steve McClure
00005  *
00006  *  This program is free software; you can redistribute it and/or modify
00007  *  it under the terms of the GNU General Public License as published by
00008  *  the Free Software Foundation; either version 2 of the License, or
00009  *  (at your option) any later version.
00010  *
00011  *  This program is distributed in the hope that it will be useful,
00012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  *  GNU General Public License for more details.
00015  *
00016  *  You should have received a copy of the GNU General Public License
00017  *  along with this program; if not, write to the Free Software
00018  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00019  *
00020  *  ---
00021  *
00022  *  See files README, COPYING and CREDITS in the root of the source
00023  *  tree for related information and legal notices.  It is expected
00024  *  that future projects/authors will amend these files as needed.
00025  *
00026  *  ---
00027  *
00028  *  fairland.c: Create a nice, new world
00029  * 
00030  *  Known contributors to this file:
00031  *     Ken Stevens, 1995
00032  *     Steve McClure, 1998
00033  */
00034 
00035 #include <config.h>
00036 
00037 /* define ORE 1 to add resources, define ORE 0 if you want to use another
00038    program to add the resources */
00039 static int ORE = 1;
00040 static int quiet = 0;
00041 
00042 /* If you don't specify these command line arguments, then these are the
00043    defaults */
00044 #define DEFAULT_SPIKE 10
00045 #define DEFAULT_MOUNTAIN 0
00046 #define DEFAULT_CONTDIST 2
00047 #define DEFAULT_ISLDIST 1
00048 
00049 /* The following five numbers refer to elevation under which (in the case of
00050    fertility or oil) or over which (in the case of iron, gold, and uranium)
00051    sectors with that elevation will contain that resource.  Elevation ranges
00052    from 0 to 100 */
00053 
00054 /* raise FERT_MAX for more fertility */
00055 #define FERT_MAX   56
00056 
00057 /* raise OIL_MAX for more oil */
00058 #define OIL_MAX    33
00059 
00060 /* lower IRON_MIN for more iron */
00061 #define IRON_MIN   22
00062 
00063 /* lower GOLD_MIN for more gold */
00064 #define GOLD_MIN   36
00065 
00066 /* lower URAN_MIN for more uranium */
00067 #define URAN_MIN   56
00068 
00069 #include <stdarg.h>
00070 #include <stdio.h>
00071 #include <unistd.h>
00072 #include "file.h"
00073 #include "misc.h"
00074 #include "nat.h"
00075 #include "optlist.h"
00076 #include "power.h"
00077 #include "prototypes.h"
00078 #include "sect.h"
00079 #include "version.h"
00080 #include "xy.h"
00081 
00082 /* do not change these 4 defines */
00083 #define LANDMIN         1       /* plate altitude for normal land */
00084 #define HILLMIN         34      /* plate altitude for hills */
00085 #define PLATMIN         36      /* plate altitude for plateau */
00086 #define HIGHMIN         98      /* plate altitude for mountains */
00087 
00088 static void qprint(const char * const fmt, ...)
00089     ATTRIBUTE((format (printf, 1, 2)));
00090 
00091 #define DEFAULT_OUTFILE_NAME "newcap_script"
00092 static const char *outfile = DEFAULT_OUTFILE_NAME;
00093 /* mark the continents with a * so you can tell them
00094    from the islands 1 = mark, 0 = don't mark. */
00095 static int AIRPORT_MARKER = 0;
00096 
00097 /* don't let the islands crash into each other.
00098    1 = don't merge, 0 = merge. */
00099 static int DISTINCT_ISLANDS = 1;
00100 
00101 static char *program_name;
00102 
00103 #define XSIZE           ((WORLD_X) / 2) /* basically world x-y size */
00104 #define YSIZE           (WORLD_Y)
00105 #define STABLE_CYCLE 4          /* stability required for perterbed capitals */
00106 #define INFINITY        999     /* a number which means "BIG" */
00107 
00108 /* these defines prevent infinite loops:
00109 */
00110 
00111 #define COAST_SEARCH_MAX 200    /* how many times do we look for a coast sector
00112                                    when growing continents and islands */
00113 #define DRIFT_BEFORE_CHECK ((WORLD_X + WORLD_Y)/2)
00114 #define DRIFT_MAX ((WORLD_X + WORLD_Y)*2)
00115 #define MOUNTAIN_SEARCH_MAX 1000        /* how long do we try to place mountains */
00116 
00117 /* handy macros:
00118 */
00119 
00120 #define new_x(newx) (((newx) + WORLD_X) % WORLD_X)
00121 #define new_y(newy) (((newy) + WORLD_Y) % WORLD_Y)
00122 #define rnd(x) (random() % (x))
00123 
00124 int secs;                       /* number of sectors grown */
00125 int ctot;                       /* total number of continents and islands grown */
00126 int *isecs;                     /* array of how large each island is */
00127 
00128 int nc, sc, di, sp, pm, ni, is, id;     /* the 8 arguments to this program */
00129 unsigned long rnd_seed;         /* optional seed can be passed as an argument */
00130 int *capx, *capy;               /* location of the nc capitals */
00131 int *mc, mcc;                   /* array and counter used for stability
00132                                    check when perturbing */
00133 int spike;                      /* are we spiking? */
00134 int mind;                       /* the final distance between capitals that
00135                                    we achieved */
00136 int dirx[] = { -2, -1, 1, 2, 1, -1 };   /* gyujnb */
00137 int diry[] = { 0, -1, -1, 0, 1, 1 };
00138 
00139 int **own;                      /* owner of the sector.  -1 means water */
00140 int **elev;                     /* elevation of the sectors */
00141 int **sectx, **secty;           /* the sectors for each continent */
00142 int **sectc;                    /* which sectors are on the coast? */
00143 int *vector;                    /* used for measuring distances */
00144 int *weight;                    /* used for placing mountains */
00145 int *dsea, *dmoun;              /* the dist to the ocean and mountain */
00146 FILE *sect_fptr;                        /* the file we write everything to */
00147 struct sctstr **sects;
00148 struct sctstr *sectsbuf;
00149 int fl_status;                  /* is anything wrong? */
00150 #define STATUS_NO_ROOM 1        /* there was no room to grow */
00151 #define NUMTRIES 10             /* keep trying to grow this many times */
00152 
00153 const char *numletter =
00154     "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
00155 
00156 static void help(char *);
00157 static void usage(void);
00158 static void parse_args(int argc, char *argv[]);
00159 static int allocate_memory(void);
00160 static void init(void);
00161 static int drift(void);
00162 static void grow_continents(void);
00163 static void create_elevations(void);
00164 static void write_sects(void);
00165 static int write_file(void);
00166 static void output(void);
00167 static int write_newcap_script(void);
00168 static int stable(void);
00169 static void elevate_land(void);
00170 static void elevate_sea(void);
00171 static int map_symbol(int x, int y);
00172 static void fl_sct_init(coord, coord, struct sctstr *);
00173 static void set_coastal_flags(void);
00174 
00175 static void print_vars(void);
00176 static void fl_move(int);
00177 static void next_coast(int c, int x, int y, int *xp, int *yp);
00178 static void grow_islands(void);
00179 
00180 /****************************************************************************
00181   MAIN
00182 ****************************************************************************/
00183 
00184 int
00185 main(int argc, char *argv[])
00186 {
00187     int opt;
00188     char *config_file = NULL;
00189     int i = 0;
00190 
00191     program_name = argv[0];
00192     rnd_seed = time(NULL);
00193 
00194     while ((opt = getopt(argc, argv, "ae:hioqR:s:v")) != EOF) {
00195         switch (opt) {
00196         case 'a':
00197             AIRPORT_MARKER = 1;
00198             break;
00199         case 'e':
00200             config_file = optarg;
00201             break;
00202         case 'i':
00203             DISTINCT_ISLANDS = 0;
00204             break;
00205         case 'o':
00206             ORE = 0;
00207             break;
00208         case 'q':
00209             quiet = 1;
00210             break;
00211         case 'R':
00212             rnd_seed = strtoul(optarg, NULL, 10);
00213             break;
00214         case 's':
00215             outfile = optarg;
00216             break;
00217         case 'h':
00218             usage();
00219             exit(0);
00220         case 'v':
00221             printf("%s\n\n%s", version, legal);
00222             exit(0);
00223         default:
00224             help(NULL);
00225             exit(1);
00226         }
00227     }
00228     srandom(rnd_seed);
00229     if (emp_config(config_file))
00230         exit(1);
00231 
00232     parse_args(argc - optind, argv + optind);
00233     if (allocate_memory() == -1)
00234         exit(-1);
00235     print_vars();
00236 
00237     do {
00238         init();
00239         if (i)
00240             qprint("\ntry #%d (out of %d)...", i + 1, NUMTRIES);
00241         qprint("\n\n        #*# ...fairland rips open a rift in the datumplane... #*#\n\n");
00242         qprint("seed is %lu\n", rnd_seed);
00243         qprint("placing capitals...\n");
00244         if (!drift())
00245             qprint("fairland: unstable drift -- try increasisg DRIFT_MAX\n");
00246         qprint("growing continents...\n");
00247         grow_continents();
00248     } while (fl_status && ++i < NUMTRIES);
00249     if (fl_status) {
00250         fputs("ERROR: World not large enough to hold continents\n",
00251               stderr);
00252         exit(1);
00253     }
00254     qprint("growing islands:");
00255     grow_islands();
00256     qprint("\nelevating land...\n");
00257     create_elevations();
00258     qprint("designating sectors...\n");
00259     if (ORE)
00260         qprint("adding resources...\n");
00261     write_sects();
00262     qprint("writing to sectors file...\n");
00263     if (write_file() == -1)
00264         exit(-1);
00265     output();
00266     write_newcap_script();
00267     if (!ORE)
00268         qprint("\t*** Resources have not been added ***\n");
00269     exit(0);
00270 }
00271 
00272 static void
00273 print_vars(void)
00274 {
00275     if (quiet)
00276         return;
00277     puts("Creating a planet with:\n");
00278     printf("%d continents\n", nc);
00279     printf("continent size: %d\n", sc);
00280     printf("number of islands: %d\n", ni);
00281     printf("average size of islands: %d\n", is);
00282     printf("spike: %d%%\n", sp);
00283     printf("%d%% of land is mountain (each continent will have %d mountains)\n",
00284            pm, (pm * sc) / 100);
00285     printf("minimum distance between continents: %d\n", di);
00286     printf("minimum distance from islands to continents: %d\n", id);
00287     printf("World dimensions: %dx%d\n", WORLD_X, WORLD_Y);
00288 }
00289 
00290 static int
00291 my_sqrt(int n)
00292 {
00293     int i;
00294 
00295     for (i = 1; i * i < n * 10000; ++i) ;
00296     return (i + 50) / 100;
00297 }
00298 
00299 /****************************************************************************
00300   PARSE COMMAND LINE ARGUMENTS
00301 ****************************************************************************/
00302 
00303 static void
00304 help(char *complaint)
00305 {
00306     if (complaint)
00307         fprintf(stderr, "%s: %s\n", program_name, complaint);
00308     fprintf(stderr, "Try -h for help.\n");
00309 }
00310 
00311 static void
00312 usage(void)
00313 {
00314     printf("Usage: %s [OPTION]... NC SC [NI] [IS] [SP] [PM] [DI] [ID]\n"
00315            "  -a              airport marker for continents\n"
00316            "  -e CONFIG-FILE  configuration file\n"
00317            "                  (default %s)\n"
00318            "  -h              display this help and exit\n"
00319            "  -i              islands may merge\n"
00320            "  -o              don't set resources\n"
00321            "  -q              quiet\n"
00322            "  -R SEED         seed for random number generator\n"
00323            "  -s SCRIPT       name of script to create (default %s)\n"
00324            "  NC              number of continents\n"
00325            "  SC              continent size\n"
00326            "  NI              number of islands (default NC)\n"
00327            "  IS              average island size (default SC/2)\n"
00328            "  SP              spike percentage: 0 = round, 100 = snake (default %d)\n"
00329            "  PM              percentage of land that is mountain (default %d)\n"
00330            "  DI              minimum distance between continents (default %d)\n"
00331            "  ID              minimum distance from islands to continents (default %d)\n",
00332            program_name, dflt_econfig, DEFAULT_OUTFILE_NAME,
00333            DEFAULT_SPIKE, DEFAULT_MOUNTAIN, DEFAULT_CONTDIST, DEFAULT_ISLDIST);
00334 }
00335 
00336 static void
00337 parse_args(int argc, char *argv[])
00338 {
00339     if (argc < 2) {
00340         help("missing arguments");
00341         exit(1);
00342     }
00343     if (argc > 8) {
00344         help("too many arguments");
00345         exit(1);
00346     }
00347     nc = atoi(argv[0]);
00348     if (nc < 1) {
00349         puts("fairland: error -- number of continents must be > 0");
00350         exit(1);
00351     }
00352 
00353     sc = atoi(argv[1]);
00354     if (sc < 1) {
00355         puts("fairland: error -- size of continents must be > 0");
00356         exit(1);
00357     }
00358 
00359     if (argc > 2)
00360         ni = atoi(argv[2]);
00361     else
00362         ni = nc;
00363 
00364     if (argc > 3)
00365         is = atoi(argv[3]);
00366     else
00367         is = sc / 2;
00368     if (is < 0)
00369         is = 0;
00370 
00371     if (argc > 4)
00372         sp = atoi(argv[4]);
00373     else
00374         sp = DEFAULT_SPIKE;
00375     if (sp < 0)
00376         sp = 0;
00377     if (sp > 100)
00378         sp = 100;
00379 
00380     if (argc > 5)
00381         pm = atoi(argv[5]);
00382     else
00383         pm = DEFAULT_MOUNTAIN;
00384     if (pm < 0)
00385         pm = 0;
00386 
00387     if (argc > 6)
00388         di = atoi(argv[6]);
00389     else
00390         di = DEFAULT_CONTDIST;
00391 
00392     if (di < 0) {
00393         puts("fairland: error -- distance between continents must be >= 0");
00394         exit(1);
00395     }
00396     if (di > WORLD_X / 2 || di > WORLD_Y / 2) {
00397         puts("fairland: error -- distance between continents too large");
00398         exit(1);
00399     }
00400 
00401     if (argc > 7)
00402         id = atoi(argv[7]);
00403     else
00404         id = DEFAULT_ISLDIST;
00405     if (id < 0) {
00406         puts("fairland: error -- distance from islands to continents must be >= 0");
00407         exit(1);
00408     }
00409     if (id > WORLD_X || id > WORLD_Y) {
00410         puts("fairland: error -- distance from islands to continents too large");
00411         exit(1);
00412     }
00413     if (nc * sc + nc * my_sqrt(sc) * 2 * (di + 1) > WORLD_X * WORLD_Y) {
00414         puts("fairland: error -- world not big enough to fit continents.");
00415         puts("arguments must satisfy:");
00416         puts("nc*sc*sc + nc*sqrt(sc)*2*(di+1) < WORLD_X * WORLD_Y");
00417         exit(1);
00418     }
00419 }
00420 
00421 /****************************************************************************
00422   VARIABLE INITIALIZATION
00423 ****************************************************************************/
00424 
00425 static int
00426 allocate_memory(void)
00427 {
00428     int i;
00429     char *fname;
00430 
00431     fname = malloc(strlen(gamedir) + 1 + strlen(empfile[EF_SECTOR].file) + 1);
00432     sprintf(fname, "%s/%s", gamedir, empfile[EF_SECTOR].file);
00433     sect_fptr = fopen(fname, "wb");
00434     if (sect_fptr == NULL) {
00435         perror(fname);
00436         return -1;
00437     }
00438     free(fname);
00439     sectsbuf = calloc((YSIZE * XSIZE), sizeof(struct sctstr));
00440     sects = calloc(YSIZE, sizeof(struct sctstr *));
00441     for (i = 0; i < YSIZE; i++)
00442         sects[i] = &sectsbuf[XSIZE * i];
00443     capx = calloc(nc, sizeof(int));
00444     capy = calloc(nc, sizeof(int));
00445     vector = calloc(WORLD_X + WORLD_Y, sizeof(int));
00446     mc = calloc(STABLE_CYCLE, sizeof(int));
00447     own = calloc(WORLD_X, sizeof(int *));
00448     elev = calloc(WORLD_X, sizeof(int *));
00449     for (i = 0; i < WORLD_X; ++i) {
00450         own[i] = calloc(WORLD_Y, sizeof(int));
00451         elev[i] = calloc(WORLD_Y, sizeof(int));
00452     }
00453     sectx = calloc(nc + ni, sizeof(int *));
00454     secty = calloc(nc + ni, sizeof(int *));
00455     sectc = calloc(nc + ni, sizeof(int *));
00456     isecs = calloc(nc + ni, sizeof(int));
00457     weight = calloc(MAX(sc, is * 2), sizeof(int));
00458     dsea = calloc(MAX(sc, is * 2), sizeof(int));
00459     dmoun = calloc(MAX(sc, is * 2), sizeof(int));
00460     for (i = 0; i < nc; ++i) {
00461         sectx[i] = calloc(sc, sizeof(int));
00462         secty[i] = calloc(sc, sizeof(int));
00463         sectc[i] = calloc(sc, sizeof(int));
00464     }
00465     for (i = nc; i < nc + ni; ++i) {
00466         sectx[i] = calloc(is * 2, sizeof(int));
00467         secty[i] = calloc(is * 2, sizeof(int));
00468         sectc[i] = calloc(is * 2, sizeof(int));
00469     }
00470 
00471     return 0;
00472 }
00473 
00474 static void
00475 init(void)
00476 {
00477     int i, j, xx = 0, yy = 0;
00478 
00479     mcc = 0;
00480     fl_status = 0;
00481 
00482     for (i = 0; i < WORLD_X; ++i) {
00483         for (j = 0; j < WORLD_Y; ++j) {
00484             own[i][j] = -1;
00485             elev[i][j] = -INFINITY;
00486         }
00487     }
00488 
00489     for (i = 0; i < nc; ++i, xx += 2) {
00490         if (xx >= WORLD_X) {
00491             ++yy;
00492             xx = yy % 2;
00493             if (yy == WORLD_Y) {
00494                 puts("fairland error: world not big enough for all the continents.\n");
00495                 exit(1);
00496             }
00497         }
00498         capx[i] = xx;
00499         capy[i] = yy;
00500     }
00501     for (i = 0; i < STABLE_CYCLE; ++i)
00502         mc[i] = i;
00503 }
00504 
00505 /****************************************************************************
00506   DRIFT THE CAPITALS UNTIL THEY ARE AS FAR AWAY FROM EACH OTHER AS POSSIBLE
00507 ****************************************************************************/
00508 
00509 /* How isolated is capital j?
00510 */
00511 static int
00512 iso(int j, int newx, int newy)
00513 {
00514     int i, md, d = WORLD_X + WORLD_Y;
00515 
00516     for (i = 0; i < nc; ++i) {
00517         if (i == j)
00518             continue;
00519         md = mapdist(capx[i], capy[i], newx, newy);
00520         if (md < d)
00521             d = md;
00522     }
00523 
00524     return d;
00525 }
00526 
00527 /* Drift all the capitals
00528 */
00529 static int
00530 drift(void)
00531 {
00532     int i, turns;
00533 
00534     for (turns = 0; turns < DRIFT_MAX; ++turns) {
00535         if (turns > DRIFT_BEFORE_CHECK && (mind = stable()))
00536             return 1;
00537         for (i = 0; i < nc; ++i)
00538             fl_move(i);
00539     }
00540     return 0;
00541 }
00542 
00543 /* Check to see if we have stabilized--can we stop drifting the capitals?
00544 */
00545 
00546 static int
00547 stable(void)
00548 {
00549     int i, isod, d = 0, stab = 1;
00550 
00551     for (i = 0; i < nc; ++i) {
00552         isod = iso(i, capx[i], capy[i]);
00553         if (isod > d)
00554             d = isod;
00555     }
00556     for (i = 0; i < STABLE_CYCLE; ++i)
00557         if (d != mc[i])
00558             stab = 0;
00559     mc[mcc] = d;
00560     mcc = (mcc + 1) % STABLE_CYCLE;
00561     return stab ? d : 0;
00562 }
00563 
00564 /* This routine does the actual drifting
00565 */
00566 
00567 static void
00568 fl_move(int j)
00569 {
00570     int i, n, newx, newy;
00571 
00572     for (i = rnd(6), n = 0; n < 6; i = (i + 1) % 6, ++n) {
00573         newx = new_x(capx[j] + dirx[i]);
00574         newy = new_y(capy[j] + diry[i]);
00575         if (iso(j, newx, newy) >= iso(j, capx[j], capy[j])) {
00576             capx[j] = newx;
00577             capy[j] = newy;
00578             return;
00579         }
00580     }
00581 }
00582 
00583 /****************************************************************************
00584   GROW THE CONTINENTS
00585 ****************************************************************************/
00586 
00587 /* Look for a coastal sector of continent c
00588 */
00589 
00590 static void
00591 find_coast(int c)
00592 {
00593     int i, j;
00594 
00595     for (i = 0; i < secs; ++i) {
00596         sectc[c][i] = 0;
00597         for (j = 0; j < 6; ++j)
00598             if (own[new_x(sectx[c][i] + dirx[j])][new_y(secty[c][i] + diry[j])] == -1)
00599                 sectc[c][i] = 1;
00600     }
00601 }
00602 
00603 /* Used for measuring distances
00604 */
00605 static int
00606 next_vector(int n)
00607 {
00608     int i;
00609 
00610     if (n == 1) {
00611         vector[0] += 1;
00612         vector[0] %= 6;
00613         return vector[0];
00614     }
00615     for (i = 1; i < n && vector[i] == vector[i - 1]; ++i) ;
00616     vector[i - 1] += 1;
00617     vector[i - 1] %= 6;
00618     return i > 1 || vector[0] > 0;
00619 }
00620 
00621 /* Test to see if we're allowed to grow there: the arguments di and id
00622 */
00623 static int
00624 try_to_grow(int c, int newx, int newy, int d)
00625 {
00626     int i, j, px, py;
00627 
00628     for (i = 1; i <= d; ++i) {
00629         for (j = 0; j < i; ++j)
00630             vector[j] = 0;
00631         do {
00632             px = newx;
00633             py = newy;
00634             for (j = 0; j < i; ++j) {
00635                 px = new_x(px + dirx[vector[j]]);
00636                 py = new_y(py + diry[vector[j]]);
00637             }
00638             if (own[px][py] != -1 &&
00639                 own[px][py] != c &&
00640                 (DISTINCT_ISLANDS || own[px][py] < nc))
00641                 return 0;
00642         } while (next_vector(i));
00643     }
00644     sectx[c][secs] = newx;
00645     secty[c][secs] = newy;
00646     own[newx][newy] = c;
00647     return 1;
00648 }
00649 
00650 /* Move along the coast in a clockwise direction.
00651 */
00652 
00653 static void
00654 next_coast(int c, int x, int y, int *xp, int *yp)
00655 {
00656     int i, nx, ny, wat = 0;
00657 
00658     if (secs == 1) {
00659         *xp = x;
00660         *yp = y;
00661         return;
00662     }
00663 
00664     for (i = 0; i < 12; ++i) {
00665         nx = new_x(x + dirx[i % 6]);
00666         ny = new_y(y + diry[i % 6]);
00667         if (own[nx][ny] == -1)
00668             wat = 1;
00669         if (wat && own[nx][ny] == c) {
00670             *xp = nx;
00671             *yp = ny;
00672             return;
00673         }
00674     }
00675 }
00676 
00677 /* Choose a sector to grow from
00678 */
00679 
00680 static int
00681 new_try(int c)
00682 {
00683     int i, starti;
00684 
00685     if (secs == 1) {
00686         if (sectc[c][0])
00687             return 0;
00688     } else {
00689         i = starti = (spike && sectc[c][secs - 1]) ? secs - 1 : rnd(secs);
00690         do {
00691             if (sectc[c][i])
00692                 return i;
00693             i = (i + 1) % secs;
00694         } while (i != starti);
00695         if (c < nc) {
00696             printf("fairland: BUG -- couldn't find coast for continent %c, sector %d.\nPlease mail stevens@math.utoronto.ca.\n",
00697                    c + 'a', secs);
00698             exit(1);
00699         } else
00700             return -1;
00701     }
00702     return -1;
00703 }
00704 
00705 /* Grow continent c by 1 sector
00706 */
00707 
00708 static int
00709 grow_one_sector(int c)
00710 {
00711     int done, coast_search, try1, x, y, newx, newy, i, n, sx, sy;
00712 
00713     spike = rnd(100) < sp;
00714     if ((try1 = new_try(c)) == -1)
00715         return 0;
00716     x = sx = sectx[c][try1];
00717     y = sy = secty[c][try1];
00718     coast_search = 0;
00719     done = 0;
00720     do {
00721         if (spike) {
00722             for (i = rnd(6), n = 0; n < 12 && !done; i = (i + 1) % 6, ++n) {
00723                 newx = new_x(x + dirx[i]);
00724                 newy = new_y(y + diry[i]);
00725                 if (own[newx][newy] == -1 &&
00726                     (n > 5 ||
00727                      (own[new_x(x+dirx[(i+5)%6])][new_y(y+diry[(i+5)%6])] == -1 &&
00728                       own[new_x(x+dirx[(i+1)%6])][new_y(y+diry[(i+1)%6])] == -1)))
00729                     if (try_to_grow(c, newx, newy, c < nc ? di : id))
00730                         done = 1;
00731             }
00732         } else
00733             for (i = rnd(6), n = 0; n < 6 && !done; i = (i + 1) % 6, ++n) {
00734                 newx = new_x(x + dirx[i]);
00735                 newy = new_y(y + diry[i]);
00736                 if (own[newx][newy] == -1)
00737                     if (try_to_grow(c, newx, newy, c < nc ? di : id))
00738                         done = 1;
00739             }
00740         next_coast(c, x, y, &x, &y);
00741         ++coast_search;
00742     } while (!done && coast_search < COAST_SEARCH_MAX &&
00743              (secs == 1 || x != sx || y != sy));
00744     if (!done && c < nc) {
00745         qprint("fairland: error -- continent %c had no room to grow!\n",
00746                numletter[c % 62]);
00747         fl_status |= STATUS_NO_ROOM;
00748     }
00749     return done;
00750 }
00751 
00752 /* Grow all the continents
00753 */
00754 static void
00755 grow_continents(void)
00756 {
00757     int c;
00758 
00759     for (c = 0; c < nc; ++c) {
00760         sectx[c][0] = capx[c];
00761         secty[c][0] = capy[c];
00762         own[sectx[c][0]][secty[c][0]] = c;
00763         sectx[c][1] = new_x(capx[c] + 2);
00764         secty[c][1] = capy[c];
00765         own[sectx[c][1]][secty[c][1]] = c;
00766     }
00767 
00768     for (secs = 2; secs < sc && !fl_status; ++secs) {
00769         for (c = 0; c < nc; ++c) {
00770             find_coast(c);
00771             grow_one_sector(c);
00772         }
00773     }
00774     for (c = 0; c < nc; ++c)
00775         find_coast(c);
00776 
00777     if (fl_status)
00778         qprint("Only managed to grow %d out of %d sectors.\n", secs, sc);
00779     ctot = nc;
00780 }
00781 
00782 /****************************************************************************
00783   GROW THE ISLANDS
00784 ****************************************************************************/
00785 
00786 /* Choose a place to start growing an island from
00787 */
00788 static int
00789 place_island(int c, int *xp, int *yp)
00790 {
00791     int d, sx, sy;
00792     int ssy = rnd(WORLD_Y);
00793     int ssx = new_x(rnd(WORLD_X / 2) * 2 + ssy % 2);
00794 
00795     if (ssx > WORLD_X - 2)
00796         ssx = new_x(ssx + 2);
00797     for (d = di + id; d >= id; --d) {
00798         sx = ssx;
00799         sy = ssy;
00800         *xp = new_x(sx + 2);
00801         for (*yp = sy; *xp != sx || *yp != sy; *xp += 2) {
00802             if (*xp >= WORLD_X) {
00803                 *yp = new_y(*yp + 1);
00804                 *xp = *yp % 2;
00805                 if (*xp == sx && *yp == sy)
00806                     break;
00807             }
00808             if (own[*xp][*yp] == -1 && try_to_grow(c, *xp, *yp, d))
00809                 return 1;
00810         }
00811     }
00812     return 0;
00813 }
00814 
00815 /* Grow all the islands
00816 */
00817 
00818 static void
00819 grow_islands(void)
00820 {
00821     int c, x, y, isiz;
00822 
00823     for (c = nc; c < nc + ni; ++c) {
00824         secs = 0;
00825         if (!place_island(c, &x, &y))
00826             return;
00827         isiz = 1 + rnd(2 * is - 1);
00828         do {
00829             ++secs;
00830             find_coast(c);
00831         } while (secs < isiz && grow_one_sector(c));
00832         find_coast(c);
00833         qprint(" %d(%d)", c - nc + 1, secs);
00834         isecs[c] = secs;
00835         ctot = c;
00836     }
00837 }
00838 
00839 /****************************************************************************
00840   CREATE ELEVATIONS
00841 ****************************************************************************/
00842 static void
00843 create_elevations(void)
00844 {
00845     elevate_land();
00846     elevate_sea();
00847 }
00848 
00849 /* Generic function for finding the distance to the closest sea, land, or
00850    mountain
00851 */
00852 static int
00853 distance_to_what(int x, int y, int flag)
00854 {
00855     int j, d, px, py;
00856 
00857     for (d = 1; d < 5; ++d) {
00858         for (j = 0; j < d; ++j)
00859             vector[j] = 0;
00860         do {
00861             px = x;
00862             py = y;
00863             for (j = 0; j < d; ++j) {
00864                 px = new_x(px + dirx[vector[j]]);
00865                 py = new_y(py + diry[vector[j]]);
00866             }
00867             switch (flag) {
00868             case 0:             /* distance to sea */
00869                 if (own[px][py] == -1)
00870                     return d;
00871                 break;
00872             case 1:             /* distance to land */
00873                 if (own[px][py] != -1)
00874                     return d;
00875                 break;
00876             case 2:             /* distance to mountain */
00877                 if (elev[px][py] == INFINITY)
00878                     return d;
00879                 break;
00880             }
00881         } while (next_vector(d));
00882     }
00883     return d;
00884 }
00885 
00886 #define ELEV elev[sectx[c][i]][secty[c][i]]
00887 #define distance_to_sea() (sectc[c][i]?1:distance_to_what(sectx[c][i], secty[c][i], 0))
00888 #define distance_to_mountain() distance_to_what(sectx[c][i], secty[c][i], 2)
00889 
00890 /* Decide where the mountains go
00891 */
00892 static void
00893 elevate_land(void)
00894 {
00895     int i, mountain_search, k, c, total, ns, nm, highest, where, h, newk,
00896         r, dk;
00897 
00898     for (c = 0; c < ctot; ++c) {
00899         total = 0;
00900         ns = (c < nc) ? sc : isecs[c];
00901         nm = (pm * ns) / 100;
00902 
00903 /* Place the mountains */
00904 
00905         for (i = 0; i < ns; ++i) {
00906             dsea[i] = distance_to_sea();
00907             weight[i] = (total += (dsea[i] * dsea[i]));
00908         }
00909 
00910         for (k = nm, mountain_search = 0;
00911              k && mountain_search < MOUNTAIN_SEARCH_MAX;
00912              ++mountain_search) {
00913             r = rnd(total);
00914             for (i = 0; i < ns; ++i)
00915                 if (r < weight[i] && ELEV == -INFINITY &&
00916                     (c >= nc ||
00917                      ((!(capx[c] == sectx[c][i] &&
00918                          capy[c] == secty[c][i])) &&
00919                       (!(new_x(capx[c] + 2) == sectx[c][i] &&
00920                          capy[c] == secty[c][i]))))) {
00921                     ELEV = INFINITY;
00922                     break;
00923                 }
00924             --k;
00925         }
00926 
00927 /* Elevate land that is not mountain and not capital */
00928 
00929         for (i = 0; i < ns; ++i)
00930             dmoun[i] = distance_to_mountain();
00931         dk = (ns - nm - ((c < nc) ? 3 : 1) > 0) ?
00932           (100 * (HIGHMIN - LANDMIN)) / (ns - nm - ((c < nc) ? 3 : 1)) :
00933           100 * INFINITY;
00934         for (k = 100 * (HIGHMIN - 1);; k -= dk) {
00935             highest = -INFINITY;
00936             where = -1;
00937             for (i = 0; i < ns; ++i) {
00938                 if (ELEV != INFINITY &&
00939                     (c >= nc || ((!(capx[c] == sectx[c][i] &&
00940                                     capy[c] == secty[c][i])) &&
00941                                  (!(new_x(capx[c] + 2) == sectx[c][i] &&
00942                                     capy[c] == secty[c][i]))))) {
00943                     h = 3 * (5 - dmoun[i]) + dsea[i];
00944                     if (h > highest) {
00945                         highest = h;
00946                         where = i;
00947                     }
00948                 }
00949             }
00950             if (where == -1)
00951                 break;
00952             newk = k / 100;
00953             if (newk >= HILLMIN && newk < PLATMIN)
00954                 newk = PLATMIN;
00955             if (newk < LANDMIN)
00956                 newk = LANDMIN;
00957             elev[sectx[c][where]][secty[c][where]] = newk;
00958             dsea[where] = -INFINITY;
00959             dmoun[where] = INFINITY;
00960         }
00961 
00962 /* Elevate the mountains and capitals */
00963 
00964         for (i = 0; i < ns; ++i) {
00965             if (ELEV == INFINITY) {
00966                 if (dsea[i] == 1)
00967                     ELEV = HILLMIN + rnd(PLATMIN - HILLMIN);
00968                 else
00969                     ELEV = HIGHMIN + rnd((256 - HIGHMIN) / 2) +
00970                       rnd((256 - HIGHMIN) / 2);
00971             } else if ((c < nc &&
00972                         ((capx[c] == sectx[c][i] && capy[c] == secty[c][i]))) ||
00973                        ((new_x(capx[c] + 2) == sectx[c][i] &&
00974                          capy[c] == secty[c][i])))
00975                 ELEV = PLATMIN;
00976         }
00977     }
00978 }
00979 
00980 #define distance_to_land() distance_to_what(x, y, 1)
00981 
00982 static void
00983 elevate_sea(void)
00984 {
00985     int x, y;
00986 
00987     for (y = 0; y < WORLD_Y; ++y) {
00988         for (x = y % 2; x < WORLD_X; x += 2) {
00989             if (elev[x][y] == -INFINITY)
00990                 elev[x][y] = -rnd((distance_to_land() * 20 + 27)) - 1;
00991         }
00992     }
00993 }
00994 
00995 /****************************************************************************
00996   ADD THE RESOURCES
00997 ****************************************************************************/
00998 
00999 static int
01000 set_fert(int e)
01001 {
01002     int fert = 0;
01003     if (e < LANDMIN)
01004         fert = LANDMIN - e + 40;
01005     else if (e < FERT_MAX)
01006         fert = (120 * (FERT_MAX - e)) / (FERT_MAX - LANDMIN);
01007     if (fert > 100)
01008         fert = 100;
01009     return fert;
01010 }
01011 
01012 static int
01013 set_oil(int e)
01014 {
01015     int oil = 0;
01016     if (e < LANDMIN)
01017         oil = (LANDMIN - e) * 2 + rnd(2);
01018     else if (e <= OIL_MAX)
01019         oil = (120 * (OIL_MAX - e + 1)) / (OIL_MAX - LANDMIN + 1);
01020     if (oil > 100)
01021         oil = 100;
01022     return oil;
01023 }
01024 
01025 static int
01026 set_iron(int e)
01027 {
01028     int iron = 0;
01029     if (e >= IRON_MIN && e < HIGHMIN)
01030         iron = (120 * (e - IRON_MIN + 1)) / (HIGHMIN - IRON_MIN);
01031     if (iron > 100)
01032         iron = 100;
01033     return iron;
01034 }
01035 
01036 static int
01037 set_gold(int e)
01038 {
01039     int gold = 0;
01040     if (e >= GOLD_MIN) {
01041         if (e < HIGHMIN)
01042             gold = (80 * (e - GOLD_MIN + 1)) / (HIGHMIN - GOLD_MIN);
01043         else
01044             gold = 100 - 20 * HIGHMIN / e;
01045     }
01046     if (gold > 100)
01047         gold = 100;
01048     return gold;
01049 }
01050 
01051 static int
01052 set_uran(int e)
01053 {
01054     int uran = 0;
01055     if (e >= URAN_MIN && e < HIGHMIN)
01056         uran = (120 * (e - URAN_MIN + 1)) / (HIGHMIN - URAN_MIN);
01057     if (uran > 100)
01058         uran = 100;
01059     return uran;
01060 }
01061 
01062 static void
01063 add_resources(struct sctstr *sct)
01064 {
01065     sct->sct_fertil = set_fert(sct->sct_elev);
01066     sct->sct_oil = set_oil(sct->sct_elev);
01067     sct->sct_min = set_iron(sct->sct_elev);
01068     sct->sct_gmin = set_gold(sct->sct_elev);
01069     sct->sct_uran = set_uran(sct->sct_elev);
01070 }
01071 
01072 /****************************************************************************
01073   DESIGNATE THE SECTORS
01074 ****************************************************************************/
01075 
01076 static void
01077 write_sects(void)
01078 {
01079     struct sctstr *sct;
01080     int c, x, y, total;
01081 
01082     /*  sct = &sects[0][0]; */
01083     sct = sectsbuf;
01084     for (y = 0; y < YSIZE; y++) {
01085         for (x = 0; x < XSIZE; x++, sct++) {
01086             fl_sct_init(x * 2 + (y & 1), y, sct);
01087             total = elev[sct->sct_x][y];
01088             if (total < LANDMIN) {
01089                 sct->sct_type = SCT_WATER;
01090             } else if (total < HILLMIN)
01091                 sct->sct_type = SCT_RURAL;
01092             else if (total < PLATMIN)
01093                 sct->sct_type = SCT_MOUNT;
01094             else if (total < HIGHMIN)
01095                 sct->sct_type = SCT_RURAL;
01096             else
01097                 sct->sct_type = SCT_MOUNT;
01098             sct->sct_elev = total;
01099             sct->sct_newtype = sct->sct_type;
01100             if (ORE)
01101                 add_resources(sct);
01102         }
01103     }
01104     if (AIRPORT_MARKER)
01105         for (c = 0; c < nc; ++c) {
01106             sects[capy[c]][capx[c] / 2 + capy[c] % 2].sct_type = SCT_AIRPT;
01107             sects[capy[c]][capx[c] / 2 + capy[c] % 2].sct_newtype = SCT_AIRPT;
01108         }
01109     set_coastal_flags();
01110 }
01111 
01112 /****************************************************************************
01113   WRITE ALL THIS STUFF TO THE FILE
01114 ****************************************************************************/
01115 static int
01116 write_file(void)
01117 {
01118     int n;
01119 
01120     n = fwrite(sectsbuf, sizeof(struct sctstr), YSIZE * XSIZE, sect_fptr);
01121     if (n <= 0) {
01122         perror(empfile[EF_SECTOR].file);
01123         return -1;
01124     }
01125     if (n != YSIZE * XSIZE) {
01126         printf("%s:partial write\n", empfile[EF_SECTOR].file);
01127         return -1;
01128     }
01129     fclose(sect_fptr);
01130     return 0;
01131 }
01132 
01133 /****************************************************************************
01134   PRINT A PICTURE OF THE MAP TO YOUR SCREEN
01135 ****************************************************************************/
01136 static void
01137 output(void)
01138 {
01139     int i, j;
01140     if (quiet == 0) {
01141         for (i = 0; i < WORLD_Y; ++i) {
01142             puts("");
01143             if (i % 2)
01144                 printf(" ");
01145             for (j = i % 2; j < WORLD_X; j += 2) {
01146                 if (own[j][i] == -1)
01147                     printf(". ");
01148                 else {
01149                     printf("%c ", map_symbol(j, i));
01150                 }
01151             }
01152         }
01153     }
01154     if (AIRPORT_MARKER)
01155         printf("\n\nEach continent is marked by a \"*\" on the map (to distinguish them from\nthe islands).  You can redesignate these airfields to wilderness sectors\none at a time, each time you add a new country to the game.\n");
01156 }
01157 
01158 static int
01159 map_symbol(int x, int y)
01160 {
01161     int c, iscap = 0;
01162 
01163     for (c = 0; c < nc; ++c)
01164         if ((x == capx[c] && y == capy[c])
01165             || (x == new_x(capx[c] + 2) && y == capy[c]))
01166             iscap = 1;
01167     if ((elev[x][y] >= HILLMIN && elev[x][y] < PLATMIN)
01168         || elev[x][y] >= HIGHMIN)
01169         return '^';
01170     return own[x][y] >= nc ? '%' : iscap ? '#' : numletter[own[x][y] % 62];
01171 }
01172 
01173 /***************************************************************************
01174   WRITE A SCRIPT FOR PLACING CAPITALS
01175 ****************************************************************************/
01176 static int
01177 write_newcap_script(void)
01178 {
01179     int c;
01180     FILE *script = fopen(outfile, "w");
01181 
01182     if (!script) {
01183         printf("fairland: error, unable to write to %s.\n", outfile);
01184         return -1;
01185     }
01186 
01187     for (c = 0; c < nc; ++c) {
01188         fprintf(script, "add %d %d %d n i\n", c + 1, c + 1, c + 1);
01189         if (AIRPORT_MARKER)
01190             fprintf(script, "des %d,%d -\n", capx[c], capy[c]);
01191         fprintf(script, "newcap %d %d,%d\n", c + 1, capx[c], capy[c]);
01192     }
01193     fprintf(script, "add %d visitor visitor v i\n", c + 1);
01194     ++c;
01195     fclose(script);
01196     qprint("\n\nA script for adding all the countries can be found in \"%s\".\n",
01197            outfile);
01198     return 0;
01199 }
01200 
01201 static void
01202 qprint(const char * const fmt, ...)
01203 {
01204     va_list ap;
01205 
01206     if (!quiet) {
01207         va_start(ap, fmt);
01208         vfprintf(stdout, fmt, ap);
01209         va_end(ap);
01210     }
01211 }
01212 
01213 static void
01214 fl_sct_init(coord x, coord y, struct sctstr *sp)
01215 {
01216     sp->ef_type = EF_SECTOR;
01217     sp->sct_x = x;
01218     sp->sct_y = y;
01219     sp->sct_dist_x = x;
01220     sp->sct_dist_y = y;
01221     sp->sct_road = 0;
01222     sp->sct_rail = 0;
01223     sp->sct_defense = 0;
01224     sp->sct_coastal = 1;
01225 }
01226 
01227 static void
01228 set_coastal_flags(void)
01229 {
01230     int i, j;
01231 
01232     qprint("setting coastal flags...\n");
01233     for (i = 0; i < nc; ++i)
01234         for (j = 0; j < sc; j++)
01235             sects[secty[i][j]][sectx[i][j] / 2].sct_coastal = sectc[i][j];
01236     for (i = nc; i < nc + ni; ++i)
01237         for (j = 0; j < isecs[i]; j++)
01238             sects[secty[i][j]][sectx[i][j] / 2].sct_coastal = sectc[i][j];
01239 }

Generated on Fri Mar 28 11:01:16 2008 for empserver by  doxygen 1.5.2