This is all of the various patches I use for my own FreeCiv games
unified in to a single huge mega-patch.  Vallimar has some improvments
to this patch which makes it so that the separate islands and giving the
humans the best islands are user-selectable parameters; I hope to have time
to make a patch which incorporates more of Vallimar's improvments
available in the near future.

Until then, this patch does the following:

* Allow the maximum research cost to be so high as to make tech advancment
  a non-factor in the game.  Note that the client acts buggy with this;
  the TCP packets assume that tech never costs more than 256 bulbs.

* Allows really tiny (20x16) and really huge (255x255) maps.  Note that
  the client is buggy with maps which are narrower than the screen width

* Makes the default map be a 40x32 map using a fixed RNG seed.  I like 
  playing this map because I am very familiar with it.

* Adds three new map generators: Josh's penisula map, Mark Isaac's tectonic
  code adapted to FreeCiv, and a variant of the tectonic code which makes
  more islands.  Note that Josh's generator makes fairly static maps, and
  that Mark's code is a good deal slower than any other generator out there
  (but also makes far more Earth-like maps)

* Makes it so the humans always gets better islands than the AI players

* Makes it so only one player is on a given island; this may mean that some
  players will end up on tiny islands if there are no suitable islands for
  the players.  Make sure to choose a map which gives each player a decent
  island.

* Adds two new parameters: slowai_tech, which makes the AI get less tech,
  and slowai_grow, which discourages the AI from building new cities.  
  These are added to give "training wheels" to new players who are
  frustrated by how hard the AI is to beat.

diff -ur freeciv-1.14.0/common/game.c freeciv-1.14.0-shr2/common/game.c
--- freeciv-1.14.0/common/game.c	Fri Oct 11 16:35:13 2002
+++ freeciv-1.14.0-shr2/common/game.c	Tue Apr  8 05:41:43 2003
@@ -714,6 +714,9 @@
   game.watchtower_extra_vision=GAME_DEFAULT_WATCHTOWER_EXTRA_VISION,
   game.allowed_city_names = GAME_DEFAULT_ALLOWED_CITY_NAMES;
 
+  game.slowai_tech = GAME_DEFAULT_SLOWAI_TECH;
+  game.slowai_grow = GAME_DEFAULT_SLOWAI_GROW;
+
   sz_strlcpy(game.rulesetdir, GAME_DEFAULT_RULESETDIR);
 
   game.firepower_factor = 1;
diff -ur freeciv-1.14.0/common/game.h freeciv-1.14.0-shr2/common/game.h
--- freeciv-1.14.0/common/game.h	Fri Oct 11 16:35:13 2002
+++ freeciv-1.14.0-shr2/common/game.h	Tue Apr  8 05:41:43 2003
@@ -217,6 +217,9 @@
     bool load_private_map; /* Only makes sense if the players are loaded. */
     bool load_settings;
   } load_options;
+
+  int slowai_tech;
+  int slowai_grow;
 };
 
 /* Unused? */
@@ -306,7 +309,7 @@
 
 #define GAME_DEFAULT_RESEARCHCOST	   10
 #define GAME_MIN_RESEARCHCOST	       4
-#define GAME_MAX_RESEARCHCOST        100
+#define GAME_MAX_RESEARCHCOST        10000
 
 #define GAME_DEFAULT_DIPLCOST        0
 #define GAME_MIN_DIPLCOST            0
@@ -455,4 +458,11 @@
 
 #define GAME_START_YEAR -4000
 
+#define GAME_DEFAULT_SLOWAI_TECH 100
+#define GAME_MIN_SLOWAI_TECH 50
+#define GAME_MAX_SLOWAI_TECH 400
+#define GAME_DEFAULT_SLOWAI_GROW 100
+#define GAME_MIN_SLOWAI_GROW 25
+#define GAME_MAX_SLOWAI_GROW 100
+
 #endif  /* FC__GAME_H */
diff -ur freeciv-1.14.0/common/map.h freeciv-1.14.0-shr2/common/map.h
--- freeciv-1.14.0/common/map.h	Fri Oct 11 16:35:13 2002
+++ freeciv-1.14.0-shr2/common/map.h	Tue Apr  8 05:41:39 2003
@@ -573,15 +573,15 @@
 #define MAP_MIN_HUTS             0
 #define MAP_MAX_HUTS             500
 
-#define MAP_DEFAULT_WIDTH        80
-#define MAP_MIN_WIDTH            40
-#define MAP_MAX_WIDTH            200
-
-#define MAP_DEFAULT_HEIGHT       50
-#define MAP_MIN_HEIGHT           25
-#define MAP_MAX_HEIGHT           100
+#define MAP_DEFAULT_WIDTH        40
+#define MAP_MIN_WIDTH            12
+#define MAP_MAX_WIDTH            255
+
+#define MAP_DEFAULT_HEIGHT       32
+#define MAP_MIN_HEIGHT           12
+#define MAP_MAX_HEIGHT           255
 
-#define MAP_DEFAULT_SEED         0
+#define MAP_DEFAULT_SEED         8675309
 #define MAP_MIN_SEED             0
 #define MAP_MAX_SEED             (MAX_UINT32 >> 1)
 
@@ -619,7 +619,7 @@
 
 #define MAP_DEFAULT_GENERATOR    1
 #define MAP_MIN_GENERATOR        1
-#define MAP_MAX_GENERATOR        5
+#define MAP_MAX_GENERATOR        8
 
 #define MAP_DEFAULT_TINYISLES    FALSE
 #define MAP_MIN_TINYISLES        FALSE
diff -ur freeciv-1.14.0/common/tech.c freeciv-1.14.0-shr2/common/tech.c
--- freeciv-1.14.0/common/tech.c	Fri Oct 11 16:35:13 2002
+++ freeciv-1.14.0-shr2/common/tech.c	Tue Apr  8 05:41:43 2003
@@ -457,6 +457,17 @@
     exit(EXIT_FAILURE);
   }
 
+  /* For casual players, the AI gets tech far too fast.  This
+     slows down how fast the AI gets tech by the value in 
+     game.slowai_tech */
+  if (!game.is_new_game && pplayer->ai.control && game.slowai_tech != 100) {
+      float fcost, factor;
+      fcost = cost;
+      factor = game.slowai_tech;
+      fcost *= (factor / 100);
+      cost = fcost;
+      }
+
   /* If we have many players, tech cost may drop to 0.  */
   if (cost == 0) {
     cost = 1;
diff -ur freeciv-1.14.0/server/gamehand.c freeciv-1.14.0-shr2/server/gamehand.c
--- freeciv-1.14.0/server/gamehand.c	Fri Oct 11 16:35:50 2002
+++ freeciv-1.14.0-shr2/server/gamehand.c	Tue Apr  8 05:41:39 2003
@@ -38,15 +38,59 @@
 {
   int i, j, x, y;
   int dx, dy;
+  int last_human_player = 0, first_ai_player;
   Unit_Type_id utype;
   int start_pos[MAX_NUM_PLAYERS]; /* indices into map.start_positions[] */
 
   if (!map.fixed_start_positions) {
-    /* except in a scenario which provides them,
-       shuffle the start positions around... */
     assert(game.nplayers==map.num_start_positions);
-    for (i=0; i<game.nplayers;i++) { /* no advantage to the romans!! */
-      j=myrand(game.nplayers);
+
+    /* We move all of the ai players after all of the humans.  This is done
+       in conjunciton with code in mapgen.c which makes sure all of the
+       starting points on good islands come before (in the 
+       map.start_positions array) all of the starting positions on bad
+       islands. 
+
+       The code assumes that each player gets their own island.
+       If not, then what this code will do is cause all of the humans
+       to share the best islands and all of the AIs to share the not-so-good
+       islands.
+
+       Note that this is an inefficient O(N^2) algorithm; with the current
+       max of 30 players, there should not be a problem */
+
+    last_human_player = game.nplayers + 1;
+
+    for(j=game.nplayers - 1; j >=0; j--) {
+        if(!game.players[j].ai.control) {
+	    last_human_player = j;
+	    break;
+	    }
+        }
+    if(last_human_player + 1< game.nplayers) {
+      for(i=0; i<last_human_player; i++) {
+        if(game.players[i].ai.control) { 
+	    /* Swap the positions */
+            x=map.start_positions[last_human_player].x;
+            y=map.start_positions[last_human_player].y;
+            map.start_positions[last_human_player].x=map.start_positions[i].x;
+            map.start_positions[last_human_player].y=map.start_positions[i].y;
+            map.start_positions[i].x=x;
+            map.start_positions[i].y=y;
+            for(j=game.nplayers - 1; j >=0 ; j--) {
+                if(!game.players[j].ai.control) {
+	            last_human_player = j;
+		    break;
+	            }
+                }
+	    }
+        }
+    }
+
+    /* except in a scenario which provides them,
+       shuffle the human's start positions around... */
+    for (i=0; i<= last_human_player;i++) { /* no advantage to the romans!! */
+      j=myrand(last_human_player + 1);
       x=map.start_positions[j].x;
       y=map.start_positions[j].y;
       map.start_positions[j].x=map.start_positions[i].x;
@@ -54,6 +98,20 @@
       map.start_positions[i].x=x;
       map.start_positions[i].y=y;
     }
+
+    /* Also shuffle all of the ai's start positions */
+    first_ai_player = last_human_player + 1;
+    for(i = first_ai_player;i < game.nplayers; i++) { 
+        j = myrand(game.nplayers - first_ai_player);
+	j += first_ai_player;
+        x=map.start_positions[j].x;
+        y=map.start_positions[j].y;
+        map.start_positions[j].x=map.start_positions[i].x;
+        map.start_positions[j].y=map.start_positions[i].y;
+        map.start_positions[i].x=x;
+        map.start_positions[i].y=y;
+    }
+    
     for(i=0; i<game.nplayers; i++) {
       start_pos[i] = i;
     } 
diff -ur freeciv-1.14.0/server/mapgen.c freeciv-1.14.0-shr2/server/mapgen.c
--- freeciv-1.14.0/server/mapgen.c	Fri Oct 11 16:35:50 2002
+++ freeciv-1.14.0-shr2/server/mapgen.c	Tue Apr  8 05:41:40 2003
@@ -19,6 +19,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <time.h>
+#include <math.h> /* For the plate tectonic simulator */
 
 #include "fcintl.h"
 #include "game.h"
@@ -41,6 +42,8 @@
 static void mapgenerator3(void);
 static void mapgenerator4(void);
 static void mapgenerator5(void);
+static void mapgenerator6(void);
+static void mapgenerator_tectonic(void);
 static void smooth_map(void);
 static void adjust_map(int minval);
 
@@ -61,6 +64,7 @@
   int x,y;                        /* upper left corner of the islands */
   int goodies;
   int starters;
+  int continent;
 };
 static struct isledata *islands;
 
@@ -258,13 +262,23 @@
   while (i > 0 && j < 500) {
     j++;
 
-    y=myrand(map.ysize*10/180)+map.ysize*110/180;
+    if(map.ysize >= 18) {
+        y=myrand(map.ysize*10/180)+map.ysize*110/180;
+        }
+    else {
+        y = map.ysize*110/180;
+	}
     x=myrand(map.xsize);
     if (map_get_terrain(x, y)==T_GRASSLAND) {
       make_desert(x,y, hmap(x, y), 50);
       i--;
     }
-    y=myrand(map.ysize*10/180)+map.ysize*60/180;
+    if(map.ysize >= 18) {
+        y=myrand(map.ysize*10/180)+map.ysize*60/180;
+        }
+    else {
+        y = map.ysize*60/180;
+	}
     x=myrand(map.xsize);
     if (map_get_terrain(x, y)==T_GRASSLAND) {
       make_desert(x,y, hmap(x, y), 50);
@@ -823,7 +837,7 @@
   1) with map.landpercent it generates a ocean/grassland map 
   2) it then calls the above functions to generate the different terrains
 **************************************************************************/
-static void make_land(void)
+static void make_land(int window_size)
 {
   int tres=(maxval*map.landpercent)/100;
   int count=0;
@@ -846,7 +860,7 @@
     else
       tres*=9;
     tres/=10;
-  } while (abs(total-count)> maxval/40);
+  } while (abs(total-count)> window_size/40);
   if (map.separatepoles) {
     make_passable();
   }
@@ -962,6 +976,7 @@
   for(i=0; i<=map.num_continents; i++) {
     islands[i].x = islands[i].y = -1;             /* flag */
     islands[i].goodies = islands[i].starters = 0;
+    islands[i].continent = 0;
   }
 
   /* get x and y positions: (top left) */
@@ -970,6 +985,7 @@
     if (islands[cont].x == -1) {
       islands[cont].x = x;
       islands[cont].y = y;
+      islands[cont].continent = cont;
     }
   } whole_map_iterate_end;
 
@@ -1062,8 +1078,8 @@
  
       /* starters are loosers */
       for (x=firstcont;x<isles;x++) {
-	if (( islands[x].goodies*4 > 3*(riches+oldisles-1)/oldisles )
-	    &&!(islands[x].goodies > (riches+oldisles-1)/oldisles)) {
+	if (1 == 1) { /* All islands get a starter since we give the
+	                 humans the best islands */
 	  freelog(LOG_VERBOSE, "islands[x].goodies=%i",islands[x].goodies);
 	  islands[x].starters= (islands[x].goodies+guard1)/mingood; 
 	  if(islands[x].starters == 0) {
@@ -1077,7 +1093,7 @@
     /* starters are winners */
     for (x=firstcont;x<isles;x++) {
       if (islands[x].goodies > (riches+oldisles-1)/oldisles) {
-	assert(islands[x].starters == 0);
+	/*assert(islands[x].starters == 0);*/
 	freelog(LOG_VERBOSE, "islands[x].goodies=%i", islands[x].goodies);
 	islands[x].starters = (islands[x].goodies+guard1)/mingood; 
 	if(islands[x].starters == 0) {
@@ -1107,6 +1123,61 @@
 }
 
 /**************************************************************************
+ Comparison function that compares the continent number on two isles
+ *************************************************************************/
+
+static int compar_isle_cont(const void *a,const void *b) 
+{
+
+  struct isledata *t;
+  int ia, ib;
+  t = (struct isledata *)a;
+  ia = t->continent;
+  t = (struct isledata *)b;
+  ib = t->continent;
+  return ia - ib;
+
+}
+
+/**************************************************************************
+ Comparison function that compares the goodies on two isles
+ *************************************************************************/
+
+static int compar_isle_goodies(const void *a,const void *b) 
+{
+
+  struct isledata *t;
+  int ia, ib;
+  t = (struct isledata *)a;
+  ia = t->goodies;
+  t = (struct isledata *)b;
+  ib = t->goodies;
+  return ib - ia;
+
+}
+
+/**************************************************************************
+ Comparison function that compares the goodness of two start points (how
+ good the island the two start points are on
+ *************************************************************************/
+
+static int compar_isle_goodness(const void *a,const void *b) 
+{
+   int ia, ib;
+   int x,y;
+   struct map_position *t;
+   t = (struct map_position *)a;
+   x=t->x;
+   y=t->y;
+   ia = islands[(int)map_get_continent(x, y)].goodies;
+   t = (struct map_position *)b;
+   x=t->x;
+   y=t->y;
+   ib = islands[(int)map_get_continent(x, y)].goodies;
+   return ib - ia;
+}
+
+/**************************************************************************
   where do the different races start on the map? well this function tries
   to spread them out on the different islands.
 
@@ -1130,21 +1201,72 @@
   if(dist>= map.ysize/2)
     dist= map.ysize/2;
 
+  /* If we make distance really big here (and do the check to make sure each
+     player gets their own island), we can 100% ensure that each player
+     gets their own island; dist is the minimum distance that two players
+     can be from one another on the same island */
+
+  dist = 10000000;
+
+
   sum=0;
   for (k=0; k<=map.num_continents; k++) {
-    sum += islands[k].starters;
-    if (islands[k].starters!=0) {
-      freelog(LOG_VERBOSE, "starters on isle %i", k);
-    }
+    sum++;
+    if((k>2 || !map.separatepoles) && k>0) {
+        islands[k].starters = 1;
+	}
+    else {
+        islands[k].starters = 0;
+	}
   }
+
+  if(game.nplayers > map.num_continents - 2) {
+      printf("Sorry, you have too many players.  Discarding some.\n");
+      game.nplayers = map.num_continents - 2;
+      printf("You now only have %d players\n",game.nplayers);
+      }
+
+  /* Sort things so that the best islands come before the bad islands */
+  qsort(&islands[1],map.num_continents,
+        sizeof(struct isledata),compar_isle_goodies);
+
+  /* And now, set starters appropriately so that, when there are
+     more islands than starters, the players always start on the
+     best islands, one island per player (and, with the other patch,
+     the humans get first nibs at the good islands) */
+  sum = 0;
+  for (k=0; k<=map.num_continents; k++) {
+    if(islands[k].starters == 1) {
+        sum++;
+        islands[k].starters = sum;
+	}
+  }
+
+  /* Since code assumes that islands[i] is the same as what map_get_continent
+     returns, re-sort to make this assumption true again */
+  qsort(&islands[1],map.num_continents,
+        sizeof(struct isledata),compar_isle_cont);
+
   assert(game.nplayers<=nr+sum);
 
   while (nr<game.nplayers) {
+    int isle_num;
     rand_map_pos(&x, &y);
-    if (islands[(int)map_get_continent(x, y)].starters != 0) {
+    isle_num = (int)map_get_continent(x, y);
+    if (islands[isle_num].starters <= game.nplayers
+        && islands[isle_num].starters > 0) {
       j++;
-      if (!is_starter_close(x, y, nr, dist)) {
-	islands[(int)map_get_continent(x, y)].starters--;
+      if(counter > 100000 && !map_has_special(x, y, S_HUT) && 
+         (map_get_continent(x, y) > 2 || !map.separatepoles) &&
+	 land_terrain(x,y)) {
+	islands[isle_num].starters = 0;
+	map.start_positions[nr].x=x;
+	map.start_positions[nr].y=y;
+	nr++;
+	}
+      else if (!is_starter_close(x, y, nr, 1000000) && land_terrain(x, y)
+        && (map_get_continent(x, y) > 2 || !map.separatepoles)) {
+	islands[isle_num].starters = 0;
 	map.start_positions[nr].x=x;
 	map.start_positions[nr].y=y;
 	nr++;
@@ -1167,6 +1289,16 @@
   }
   map.num_start_positions = game.nplayers;
 
+  /* Sort the array so that the lowered numbered starting positions are
+     always on the best isles; this is used in conjunction with code in
+     gamehand.c to make sure that the humans get the best islands; the
+     code works best when each player gets their own island (otherwise
+     all of the humans will share the best islands and all of the AIs will
+     share the worse islands) */
+
+  qsort(map.start_positions,map.num_start_positions,
+        sizeof(struct map_position),compar_isle_goodness);
+
   free(islands);
   islands = NULL;
 }
@@ -1196,6 +1328,10 @@
     map_allocate();
     /* if one mapgenerator fails, it will choose another mapgenerator */
     /* with a lower number to try again */
+    if (map.generator == 6 )
+      mapgenerator6();
+    if (map.generator == 7 || map.generator == 8)
+      mapgenerator_tectonic();
     if (map.generator == 5 )
       mapgenerator5();
     if (map.generator == 4 )
@@ -1245,7 +1381,7 @@
   /*!PS: I don't have the time to have several test runs */
   /* to find a mapping from percents to generator 1 settings. */
 
-  if(map.generator==1){
+  if(map.generator==1 || map.generator==7){
     /*map.riverlength*= 10; */
     /*I leave this out, people will get too upset 
       if they have to change all their scripts */
@@ -1289,7 +1425,7 @@
 
 /**************************************************************************
   since the generated map will always have a positive number as minimum height
-  i reduce the height so the lowest height is zero, this makes calculations
+  reduce the height so the lowest height is zero, this makes calculations
   easier
 **************************************************************************/
 static void adjust_map(int minval)
@@ -1338,7 +1474,829 @@
   maxval-=minval;
   adjust_map(minval);
 
-  make_land();
+  make_land(maxval);
+  free(height_map);
+  height_map = NULL;
+}
+
+/**************************************************************************
+ The following code was written by Mark Isaak in 1988 and converted in to
+ a freeciv-compatible form 2003 by Sam Trenholme
+ *************************************************************************/
+
+/*
+ * A program to simulate tectonics.
+ *
+ * Copyright 1988 by Mark Isaak.
+ * Copyright 2003 Sam Trenholme; with Mark Isaak's permission, I have
+ * relicensed this under the GPL.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ *
+ * Wish list (from 1988):
+ *      Do it on a sphere, not a square torus (not wanted for FreeCiv)
+ *	Give plates angular momentum
+ *	Make some constants variable
+ *	Horsts and grabens
+ *	Let 2 plates fuse together and/or 1 plate split apart sometimes
+ *	Astroblemes
+ *	Take density of rock into account
+ */
+
+/*
+ * ---------- Structures -----------------------------------------------------
+ */
+
+/*
+ * ---------- Defined parameters ---------------------------------------------
+ *
+ * Some notes on Earth for comparison:
+ *   Mean elevation of land = 840 m.
+ *   Kilamanjaro = 5895 m.
+ *   Everest = 8848 m.
+ *   Dead Sea = -400 m.
+ *   Gread Rift Valley = -150 m.
+ *   Andes = 6950 m.
+ *   Marianas Trench = -11000 m.
+ *   Mean depth of sea = -3810 m.
+ */
+
+struct tectonic_data {
+
+  int   xsize; /* Size of map along x axis */
+  int   ysize; /* Size of map along y axis */
+  int   nplates;	/* # of plates in world at genesis */
+  int   maxplates;	/* maximum # of plates we can have */
+  int   ncontinents;	/* # of plates which are continental */
+  int   nhotspots;	/* # of hot spots */
+
+  int   landelev;	/* starting elevation of land */
+  int   oceanelev;	/* starting elevation of oceans */
+
+  int   epochmove;	/* max movement during one epoch */
+
+  float foldfactor;	/* how much to increase ht. of folds */
+  int   riftelev;	/* elevation for rifts */
+  int   subsumeheight; 	/* elevation change for coastal ranges */
+  int   trenchheight;	/* elev. change for trench by coastal range */
+  int   hotspotheight; 	/* average ht. growth of hot spots */
+
+  int   riftminerals; 	/* minerals in rifts */
+  int   subsumeminerals; 	/* minerals in costal ranges */
+  int   archipminerals; 	/* minerals in archipelagos */
+  int   hotspotminerals; 	/* minerals in underwater hot spots */
+ 
+  float erodefactor; 	/* how much of a height to erode */
+  float submergefactor; 	/* how being underwater slows erosion */
+  int   skyelev;		/* no erosion above this */
+  int   waveaction; 	/* erosion due entirely to waves */
+  int   subsidefactor; 	/* how fast ocean floor sinks */
+  int   basinelev; 	/* how far ocean floor can sink */
+
+};
+
+static struct tectonic_data tectonic;
+
+void init_tec_data() {
+  tectonic.xsize = map.xsize; /* Width of map */
+  tectonic.ysize = map.ysize; /* Height of map */
+
+  tectonic.nplates = 20; /* # of plates in world at genesis */
+  tectonic.maxplates = 27; /* maximum # of plates we can have */
+  tectonic.ncontinents = 5; /* # of plates which are continental */
+  tectonic.nhotspots = 10; /* # of hot spots */
+
+  tectonic.landelev = 800; /* starting elevation of land */
+  tectonic.oceanelev = -1000; /* starting elevation of oceans */
+
+  tectonic.epochmove = 5; /* max movement during one epoch */
+
+  tectonic.foldfactor = 1.2; /* how much to increase ht. of folds */
+  tectonic.riftelev = -400; /* elevation for rifts */
+  tectonic.subsumeheight = 1200; /* elevation change for coastal ranges */
+  tectonic.trenchheight = -500; /* elev. change for trench by coastal range */
+  tectonic.hotspotheight = 600; /* average ht. growth of hot spots */
+
+  tectonic.riftminerals = 7; /* minerals in rifts */
+  tectonic.subsumeminerals = 3; /* minerals in costal ranges */
+  tectonic.archipminerals = 5; /* minerals in archipelagos */
+  tectonic.hotspotminerals = 2; /* minerals in underwater hot spots */
+
+  tectonic.erodefactor = 0.023; /* how much of a height to erode */
+  tectonic.submergefactor = 0.2; /* how being underwater slows erosion */
+  tectonic.skyelev = 10000; /* no erosion above this */
+  tectonic.waveaction = 400; /* erosion due entirely to waves */
+  tectonic.subsidefactor = 0.05; /* how fast ocean floor sinks */
+  tectonic.basinelev = -9000; /* how far ocean floor can sink */
+  }
+
+/*
+ * Tectonic plate
+ */
+typedef struct {
+    float	xvec, yvec;	/* direction of plate movement */
+    short	dx, dy;		/* how far do move plate per age */
+    bool	continent;	/* whether initially continent or ocean */
+} Plate_t;
+
+/*
+ * Hot spot
+ */
+typedef struct {
+    short	x, y;		/* location of hot spot */
+    short	howhot;		/* how much it spews forth */
+} Hotspot_t;
+
+/*
+ * A piece of land which moves around
+ */
+typedef struct rock_s {
+    short	elev;		/* elevation */
+    short	elevchange;	/* change in elevation due to erosion */
+    Plate_t	*plate;		/* which plate it belongs to */
+    bool	moved;		/* whether we've moved this iteration */
+    long	minerals;	/* how many minerals */
+    struct rock_s *next;	/* next in linked list */
+} Rock_t;
+
+/*
+ * ---------- Global variables -----------------------------------------------
+ */
+Rock_t	*surface[1024][1024];	/* surface of planet */
+Plate_t	plates[1024];		/* descriptions of plates */
+Hotspot_t	hotspots[1024];	/* descriptions of hot spots */
+
+int	nplates;			/* # of tectonic plates */
+Rock_t	*freeRockList;			/* ptr to list of unused rocks */
+short	maxelev, minelev;		/* elevation extremes */
+
+/*
+ * ---------- Utilities ------------------------------------------------------
+ */
+/*
+ * Return a random number from 0 to n-1.
+ */
+  int
+Rnd(n)
+  int n;
+{
+    return myrand(n); /* Freeciv's RNG */
+}
+
+/*
+ * Wraparound from one side of the world to the other.
+ */
+  int
+Wrap_x(val)
+  register int val;	/* x coordinate */
+{
+    if (val < 0)
+        val += tectonic.xsize;
+    else if (val >= tectonic.xsize)
+        val -= tectonic.xsize;
+    return (val);
+}
+
+  int
+Wrap_y(val)
+  register int val;	/* y coordinate */
+{
+    if (val < 0)
+        val += tectonic.ysize;
+    else if (val >= tectonic.ysize)
+        val -= tectonic.ysize;
+    return (val);
+}
+
+/*
+ * Allocate a rock.
+ */
+  Rock_t *
+AllocRock(pl)
+  Plate_t	*pl;	/* which plate it goes on */
+{
+    Rock_t	*rp;	/* ptr to rock */
+
+    if (freeRockList) {
+        rp = freeRockList;
+        freeRockList = rp->next;
+    } else
+        rp = (Rock_t *)malloc(sizeof(Rock_t));
+    rp->plate = pl;
+    rp->elev = (pl->continent ? tectonic.landelev : tectonic.oceanelev);
+    rp->elevchange = 0;
+    rp->moved = FALSE;
+    rp->minerals = 0;
+    rp->next = NULL;
+    return (rp);
+}
+
+/*
+ * ---------- Initialization -------------------------------------------------
+ */
+
+/*
+ * Put plates at random spots on the planet.
+ * Make tectonic.ncontinents of them land, the rest ocean.
+ */
+int SeedPlates()
+{
+    register int i;
+    register Plate_t *pl;	/* ptr to plates */
+
+    pl = &plates[0];
+    for (i = 0; i < tectonic.nplates; ++i, ++pl) {
+        pl->dx = Rnd(tectonic.xsize);
+        pl->dy = Rnd(tectonic.ysize);
+        /*
+         * Check if this spot is already taken.
+         */
+        if (surface[pl->dx][pl->dy]) {
+            --pl;
+            --i;
+            continue;	/* try again */
+        }
+        surface[pl->dx][pl->dy] = (Rock_t *)pl;
+        pl->xvec = 0.0;
+        pl->yvec = 0.0;
+        pl->continent = (i < tectonic.ncontinents);
+    }
+    return 0;
+}
+
+/*
+ * Initialize the surface.  Allocate a rock for each spot on the surface
+ * and assign it to the nearest plate.
+ */
+int InitSurface()
+{
+    register short	dx, dy;		/* x, y distance */
+    register long	dist;		/* distance */
+    register short	x, y;		/* location on surface */
+    register Plate_t	*pl;		/* plate pointer */
+    register Plate_t	*bestpl;	/* closest plate */
+    register long	bestdist;	/* distance of closest plate */
+    register int	i;		/* plate counter */
+
+    bestpl = NULL;
+
+    for (x = 0; x < tectonic.xsize; ++x) {
+        for (y = 0; y < tectonic.ysize; ++y) {
+            bestdist = 2 * tectonic.xsize * tectonic.ysize;
+            for (pl = &plates[0], i = 0; i < tectonic.nplates; ++i, ++pl) {
+                dx = pl->dx - x;
+                if (dx < 0)
+                    dx = -dx;
+                if (dx > tectonic.xsize/2)
+                    dx = tectonic.xsize - dx;
+                dy = pl->dy - y;
+                if (dy < 0)
+                    dy = -dy;
+                if (dy > tectonic.ysize/2)
+                    dy = tectonic.ysize - dy;
+                dist = dx * dx + dy * dy;
+                if (dist < bestdist) {
+                    bestdist = dist;
+                    bestpl = pl;
+                }
+            }
+            surface[x][y] = AllocRock(bestpl);
+        }
+    }
+    return 0;
+}
+
+/*
+ * Place some hot spots randomly.
+ */
+int SeedHotSpots()
+{
+    register int	i;	/* hot spot counter */
+    register Hotspot_t	*hsp;	/* hot spot pointer */
+
+    for (hsp = &hotspots[0], i = 0; i < tectonic.nhotspots; ++i, ++hsp) {
+        hsp->x = Rnd(tectonic.xsize);
+        hsp->y = Rnd(tectonic.ysize);
+        hsp->howhot = (Rnd(tectonic.hotspotheight) + Rnd(tectonic.hotspotheight) +
+          Rnd(tectonic.hotspotheight) + Rnd(tectonic.hotspotheight) + 2) >> 1;
+    }
+    return 0;
+}
+
+/*
+ * All initialization.
+ */
+int Tec_Init()
+{
+    nplates = tectonic.nplates;
+    SeedPlates();
+    InitSurface();
+    SeedHotSpots();
+    return 0;
+}
+
+/*
+ * ---------- Land movement --------------------------------------------------
+ */
+/*
+ * Assign each of the plates a direction and distance (velocity) to move.
+ * Velocity is added to previous value, but isn't allowed to exceed
+ * tectonic.epochmove in any direction.
+ */
+int AssignDirections()
+{
+    register int	i;	/* plate counter */
+    register Plate_t	*pl;	/* ptr to plates */
+    float		dist;	/* magnitude of vector */
+    register int	dir;	/* direction of vector */
+#define DEGtoRAD	(3.1415926 / 180.0)
+
+    for (pl = &plates[0], i = 0; i < nplates; ++i, ++pl) {
+        dir = Rnd(360);
+        dist = (float)(Rnd(101) + Rnd(101) + Rnd(101) - 150) *
+          (float)tectonic.epochmove/150.0;
+        pl->xvec += dist * cos(dir * DEGtoRAD);
+        pl->yvec += dist * sin(dir * DEGtoRAD);
+        if ((dist = hypot(pl->xvec, pl->yvec)) > (float)tectonic.epochmove) {
+            dist = (float)tectonic.epochmove / dist;
+            pl->xvec *= dist;
+            pl->yvec *= dist;
+        }
+    }
+    return 0;
+}
+
+/*
+ * Set plates' dx,dy to the vector given by (i+1)*v/tectonic.epochmove - i*v/tectonic.epochmove,
+ * where v = plate's (xvec,yvec) vector.  This arrangement makes the sum of
+ * (dx,dy) over an epoch equal to (xvec,yvec).
+ */
+int SetMovementVectors(iter)
+  int iter;	/* which iteration */
+{
+    register int	i;	/* plate counter */
+    register Plate_t	*pl;	/* ptr to plates */
+
+    for (pl = &plates[0], i = 0; i < nplates; ++i, ++pl) {
+        pl->dx = (short)((iter + 1) * pl->xvec / tectonic.epochmove) -
+          (short)(iter * pl->xvec / tectonic.epochmove);
+        pl->dy = (short)((iter + 1) * pl->yvec / tectonic.epochmove) -
+          (short)(iter * pl->yvec / tectonic.epochmove);
+    }
+    return 0;
+}
+
+/*
+ * Move each rock according to its plate's movement vector.
+ * Use the rock's 'next' field to keep track of rocks that pile up.
+ */
+int MovePlates()
+{
+    register int	x, y;		/* where in the world */
+    register int	newx, newy;	/* rock's new home */
+    register Rock_t	*rp;		/* rock pointer */
+    register Rock_t	*prevrp;	/* rock before rp */
+    register Rock_t	*nextrp;	/* rock after rp */
+
+    for (x = 0; x < tectonic.xsize; ++x) {
+        for (y = 0; y < tectonic.ysize; ++y) {
+            prevrp = NULL;
+            for (rp = surface[x][y]; rp; prevrp = rp, rp = nextrp) {
+                nextrp = rp->next;
+                if (rp->moved)
+                    continue;
+                /*
+                 * Figure out where to move rock.
+                 */
+                rp->moved = TRUE;
+                if ((rp->plate->dx == 0) && (rp->plate->dy == 0))
+                    continue;
+                newx = Wrap_x(x + rp->plate->dx);
+                newy = Wrap_y(y + rp->plate->dy);
+                /*
+                 * Move rock.
+                 */
+                if (prevrp == NULL)
+                    surface[x][y] = rp->next;
+                else
+                    prevrp->next = rp->next;
+                rp->next = surface[newx][newy];
+                surface[newx][newy] = rp;
+            }
+        }
+    }
+    return 0;
+}
+
+/*
+ * ---------- Mountain building and the like ---------------------------------
+ */
+
+/*
+ * Create land where plates have spread apart.
+ */
+int Rift(x, y)
+  int x, y;
+{
+    register Plate_t	*pl;	/* which plate new land goes with */
+    register int	px, py;	/* offset at which to look for plate */
+    register long	tries;	/* # of failures to find plate */
+    register Rock_t	*rp;	/* new rock */
+
+    pl = NULL;
+
+    /*
+     * Find a plate to go with.
+     */
+    for (tries = 0; tries < 30; ++tries) {
+        px = Wrap_x(x + Rnd(3) - 1);
+        py = Wrap_y(y + Rnd(3) - 1);
+        if ((rp = surface[px][py]) != NULL) {
+            pl = rp->plate;
+            break;
+        }
+    }
+    /*
+     * Can't find a plate; start a new one.
+     */
+    if (pl == NULL) {
+        if (nplates < tectonic.maxplates)
+            ++nplates;
+        pl = &plates[nplates - 1];
+        pl->xvec = pl->yvec = 0.0;
+        pl->dx = pl->dy = 0;
+        pl->continent = FALSE;
+    }
+    /*
+     * Make rock.
+     */
+    rp = AllocRock(pl);
+    rp->elev = tectonic.riftelev;
+    rp->minerals = tectonic.riftminerals;	/* should distinguish between ocean and continental rifts!!! */
+    surface[x][y] = rp;
+    return 0;
+}
+
+/*
+ * Raise (or lower) land near x,y by height.
+ * Determine direction from x,y by rp's plate's vector.
+ */
+int ElevNearby(x, y, rp, height)
+  int x, y;
+  Rock_t *rp;
+  short height;
+{
+    float	h;	/* hypotenuse of rp->plate->vec */
+    int		x2, y2;	/* rock to raise */
+
+    h = hypot(rp->plate->xvec, rp->plate->yvec);
+    if (h < 0.01) {
+        x2 = x;
+        y2 = y;
+    } else {
+        x2 = Wrap_x(x + (int)(rp->plate->xvec / h + 1.5) - 1);
+        y2 = Wrap_y(y + (int)(rp->plate->yvec / h + 1.5) - 1);
+    }
+    if (surface[x2][y2])
+        surface[x2][y2]->elev += height;
+    return 0;
+}
+
+/*
+ * Continent hits continent; mountains are pushed up.
+ * New height = taller + 0.5 * shorter.  The leftover height is
+ * distributed to other rocks in the area.
+ */
+int Fold(x, y)
+  int x, y;
+{
+    register Rock_t	*rp0;		/* first rock on list */
+    register Rock_t	*rp1;		/* short rock */
+    register Rock_t	*rp2;		/* tall rock */
+    register short	halfshort;	/* half the short elevation */
+
+    rp0 = surface[x][y];
+    if (rp0->next->elev > rp0->elev) {
+        rp1 = rp0;
+        rp2 = rp1->next;
+    } else {
+        rp2 = rp0;
+        rp1 = rp2->next;
+    }
+    halfshort = (rp1->elev >> 1) * tectonic.foldfactor;
+    ElevNearby(x, y, rp1, (halfshort + 1) >> 1);
+    ElevNearby(x, y, rp2, halfshort >> 1);
+    /*
+     * Combine rocks and free a rock structure.
+     */
+    rp0->elev = rp2->elev + halfshort;
+    rp0->minerals = (rp1->minerals + rp2->minerals + 1) >> 1;
+    rp0->plate = Rnd(2) ? rp1->plate : rp2->plate;
+
+    rp1 = rp0->next;
+    rp0->next = rp1->next;
+    rp1->next = freeRockList;
+    freeRockList = rp1;
+    return 0;
+}
+
+/*
+ * Create a costal range where oceanic plate is subsumed by continental
+ * plate.  Simply add a constant height and minerals to the continent.
+ */
+int Subsume(x, y)
+  int x, y;
+{
+    register Rock_t	*rp0;		/* first rock on list */
+    register Rock_t	*rp1;		/* underwater rock */
+    register Rock_t	*rp2;		/* higher rock */
+
+    rp0 = surface[x][y];
+    if (rp0->next->elev > rp0->elev) {
+        rp1 = rp0;
+        rp2 = rp1->next;
+    } else {
+        rp2 = rp0;
+        rp1 = rp2->next;
+    }
+    ElevNearby(x, y, rp2, tectonic.trenchheight);
+    if (rp2->elev < 0)
+        rp0->minerals = rp2->minerals + tectonic.archipminerals;
+    else
+        rp0->minerals = rp2->minerals + tectonic.subsumeminerals;
+    rp0->elev = rp2->elev + tectonic.subsumeheight;
+    rp0->plate = rp2->plate;
+
+    rp1 = rp0->next;
+    rp0->next = rp1->next;
+    rp1->next = freeRockList;
+    freeRockList = rp1;
+    return 0;
+}
+
+/*
+ * For each spot on the surface, combine rocks which were piled on top of
+ * each other in MovePlates() into mountains,
+ * and create rifts where plates have spread apart leaving nothing.
+ */
+int MountainBuild()
+{
+    register int	x, y;		/* where in the world */
+    register Rock_t	*rp;		/* rock pointer */
+    register Rock_t	*rp2;		/* another rock pointer */
+
+    for (x = 0; x < tectonic.xsize; ++x) {
+        for (y = 0; y < tectonic.ysize; ++y) {
+            rp = surface[x][y];
+            if (rp == NULL)
+                Rift(x, y);
+            else while ((rp2 = rp->next) != NULL) {
+                if ((rp->elev > 0) && (rp2->elev > 0))
+                    Fold(x, y);		/* land + land */
+                else
+                    Subsume(x, y);	/* land or ocean + ocean */
+            }
+        }
+    }
+    return 0;
+}
+
+/*
+ * Increase elevation of hot spots.  If underwater, increase minerals.
+ */
+int EruptHotSpots()
+{
+    register int	i;	/* hot spot counter */
+    register Hotspot_t	*hsp;	/* hot spot pointer */
+    register Rock_t	*rp;	/* rock pointer */
+
+    for (hsp = &hotspots[0], i = 0; i < tectonic.nhotspots; ++i, ++hsp) {
+        rp = surface[hsp->x][hsp->y];
+        if (rp->elev <= 0)
+            rp->minerals += tectonic.hotspotminerals;
+        rp->elev += hsp->howhot;
+    }
+    return 0;
+}
+
+/*
+ * Wear down mountains.  Amount of wear is proportional to the average
+ * difference in height between a rock and the surrounding rocks,
+ * except very tall mountains don't erode much (no weather),
+ * there's more erosion around seashores (waves), and underwater
+ * mountains don't erode much.
+ * Also lower sea floors.
+ * Mark everything unmoved.
+ */
+int Erode()
+{
+    register int	x, y;		/* where in the world */
+    register Rock_t	*rp0;		/* rock pointer */
+    register Rock_t	*rp1;		/* pointer to nearby rock */
+    register short	elev0, elev1;	/* elevation of rp0, rp1 */
+    register int	i, j;		/* indices to nearby rocks */
+    register short	diff;		/* difference in height, roughly */
+    register long	depth;		/* average height of area */
+    int			diffplates;	/* nearby rocks belonging to other plates */
+    int			deepplates;	/* # of nearby rocks under water */
+
+    rp1 = NULL;
+
+    for (x = 0; x < tectonic.xsize; ++x) {
+        for (y = 0; y < tectonic.ysize; ++y) {
+            rp0 = surface[x][y];
+            elev0 = rp0->elev;
+            depth = elev0;
+            if (elev0 < 0)
+                elev0 = elev0 * tectonic.submergefactor;
+            else if (elev0 > tectonic.skyelev)
+                elev0 = tectonic.skyelev;
+            diffplates = 0;
+            deepplates = 0;
+            for (i = -1; i <= 1; ++i) {
+                for (j = -1; j <= 1; ++j) {	/* for each surrounding rock */
+                    if ((i == 0) && (j == 0))
+                        continue;
+                    rp1 = surface[Wrap_x(x + i)][Wrap_y(y + j)];
+                    elev1 = rp1->elev;
+                    if (rp1->plate != rp0->plate)
+                        ++diffplates;
+                    depth += elev1;
+                    if (elev1 < 0) {
+                        ++deepplates;
+                        elev1 = elev1 * tectonic.submergefactor;
+                    } else if (elev1 > tectonic.skyelev)
+                        elev1 = tectonic.skyelev;
+                    /*
+                     * Erode according to diff between elev0, elev1.
+                     */
+                    diff = elev0 - elev1;
+                    if ((elev0 <= 0 && elev1 >= 0) ||
+                      (elev0 >= 0 && elev1 <= 0)) {
+                        diff += tectonic.waveaction;
+                    }
+                    diff = tectonic.erodefactor * diff + 0.5;
+                    rp0->elevchange -= diff;
+                    rp1->elevchange += diff;
+                }
+            }
+            /*
+             * If 7/8 of the rocks surrounding us belong to different
+             * plates, join the crowd.
+             */
+            if (diffplates >= 7) {
+                do {
+                    i = Rnd(3) - 2;
+                    j = Rnd(3) - 2;
+                    if (i == 0 && j == 0)
+                        continue;
+                    rp1 = surface[Wrap_x(x + i)][Wrap_y(y + j)];
+                } while (rp1->plate == rp0->plate);
+                rp0->plate = rp1->plate;
+            }
+            /*
+             * Compute subsidence of ocean basins.
+             */
+            if (deepplates >= (rp0->elev >= 0 ? 8 : 6)) {
+                rp0->elevchange -= tectonic.subsidefactor * (depth/9 - tectonic.basinelev);
+            }
+        }
+    }
+    /*
+     * Incorporate elevchange; mark things moved.
+     */
+    for (x = 0; x < tectonic.xsize; ++x) {
+        for (y = 0; y < tectonic.ysize; ++y) {
+            rp0 = surface[x][y];
+            rp0->moved = FALSE;
+            rp0->elev += rp0->elevchange;
+            rp0->elevchange = 0;
+        }
+    }
+    return 0;
+}
+
+/*
+ * Give all the plates directions, move them, and see what happens.
+ * They may move a maximum of tectonic.epochmove cells per epoch.
+ */
+int Epoch()
+{
+    register int i;
+
+    AssignDirections();
+    for (i = 0; i < tectonic.epochmove; ++i) {
+        SetMovementVectors(i);
+        MovePlates();
+        MountainBuild();
+        EruptHotSpots();
+        Erode();
+    }
+    return 0;
+}
+
+/*************************************************************************
+ This ends Mark's plate tectonic code 
+ *************************************************************************/
+
+/**************************************************************************
+  mapgenerator_tectonic: Use Mark Isaak's plate tectonic simulation code 
+                         to generate a realistic world.
+**************************************************************************/
+static void mapgenerator_tectonic(void)
+{
+  int i;
+  int minval=5000000;
+  height_map=fc_malloc (sizeof(int)*map.xsize*map.ysize);
+
+  adjust_terrain_param();
+ 
+  /* Now, call Mark's teconic simulator to create a height map */
+
+  init_tec_data();
+  Tec_Init();
+  for(i=0;i<45;i++)
+      Epoch();
+
+  /* Convert the tectonically-synthsized map in to a freeciv height field */
+
+  whole_map_iterate(x, y) {
+    hmap(x, y) = surface[x][y]->elev;
+  } whole_map_iterate_end;
+
+  /* Mark's code generates negative values; adjust
+     To do: Add code which uses Mark's idea of what should be land
+            and water to determine where the oceans go; modify Mark's
+	    code to use map.landpercent */
+  minval = 600000;
+
+  whole_map_iterate(x, y) {
+
+    /* Hackish code to discard some more extreme values */
+    if (hmap(x, y) < -400) {
+      hmap(x, y) = -400;
+      }
+    if (hmap(x, y) > 10000) {
+      hmap(x, y) = 10000;
+      }
+
+    if (hmap(x, y) < minval) {
+      minval = hmap(x, y);
+      }
+  } whole_map_iterate_end;
+
+  if(minval < 0) {
+      whole_map_iterate(x, y) {
+          hmap(x, y) -= minval;
+	  } whole_map_iterate_end;
+      }
+
+  /* If we are using map generator eight, make the pure plate tectonic
+     land more lumpy.  We do this because the plate tectonic code simulates
+     the Earth's processes too well to make a traditional FreeCiv game: It
+     makes large stretches of land and water instead of the FreeCiv 
+     model of having many islands.  
+
+     The alleviates this by adding some lumpyness to the generated land;
+     this makes the continents smaller and the islands more numerous */
+
+  if(map.generator == 8) {
+      for (i=0;i<(map.xsize*map.ysize)/2;i++) {
+          int x,y;
+	  rand_map_pos(&x, &y);
+	  hmap(x, y) += myrand(4000);
+	  }
+
+      /* Smooth things out just a tad */
+      smooth_map();
+      smooth_map();
+      }
+
+  /* Find the minval and maxval now that we have made a plate tectonic
+     simulated map */
+
+  whole_map_iterate(x, y) {
+    if (hmap(x, y) > maxval)
+      maxval = hmap(x, y);
+    if (hmap(x, y) < minval)
+      minval = hmap(x, y);
+  } whole_map_iterate_end;
+
+  maxval-=minval;
+  adjust_map(minval);
+
+  /* based on map.landpercent, separate land from water */
+  make_land(maxval / 5);
+
   free(height_map);
   height_map = NULL;
 }
@@ -1394,7 +2352,7 @@
     rand_map_pos(&x, &y);
     l=myrand(6);
     if (map_get_terrain(x, y)!=T_OCEAN && 
-	( map_get_terrain(x, y)!=T_ARCTIC || l<3 )
+	( map_get_terrain(x, y)!=T_ARCTIC )
 	) {
       if (!is_hut_close(x,y)) {
 	number--;
@@ -2208,7 +3166,271 @@
   maxval -= minval;
   adjust_map(minval);
   
-  make_land();
+  make_land(maxval);
   free(height_map);
   height_map = NULL;
 }
+
+static int is_ocean(int l)
+{
+  return (l == T_OCEAN);
+}
+
+static int land_terrain(int x, int y)
+{
+  return !is_ocean(map_get_terrain(x,y));
+}
+
+static int border_score(int x, int y)
+{
+  int adj_score = 2;
+  int diag_score = 1;
+  int score = 0;
+  score += land_terrain(x-1,y) ? adj_score : 0;
+  score += land_terrain(x+1,y) ? adj_score : 0;
+  score += land_terrain(x,y-1) ? adj_score : 0;
+  score += land_terrain(x,y+1) ? adj_score : 0;
+  score += land_terrain(x-1,y-1) ? diag_score : 0;
+  score += land_terrain(x+1,y-1) ? diag_score : 0;
+  score += land_terrain(x-1,y+1) ? diag_score : 0;
+  score += land_terrain(x+1,y+1) ? diag_score : 0;
+  return score;
+}
+
+static int random_new_land(int x, int y)
+{
+  int score;
+  int random;
+  if(land_terrain(x, y)) {
+    return T_PLAINS;
+  }
+  score = border_score(x, y);
+  random = myrand(4*2+4*1);
+  return random + 1 > score  ? T_OCEAN : T_GRASSLAND;
+  
+}
+
+/* Creates a peninsula and put the player's starting position
+   on it; this is used by map generator 6 */
+static void create_peninsula(int x, int y,int player_number,
+			     int width, int height,int direction,
+			     int remaining_count)
+{
+  int cx, cy;
+
+  cx = x+width/2;
+  for(cy = y; cy != y + (height + 1)*direction; 
+      cy += direction) {
+    map_set_terrain(cx, cy, T_GRASSLAND);
+    map_set_continent(cx, cy, 1);
+  }
+  remaining_count -= height; /* account for the already added strip */
+  cy = y;
+  for(cx = x + width/2 - 2; cx <= x + width/2 + 2; cx ++) {
+    map_set_terrain(cx, cy, T_GRASSLAND);
+    map_set_continent(cx, cy, 1);    
+  }
+  remaining_count -= 4; /* account for extra 4 (not 5 because that would 
+			double count the one already done by height strip.*/
+  
+  while (remaining_count > 0) {
+    for(cx = x; cx < x+width; cx++) {
+      for(cy = y; cy != y + (height + 1)*direction; cy += direction) {
+	int new_terrain = random_new_land(cx, cy);
+	if(new_terrain == T_GRASSLAND){
+	  remaining_count--;
+	  map_set_terrain(cx, cy, T_GRASSLAND);
+	  map_set_continent(cx, cy, 1);
+	  hmap(cx, cy) = 20 * (cx - x) * (width - (cx - x)) + myrand(80) - 40;
+	}
+	if(remaining_count <= 0) {
+	  goto done_with_peninsula;
+	}
+      }
+    }
+  }
+
+ done_with_peninsula:
+
+  map.start_positions[player_number].x = x+width/2;
+  map.start_positions[player_number].y = y;
+}
+
+/* This generator creates a map with one penisula for each 
+   player and an isthmus between.  It creates a central
+   ocean and puts the peninsulas around the edges.  It is 
+   intented for quicker games. Should look something like this:
+   *****************************
+   **  *****  *****  *****    ** 
+   *    ***    ***    ***      *
+   *                           *
+   *                           *
+   *    ***    ***             *
+   **  *****  *****           **
+   *****************************
+ */
+static void mapgenerator6(void)
+{
+  int peninsulas = game.nplayers;
+  int peninsulas_on_one_side = (peninsulas + 1)/2;
+  int isthmus_width = 10;
+  int peninsula_separation = 5;
+  int peninsula_width = 
+    (map.xsize - isthmus_width - peninsula_separation)
+      /(peninsulas_on_one_side) - peninsula_separation;
+  /* if landpercent <= 50, then make shorter peninsulas */
+  int peninsula_height = (map.landpercent <= 50) 
+    ? map.ysize/3
+      : (map.ysize*2)/5;
+  int continent_percent = (map.landpercent <= 50) 
+    ? (map.landpercent * 3)/2 
+      : (map.landpercent * 5)/4;
+  int i, x, y;
+  int polar_height = 3;
+  int remaining_count = 
+    MIN(isthmus_width * (map.ysize - polar_height*2),
+	((isthmus_width+peninsula_separation) * (map.ysize - polar_height*2) 
+	 * continent_percent)/100);
+ 
+  height_map = fc_malloc(sizeof(int) * map.xsize * map.ysize);
+
+  /* initialize everything to ocean */
+  for (y = 0 ; y < map.ysize ; y++) 
+    for (x = 0 ; x < map.xsize ; x++) {
+      map_set_terrain(x, y, T_OCEAN);
+      map_set_continent(x, y, 0);
+      hmap(x, y) = 0;
+    }
+
+  /* create polar regions */
+  for (x = 0 ; x < map.xsize; x++) {
+    for (y = 0; y < polar_height; y++) {
+      int rand_num = myrand(9);
+      map_set_terrain(x, y, rand_num > 7 ? T_ARCTIC :
+		      (rand_num < 2 ? T_MOUNTAINS : T_TUNDRA));
+      map_set_continent(x, y, 1);
+      rand_num = myrand(9);
+      map_set_terrain(x, map.ysize-1-y, rand_num > 7 ? T_ARCTIC :
+		      (rand_num < 2 ? T_MOUNTAINS : T_TUNDRA));
+      map_set_continent(x, map.ysize-1-y, 1);
+    }
+  }
+  
+  /* build polar regions road */
+  for (x = 0 ; x < map.xsize; x++) {
+	  y = polar_height-1;
+	  if(map_build_road_time(x,y-1) < map_build_road_time(x,y))
+	  {
+		  map_set_special(x,y-1,S_ROAD);
+	  } else {
+		  map_set_special(x,y,S_ROAD);
+	  }
+	  y = map.ysize-polar_height;
+	  if(map_build_road_time(x,y+1) < map_build_road_time(x,y))
+	  {
+		  map_set_special(x,y+1,S_ROAD);
+	  } else {
+		  map_set_special(x,y,S_ROAD);
+	  }
+  }
+
+  map.num_continents = 1;
+
+  /* create isthmus centeral strip */
+  x = isthmus_width/2;
+  for (y = polar_height; y < map.ysize - polar_height; y++) {
+    map_set_terrain(x, y, T_GRASSLAND);
+    map_set_continent(x, y, 1);
+    hmap(x, y) = 100 * x * (isthmus_width - x) + (myrand(400) - 200);
+  }
+
+  remaining_count -= (map.ysize - 2*polar_height) * 1;
+
+  /* add additional isthmus area randomly */
+  while(remaining_count > 0) {
+    for (y = polar_height; y < map.ysize/2; y++) {
+      for (x = 0; x < isthmus_width; x++) {
+	int new_terrain = random_new_land(x, y);
+	if(new_terrain == T_GRASSLAND){
+	  remaining_count--;
+	  map_set_terrain(x, y, T_GRASSLAND);
+	  map_set_continent(x, y, 1);
+	  hmap(x, y) = 100 * x * (isthmus_width - x) + (myrand(400) - 200);
+	}
+	if(remaining_count <= 0) {
+	  goto done_with_isthmus;
+	}
+      }
+    }
+    
+    for (y = map.ysize - polar_height - 1; y >= map.ysize/2; y--) {
+      for (x = 0; x < isthmus_width; x++) {
+	int new_terrain = random_new_land(x, y);
+	if(new_terrain == T_GRASSLAND){
+	  remaining_count--;
+	  map_set_terrain(x, y, T_GRASSLAND);
+	  map_set_continent(x, y, 1);
+	  hmap(x, y) = 100 * x * (isthmus_width - x) + (myrand(400) - 200);
+	}
+	if(remaining_count <= 0) {
+	  goto done_with_isthmus;
+	}
+      }
+    }
+  }
+ done_with_isthmus:
+  
+  /* setup peninsulas */
+  for(i = 0; i < game.nplayers; i++) {
+    /* direction is the direction to increment from the x and y location */
+    int direction = (i < peninsulas_on_one_side) ? -1 : 1;
+    int index = (direction == -1) ? i : i - peninsulas_on_one_side;
+    int width = (continent_percent < 95) 
+      ? peninsula_width
+	: peninsula_width + peninsula_separation - 1;/*Give more room */
+    int height = peninsula_height-polar_height;
+    int peninsula_remaining_count = 
+      MIN(width*height,
+	  ((width+peninsula_separation)*height*continent_percent)/100);
+    x = index * (peninsula_width + peninsula_separation) 
+      + isthmus_width + peninsula_separation;
+    y = (direction == -1) 
+      ? peninsula_height 
+	: map.ysize - 1 - peninsula_height;
+    create_peninsula(x,y,i,width,height,direction,peninsula_remaining_count);
+  }
+
+  map.num_start_positions = game.nplayers;
+  
+  /* setup terrain */
+  make_mountains(1600);
+  make_swamps();
+  make_forests();
+  make_deserts();
+  make_plains();
+  make_fair();
+  make_rivers();
+
+  /* create isthmus road */
+  {
+    int last_x, middle_x = isthmus_width/2;
+    last_x = middle_x;
+    for (y = polar_height - 1; y < map.ysize - polar_height + 1; y++) {
+      int best_x = middle_x;
+      int min_build = 100;
+      for(x = MAX(last_x - 1, middle_x - 1);
+	  x != MIN(last_x + 1,middle_x + 1) + 1; x++) {
+	if(land_terrain(x,y) && map_build_road_time(x,y) < min_build) {
+	  best_x = x;
+	  min_build = map_build_road_time(x,y);
+	}
+      }
+      map_set_special(best_x,y,S_ROAD);
+      last_x = best_x;
+    }
+  }
+  
+
+  free(height_map);
+	
+}
diff -ur freeciv-1.14.0/server/savegame.c freeciv-1.14.0-shr2/server/savegame.c
--- freeciv-1.14.0/server/savegame.c	Sun Dec  8 15:32:30 2002
+++ freeciv-1.14.0-shr2/server/savegame.c	Tue Apr  8 05:41:43 2003
@@ -1862,6 +1862,12 @@
 	secfile_lookup_int_default(file, game.allowed_city_names,
 				   "game.allowed_city_names"); 
 
+    game.slowai_tech = secfile_lookup_int_default(file,
+        GAME_DEFAULT_SLOWAI_TECH, "game.slowai_tech");
+
+    game.slowai_grow = secfile_lookup_int_default(file,
+        GAME_DEFAULT_SLOWAI_GROW, "game.slowai_grow");
+
     if(game.civstyle == 1) {
       string = "civ1";
     } else {
@@ -2184,6 +2190,8 @@
   secfile_insert_int(file, game.watchtower_vision, "game.watchtower_vision");
   secfile_insert_int(file, game.watchtower_extra_vision, "game.watchtower_extra_vision");
   secfile_insert_int(file, game.allowed_city_names, "game.allowed_city_names");
+  secfile_insert_int(file, game.slowai_tech, "game.slowai_tech");
+  secfile_insert_int(file, game.slowai_grow, "game.slowai_grow");
 
   if (TRUE) {
     /* Now always save these, so the server options reflect the
diff -ur freeciv-1.14.0/server/settlers.c freeciv-1.14.0-shr2/server/settlers.c
--- freeciv-1.14.0/server/settlers.c	Fri Nov 15 19:14:22 2002
+++ freeciv-1.14.0-shr2/server/settlers.c	Tue Apr  8 05:41:43 2003
@@ -34,6 +34,7 @@
 #include "aiunit.h"
 #include "aidata.h"
 
+#include "rand.h"
 #include "settlers.h"
 
 /* negative: in_city_radius, 0: unassigned, positive: city_des */
@@ -1262,8 +1263,8 @@
   /* Decide whether to build a new city:
    * if so, modify: gx, gy, best_newv, best_act
    */
-  if (unit_flag(punit, F_CITIES) &&
-      pplayer->ai.control) {
+  if (unit_flag(punit, F_CITIES) && pplayer->ai.control &&
+      (myrand(101) <= game.slowai_grow)) {
     int nx, ny;
     int want = evaluate_city_building(punit, &nx, &ny, &ferryboat);
 
diff -ur freeciv-1.14.0/server/stdinhand.c freeciv-1.14.0-shr2/server/stdinhand.c
--- freeciv-1.14.0/server/stdinhand.c	Fri Nov 15 19:14:22 2002
+++ freeciv-1.14.0-shr2/server/stdinhand.c	Tue Apr  8 05:41:43 2003
@@ -239,7 +239,9 @@
        "additional\n"
        "    smaller islands.\n"
        "5 = one or more large earthlike continents with some scatter.\n"
-       "Note: values 2,3 and 4 generate \"fairer\" (but more boring) "
+       "6 = equally sized peninsulas with one player each surrounding  \n"
+       "    an inland sea.\n"
+       "Note: values 2,3,4 and 6 generate \"fairer\" (but more boring) "
        "maps.\n"
        "(Zero indicates a scenario map.)"), NULL,
 	  MAP_MIN_GENERATOR, MAP_MAX_GENERATOR, MAP_DEFAULT_GENERATOR)
@@ -820,6 +822,24 @@
 	     "40=debuging logging."), NULL, 
 	  0, 40, 20)
 
+  GEN_INT("slowaitech", game.slowai_tech, SSET_RULES_FLEXIBLE, 
+          SSET_SERVER_ONLY, N_("Adjust AI tech advancement by %"),
+	  N_("This value determines how much slower the AI will learn "
+	     "new technologies compared to human players. Setting this to "
+	     "a value greater than 100 will slow their advancement.  For "
+	     "example, giving this a value of 200 slows down the AI's tech "
+	     "advancment by half."), NULL, GAME_MIN_SLOWAI_TECH, 
+	  GAME_MAX_SLOWAI_TECH, GAME_DEFAULT_SLOWAI_TECH)
+
+  GEN_INT("slowaigrowth", game.slowai_grow, SSET_RULES_FLEXIBLE, 
+          SSET_SERVER_ONLY, N_("Adjust city growth by %"),
+	  N_("This value determines how much slower the AI will expand in "
+	  "relation to human players. Setting a percent value under "
+	  "100 will cause them to expand at about the specified rate "
+	  "as compared to normal AI expansion."), NULL, 
+	  GAME_MIN_SLOWAI_GROW, GAME_MAX_SLOWAI_GROW, GAME_DEFAULT_SLOWAI_GROW)
+
+
   GEN_END
 };
 
Only in freeciv-1.14.0-shr2/server: stdinhand.c.orig
