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: Sat, 6 Nov 2004 16:01:15 -0800
Reply-to: rt@xxxxxxxxxxx

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

I wrote:
...
> Attached is a new, simplified, version of the patch.
...

Grrr. Some compilation warnings slipped through. The attached version is 
cleaner.

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-07 00:02:41.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-07 00:02:42.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-07 00:02:42.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-07 00:02:41.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,514 @@
  
   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)
+{
+  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-07 00:02:41.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-07 00:02:40.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-07 00:02:40.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-07 00:02:40.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-07 00:02:42.000000000 +0000
@@ -17,6 +17,7 @@
 *~
 .#*
 .deps
+.svn
 CVS
 Freeciv.h
 Makefile

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