#include "version.h"
#include "xy.h"
-/* define ORE 1 to add resources, define ORE 0 if you want to use another
- program to add the resources */
-static int ORE = 1;
-static int quiet = 0;
-
-/* If you don't specify these command line arguments, then these are the
- defaults */
-#define DEFAULT_SPIKE 10
-#define DEFAULT_MOUNTAIN 0
-#define DEFAULT_CONTDIST 2
-#define DEFAULT_ISLDIST 1
-
/* The following five numbers refer to elevation under which (in the case of
fertility or oil) or over which (in the case of iron, gold, and uranium)
sectors with that elevation will contain that resource. Elevation ranges
static void qprint(const char * const fmt, ...)
ATTRIBUTE((format (printf, 1, 2)));
-#define DEFAULT_OUTFILE_NAME "newcap_script"
-static const char *outfile = DEFAULT_OUTFILE_NAME;
-/* mark the continents with a * so you can tell them
- from the islands 1 = mark, 0 = don't mark. */
-static int AIRPORT_MARKER = 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 char *program_name;
+static int quiet;
+#define DEFAULT_OUTFILE_NAME "newcap_script"
+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 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 nc, sc, di, sp, pm, ni, is, id; /* the 8 args to this program */
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 *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 =
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);
{
int opt;
char *config_file = NULL;
- int i = 0;
+ int try, done;
unsigned rnd_seed = 0;
int seed_set = 0;
program_name = argv[0];
- while ((opt = getopt(argc, argv, "ae:hioqR:s:v")) != EOF) {
+ while ((opt = getopt(argc, argv, "e:hiqR:s:v")) != EOF) {
switch (opt) {
- case 'a':
- AIRPORT_MARKER = 1;
- break;
case 'e':
config_file = optarg;
break;
case 'i':
DISTINCT_ISLANDS = 0;
break;
- case 'o':
- ORE = 0;
- break;
case 'q':
quiet = 1;
break;
exit(1);
}
}
- parse_args(argc - optind, argv + optind);
if (!seed_set)
rnd_seed = pick_seed();
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");
- if (ORE)
- qprint("adding resources...\n");
- write_newcap_script();
+ 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);
output();
qprint("\n\nA script for adding all the countries can be found in \"%s\".\n",
outfile);
- if (!ORE)
- qprint("\t*** Resources have not been added ***\n");
exit(0);
}
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)
{
usage(void)
{
printf("Usage: %s [OPTION]... NC SC [NI] [IS] [SP] [PM] [DI] [ID]\n"
- " -a airport marker for continents\n"
" -e CONFIG-FILE configuration file\n"
" (default %s)\n"
" -i islands may merge\n"
- " -o don't set resources\n"
" -q quiet\n"
" -R SEED seed for random number generator\n"
" -s SCRIPT name of script to create (default %s)\n"
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);
}
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");
- }
}
/****************************************************************************
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 *));
elev = calloc(WORLD_X, sizeof(int *));
for (i = 0; i < WORLD_X; ++i) {
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;
}
/****************************************************************************
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);
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
{
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)
{
int i, j, px, py;
+ if (own[newx][newy] != -1)
+ return 0;
+
for (i = 1; i <= d; ++i) {
for (j = 0; j < i; ++j)
vector[j] = 0;
return 0;
} while (next_vector(i));
}
- sectx[c][secs] = newx;
- secty[c][secs] = newy;
+ sectx[c][isecs[c]] = newx;
+ secty[c][isecs[c]] = newy;
+ isecs[c]++;
own[newx][newy] = c;
return 1;
}
{
int i, nx, ny, wat = 0;
- if (secs == 1) {
+ if (isecs[c] == 1) {
*xp = x;
*yp = y;
return;
*/
static int
-new_try(int c)
+new_try(int c, int spike)
{
+ int secs = isecs[c];
int i, starti;
if (secs == 1) {
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];
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 (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))
done = 1;
}
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, c < nc ? di : id))
+ 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;
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], di)
+ || !try_to_grow(c, new_x(capx[c] + 2), capy[c], di)) {
+ 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;
}
/****************************************************************************
if (*xp == sx && *yp == sy)
break;
}
- if (own[*xp][*yp] == -1 && try_to_grow(c, *xp, *yp, d))
+ if (try_to_grow(c, *xp, *yp, d))
return 1;
}
}
static void
grow_islands(void)
{
- int c, x, y, isiz;
+ int stunted_islands = 0;
+ int c, secs, x, y, isiz;
for (c = nc; c < nc + ni; ++c) {
- secs = 0;
- if (!place_island(c, &x, &y))
- return;
+ if (!place_island(c, &x, &y)) {
+ 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));
}
/****************************************************************************
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] = -INFINITY;
+ }
elevate_land();
elevate_sea();
}
for (c = 0; c < ctot; ++c) {
total = 0;
- ns = (c < nc) ? sc : isecs[c];
+ ns = isecs[c];
nm = (pm * ns) / 100;
/* Place the mountains */
}
}
+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
****************************************************************************/
write_sects(void)
{
struct sctstr *sct;
- int c, 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;
- if (ORE)
- add_resources(sct);
+ add_resources(sct);
}
}
- if (AIRPORT_MARKER)
- for (c = 0; c < nc; ++c) {
- sct = getsectp(capx[c], capy[c]);
- sct->sct_type = SCT_AIRPT;
- sct->sct_newtype = SCT_AIRPT;
- }
set_coastal_flags();
}
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++) {
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("# ");
}
}
}
}
- if (AIRPORT_MARKER)
- printf("\n\nEach continent is marked by a \"*\" on the map (to distinguish them from\n"
- "the islands). You can redesignate these airfields to wilderness sectors\n"
- "one at a time, each time you add a new country to the game.\n");
-}
-
-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 ? '%' : '#';
}
/*
FILE *script = fopen(outfile, "w");
if (!script) {
- printf("fairland: error, unable to write to %s.\n", outfile);
- return -1;
+ 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);
- if (AIRPORT_MARKER)
- fprintf(script, "des %d,%d -\n", capx[c], capy[c]);
fprintf(script, "newcap %d %d,%d\n", c + 1, capx[c], capy[c]);
}
fprintf(script, "add %d visitor visitor v\n", c + 1);
fclose(script);
- return 0;
+ return 1;
}
static 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];