Complete.Org: Mailing Lists: Archives: freeciv-dev: October 2004:
[Freeciv-Dev] (PR#10308) PATCH: New startpos option and rational control
Home

[Freeciv-Dev] (PR#10308) PATCH: New startpos option and rational control

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
To: undisclosed-recipients: ;
Subject: [Freeciv-Dev] (PR#10308) PATCH: New startpos option and rational control of generators
From: "Marcelo Burda" <mburda@xxxxxxxxx>
Date: Mon, 11 Oct 2004 01:17:49 -0700
Reply-to: rt@xxxxxxxxxxx

<URL: http://rt.freeciv.org/Ticket/Display.html?id=10308 >

LASTCHANGE: UPDATE 
 
Hi, 
i undesrtand that maintainers are very busy with the own patches, but 
maybe maintainers can spend some time to see patches of others 
developer! 
 
;-) 
 
Marcelo  
Index: common/map.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/map.c,v
retrieving revision 1.200
diff -u -r1.200 map.c
--- common/map.c        7 Oct 2004 19:15:19 -0000       1.200
+++ common/map.c        11 Oct 2004 08:11:14 -0000
@@ -196,6 +196,7 @@
   map.wetness               = MAP_DEFAULT_WETNESS;
   map.steepness             = MAP_DEFAULT_STEEPNESS;
   map.generator             = MAP_DEFAULT_GENERATOR;
+  map.startpos              = MAP_DEFAULT_STARTPOS;
   map.tinyisles             = MAP_DEFAULT_TINYISLES;
   map.separatepoles         = MAP_DEFAULT_SEPARATE_POLES;
   map.alltemperate          = MAP_DEFAULT_ALLTEMPERATE;
Index: common/map.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/map.h,v
retrieving revision 1.220
diff -u -r1.220 map.h
--- common/map.h        7 Oct 2004 19:15:19 -0000       1.220
+++ common/map.h        11 Oct 2004 08:11:14 -0000
@@ -172,6 +172,7 @@
   int huts;
   int landpercent;
   int generator;
+  int startpos;
   bool tinyisles;
   bool separatepoles;
   bool alltemperate;
@@ -649,7 +650,11 @@
 
 #define MAP_DEFAULT_GENERATOR    1
 #define MAP_MIN_GENERATOR        1
-#define MAP_MAX_GENERATOR        5
+#define MAP_MAX_GENERATOR        3
+
+#define MAP_DEFAULT_STARTPOS     0
+#define MAP_MIN_STARTPOS         0
+#define MAP_MAX_STARTPOS         4
 
 #define MAP_DEFAULT_TINYISLES    FALSE
 #define MAP_MIN_TINYISLES        FALSE
Index: server/maphand.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/maphand.c,v
retrieving revision 1.149
diff -u -r1.149 maphand.c
--- server/maphand.c    29 Sep 2004 02:24:24 -0000      1.149
+++ server/maphand.c    11 Oct 2004 08:11:15 -0000
@@ -57,11 +57,16 @@
   is_land tells us whether we are assigning continent numbers or ocean 
   numbers.
 **************************************************************************/
-static void assign_continent_flood(struct tile *ptile, bool is_land, int nr)
+static void assign_continent_flood(struct tile *ptile, const bool is_land,
+                                  const int nr,const bool skip_unsafe)
 {
   if (map_get_continent(ptile) != 0) {
     return;
   }
+  
+  if (skip_unsafe && terrain_has_flag(map_get_terrain(ptile), TER_UNSAFE)) {
+    return;
+  }
 
   if (!XOR(is_land, is_ocean(map_get_terrain(ptile)))) {
     return;
@@ -77,7 +82,7 @@
   }
 
   adjc_iterate(ptile, tile1) {
-    assign_continent_flood(tile1, is_land, nr);
+    assign_continent_flood(tile1, is_land, nr, skip_unsafe);
   } adjc_iterate_end;
 }
 
@@ -117,10 +122,10 @@
 
   Continents have numbers 1 to map.num_continents _inclusive_.
   Oceans have (negative) numbers -1 to -map.num_oceans _inclusive_.
-  
+
   Also recalculate lake_surrounders[] arrays
 **************************************************************************/
-void assign_continent_numbers(void)
+void assign_continent_numbers(const bool skip_unsafe)
 {
   int i;
   
@@ -140,18 +145,23 @@
 
   /* Assign new numbers */
   whole_map_iterate(ptile) {
+    const Terrain_type_id ter = map_get_terrain(ptile);
+
     if (map_get_continent(ptile) != 0) {
       /* Already assigned. */
       continue;
     }
-    if (!is_ocean(map_get_terrain(ptile))) {
-      map.num_continents++;
-      assert(map.num_continents < MAP_NCONT);
-      assign_continent_flood(ptile, TRUE, map.num_continents);
-    } else {
-      map.num_oceans++;
-      assert(map.num_oceans < MAP_NCONT);
-      assign_continent_flood(ptile, FALSE, -map.num_oceans);
+
+    if (!skip_unsafe || !terrain_has_flag(ter, TER_UNSAFE)) {
+      if (!is_ocean(ter)) {
+       map.num_continents++;
+       assert(map.num_continents < MAP_NCONT);
+       assign_continent_flood(ptile, TRUE, map.num_continents, skip_unsafe);
+      } else {
+       map.num_oceans++;
+       assert(map.num_oceans < MAP_NCONT);
+       assign_continent_flood(ptile, FALSE, -map.num_oceans, skip_unsafe);
+      }
     }
   } whole_map_iterate_end;
 
@@ -1463,7 +1473,7 @@
   }
 
   if (change_type != OLC_NONE) {
-    assign_continent_numbers();
+    assign_continent_numbers(FALSE);
     allot_island_improvs();
 
     /* New continent numbers for all tiles to all players */
Index: server/maphand.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/maphand.h,v
retrieving revision 1.48
diff -u -r1.48 maphand.h
--- server/maphand.h    29 Sep 2004 02:24:24 -0000      1.48
+++ server/maphand.h    11 Oct 2004 08:11:16 -0000
@@ -52,7 +52,7 @@
 /* The maximum number of continents and oceans. */
 #define MAP_NCONT 300
 
-void assign_continent_numbers(void);
+void assign_continent_numbers(const bool skip_unsafe);
 
 void global_warming(int effect);
 void nuclear_winter(int effect);
@@ -106,4 +106,5 @@
                                               Terrain_type_id oldter);
 int get_continent_size(Continent_id id);
 int get_ocean_size(Continent_id id);
+
 #endif  /* FC__MAPHAND_H */
Index: server/savegame.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/savegame.c,v
retrieving revision 1.197
diff -u -r1.197 savegame.c
--- server/savegame.c   4 Oct 2004 04:37:33 -0000       1.197
+++ server/savegame.c   11 Oct 2004 08:11:17 -0000
@@ -384,8 +384,6 @@
                secfile_lookup_str(file, "map.t%03d", line),
                ptile->terrain = char2terrain(ch));
 
-  assign_continent_numbers();
-
   whole_map_iterate(ptile) {
     ptile->spec_sprite = secfile_lookup_str_default(file, NULL,
                                "map.spec_sprite_%d_%d",
Index: server/settings.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/settings.c,v
retrieving revision 1.8
diff -u -r1.8 settings.c
--- server/settings.c   10 Oct 2004 20:02:02 -0000      1.8
+++ server/settings.c   11 Oct 2004 08:11:18 -0000
@@ -277,21 +277,27 @@
   GEN_INT("generator", map.generator,
          SSET_MAP_GEN, SSET_GEOLOGY, SSET_VITAL,  SSET_TO_CLIENT,
          N_("Method used to generate map"),
-         N_("1 = standard, with random continents;\n\n"
-            "2 = equally sized large islands with one player each, and "
-            "twice that many smaller islands;\n\n"
-            "3 = equally sized large islands with one player each, and "
-            "a number of other islands of similar size;\n\n"
-            "4 = equally sized large islands with two players on every "
-            "island (or one with three players for an odd number of "
-            "players), and additional smaller islands;\n\n"
-            "5 = one or more large earthlike continents with some "
-            "scatter.\n\n"
-            "Note: values 2, 3 and 4 generate fairer but more boring "
-            "maps.\n"
-            "(Zero indicates a scenario map.)"), NULL,
+         N_("1 = Full random height map based generator    [4]\n"
+            "2 = Pseudo-fractal height map based generator [3]\n"
+            "3 = Islands based generator                   [1]\n"
+            "(Zero indicates a scenario map.)\n"
+            "Notes: generator 3 generate fairer (but more boring) maps.\n"
+            "numbers in [] are default value for startpos"
+            ), NULL,
          MAP_MIN_GENERATOR, MAP_MAX_GENERATOR, MAP_DEFAULT_GENERATOR)
 
+  GEN_INT("startpos", map.startpos,
+         SSET_MAP_GEN, SSET_GEOLOGY, SSET_VITAL,  SSET_TO_CLIENT,
+         N_("Method used to choice starts pos"),
+         N_("0 = Developer choice, default of the generator\n"
+            "1 = try to place one player per continent\n"
+            "2 = try to place 2 players per continent\n"
+            "3 = try to place all players on some continent\n"
+            "4 = place number of players depending on size of continent\n"
+            "Note: generators try to do the right number of continents "
+            "for the choice of start pos and to the number of players"),
+         NULL, MAP_MIN_STARTPOS, MAP_MAX_STARTPOS, MAP_DEFAULT_STARTPOS)
+
   GEN_BOOL("tinyisles", map.tinyisles,
           SSET_MAP_GEN, SSET_GEOLOGY, SSET_RARE, SSET_TO_CLIENT,
           N_("Presence of 1x1 islands"),
Index: server/srv_main.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/srv_main.c,v
retrieving revision 1.202
diff -u -r1.202 srv_main.c
--- server/srv_main.c   8 Oct 2004 05:01:52 -0000       1.202
+++ server/srv_main.c   11 Oct 2004 08:11:19 -0000
@@ -103,7 +103,6 @@
 #include "citymap.h"
 
 #include "mapgen.h"
-#include "startpos.h"
 
 #include "srv_main.h"
 
@@ -1771,17 +1770,11 @@
   }
    
   /* if we have a tile map, and map.generator==0, call map_fractal_generate
-     anyway, to make the specials and huts */
+     anyway, to make the specials, huts and assign_continent_numbers */
   if (map_is_empty() || (map.generator == 0 && game.is_new_game)) {
     map_fractal_generate(TRUE);
   }
 
-  /*
-   * Don't assign continent numbers here. We have to do it later,
-   * because generators 2-4 use their own continent numbering
-   * in create_start_positions(). For other generators continent numbers
-   * are already assigned.
-   */
   gamelog_map();
   /* start the game */
 
@@ -1806,18 +1799,8 @@
        * we don't want to change it. */
       game.max_players = game.nplayers;
     }
-
-    /* we don't want random start positions in a scenario which already
-       provides them. -- Gudy */
-    if(map.num_start_positions == 0) {
-      create_start_positions();
-    }
-
   }
 
-  /* start positions are created, now we can do this safely */
-  assign_continent_numbers();
-
   /* Set up alliances based on team selections */
   if (game.is_new_game) {
    players_iterate(pplayer) {
Index: server/generator/height_map.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/generator/height_map.c,v
retrieving revision 1.5
diff -u -r1.5 height_map.c
--- server/generator/height_map.c       29 Sep 2004 02:24:24 -0000      1.5
+++ server/generator/height_map.c       11 Oct 2004 08:11:19 -0000
@@ -157,7 +157,7 @@
 
   All X and Y values used in this function are in native coordinates.
 **************************************************************************/
-void make_pseudofractal1_hmap(void)
+void make_pseudofractal1_hmap(int extra_div)
 {
   const bool xnowrap = !topo_has_flag(TF_WRAPX);
   const bool ynowrap = !topo_has_flag(TF_WRAPY);
@@ -166,8 +166,8 @@
    * How many blocks should the x and y directions be divided into
    * initially. 
    */
-  const int xdiv = 6;          
-  const int ydiv = 5;
+  const int xdiv = 5 + extra_div;              
+  const int ydiv = 5 + extra_div;
 
   int xdiv2 = xdiv + (xnowrap ? 1 : 0);
   int ydiv2 = ydiv + (ynowrap ? 1 : 0);
Index: server/generator/height_map.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/generator/height_map.h,v
retrieving revision 1.3
diff -u -r1.3 height_map.h
--- server/generator/height_map.h       29 Sep 2004 02:24:24 -0000      1.3
+++ server/generator/height_map.h       11 Oct 2004 08:11:19 -0000
@@ -37,6 +37,6 @@
 void normalize_hmap_poles(void);
 void renormalize_hmap_poles(void);
 void make_random_hmap(int smooth);
-void make_pseudofractal1_hmap(void);
+void make_pseudofractal1_hmap(int div);
 
 #endif  /* FC__HEIGHT__MAP_H */
Index: server/generator/mapgen.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/generator/mapgen.c,v
retrieving revision 1.13
diff -u -r1.13 mapgen.c
--- server/generator/mapgen.c   8 Oct 2004 05:01:53 -0000       1.13
+++ server/generator/mapgen.c   11 Oct 2004 08:11:20 -0000
@@ -31,9 +31,9 @@
 #include "shared.h"
 #include "srv_main.h"
 
-#include "mapgen.h"
 
 #include "height_map.h"
+#include "mapgen.h"
 #include "mapgen_topology.h"
 #include "startpos.h"
 #include "temperature_map.h"
@@ -300,7 +300,7 @@
 ****************************************************************************/
 static void make_polar_land(void)
 {
-  assign_continent_numbers();
+  assign_continent_numbers(FALSE);
   whole_map_iterate(ptile) {
     if ((tmap_is(ptile, TT_FROZEN ) &&
        ok_for_separate_poles(ptile))
@@ -945,8 +945,6 @@
   destroy_placed_map();
 
   make_rivers(); /* use a new placed_map. destroy older before call */
-
-  assign_continent_numbers();
 }
 
 /**************************************************************************
@@ -1020,24 +1018,31 @@
     adjust_terrain_param();
     /* if one mapgenerator fails, it will choose another mapgenerator */
     /* with a lower number to try again */
-    if (map.generator == 5 ) {
-      make_pseudofractal1_hmap();
-    }
-    if (map.generator == 4) {
-      mapgenerator4();
-    }
-    if (map.generator == 3) {
-      mapgenerator3();
+    
+    if (map.generator == 3) {/* 2 or 3 player per isle*/
+      if (map.startpos == 2 || (map.startpos == 3)  ) { 
+       mapgenerator4();
+      }
+      if (map.startpos <= 1 ) { /* single player per isle */
+       mapgenerator3();
+      }
+      if (map.startpos == 4 ) { /* "variable" single player*/
+       mapgenerator2();
+      }
     }
-    if (map.generator == 2) {
-      mapgenerator2();
+
+    if (map.generator == 2 ) {
+      make_pseudofractal1_hmap(1 +
+       ((map.startpos == 0 || map.startpos == 3)  ? 0 : game.nplayers));
     }
+
     if (map.generator == 1 ) {
-      make_random_hmap(1 + SQSIZE);
+      make_random_hmap(MAX(1, 1 + SQSIZE 
+                          - (map.startpos ? game.nplayers / 4: 0)));
     }
 
     /* if hmap only generator make anything else */
-    if (map.generator == 1 || map.generator == 5) {
+    if (map.generator == 1 || map.generator == 2) {
       make_land();
       free(height_map);
       height_map = NULL;
@@ -1045,8 +1050,6 @@
     if (!map.tinyisles) {
       remove_tiny_islands();
     }
-  } else {
-    assign_continent_numbers();
   }
 
   if (!temperature_is_initialized()) {
@@ -1065,6 +1068,33 @@
   /* restore previous random state: */
   set_myrand_state(rstate);
   destroy_tmap();
+
+  /* we don't want random start positions in a scenario which already
+       provides them. */
+  if (map.num_start_positions == 0) {
+    switch (map.generator) {
+    case 0:
+    case 1:
+      create_start_positions(map.startpos);
+      break;
+    case 2:
+      if (map.startpos == 0) {
+       create_start_positions(MT_ALL);
+      } else {
+       create_start_positions(map.startpos);
+      };
+      break;
+    case 3:
+      if (map.startpos <= 1 || (map.startpos == 4)  ) {
+       create_start_positions(MT_SINGLE);
+      } else {
+       create_start_positions(MT_2or3);
+      };
+      break;
+    }
+  }
+
+  assign_continent_numbers(FALSE);
 }
 
 /**************************************************************************
@@ -1130,9 +1160,6 @@
        number--;
        map_set_special(ptile, S_HUT);
        set_placed_near_pos(ptile, 3);
-           /* Don't add to islands[].goodies because islands[] not
-              setup at this point, except for generator>1, but they
-              have pre-set starters anyway. */
       }
     }
   }
@@ -1559,7 +1586,6 @@
     if (i <= 0) {
       return FALSE;
     }
-    islands[pstate->isleindex].starters = starters;
     assert(starters >= 0);
     freelog(LOG_VERBOSE, "island %i", pstate->isleindex);
 
@@ -1620,9 +1646,7 @@
 **************************************************************************/
 static void initworld(struct gen234_state *pstate)
 {
-  int i;
   height_map = fc_malloc(sizeof(int) * map.ysize * map.xsize);
-  islands = fc_malloc((MAP_NCONT+1)*sizeof(struct isledata));
   create_placed_map(); /* land tiles which aren't placed yet */
   create_tmap(FALSE);
   
@@ -1640,12 +1664,7 @@
   
   /* Set poles numbers.  After the map is generated continents will 
    * be renumbered. */
-  assign_continent_numbers(); 
-
   make_island(0, 0, pstate, 0);
-  for(i = 0; i <= map.num_continents; i++ ) {
-      islands[i].starters = 0;
-  }
 }  
 
 /* This variable is the Default Minimum Specific Island Size, 
Index: server/generator/startpos.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/generator/startpos.c,v
retrieving revision 1.3
diff -u -r1.3 startpos.c
--- server/generator/startpos.c 29 Sep 2004 02:24:24 -0000      1.3
+++ server/generator/startpos.c 11 Oct 2004 08:11:20 -0000
@@ -1,5 +1,5 @@
 /********************************************************************** 
- Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
+ Freeciv - Copyright (C) 1996 - 2004 The Freeciv Project Team 
    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)
@@ -23,8 +23,17 @@
 
 #include "mapgen_topology.h"
 #include "startpos.h"
+#include "utilities.h"
 
-struct isledata *islands;
+typedef struct {
+  Continent_id id;
+  int size;
+  int goodies;
+  int starters;
+  int total;
+} islands_data_type;
+islands_data_type *islands;
+int *islands_index;
 
 /****************************************************************************
   Return an approximation of the goodness of a tile to a civilization.
@@ -54,8 +63,8 @@
   map_set_special(ptile, S_ROAD);
   map_mine_tile(ptile);
   mine_bonus = (get_food_tile(ptile)
-                + get_shields_tile(ptile)
-                + get_trade_tile(ptile)) - value;
+               + get_shields_tile(ptile)
+               + get_trade_tile(ptile)) - value;
 
   ptile->terrain = old_terrain;
   ptile->special = old_special;
@@ -65,116 +74,10 @@
   return value;
 }
 
-/**************************************************************************
- Allocate islands array and fill in values.
- Note this is only used for map.generator <= 1 or >= 5, since others
- setups islands and starters explicitly.
-**************************************************************************/
-static void setup_isledata(void)
-{
-  int starters = 0;
-  int min,  i;
-  
-  assert(map.num_continents > 0);
-  
-  /* allocate + 1 so can use continent number as index */
-  islands = fc_calloc((map.num_continents + 1), sizeof(struct isledata));
-
-  /* add up all the resources of the map */
-  whole_map_iterate(ptile) {
-    /* number of different continents seen from (x,y) */
-    int seen_conts = 0;
-    /* list of seen continents */
-    Continent_id conts[CITY_TILES]; 
-    int j;
-    
-    /* add tile's value to each continent that is within city 
-     * radius distance */
-    map_city_radius_iterate(ptile, tile1) {
-      /* (x1,y1) is possible location of a future city which will
-       * be able to get benefit of the tile (x,y) */
-      if (is_ocean(map_get_terrain(tile1)) 
-         || map_colatitude(tile1) <= 2 * ICE_BASE_LEVEL) { 
-       /* Not land, or too cold. */
-        continue;
-      }
-      for (j = 0; j < seen_conts; j++) {
-       if (map_get_continent(tile1) == conts[j]) {
-          /* Continent of (x1,y1) is already in the list */
-         break;
-        }
-      }
-      if (j >= seen_conts) { 
-       /* we have not seen this continent yet */
-       assert(seen_conts < CITY_TILES);
-       conts[seen_conts] = map_get_continent(tile1);
-       seen_conts++;
-      }
-    } map_city_radius_iterate_end;
-    
-    /* Now actually add the tile's value to all these continents */
-    for (j = 0; j < seen_conts; j++) {
-      islands[conts[j]].goodies += get_tile_value(ptile);
-    }
-  } whole_map_iterate_end;
-  
-  /* now divide the number of desired starting positions among
-   * the continents so that the minimum number of resources per starting 
-   * position is as large as possible */
-  
-  /* set minimum number of resources per starting position to be value of
-   * the best continent */
-  min = 0;
-  for (i = 1; i <= map.num_continents; i++) {
-    if (min < islands[i].goodies) {
-      min = islands[i].goodies;
-    }
-  }
-  
-  /* place as many starting positions as possible with the current minumum
-   * number of resources, if not enough are placed, decrease the minimum */
-  while ((starters < game.nplayers) && (min > 0)) {
-    int nextmin = 0;
-    
-    starters = 0;
-    for (i = 1; i <= map.num_continents; i++) {
-      int value = islands[i].goodies;
-      
-      starters += value / min;
-      if (nextmin < (value / (value / min + 1))) {
-        nextmin = value / (value / min + 1);
-      }
-    }
-    
-    freelog(LOG_VERBOSE,
-           "%d starting positions allocated with\n"
-            "at least %d resouces per starting position; \n",
-            starters, min);
-
-    assert(nextmin < min);
-    /* This choice of next min guarantees that there will be at least 
-     * one more starter on one of the continents */
-    min = nextmin;
-  }
-  
-  if (min == 0) {
-    freelog(LOG_VERBOSE,
-            "If we continue some starting positions will have to have "
-            "access to zero resources (as defined in get_tile_value). \n");
-    freelog(LOG_FATAL,
-            "Cannot create enough starting position and will abort.\n"
-            "Please report this bug at " WEBSITE_URL);
-    abort();
-  } else {
-    for (i = 1; i <= map.num_continents; i++) {
-      islands[i].starters = islands[i].goodies / min;
-    }
-  }
-}
-
 struct start_filter_data {
-  int count; /* Number of existing start positions. */
-  int dist; /* Minimum distance between starting positions. */
+  int count;                   /* Number of existing start positions. */
+  int min_value;
+  int *value;
 };
 
 /**************************************************************************
@@ -189,88 +92,258 @@
     'nr' is the number of other start positions in
     map.start_positions to check for too closeness.
 **************************************************************************/
-static bool is_valid_start_pos(const struct tile *ptile, const void *dataptr)
+static bool is_valid_start_pos(const struct tile *ptile,
+                              const void *dataptr)
 {
-  const struct start_filter_data *data = dataptr;
+  const struct start_filter_data *pdata = dataptr;
   int i;
-  Terrain_type_id t = map_get_terrain(ptile);
 
-  if (is_ocean(map_get_terrain(ptile))) {
-    return FALSE;
-  }
+  /* Only start on certain terrain types. */  
+  if (pdata->value[ptile->index] < pdata->min_value) {
+      return FALSE;
+  } 
 
-  if (islands[(int)map_get_continent(ptile)].starters == 0) {
+  if (islands[islands_index[(int) map_get_continent(ptile)]].starters == 0) {
     return FALSE;
   }
 
-  /* Only start on certain terrain types. */
-  if (!terrain_has_flag(t, TER_STARTER)) {
-    return FALSE;
-  }
-  
   /* Don't start on a hut. */
   if (map_has_special(ptile, S_HUT)) {
     return FALSE;
   }
-  
-  /* Nobody will start on the poles since they aren't valid terrain. */
 
   /* Don't start too close to someone else. */
-  for (i = 0; i < data->count; i++) {
+  for (i = 0; i < pdata->count; i++) {
     struct tile *tile1 = map.start_positions[i].tile;
 
-    if (map_get_continent(ptile) == map_get_continent(tile1)
-       && real_map_distance(ptile, tile1) < data->dist) {
+    if ((map_get_continent(ptile) == map_get_continent(tile1)
+        && (real_map_distance(ptile, tile1) * 1000 / pdata->min_value
+            <= (sqrt(get_continent_size(map_get_continent(ptile))
+                     /
+                     islands[islands_index
+                             [(int) map_get_continent(ptile)]].total)
+            )))
+       || (real_map_distance(ptile, tile1) * 1000 / pdata->min_value < 5)) {
       return FALSE;
     }
   }
-
   return TRUE;
 }
 
+/*************************************************************************
+ help function for qsort
+ *************************************************************************/
+static int compare_islands(const void *A, const void *B)
+{
+  return -(((islands_data_type *) A)->goodies
+          - ((islands_data_type *) B)->goodies);
+}
+
+/*********************************************
+ initialize with some 0 values, call malloc, etc
+********************************************/
+static void initialize_isle_data(void)
+{
+  int nr;
+  islands =
+      fc_malloc((map.num_continents + 1) * sizeof(islands_data_type));
+  islands_index = fc_malloc((map.num_continents + 1) * sizeof(int));
+
+  for (nr = 1; nr <= map.num_continents; nr++) {
+    islands[nr].id = nr;
+    islands[nr].size = get_continent_size(nr);
+    islands[nr].goodies = 0;
+    islands[nr].starters = 0;
+    islands[nr].total = 0;
+  }
+}
+
+/**************************************************************
+ help function for select good start tiles
+ **************************************************************/
+static bool filter(const struct tile *ptile, const void *data)
+{
+  return terrain_has_flag(map_get_terrain(ptile), TER_STARTER);
+}
+
 /**************************************************************************
-  where do the different races start on the map? well this function tries
+  where do the different nations start on the map? well this function tries
   to spread them out on the different islands.
+  MT_SIGLE one player per isle
+  MT_2or3 2 player per isle if number of players is even
+  MT_ALL  all player in single isle
+  MT_VARIABLE at last 2 player per isle
 **************************************************************************/
-void create_start_positions(void)
+void create_start_positions(mode_type mode)
 {
   struct tile *ptile;
   int k, sum;
   struct start_filter_data data;
-  
-  if (!islands) {
-    /* Isle data is already setup for generators 2, 3, and 4. */
-    setup_isledata();
+  int tile_value_aux[MAX_MAP_INDEX], tile_value[MAX_MAP_INDEX];
+  int min_goodies_per_player = 2000;
+  int total_goodies = 0;
+
+  /* unsafe terrains separate continents for this task
+     else littles green isles near poles can be populated by
+     a civilization if pole touch a big one! */
+  assign_continent_numbers(TRUE);
+
+  /* create_start_positions not known what is the right default,
+     then use MT_VARIABLE */
+  if(mode == MT_DEFAULT) {
+      mode = MT_VARIABLE;
   }
 
-  data.dist = MIN(40, MIN(map.xsize / 2, map.ysize / 2));
+  /* get the tile value */
+  whole_map_iterate(ptile) {
+    tile_value_aux[ptile->index] = get_tile_value(ptile);
+  } whole_map_iterate_end;
+
+  /* select the best tiles */
+  whole_map_iterate(ptile) {
+    int this_tile_value = tile_value_aux[ptile->index];
+    int lcount = 0, bcount = 0;
+    map_city_radius_iterate(ptile, ptile1) {
+      if (this_tile_value > tile_value_aux[ptile1->index]) {
+       lcount++;
+      } else if (this_tile_value < tile_value_aux[ptile1->index]) {
+       bcount++;
+      }
+    }
+    map_city_radius_iterate_end;
+    if (lcount <= bcount) {
+      this_tile_value = 0;
+    }
+    tile_value[ptile->index] = 100 * this_tile_value;
+  } whole_map_iterate_end;
+  /* get a average value */
+  smooth_int_map(tile_value, TRUE);
+
+  initialize_isle_data();
+
+  /* oceans are not good for start, discard its */
+  whole_map_iterate(ptile) {
+    if (!filter(ptile, NULL)) {
+      tile_value[ptile->index] = 0;
+    } else {
+      islands[map_get_continent(ptile)].goodies
+         += tile_value[ptile->index];
+      total_goodies += tile_value[ptile->index];
+    }
+  } whole_map_iterate_end;
+
+  /* evaluate the best places on the map */
+  adjust_int_map_filtered(tile_value, 1000, NULL, filter);
+ 
+  /* best islands first */
+  qsort(islands + 1, (map.num_continents),
+       sizeof(islands_data_type), compare_islands);
+
+  /* some times we can't place start pos as wanted then choixe the best way */
+  if (mode == MT_SINGLE && map.num_continents < game.nplayers + 3) {
+    mode = MT_2or3;
+  }
+
+  if (mode == MT_2or3 && map.num_continents < game.nplayers / 2 + 4) {
+    mode = MT_VARIABLE;
+  }
+
+  if (mode == MT_ALL 
+       && (islands[1].goodies < game.nplayers * min_goodies_per_player
+          || (islands[1].goodies < total_goodies * 0.5))) {
+    mode = MT_VARIABLE;
+  }
+
+  /* the variable way is the last posibility */
+  if (mode == MT_VARIABLE) {
+    min_goodies_per_player = total_goodies * 0.65 / game.nplayers;
+  }
+
+  { 
+    int nr, to_place = game.nplayers, first = 1;
+    /* inizialize islands_index */
+    for (nr = 1; nr <= map.num_continents; nr++) {
+      islands_index[islands[nr].id] = nr;
+    }
+
+    /* sear for best first island for fairness */    
+    if ((mode == MT_SINGLE) || (mode == MT_2or3)) {
+      float var_goodies, best = (float) HUGE_VAL;
+      int num_islands =
+         (mode == MT_SINGLE) ? game.nplayers : (game.nplayers / 2);
+
+      for (nr = 1; nr <= 1 + map.num_continents - num_islands; nr++) {
+       if (islands[nr + num_islands - 1].goodies
+           < min_goodies_per_player) {
+         break;
+       }
+       var_goodies
+           = (islands[nr].goodies - islands[nr + num_islands - 1].goodies)
+           / (islands[nr + num_islands - 1].goodies);
+
+       if (var_goodies < best * 0.9) {
+         best = var_goodies;
+         first = nr;
+       }
+      }
+    }
+
+    /* set starters per isle */
+    if (mode == MT_ALL) {
+       islands[1].starters = to_place;
+       islands[1].total = to_place;
+       to_place = 0;
+    }
+    for (nr = 1; nr <= map.num_continents; nr++) {
+      
+      if (mode == MT_SINGLE && (to_place > 0) && (nr >= first)) {
+       islands[nr].starters = 1;
+       islands[nr].total = 1;
+       to_place--;
+      }
+      if (mode == MT_2or3 && (to_place > 0) && (nr >= first)) {
+       islands[nr].starters = 2 + (nr == 1 ? (game.nplayers % 2) : 0);
+       to_place -= islands[nr].total = islands[nr].starters;
+      }
+
+      if (mode == MT_VARIABLE && (to_place > 0)) {
+       islands[nr].starters = MAX(1, islands[nr].goodies 
+                                  / min_goodies_per_player);
+       to_place -= islands[nr].total = islands[nr].starters;
+      }
+    }
+  }
 
   data.count = 0;
+  data.value = tile_value;
+  data.min_value = 900;
   sum = 0;
   for (k = 1; k <= map.num_continents; k++) {
-    sum += islands[k].starters;
-    if (islands[k].starters != 0) {
+    sum += islands[islands_index[k]].starters;
+    if (islands[islands_index[k]].starters != 0) {
       freelog(LOG_VERBOSE, "starters on isle %i", k);
     }
   }
   assert(game.nplayers <= data.count + sum);
 
+  /* now search the best place and set start_positions */
   map.start_positions = fc_realloc(map.start_positions,
                                   game.nplayers
                                   * sizeof(*map.start_positions));
   while (data.count < game.nplayers) {
     if ((ptile = rand_map_pos_filtered(&data, is_valid_start_pos))) {
-      islands[(int)map_get_continent(ptile)].starters--;
+      islands[islands_index[(int) map_get_continent(ptile)]].starters--;
       map.start_positions[data.count].tile = ptile;
       map.start_positions[data.count].nation = NO_NATION_SELECTED;
-      freelog(LOG_DEBUG, "Adding %d,%d as starting position %d.",
-             ptile->x, ptile->y, data.count);
+      freelog(LOG_DEBUG,
+             "Adding %d,%d as starting position %d, %d goodies on islands.",
+             ptile->x, ptile->y, data.count,
+             islands[islands_index[(int) map_get_continent(ptile)]].goodies);
       data.count++;
 
     } else {
-      
-      data.dist--;
-      if (data.dist == 0) {
+      data.min_value *= 0.9;
+      if (data.min_value <= 10) {
        die(_("The server appears to have gotten into an infinite loop "
              "in the allocation of starting positions, and will abort.\n"
              "Maybe the numbers of players/ia is too much for this map.\n"
@@ -281,5 +354,7 @@
   map.num_start_positions = game.nplayers;
 
   free(islands);
+  free(islands_index);
   islands = NULL;
+  islands_index = NULL;
 }
Index: server/generator/startpos.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/generator/startpos.h,v
retrieving revision 1.1
diff -u -r1.1 startpos.h
--- server/generator/startpos.h 16 Sep 2004 09:53:11 -0000      1.1
+++ server/generator/startpos.h 11 Oct 2004 08:11:20 -0000
@@ -13,12 +13,9 @@
 #ifndef FC__STARTING_POSITIONS
 #define FC__STARTING_POSITIONS
 
-void create_start_positions(void);
-/* This is manipulated directly by gen234 */
-struct isledata {
-  int goodies;
-  int starters;
-};
-extern struct isledata *islands;
+typedef  enum {MT_DEFAULT = 0, MT_SINGLE, MT_2or3, MT_ALL, MT_VARIABLE}
+         mode_type;
+
+void create_start_positions(mode_type mode);
 
 #endif
Index: server/generator/utilities.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/generator/utilities.c,v
retrieving revision 1.9
diff -u -r1.9 utilities.c
--- server/generator/utilities.c        7 Oct 2004 19:15:19 -0000       1.9
+++ server/generator/utilities.c        11 Oct 2004 08:11:20 -0000
@@ -101,15 +101,18 @@
   The lowest 20% of tiles will have values lower than 0.2 * int_map_max.
 
 **************************************************************************/
-void adjust_int_map(int *int_map, int int_map_max)
+void adjust_int_map_filtered(int *int_map, int int_map_max, void *data,
+                                  bool (*filter)(const struct tile *ptile,
+                                                 const void *data))
 {
-  int minval = *int_map, maxval = minval;
+  int minval = +(int)HUGE_VAL, maxval = -(int)HUGE_VAL, total = 0;
 
   /* Determine minimum and maximum value. */
-  whole_map_iterate(ptile) {
+  whole_map_iterate_filtered(ptile, data, filter) {
     maxval = MAX(maxval, int_map[ptile->index]);
     minval = MIN(minval, int_map[ptile->index]);
-  } whole_map_iterate_end;
+    total++;
+  } whole_map_iterate_filtered_end;
 
   {
     int const size = 1 + maxval - minval;
@@ -120,21 +123,21 @@
     /* Translate value so the minimum value is 0
        and count the number of occurencies of all values to initialize the 
        frequencies[] */
-    whole_map_iterate(ptile) {
+    whole_map_iterate_filtered(ptile, data, filter) {
       int_map[ptile->index] = (int_map[ptile->index] - minval);
       frequencies[int_map[ptile->index]]++;
-    } whole_map_iterate_end;
+    } whole_map_iterate_filtered_end;
 
     /* create the linearize function as "incremental" frequencies */
     for(i =  0; i < size; i++) {
       count += frequencies[i]; 
-      frequencies[i] = (count * int_map_max) / MAX_MAP_INDEX;
+      frequencies[i] = (count * int_map_max) / total;
     }
 
     /* apply the linearize function */
-    whole_map_iterate(ptile) {
+    whole_map_iterate_filtered(ptile, data, filter) {
       int_map[ptile->index] = frequencies[int_map[ptile->index]];
-    } whole_map_iterate_end;
+    } whole_map_iterate_filtered_end;
   }
 }
 
Index: server/generator/utilities.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/generator/utilities.h,v
retrieving revision 1.4
diff -u -r1.4 utilities.h
--- server/generator/utilities.h        1 Oct 2004 17:53:02 -0000       1.4
+++ server/generator/utilities.h        11 Oct 2004 08:11:20 -0000
@@ -56,11 +56,23 @@
 #define iterate_axe_end \
     } \
 } 
+#define whole_map_iterate_filtered(ptile, pdata, pfilter)               \
+  whole_map_iterate(ptile) {                                            \
+    if((pfilter) != NULL && !(pfilter)(ptile, (pdata))) {               \
+      continue;                                                         \
+    }
+
+#define whole_map_iterate_filtered_end } whole_map_iterate_end
 
 bool is_normal_nat_pos(int x, int y);
 
 /* int maps tools */
-void adjust_int_map(int *int_map, int int_map_max);
+void adjust_int_map_filtered(int *int_map, int int_map_max, void *data,
+                                  bool (*filter)(const struct tile *ptile,
+                                                 const void *data));
+#define adjust_int_map(int_map, int_map_max) \
+  adjust_int_map_filtered(int_map, int_map_max, (void *)NULL, \
+            (bool (*)(const struct tile *ptile, const void *data) )NULL)
 void smooth_int_map(int *int_map, bool zeroes_at_edges);
 
 /* placed_map tool*/

[Prev in Thread] Current Thread [Next in Thread]