Complete.Org: Mailing Lists: Archives: freeciv-ai: November 2004:
[freeciv-ai] Re: (PR#8992) Patch: Building ferries
Home

[freeciv-ai] Re: (PR#8992) Patch: Building ferries

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
To: Gregory.Berkolaiko@xxxxxxxxxxxxx
Subject: [freeciv-ai] Re: (PR#8992) Patch: Building ferries
From: "Benedict Adamson" <badamson@xxxxxxxxxxx>
Date: Fri, 5 Nov 2004 18:27:15 -0800
Reply-to: rt@xxxxxxxxxxx

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

Gregory Berkolaiko wrote:
...
> My experience in AI tells me that simple WAGs usually work at least as
> good as long and complicated models.
...
> Now what should be done.  The code in the present state is not commitable.
> It's way to complicated and the gain is not obvious.  I suggest we
> backtrack a lot
...
> We can leave some stub functions for future more complicated logic
> (like new_boats_to_build) and functions like select_ferry_builders should
> definitely stay.
...
> P.S. And we need to remove the word "desiradata" from the comment.
...

Attached is a new, simplified, version of the patch. The 
new_boats_to_build function now implements a feedback control system. It 
tries to control the ferry utilisation and queue length. The patch 
therefore does not have the code for counting warships or simulating 
shipping.

As you said, this simple code seems to perform well. It has several 
#define-d constants, which are not-so WA-ed WAGS: the degree of ferry 
utilisation we want, the queue length we want and some relaxation times.

BTW, you might be interested in the book 'Simple Heuristics that Make Us 
Smart', G Gigerenzer, P M Todd et al, Oxford University Press 1999.

diff -ru -Xfreeciv.PR8992/diff_ignore vendor.freeciv.current/ai/advdomestic.c 
freeciv.PR8992/ai/advdomestic.c
--- vendor.freeciv.current/ai/advdomestic.c     2004-10-22 23:16:36.000000000 
+0100
+++ freeciv.PR8992/ai/advdomestic.c     2004-11-06 02:10:36.000000000 +0000
@@ -204,7 +204,6 @@
       choice->type = CT_NONMIL;
       choice->choice = unit_type; /* default */
       choice->need_boat = TRUE;
-      ai_choose_role_unit(pplayer, pcity, choice, L_FERRYBOAT, -want);
     }
   }
 
diff -ru -Xfreeciv.PR8992/diff_ignore vendor.freeciv.current/ai/advmilitary.c 
freeciv.PR8992/ai/advmilitary.c
--- vendor.freeciv.current/ai/advmilitary.c     2004-11-03 19:46:36.000000000 
+0000
+++ freeciv.PR8992/ai/advmilitary.c     2004-11-06 02:10:36.000000000 +0000
@@ -962,8 +962,8 @@
 3. calculates the relevant stats of the victim.
 4. finds the best attacker for this type of victim (in process_attacker_want)
 5. if we still want to attack, records the best attacker in choice.
-If the target is overseas, the function might suggest building a ferry
-to carry a land attack unit, instead of the land attack unit itself.
+If the target is overseas, the function might suggest using a ferry
+to carry a land attack unit.
 **************************************************************************/
 static void kill_something_with(struct player *pplayer, struct city *pcity, 
                                struct unit *myunit, struct ai_choice *choice)
@@ -1126,23 +1126,9 @@
   if (best_choice.want > choice->want) {
     /* We want attacker more than what we have selected before */
     copy_if_better_choice(&best_choice, choice);
-    CITY_LOG(LOG_DEBUG, pcity, "ksw: %s has chosen attacker, %s, want=%d",
-            pcity->name, unit_types[choice->choice].name, choice->want);
-
-    if (go_by_boat && !ferryboat) { /* need a new ferry */
-      /* We might need a new boat even if there are boats free,
-       * if they are blockaded or in inland seas*/
-      assert(is_ground_unit(myunit));
-      ai_choose_role_unit(pplayer, pcity, choice, L_FERRYBOAT, choice->want);
-      if (SEA_MOVING == unit_types[choice->choice].move_type) {
-        struct ai_data *ai = ai_data_get(pplayer);
-
-        freelog(LOG_DEBUG,
-                "%s has chosen attacker ferry, %s, want=%d, %d of %d free",
-                pcity->name, unit_types[choice->choice].name, choice->want,
-                ai->stats.available_boats, ai->stats.boats);
-      } /* else can not build ferries yet */
-    }
+    CITY_LOG(LOG_DEBUG, pcity, "has chosen attacker, %s, want=%d%s",
+            unit_types[choice->choice].name, choice->want, 
+            (best_choice.need_boat ? "(need boat)" : ""));
   } 
 }
 
@@ -1390,9 +1376,7 @@
     destroy_unit_virtual(virtualunit);
   }
 
-  /* Consider a land attacker or a ferried land attacker
-   * (in which case, we might want a ferry before an attacker)
-   */
+  /* Consider a land attacker or a ferried land attacker */
   unit_type = ai_choose_attacker(pcity, LAND_MOVING);
   if (unit_type >= 0) {
     virtualunit = create_unit_virtual(pplayer, pcity, unit_type, 1);
diff -ru -Xfreeciv.PR8992/diff_ignore vendor.freeciv.current/ai/aicity.c 
freeciv.PR8992/ai/aicity.c
--- vendor.freeciv.current/ai/aicity.c  2004-11-03 19:46:36.000000000 +0000
+++ freeciv.PR8992/ai/aicity.c  2004-11-06 02:10:36.000000000 +0000
@@ -46,6 +46,7 @@
 #include "advdomestic.h"
 #include "advmilitary.h"
 #include "aidata.h"
+#include "aiferry.h"
 #include "aihand.h"
 #include "ailog.h"
 #include "aitools.h"
@@ -981,6 +982,8 @@
     ai_city_choose_build(pplayer, pcity);
   } city_list_iterate_end;
 
+  aiferry_choose_build_global(pplayer);
+
   ai_spend_gold(pplayer);
 }
 
diff -ru -Xfreeciv.PR8992/diff_ignore vendor.freeciv.current/ai/aiferry.c 
freeciv.PR8992/ai/aiferry.c
--- vendor.freeciv.current/ai/aiferry.c 2004-11-03 19:46:36.000000000 +0000
+++ freeciv.PR8992/ai/aiferry.c 2004-11-06 02:10:36.000000000 +0000
@@ -14,24 +14,30 @@
 #ifdef HAVE_CONFIG_H
 #include <config.h>
 #endif
+#include <math.h>
 
 #include "log.h"
+#include "rand.h"
 #include "unit.h"
 
 #include "path_finding.h"
 #include "pf_tools.h"
 
 #include "hand_gen.h"
+#include "maphand.h"
 #include "unithand.h"
 #include "unittools.h"
 
 #include "aidata.h"
 #include "aiexplorer.h"
+#include "aihand.h"
+#include "aihunt.h"
 #include "ailog.h"
 #include "aitools.h"
 #include "aiunit.h"
 
 #include "aiferry.h"
+#include "settlers.h" /*for amortize*/
 
  
 /* =================== constants with special meaning =================== */
@@ -51,12 +57,41 @@
 
 /* Logging in ferry management functions */
 #define LOGLEVEL_FERRY LOG_DEBUG
+#define LOGLEVEL_BUILDFERRY LOG_NORMAL
 /* Logging in go_by_boat functions */
 #define LOGLEVEL_GOBYBOAT LOG_DEBUG
 /* Logging in find_ferry functions */
 #define LOGLEVEL_FINDFERRY LOG_DEBUG
 /* Extra consistency checks */
 #define DEBUG_FERRY_STATS 0
+/*
+ * The maximum number of turns to look ahead.
+ * For each city, the code looks ahead no further than the completion of the
+ * item currently being built. This should therefore not be much longer
+ * than a typical build time.
+ */
+#define MAX_TURNS_HORIZON 10
+
+
+
+/*
+ * Data about one sea area for one player.
+ */
+struct sea_stats {
+  unsigned int future_psngrs;
+
+  unsigned int psngrs;
+  unsigned int boats;
+  unsigned int avail_boats;
+
+  unsigned int sea_area;
+  unsigned int n_cities; /*our cities*/
+
+  int ferry_want;
+
+  /*extra_passengers[t] = increment in passengers in turn t from now*/
+  unsigned int extra_passengers[MAX_TURNS_HORIZON];
+};
 
 
 /* ========= managing statistics and boat/passanger assignments ======== */
@@ -73,7 +108,7 @@
   ai->stats.available_boats = 0;
  
   unit_list_iterate(pplayer->units, punit) {
-    if (is_sailing_unit(punit) && is_ground_units_transport(punit)) {
+    if (unit_has_role(punit->type, L_FERRYBOAT)) {
       ai->stats.boats++;
       if (punit->ai.passenger == FERRY_AVAILABLE) {
        ai->stats.available_boats++;
@@ -95,8 +130,8 @@
   struct ai_data *ai = ai_data_get(pplayer);
   int n = 1;
 
-  freelog(LOG_NORMAL, "Boat stats for %s[%d]", 
-         pplayer->name, pplayer->player_no);
+  freelog(LOG_NORMAL, "Boat stats for %s[%d] in turn %d", 
+         pplayer->name, pplayer->player_no, game.turn);
   freelog(LOG_NORMAL, "Registered: %d free out of total %d",
          ai->stats.available_boats, ai->stats.boats);
   unit_list_iterate(pplayer->units, punit) {
@@ -867,3 +902,515 @@
  
   return;
 }
+
+/*
+ * WAGs for controlling the new_boats_to_build function.
+ */
+#define TARGET_UTILISATION 0.4
+#define TARGET_QUEUE 0.7
+#define T_FUTURE 0.2
+#define T_UTILISATION 3.0
+#define T_QUEUE 4.0
+/****************************************************************************
+ Convert facts about a sea into an opinion about how many boats to build
+ for that sea.
+
+ We should build more ferries
+ * if we have many passengers waiting,
+ * if our boats are slow and have far to travel,
+ * if we expect many more passengers in the future,
+ * if we expect heavy losses from enemy hunters.
+
+ The function implements a simple feedback control system.
+ However, it overides that calculation to prevent building boats on ponds
+ and to prevent passengers waiting forever.
+****************************************************************************/
+static unsigned int new_boats_to_build(const struct sea_stats *sea,
+                                       unsigned int turns_horizon)
+{
+  unsigned int all_psngrs = sea->psngrs + sea->future_psngrs;
+  bool force_no_build = (sea->sea_area < 49) || (0 == all_psngrs);
+  bool force_build = !force_no_build && (0 == sea->boats && 0 < all_psngrs);
+  double build, build_utilisation, build_queue;
+  double psngrs = sea->psngrs;
+  double boats = sea->boats;
+  double avail_boats = sea->avail_boats;
+  double queue;
+  unsigned int t;
+
+  assert(avail_boats <= boats);
+
+  /* Include the effect of future passengers.
+   * That is, anticipate future changes. */
+  for(t = 1; t < turns_horizon; t++) {
+    double weight = T_FUTURE / ((double)t);
+    psngrs += weight * (double)sea->extra_passengers[t];
+  }
+
+  if (0 < boats) {
+    queue = psngrs / boats;
+  }else {
+    queue = psngrs;
+  }
+
+  /* Calculate each contribution */
+  build_utilisation = (boats - avail_boats - TARGET_UTILISATION * boats)
+    * (1.0/T_UTILISATION);
+  build_queue = (queue - TARGET_QUEUE) * (1.0/T_QUEUE);
+
+  build = build_utilisation + build_queue;
+
+  if (build < 1 && force_build) {
+    /*Ensure we will always have at least one ferry
+     *for each sea that has passengers.
+     *Prevents passengers waiting forever*/
+    build = 1;
+  } else if (force_no_build) {
+    build = 0;
+  }
+
+  build = MAX(0.0, floor(build + 0.5));
+
+  freelog(LOGLEVEL_BUILDFERRY,
+         "Ferries: psngrs %3.1f, queue %3.1f, build %3.1f=%3.1f%+3.1f%",
+         psngrs, queue, build, build_utilisation, build_queue);
+
+  return build;
+}
+
+/****************************************************************************
+  Abstract interface for using the ferry building mode of a city
+****************************************************************************/
+static enum city_ferry_mode get_city_ferry_mode(const struct city *acity)
+{
+  return acity->ai.ferry_mode;
+}
+
+static void set_city_ferry_mode(struct city *acity,
+                               const enum city_ferry_mode mode)
+{
+  acity->ai.ferry_mode = mode;
+}
+
+/*
+ * Constants used for assessing the suitability of a city for ferry building
+ */
+#define SCORE_NEVER -20000
+#define SCORE_TIME_SCALE 1000
+#define TURNS_FACTOR_BUILDING 2400
+#define TURNS_FACTOR_NONMIL    600
+#define TURNS_FACTOR_ATTACKER  840
+#define TURNS_FACTOR_DEFENDER 8400
+#define TURNS_FACTOR_VETERAN   600
+/* Least Common Multiple of 2,3,..,8 (a very divisible number) */
+#define DENOM 840
+/****************************************************************************
+ Evaluate how suitable a city is as a ferry builder.
+ Larger values mean more suitable. only positive values indicate suitable
+ cities.
+
+ Cities to consider for ferry building.
+ * Only cities on the shore of the current ocean are suitable.
+ * Only cities not already building ferries are suitable.
+ * Cities that can complete a boat sooner are better.
+ * Cities that want a boat themselves are better.
+ * Cities building defenders are unsuitable.
+ * Cities with port facilities are better.
+ * Cities not building wonders are better.
+****************************************************************************/
+static int ferry_builder_score(struct player *pplayer, struct city *acity,
+                               Unit_Type_id boattype)
+{
+  int score = 0;
+  Impr_Type_id port_facility;
+  int turns;
+
+  assert(0 <= acity->ai.choice.want);
+
+
+  /* Consider only appropriate cities: */
+  if (get_city_ferry_mode(acity) != CFM_COUNTED
+      || !can_build_unit(acity, boattype)
+      || (!acity->is_building_unit && is_wonder(acity->currently_building))) {
+    return SCORE_NEVER;
+  }
+
+  if (acity->ai.choice.need_boat) {
+    /* if you *really* want a ferry, you should be prepared to build it
+     * yourself */
+    score = acity->ai.choice.want;
+  } else {
+    /* if you do not much care for what you are doing, you might be asked
+     * to build a ferry instead */
+    score = -acity->ai.choice.want;
+  }
+
+  turns = city_turns_to_build(acity, boattype, TRUE, TRUE);
+
+  switch (acity->ai.choice.type) {
+  case CT_BUILDING:
+    turns = turns * TURNS_FACTOR_BUILDING / DENOM;
+    break;
+  case CT_NONMIL: 
+    turns = turns * TURNS_FACTOR_NONMIL / DENOM;
+    break;
+  case CT_ATTACKER:
+    turns = turns * TURNS_FACTOR_ATTACKER / DENOM;
+    break;
+  case CT_DEFENDER:
+    turns = turns * TURNS_FACTOR_DEFENDER / DENOM;
+    break;
+  default:
+    break;
+  }
+
+  /* Prefer building veteran units (resistant to hunters) */
+  port_facility = ai_find_source_building(pplayer, EFT_SEA_VETERAN);
+  if (port_facility != B_LAST && city_got_building(acity, port_facility)) {
+    turns = turns * TURNS_FACTOR_VETERAN / DENOM;
+  }
+
+  /* Sooner is better */
+  score += SCORE_TIME_SCALE / MAX(1, turns);
+
+  return score;
+}
+
+/****************************************************************************
+  Adjust wants in the cities needing or building ferries
+****************************************************************************/
+static void adjust_wants(struct player *pplayer, bool shortage, int ferry_want)
+{
+  city_list_iterate(pplayer->cities, acity) {
+    if (get_city_ferry_mode(acity) == CFM_BUILDING) {
+      /*Building a ferry here.*/
+      acity->ai.choice.want = ferry_want;
+    } else if(shortage && get_city_ferry_mode(acity) == CFM_COUNTED
+             && acity->ai.choice.need_boat && ai_fuzzy(pplayer, TRUE)) {
+      /*We have a shortage, and this city will make the shortage worse,
+       so spending gold here would be foolish*/
+      acity->ai.choice.want = MIN(ferry_want - 1,
+                                 acity->ai.choice.want / 2);
+    }
+  } city_list_iterate_end;
+}
+
+/****************************************************************************
+  Initialise the statistics about one sea for one player.
+****************************************************************************/
+static void sea_init(struct sea_stats *sea, unsigned int turns_horizon)
+{
+  unsigned int t;
+
+  assert(sea);
+  assert(turns_horizon <= MAX_TURNS_HORIZON);
+
+  sea->future_psngrs = 0;
+
+  sea->psngrs = 0;
+  sea->boats = 0;
+  sea->avail_boats = 0;
+
+  sea->sea_area = 0;
+  sea->n_cities = 0;
+
+  sea->ferry_want = 0;
+  
+  for(t = 0; t < turns_horizon; t++) {
+    sea->extra_passengers[t] = 0;
+  }
+}
+
+/****************************************************************************
+  Add, to the various statistics about a sea for one player,
+  the contribution of a city.
+****************************************************************************/
+static void sea_examine_city(struct player *pplayer,
+                            struct sea_stats *sea,
+                            struct city *acity,
+                            unsigned int turns_horizon)
+{
+  bool is_building_unit;
+  int turns_to_build;
+  unsigned int te;
+  assert(acity);
+
+  if (acity->owner != pplayer->player_no) return;
+
+  is_building_unit = is_unit_choice_type(acity->ai.choice.type);
+  turns_to_build = 
+    is_building_unit?
+    city_turns_to_build(acity, acity->ai.choice.choice, is_building_unit,
+                       TRUE)
+    :FC_INFINITY;
+  assert(0 < turns_to_build);
+  te = MIN(turns_to_build, turns_horizon - 1);
+  
+  assert(get_city_ferry_mode(acity) == CFM_UNPROCESSED);
+  set_city_ferry_mode(acity, CFM_COUNTED);
+
+  sea->n_cities++;
+  if (acity->ai.choice.need_boat) {
+    sea->future_psngrs++;
+    sea->extra_passengers[te]++;
+    sea->ferry_want += acity->ai.choice.want;
+  }
+}
+
+/****************************************************************************
+  Add, to the various statistics about a sea for one player,
+  the contribution of a unit.
+****************************************************************************/
+static void sea_examine_unit(struct player *pplayer,
+                            struct sea_stats *sea,
+                            struct unit *aunit,
+                            unsigned int turns_horizon)
+{
+  struct player *owner = get_player(aunit->owner);
+  if (owner != pplayer) {
+    return;
+  } else if (unit_has_role(aunit->type, L_FERRYBOAT)) {
+    sea->boats++;
+    if (aunit->ai.passenger == FERRY_AVAILABLE) {
+      sea->avail_boats++;
+    } else if (aunit->ai.passenger == 0) {
+      freelog(LOG_ERROR, "Passenger field set to 0");
+      sea->avail_boats++;
+    }
+  } else if (aunit->ai.ferryboat == FERRY_WANTED) {
+    sea->psngrs++;
+  }
+}
+
+/****************************************************************************
+  Add, to the various statistics about a sea for one player,
+  the contribution of a tile.
+  The tile may be in the sea or on its shore.
+****************************************************************************/
+static void sea_examine_tile(struct player *pplayer,
+                            struct sea_stats *sea,
+                            struct tile *ptile,
+                            unsigned int turns_horizon)
+{
+  struct city *acity = map_get_city(ptile);
+  bool ocean = is_ocean(map_get_terrain(ptile));
+
+  if (ocean) {
+    sea->sea_area++;
+  }
+
+  if (ai_handicap(pplayer, H_MAP) && !map_is_known(ptile, pplayer)) {
+    /* The tile is unknown */
+    return;
+  }
+  if (ai_handicap(pplayer, H_FOG) 
+      && !map_is_known_and_seen(ptile, pplayer)) {
+    /* The tile is fogged */
+    return;
+  }
+
+  if (acity) {
+    sea_examine_city(pplayer, sea, acity, turns_horizon);
+  }
+
+  unit_list_iterate(ptile->units, aunit) {
+    sea_examine_unit(pplayer, sea, aunit, turns_horizon);
+  } unit_list_iterate_end;      
+}
+
+/*
+ * The following two constants are to ensure that having many waiting
+ * passengers will increase the 'want' for ferries.
+ * WAIT_FERRY_WANT_0:
+ *    The minimum 'want' for ferries if there are any massengers waiting.
+ * WAIT_FERRY_WANT_1:
+ *    The extra 'want' for each currently waiting passenger per boat.
+ */
+#define WAIT_FERRY_WANT_0 200
+#define WAIT_FERRY_WANT_1 400
+/****************************************************************************
+  Compute various statistics about a sea for one player,
+  for use in determining how many ferries to build there.
+
+  *sea:
+     the computed statistics
+  turns_horizon:
+     maximum look-ahead for statistics
+  pfmap:
+     a map of the sea and its shore
+****************************************************************************/
+static void sea_examine_sea(struct player *pplayer,
+                           struct sea_stats *sea,
+                           unsigned int turns_horizon,
+                           struct pf_map *pfmap)
+{
+  unsigned int n_enemy_targets;
+  sea_init(sea, turns_horizon);
+
+  /* We want to consider the place we are currently in too, hence the 
+   * do-while loop */
+  do {/*all tiles in the sea or on its shore*/
+    struct pf_position pos;
+    pf_next_get_position(pfmap, &pos);
+    sea_examine_tile(pplayer, sea, pos.tile, turns_horizon);
+  } while (pf_next(pfmap));
+
+  /*convert total to average*/
+  sea->ferry_want = sea->ferry_want / MAX(1, sea->future_psngrs);
+
+  /*if there are many waiting passsengers, increase the 'want' for ferries*/
+  if (sea->psngrs) {
+    int waiters_want = WAIT_FERRY_WANT_0
+                       + sea->psngrs * WAIT_FERRY_WANT_1 / MAX(1, sea->boats);
+    sea->ferry_want = MAX(sea->ferry_want, waiters_want);
+  }
+
+  assert(sea->avail_boats <= sea->boats);
+}
+
+/****************************************************************************
+  Choose the cities that will build the boats
+****************************************************************************/
+static void select_ferry_builders(struct player *pplayer,
+                                 unsigned int boats_to_be_built,
+                                 Unit_Type_id boattype)
+{
+  while (boats_to_be_built > 0) {
+    int best = SCORE_NEVER;
+    struct city *bestcity = NULL;
+
+    /*Find the best city to build the next ferry*/
+    city_list_iterate(pplayer->cities, acity) {
+      int score = ferry_builder_score(pplayer, acity, boattype);
+      if (best < score && 0 < score) {
+       assert(is_ocean_near_tile(acity->tile));
+       best = score;
+       bestcity = acity;
+      }
+    } city_list_iterate_end;
+
+    if (!bestcity) {
+      break;
+    }
+
+    /* FIXME: separate a function ai_set_build from 
+     * aicity.c:ai_city_choose_build and use it here */
+    CITY_LOG(LOGLEVEL_BUILDFERRY, bestcity, "will build a boat");
+    bestcity->currently_building = boattype;
+    bestcity->is_building_unit = TRUE;
+    set_city_ferry_mode(bestcity, CFM_BUILDING);
+    boats_to_be_built--;
+  }
+
+  if (0 < boats_to_be_built) {
+    freelog(LOGLEVEL_BUILDFERRY,
+           "Ferries: %s unable to build %d boats",
+           pplayer->name, boats_to_be_built);
+  }
+}
+
+/****************************************************************************
+  A global ferry build selector.  Estimates our want for the boats and finds
+  cities who will build them.
+****************************************************************************/
+void aiferry_choose_build_global(struct player *pplayer)
+{
+  struct city *pcity;
+  /* The worst case is that each of our cities
+   * is on the coast of a different ocean:*/
+  int inf_loop_guard = city_list_size(&pplayer->cities);
+
+  /*The harder the AI, the further ahead it looks:*/
+  unsigned int turns_horizon = MAX(2, MAX_TURNS_HORIZON
+                                      - (2 * MAX_TURNS_HORIZON
+                                         * pplayer->ai.fuzzy / 1000));
+  /* Path-finding stuff */
+  struct pf_map *pfmap;
+  struct pf_parameter parameter;
+
+  /*Choose type of boats to build*/
+  Unit_Type_id boattype = best_role_unit_for_player(pplayer, L_FERRYBOAT);
+  if (boattype == U_LAST) {
+    freelog(LOGLEVEL_BUILDFERRY,
+            "Ferries: %s lacks the technology to build boats",
+            pplayer->name);
+    return;
+  }
+
+  /* Initialize */
+  city_list_iterate(pplayer->cities, acity) {
+    set_city_ferry_mode(acity, CFM_UNPROCESSED);
+  } city_list_iterate_end;
+
+  parameter.turn_mode = TM_NONE;
+  parameter.get_MC = sea_overlap_move; /*an ocean and its shore*/
+  parameter.get_TB = NULL;
+  parameter.get_EC = NULL;
+  parameter.get_costs = NULL;
+  parameter.get_zoc = NULL;
+  parameter.is_pos_dangerous = NULL;
+  BV_CLR_ALL(parameter.unit_flags);
+  parameter.owner = pplayer;
+  parameter.omniscience = TRUE; /*this code handles ignorance itself*/
+  /* These don't matter */
+  parameter.moves_left_initially = 0;
+  parameter.move_rate = 3;
+
+  do {/*each ocean for which we have coastal cities*/
+    struct sea_stats sea;
+    unsigned int boats_to_be_built, shortage;
+
+    /* Find an unprocessed coastal city;
+     * such a city is on the shore of an unprocessed ocean. */
+    pcity = NULL;
+    city_list_iterate(pplayer->cities, acity) {
+      if (get_city_ferry_mode(acity) == CFM_UNPROCESSED 
+          && is_ocean_near_tile(acity->tile)) {
+        pcity = acity;
+        break;
+      }
+    } city_list_iterate_end;
+
+    if (!pcity) {/*no more oceans*/
+      break;
+    }
+
+    /* Get a map of the sea plus shore accessible from pcity*/
+    /* If the city is adjacent to more than one ocean,
+     * or oceans are accessible from the city through channels in allied
+     * cities, this processes all those oceans as one sea.*/
+    parameter.start_tile = pcity->tile;
+    pfmap = pf_create_map(&parameter);
+
+    /* Part 1: measure the demand for boats for this ocean*/
+    sea_examine_sea(pplayer, &sea, turns_horizon, pfmap);
+
+    pf_destroy_map(pfmap);
+
+    freelog(LOGLEVEL_BUILDFERRY, "Sea near %s's %s: %d/%d boats, "
+            "%d+%d/%d waiting, want %d", pplayer->name, pcity->name,
+            sea.avail_boats, sea.boats, sea.psngrs, sea.future_psngrs,
+           sea.n_cities, sea.ferry_want);
+
+    /* Part 2: Decide how many boats we want built */
+    boats_to_be_built = new_boats_to_build(&sea, turns_horizon);
+    freelog(LOGLEVEL_BUILDFERRY,
+            "Sea near %s's %s: should build %d boats", 
+            pplayer->name, pcity->name, boats_to_be_built);
+    shortage = 0 < boats_to_be_built;
+
+    /* Part 3: Decide where to build the boats */
+    select_ferry_builders(pplayer, boats_to_be_built, boattype);
+    adjust_wants(pplayer, shortage, sea.ferry_want);
+
+    /* Part 4: Cleanup */
+    city_list_iterate(pplayer->cities, acity) {
+      if (get_city_ferry_mode(acity) == CFM_COUNTED
+          || get_city_ferry_mode(acity) == CFM_BUILDING) { 
+        set_city_ferry_mode(acity, CFM_DONE);
+      }
+    } city_list_iterate_end;
+
+    inf_loop_guard--;
+  } while(inf_loop_guard > 0);
+}
diff -ru -Xfreeciv.PR8992/diff_ignore vendor.freeciv.current/ai/aiferry.h 
freeciv.PR8992/ai/aiferry.h
--- vendor.freeciv.current/ai/aiferry.h 2004-10-22 23:16:36.000000000 +0100
+++ freeciv.PR8992/ai/aiferry.h 2004-11-06 02:10:36.000000000 +0000
@@ -48,4 +48,10 @@
  */
 void ai_manage_ferryboat(struct player *pplayer, struct unit *punit);
 
+/* 
+ * A function for a centralized (i.e. per-civilization rather than per-city
+ * estimation of need to build more ferries and where to build them
+ */
+void aiferry_choose_build_global(struct player *pplayer);
+
 #endif /* FC__AIFERRY_H */
diff -ru -Xfreeciv.PR8992/diff_ignore 
vendor.freeciv.current/common/aicore/pf_tools.c 
freeciv.PR8992/common/aicore/pf_tools.c
--- vendor.freeciv.current/common/aicore/pf_tools.c     2004-10-22 
23:16:36.000000000 +0100
+++ freeciv.PR8992/common/aicore/pf_tools.c     2004-11-06 02:10:36.000000000 
+0000
@@ -80,9 +80,9 @@
   anywhere, unless we are leaving a friendly city, in which
   case we can move into the ocean but not into the land.
 ************************************************************/
-static int sea_overlap_move(const struct tile *ptile, enum direction8 dir,
-                           const struct tile *ptile1,
-                           struct pf_parameter *param)
+int sea_overlap_move(const struct tile *ptile, enum direction8 dir,
+                    const struct tile *ptile1,
+                    struct pf_parameter *param)
 {
   if (is_ocean(ptile->terrain)) {
     return SINGLE_MOVE;
diff -ru -Xfreeciv.PR8992/diff_ignore 
vendor.freeciv.current/common/aicore/pf_tools.h 
freeciv.PR8992/common/aicore/pf_tools.h
--- vendor.freeciv.current/common/aicore/pf_tools.h     2004-10-22 
23:16:36.000000000 +0100
+++ freeciv.PR8992/common/aicore/pf_tools.h     2004-11-06 02:10:36.000000000 
+0000
@@ -29,6 +29,10 @@
 enum tile_behavior no_fights(const struct tile *ptile, enum known_type known,
                             struct pf_parameter *param);
 
+int sea_overlap_move(const struct tile *ptile, enum direction8 dir,
+                    const struct tile *ptile1,
+                    struct pf_parameter *param);
+
 #define pf_iterator(map, position) {                       \
   struct pf_position position;                             \
   while (pf_next(map)) {                                   \
diff -ru -Xfreeciv.PR8992/diff_ignore vendor.freeciv.current/common/city.h 
freeciv.PR8992/common/city.h
--- vendor.freeciv.current/common/city.h        2004-10-22 23:16:36.000000000 
+0100
+++ freeciv.PR8992/common/city.h        2004-11-06 02:10:36.000000000 +0000
@@ -161,6 +161,16 @@
 #define ASSERT_REAL_CHOICE_TYPE(type)                                    \
         assert(type >= 0 && type < CT_LAST /* && type != CT_NONE */ );
 
+/*
+ * Used while determining how many and where to build ferries.
+ */  
+enum city_ferry_mode {
+  CFM_UNPROCESSED,
+  CFM_COUNTED,
+  CFM_BUILDING,
+  CFM_DONE
+};
+
 
 struct ai_choice {
   int choice;            /* what the advisor wants */
@@ -212,6 +222,7 @@
 
   int worth; /* Cache city worth here, sum of all weighted incomes */
   int next_recalc; /* Only recalc every Nth turn */
+  enum city_ferry_mode ferry_mode;
 };
 
 struct city {
diff -ru -Xfreeciv.PR8992/diff_ignore vendor.freeciv.current/diff_ignore 
freeciv.PR8992/diff_ignore
--- vendor.freeciv.current/diff_ignore  2004-10-22 23:16:37.000000000 +0100
+++ freeciv.PR8992/diff_ignore  2004-11-06 02:10:37.000000000 +0000
@@ -17,6 +17,7 @@
 *~
 .#*
 .deps
+.svn
 CVS
 Freeciv.h
 Makefile

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