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

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

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
Subject: [freeciv-ai] (PR#8992) Patch: Building ferries
From: "Gregory Berkolaiko" <Gregory.Berkolaiko@xxxxxxxxxxxxx>
Date: Sun, 14 Nov 2004 18:09:48 -0800
Reply-to: rt@xxxxxxxxxxx

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

> 
> I chopped your code up to my liking, without actually changing the
> logic.  Removed things that I thought are unnecessary, in particular
> horizon.  Right now I am reworking it to use channels and not to use PF.
>  I want to make trial runs for speed and then select the code which
> looks the cutest of course ;)

I am making progress very slowly...  Here is a version of the patch
which avoids the use of PF and uses channels information instead.  The
channels patch is included.  Seems to work but I have only tested it on
old savegames I seem to have and they have only one ocean.  Blame Mike
for not making an up-to-date version of CivWorld.

I didn't change the formulas.  I want to time the two latest patches and
see how they perform.  But won't do it today.

Best wishes,
Gr
? core.2517
? core.2650
Index: ai/advdomestic.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/advdomestic.c,v
retrieving revision 1.121
diff -u -r1.121 advdomestic.c
--- ai/advdomestic.c    20 Oct 2004 18:20:53 -0000      1.121
+++ ai/advdomestic.c    15 Nov 2004 02:05:09 -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);
     }
   }
 
Index: ai/advmilitary.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/advmilitary.c,v
retrieving revision 1.176
diff -u -r1.176 advmilitary.c
--- ai/advmilitary.c    30 Oct 2004 14:04:54 -0000      1.176
+++ ai/advmilitary.c    15 Nov 2004 02:05:10 -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)" : ""));
   } 
 }
 
@@ -1200,6 +1186,8 @@
     if ((id = ai_find_source_building(pplayer, EFT_LAND_VETERAN)) != B_LAST) {
       choice->choice = id;
       choice->type = CT_BUILDING;
+      /* Not yet... */
+      choice->need_boat = FALSE;
     }
     break;
   case SEA_MOVING:
@@ -1390,9 +1378,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);
Index: ai/aicity.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aicity.c,v
retrieving revision 1.177
diff -u -r1.177 aicity.c
--- ai/aicity.c 31 Oct 2004 21:52:20 -0000      1.177
+++ ai/aicity.c 15 Nov 2004 02:05:11 -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,10 @@
     ai_city_choose_build(pplayer, pcity);
   } city_list_iterate_end;
 
+  if (!is_barbarian(pplayer)) {
+    aiferry_choose_build_global(pplayer);
+  }
+
   ai_spend_gold(pplayer);
 }
 
Index: ai/aidata.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aidata.c,v
retrieving revision 1.46
diff -u -r1.46 aidata.c
--- ai/aidata.c 23 Oct 2004 19:01:01 -0000      1.46
+++ ai/aidata.c 15 Nov 2004 02:05:11 -0000
@@ -134,7 +134,8 @@
 void ai_data_turn_init(struct player *pplayer) 
 {
   struct ai_data *ai = &aidata[pplayer->player_no];
-  int i, nuke_units = num_role_units(F_NUCLEAR);
+  int i, j, k;
+  int nuke_units = num_role_units(F_NUCLEAR);
   bool danger_of_nukes = FALSE;
   int ally_strength = -1;
   struct player *ally_strongest = NULL;
@@ -225,6 +226,52 @@
   /* Increase from fear to terror if opponent actually has nukes */
   if (danger_of_nukes) ai->threats.nuclear++; /* sum of both fears */
 
+  /*** Channels ***/
+
+  /* Ways to cross from one ocean to another through a city. */
+  ai->channels = fc_calloc((ai->num_oceans + 1) * (ai->num_oceans + 1), 
sizeof(int));
+  players_iterate(aplayer) {
+    if (pplayers_allied(pplayer, aplayer)) {
+      city_list_iterate(aplayer->cities, pcity) {
+        adjc_iterate(pcity->tile, tile1) {
+          if (is_ocean(tile1->terrain)) {
+            adjc_iterate(pcity->tile, tile2) {
+              if (is_ocean(tile2->terrain) 
+                  && map_get_continent(tile1) != map_get_continent(tile2)) {
+                ai->channels[(-tile1->continent) * ai->num_oceans
+                             + (-tile2->continent)] = TRUE;
+                ai->channels[(-tile2->continent) * ai->num_oceans
+                             + (-tile1->continent)] = TRUE;
+              }
+            } adjc_iterate_end;
+          }
+        } adjc_iterate_end;
+      } city_list_iterate_end;
+    }
+  } players_iterate_end;
+
+  /* If we can go i -> j and j -> k, we can also go i -> k. */
+  for(i = 1; i <= ai->num_oceans; i++) {
+    for(j = 1; j <= ai->num_oceans; j++) {
+      if (ai->channels[i * ai->num_oceans + j]) {
+        for(k = 1; k <= ai->num_oceans; k++) {
+          ai->channels[i * ai->num_oceans + k] |= 
+            ai->channels[j * ai->num_oceans + k];
+        }
+      }
+    }
+  }
+#if 0
+  freelog(LOG_NORMAL, "Player %s:", pplayer->name);
+  for(i = 1; i <= ai->num_oceans; i++) {
+    for(j = 1; j <= ai->num_oceans; j++) {
+      if (ai->channels[i * ai->num_oceans + j]) {
+        freelog(LOG_NORMAL, "oceans %d and %d are connected", i, j);
+      }
+    }
+  }
+#endif
+
   /*** Exploration ***/
 
   ai->explore.land_done = TRUE;
@@ -426,6 +473,9 @@
 
   free(ai->stats.cities);
   ai->stats.cities = NULL;
+
+  free(ai->channels);
+  ai->channels = NULL;
 }
 
 /**************************************************************************
@@ -458,7 +508,7 @@
                                    * sizeof(*ai->government_want)));
   memset(ai->government_want, 0,
         (game.government_count + 1) * sizeof(*ai->government_want));
-
+  ai->channels = NULL;
   ai->diplomacy.target = NULL;
   ai->diplomacy.strategy = WIN_OPEN;
   ai->diplomacy.timer = 0;
@@ -481,3 +531,17 @@
     ai->diplomacy.player_intel[i].warned_about_space = 0;
   }
 }
+
+/**************************************************************************
+  Is there a channel going from ocean c1 to ocean c2?
+  Returns FALSE if either is not an ocean.
+**************************************************************************/
+bool ai_channel(struct player *pplayer, Continent_id c1, Continent_id c2)
+{
+  struct ai_data *ai = ai_data_get(pplayer);
+
+  if (c1 >= 0 || c2 >= 0) {
+    return FALSE;
+  }
+  return (c1 == c2 || ai->channels[(-c1) * ai->num_oceans + (-c2)]);
+}
Index: ai/aidata.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aidata.h,v
retrieving revision 1.19
diff -u -r1.19 aidata.h
--- ai/aidata.h 23 Oct 2004 19:01:01 -0000      1.19
+++ ai/aidata.h 15 Nov 2004 02:05:11 -0000
@@ -97,6 +97,9 @@
     bool sea_done;    /* nothing more to explore at sea */
   } explore;
 
+  /* Keep track of available ocean channels */
+  bool *channels;
+
   /* This struct is used for statistical unit building, eg to ensure
    * that we don't build too few or too many units of a given type. */
   struct {
@@ -149,4 +152,6 @@
 
 struct ai_data *ai_data_get(struct player *pplayer);
 
+bool ai_channel(struct player *pplayer, Continent_id c1, Continent_id c2);
+
 #endif
Index: ai/aiferry.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aiferry.c,v
retrieving revision 1.10
diff -u -r1.10 aiferry.c
--- ai/aiferry.c        8 Nov 2004 14:14:30 -0000       1.10
+++ ai/aiferry.c        15 Nov 2004 02:05:12 -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,37 @@
 
 /* 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 psngrs;
+  unsigned int boats;
+  unsigned int avail_boats;
+  double future_psngrs_weight;
+  unsigned int n_cities; /*our cities*/
+  unsigned int future_psngrs;
+  bool processed;
+
+  int ferry_want;
+};
 
 
 /* ========= managing statistics and boat/passanger assignments ======== */
@@ -73,7 +104,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 +126,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) {
@@ -870,3 +901,388 @@
  
   return;
 }
+
+
+/* ========================= boat building =============================== */
+
+/****************************************************************************
+  A service function, returns the adjacent tile which is ocean (ptile if 
+  ptile is ocean) and NULL if no ocean around.
+  NB: There might be more than one ocean around a tile!
+****************************************************************************/
+static int find_ocean_id_near_tile(struct tile *ptile)
+{
+
+  if (is_ocean(ptile->terrain)) {
+    return (-map_get_continent(ptile));
+  }
+
+  adjc_iterate(ptile, atile) {
+    if (is_ocean(atile->terrain)) {
+      return (-map_get_continent(atile));
+    }
+  } adjc_iterate_end;
+
+  return 0;
+}
+
+/*
+ * WAGs for controlling the new_boats_to_build function.
+ */
+#define TARGET_UTILISATION 0.4
+#define TARGET_QUEUE 0.7
+#define T_UTILISATION 3.0
+#define T_QUEUE 4.0
+/*
+ * The following two constants are to ensure that having many waiting
+ * passengers will increase the 'want' for ferries.
+ */
+/* The minimum 'want' for ferries if there are any massengers waiting. */
+#define WAIT_FERRY_WANT_0 200
+/*    The extra 'want' for each currently waiting passenger per boat. */
+#define WAIT_FERRY_WANT_1 400
+/****************************************************************************
+ 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 and/or expect more in the future
+ * if the boats we have are very busy
+
+ 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(struct sea_stats *sea)
+{
+  bool force_no_build = !(0 < sea->psngrs + sea->future_psngrs);
+  bool force_build = (0 == sea->boats 
+                      && 0 < sea->psngrs + sea->future_psngrs);
+  double build, build_utilisation, build_queue;
+  double psngrs = sea->psngrs + sea->future_psngrs_weight;
+  double boats = sea->boats;
+  double avail_boats = sea->avail_boats;
+  double queue;
+
+  assert(avail_boats <= boats);
+
+  queue = psngrs / MAX(1, boats);
+
+  /* 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));
+
+  /*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);
+  }
+
+  freelog(LOGLEVEL_BUILDFERRY, "Ferries: psngrs %3.1f, queue %3.1f, "
+          "build %3.1f=%3.1f%+3.1f (want %d)", psngrs, queue, 
+          build, build_utilisation, build_queue, sea->ferry_want);
+
+  return build;
+}
+
+/* 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 *pcity,
+                               Unit_Type_id boattype, int ocean_id)
+{
+  int score = 0;
+  Impr_Type_id port_facility;
+  int turns;
+
+  assert(0 <= pcity->ai.choice.want);
+
+  /* Consider only appropriate cities: */
+  if (!ai_channel(pplayer, -find_ocean_id_near_tile(pcity->tile), -ocean_id)
+      || !can_build_unit(pcity, boattype)
+      || (!pcity->is_building_unit && is_wonder(pcity->currently_building))) {
+    return 0;
+  }
+
+  if (pcity->ai.choice.need_boat) {
+    /* if you *really* want a ferry, you should be prepared to build it
+     * yourself */
+    score = pcity->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 = -pcity->ai.choice.want;
+  }
+
+  turns = city_turns_to_build(pcity, boattype, TRUE, TRUE);
+
+  /* Make some adjustments depending on what city is building now */
+  switch (pcity->ai.choice.type) {
+  case CT_BUILDING:
+    /* Dont like to stop buildings */
+    turns = turns * 3;
+    break;
+  case CT_NONMIL:
+    turns = turns * 2 / 3;
+    break;
+  case CT_DEFENDER:
+    return 0;
+  }
+
+  /* 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(pcity, port_facility)) {
+    turns = turns * 2 / 3;
+  }
+
+  /* Sooner is better */
+  score += DENOM / MAX(1, turns);
+
+  return score;
+}
+
+/****************************************************************************
+  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, 
+                                  int ferry_want, int ocean_id)
+{
+  while (boats_to_be_built > 0) {
+    int best = 0;
+    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, ocean_id);
+      if (best < 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;
+    boats_to_be_built--;
+    bestcity->ai.choice.want = ferry_want;
+  }
+
+  if (0 < boats_to_be_built) {
+    freelog(LOGLEVEL_BUILDFERRY,
+           "Ferries: %s unable to build %d boats",
+           pplayer->name, boats_to_be_built);
+  }
+}
+
+/****************************************************************************
+  Adjust wants in the cities needing or building ferries.  Otherwise AI buys
+  new passengers before boats get built.  "Shortage" is TRUE if we couldn't 
+  order as many boats as we wanted.
+****************************************************************************/
+static void adjust_wants(struct player *pplayer, int ferry_want, int ocean_id)
+{
+  city_list_iterate(pplayer->cities, acity) {
+    if (ai_channel(pplayer, -find_ocean_id_near_tile(acity->tile), 
+                   -ocean_id) 
+        && !(acity->is_building_unit 
+             && unit_has_role(acity->currently_building, L_FERRYBOAT))
+        && acity->ai.choice.need_boat) {
+      /*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;
+}
+
+/* Weight of one future passenger (if weight of an existing passenger is 1) 
+ * Also to be divided by the time required to finish building */
+#define WEIGHT_FUTURE 0.2
+/****************************************************************************
+  Add, to the various statistics about a sea for one player,
+  the contribution of a city.
+****************************************************************************/
+static void sea_examine_city(struct sea_stats *sea, struct city *pcity)
+{
+  int ocean_id = find_ocean_id_near_tile(pcity->tile);
+
+  if (ocean_id == 0) {
+    /* If the city is inland, on which ocean do we build the boat? */
+    return;
+  }
+
+  //  assert(get_city_ferry_mode(pcity) == CFM_UNPROCESSED);
+  //  set_city_ferry_mode(pcity, CFM_COUNTED);
+  
+  if (pcity->ai.choice.need_boat) {
+    int turns_to_build = city_turns_to_build(pcity, pcity->ai.choice.choice, 
+                                             TRUE, TRUE);
+    
+    /* Err... Buildings don't need boats, right? */
+    assert(is_unit_choice_type(pcity->ai.choice.type));
+    assert(0 < turns_to_build);
+    
+    sea[ocean_id].n_cities++;
+    sea[ocean_id].future_psngrs++;
+    sea[ocean_id].future_psngrs_weight += 
+      WEIGHT_FUTURE / ((double)turns_to_build);
+    sea[ocean_id].ferry_want += pcity->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 sea_stats *sea, struct unit *punit)
+{
+  int ocean_id = find_ocean_id_near_tile(punit->tile);
+
+  if (ocean_id == 0) {
+    return;
+  }
+
+  if (unit_has_role(punit->type, L_FERRYBOAT)) {
+    /* We are a boat */
+    sea[ocean_id].boats++;
+    if (punit->ai.passenger == FERRY_AVAILABLE) {
+      sea[ocean_id].avail_boats++;
+    } else if (punit->ai.passenger == 0) {
+      freelog(LOG_ERROR, "Passenger field set to 0");
+      sea[ocean_id].avail_boats++;
+    }
+  } else if (punit->ai.ferryboat == FERRY_WANTED) {
+    /* We want a boat */
+    sea[ocean_id].psngrs++;
+  }
+}
+
+/****************************************************************************
+  Compute various statistics about a sea for one player,
+  for use in determining how many ferries to build there.
+
+  *sea:
+     the computed statistics
+****************************************************************************/
+static void sea_examine(struct player *pplayer, struct sea_stats *sea)
+{
+  city_list_iterate(pplayer->cities, acity) {
+    sea_examine_city(sea, acity); 
+  } city_list_iterate_end;
+
+  unit_list_iterate(pplayer->units, aunit) {
+    sea_examine_unit(sea, aunit);
+  } unit_list_iterate_end;      
+}
+
+/****************************************************************************
+  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)
+{
+  /* This will update the data if necessary */
+  struct ai_data *ai = ai_data_get(pplayer);
+  struct sea_stats *sea = fc_calloc(ai->num_oceans + 1, 
+                                    sizeof(struct sea_stats));
+  /*Choose type of boats to build*/
+  Unit_Type_id boattype = best_role_unit_for_player(pplayer, L_FERRYBOAT);
+  int ocean_id;
+
+  sea_examine(pplayer, sea);
+
+  if (boattype == U_LAST) {
+    freelog(LOGLEVEL_BUILDFERRY,
+            "Ferries: %s lacks the technology to build boats",
+            pplayer->name);
+    /* TODO: Boost the tech! */
+    return;
+  }
+
+  for(ocean_id = 1; ocean_id <= ai->num_oceans; ocean_id++) {
+    struct sea_stats sea_total;
+    unsigned int boats_to_be_built;
+    int j;
+
+    if (sea[ocean_id].processed) {
+      continue;
+    }
+
+    /* Part 1: measure the demand for boats for this ocean*/
+    memcpy(&sea_total, &sea[ocean_id], sizeof(sea_total));
+
+    for(j = ocean_id + 1; j <= ai->num_oceans; j++) {
+      if (ai_channel(pplayer, -ocean_id, -j)) {
+        sea_total.psngrs += sea[j].psngrs;
+        sea_total.boats +=  sea[j].boats;
+        sea_total.avail_boats += sea[j].avail_boats;
+        sea_total.future_psngrs += sea[j].future_psngrs;
+        sea_total.future_psngrs_weight += sea[j].future_psngrs_weight;
+        sea_total.n_cities += sea[j].n_cities;
+        sea_total.ferry_want += sea[j].ferry_want;
+        
+        sea[j].processed = TRUE;
+      }
+    }
+
+    sea_total.ferry_want = 
+      sea_total.ferry_want / MAX(1.0, sea_total.future_psngrs);
+
+    freelog(LOGLEVEL_BUILDFERRY, "Sea number %d(%s): %d/%d boats, "
+            "%d+%d/%d waiting, want %d", ocean_id, pplayer->name,
+            sea_total.avail_boats, sea_total.boats, sea_total.psngrs, 
+            sea_total.future_psngrs, sea_total.n_cities, 
+            sea_total.ferry_want);
+
+    /* Part 2: Decide how many boats we want built */
+    boats_to_be_built = new_boats_to_build(&sea_total);
+    freelog(LOGLEVEL_BUILDFERRY,
+            "Sea number %d(%s): should build %d boats", 
+            ocean_id, pplayer->name, boats_to_be_built);
+    if (0 == boats_to_be_built) {
+      continue;
+    }
+
+    /* Part 3: Decide where to build the boats */
+    select_ferry_builders(pplayer, boats_to_be_built, boattype, 
+                          sea_total.ferry_want, ocean_id);
+    adjust_wants(pplayer, sea_total.ferry_want, ocean_id);
+  }
+}
Index: ai/aiferry.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aiferry.h,v
retrieving revision 1.3
diff -u -r1.3 aiferry.h
--- ai/aiferry.h        29 Sep 2004 02:24:18 -0000      1.3
+++ ai/aiferry.h        15 Nov 2004 02:05:12 -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 */
Index: common/city.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/city.h,v
retrieving revision 1.166
diff -u -r1.166 city.h
--- common/city.h       10 Nov 2004 17:01:59 -0000      1.166
+++ common/city.h       15 Nov 2004 02:05:13 -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 {
Index: common/aicore/pf_tools.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/aicore/pf_tools.c,v
retrieving revision 1.24
diff -u -r1.24 pf_tools.c
--- common/aicore/pf_tools.c    20 Oct 2004 18:20:53 -0000      1.24
+++ common/aicore/pf_tools.c    15 Nov 2004 02:05:14 -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;
Index: common/aicore/pf_tools.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/aicore/pf_tools.h,v
retrieving revision 1.8
diff -u -r1.8 pf_tools.h
--- common/aicore/pf_tools.h    29 Sep 2004 02:24:23 -0000      1.8
+++ common/aicore/pf_tools.h    15 Nov 2004 02:05:14 -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)) {                                   \
Index: server/gotohand.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/gotohand.c,v
retrieving revision 1.187
diff -u -r1.187 gotohand.c
--- server/gotohand.c   31 Oct 2004 22:32:33 -0000      1.187
+++ server/gotohand.c   15 Nov 2004 02:05:17 -0000
@@ -33,6 +33,7 @@
 #include "unithand.h"
 #include "unittools.h"
 
+#include "aidata.h"
 #include "aitools.h"
 
 #include "gotohand.h"
@@ -1217,6 +1218,9 @@
 bool goto_is_sane(struct unit *punit, struct tile *ptile, bool omni)
 {  
   struct player *pplayer = unit_owner(punit);
+  struct city *pcity = map_get_city(ptile);
+  Continent_id my_cont = map_get_continent(punit->tile);
+  Continent_id target_cont = map_get_continent(ptile);
 
   if (same_pos(punit->tile, ptile)) {
     return TRUE;
@@ -1235,36 +1239,57 @@
        * and with a boat */
       if (ground_unit_transporter_capacity(ptile, pplayer) > 0) {
         adjc_iterate(ptile, tmp_tile) {
-          if (map_get_continent(tmp_tile) == map_get_continent(punit->tile))
+          if (map_get_continent(tmp_tile) == my_cont) {
             /* The target is adjacent to our continent! */
             return TRUE;
+          }
         } adjc_iterate_end;
       }
     } else {
       /* Going to a land tile: better be our continent */
-      if (map_get_continent(punit->tile) == map_get_continent(ptile)) {
+      if (my_cont == target_cont) {
         return TRUE;
       } else {
         /* Well, it's not our continent, but maybe we are on a boat
          * adjacent to the target continent? */
        adjc_iterate(punit->tile, tmp_tile) {
-         if (map_get_continent(tmp_tile) == map_get_continent(ptile)) {
+         if (map_get_continent(tmp_tile) == target_cont) {
            return TRUE;
           }
        } adjc_iterate_end;
       }
-    }
-      
+    }      
     return FALSE;
 
   case SEA_MOVING:
-    if (is_ocean(map_get_terrain(ptile))
-        || is_ocean_near_tile(ptile)) {
-      /* The target is sea or is accessible from sea 
-       * (allow for bombardment and visiting ports) */
-      return TRUE;
+    if (!is_ocean(map_get_terrain(punit->tile))) {
+      /* Oops, we are not in the open waters.  What ocean do we have 
+       * access to?  We can assume we are in a city and any oceans adjacent
+       * are connected. */
+      adjc_iterate(punit->tile, tmp_tile) {
+        if (is_ocean(map_get_terrain(tmp_tile))) {
+          my_cont = map_get_continent(tmp_tile);
+          break;
+        }
+      } adjc_iterate_end;
     }
-    return FALSE;
+
+    if (is_ocean(map_get_terrain(ptile))) {
+      if (ai_channel(pplayer, target_cont, my_cont)) {
+        return TRUE; /* Ocean -> Ocean travel ok. */
+      }
+    } else if ((pcity && pplayers_allied(city_owner(pcity), pplayer))
+               || !unit_flag(punit, F_NO_LAND_ATTACK)) {
+      /* Not ocean, but allied city or can bombard, checking if there is 
+       * good ocean adjacent */
+      adjc_iterate(ptile, tmp_tile) {
+        if (is_ocean(map_get_terrain(tmp_tile)) 
+            && ai_channel(pplayer, my_cont, map_get_continent(tmp_tile))) {
+          return TRUE;
+        }
+      } adjc_iterate_end;
+    }
+    return FALSE; /* Not ok. */
 
   default:
     return TRUE;

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