Complete.Org: Mailing Lists: Archives: freeciv-ai: September 2003:
[freeciv-ai] Re: (PR#6308) Cleanup of ferry handling
Home

[freeciv-ai] Re: (PR#6308) Cleanup of ferry handling

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
To: undisclosed-recipients: ;
Subject: [freeciv-ai] Re: (PR#6308) Cleanup of ferry handling
From: "Gregory Berkolaiko" <Gregory.Berkolaiko@xxxxxxxxxxxx>
Date: Sun, 28 Sep 2003 13:29:26 -0700
Reply-to: rt@xxxxxxxxxxxxxx

An update of the patch.

In addition to other changes, we now keep register of potential passengers 
and make sure boat do not go looking for them if they don't exist, since 
it's a rather wasteful procedure.

I think it is ready to go in.

I thoroughly tested it, it seems to work just fine.

G.

On Sun, 28 Sep 2003, Gregory Berkolaiko wrote:

> 
> I will attach a patch (in the next email) which cleans and rewrites two
> functions:  ai_manage_ferryboat and ai_military_gothere.
> 
> ai_mil_gothere ceases to be military, settlers can also use it.
> If a unit want to go by boat, it can come to the shore and shout for
> one.  A boat will come.
> 
> ai_manage_ferryboat doesn't drive loaded boats anymore, only the empty
> ones.  If boat is full, it will pass the control to the
> passenger-in-charge.  If boat is empty, it will look for passengers
> using PF and go there.
> 
> Some of the calling code has been changed too.
> 
> Problems / todos:
> 1. Units do not agree on a rendez-vous point.  Currently units will wait
> on the shore for the boats to come, which is ok unless there are railroads.
> 
> 2. Settlers have different handling, they are done in the end of turn,
> so some hacks had to be introduced.  We should change settlers imo.
> 
> 3. Building code is not too great -- currently there are usually more
> boats than passengers.
> 
> 4. We need to keep track of number of passengers and free boats. 
> Currently boats seek passengers which is suboptimal when there are many
> boats.  If there are no passengers at all, boats might as well not bother.
> 
> I would like to do 4 before the code is committed, possibly 3 as well.
> And move gothere function into aitools.
> 
> Best wishes,
> G.

? ttt.gz
Index: ai/aitools.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aitools.c,v
retrieving revision 1.92
diff -u -r1.92 aitools.c
--- ai/aitools.c        2003/09/28 01:04:59     1.92
+++ ai/aitools.c        2003/09/28 20:22:32
@@ -51,6 +51,18 @@
 
 #include "aitools.h"
 
+/* Boat register.  Ideally we want it on a per-player basis, but
+ * it is hard to ensure consistency of the numbers */
+struct {
+  int gameturn;              /* For validity checks */
+  int playerid;              /* For validity checks */
+  int psngrs;                /* Number of potential passengers */
+  int boats;                 /* Number of seeking boats */
+} boat_reg;
+
+/* Forward declarations */
+static int stack_attack_value(int x, int y);
+
 /**************************************************************************
   Amortize a want modified by the shields (build_cost) we risk losing.
   We add the build time of the unit(s) we risk to amortize delay.  The
@@ -137,6 +149,7 @@
 /**************************************************************************
   This will eventually become the ferry-enabled goto. For now, it just
   wraps ai_unit_goto()
+  TODO: Kill me.  Use ai_gothere instead.
 **************************************************************************/
 bool ai_unit_gothere(struct unit *punit)
 {
@@ -148,6 +161,271 @@
   }
 }
 
+/****************************************************************************
+  Check the boat register for validity.  If not valid, reset.
+****************************************************************************/
+static void br_check(struct player *pplayer)
+{
+  if (game.turn != boat_reg.gameturn 
+      || pplayer->player_no != boat_reg.playerid) {
+    boat_reg.gameturn = game.turn;
+    boat_reg.playerid = pplayer->player_no;
+    boat_reg.psngrs = 0;
+    boat_reg.boats = 0;
+  }
+}
+
+/****************************************************************************
+  Request a boat and enter ourselves into the register.
+****************************************************************************/
+static void br_request_boat(struct unit *punit)
+{
+  punit->ai.ferryboat = FERRY_WANTED;
+  br_check(unit_owner(punit));
+  boat_reg.psngrs++;  
+}
+
+/****************************************************************************
+  Request a passenger and enter ourselves into the register.
+****************************************************************************/
+void br_request_cargo(struct unit *punit)
+{
+  punit->ai.passenger = FERRY_SEEKING;
+  br_check(unit_owner(punit));
+  boat_reg.boats++;  
+}
+
+/****************************************************************************
+  See if there are any passengers wanting a ride anywhere in the world.
+****************************************************************************/
+int br_boat_demand(struct unit *punit)
+{
+  br_check(unit_owner(punit));
+
+  return boat_reg.psngrs - boat_reg.boats;
+}
+
+/****************************************************************************
+  A helper function for ai_gothere.  Estimates the dangers we will
+  be facing at our destination and tries to find/request a bodyguard if 
+  needed.
+****************************************************************************/
+static void ai_gothere_bodyguard(struct unit *punit, int dest_x, int dest_y)
+{
+  int danger;
+  struct city *dcity;
+  struct tile *ptile;
+
+  
+  if (is_barbarian(unit_owner(punit)))  {
+    /* barbarians must have more courage (= less brains) */
+    punit->ai.bodyguard = BODYGUARD_NONE;
+    return;
+  }
+
+  /* Estimate enemy attack power. */
+  danger = stack_attack_value(dest_x, dest_y);
+  if ((dcity = map_get_city(dest_x, dest_y))) {
+    /* Assume enemy will build another defender, add it's attack strength */
+    int d_type = ai_choose_defender_versus(dcity, punit->type);
+    danger += 
+      unittype_att_rating(d_type, do_make_unit_veteran(dcity, d_type), 
+                          SINGLE_MOVE, unit_types[d_type].hp);
+  }
+  danger *= POWER_DIVIDER;
+
+  /* If we are fast, there is less danger. */
+  danger /= (unit_type(punit)->move_rate / SINGLE_MOVE);
+  if (unit_flag(punit, F_IGTER)) {
+    danger /= 1.5;
+  }
+
+  ptile = map_get_tile(punit->x, punit->y);
+  /* We look for the bodyguard where we stand. */
+  if (!unit_list_find(&ptile->units, punit->ai.bodyguard)) {
+    int my_def = (punit->hp * (punit->veteran ? 15 : 10)
+                  * unit_type(punit)->defense_strength);
+    
+    /* FIXME: danger is multiplied by POWER_FACTOR, my_def isn't. */
+    if (danger >= my_def) {
+      UNIT_LOG(LOGLEVEL_BODYGUARD, punit, 
+               "wants a bodyguard, danger=%d, my_def=%d", danger, my_def);
+      punit->ai.bodyguard = BODYGUARD_WANTED;
+    } else {
+      punit->ai.bodyguard = BODYGUARD_NONE;
+    }
+  }
+
+  /* What if we have a bodyguard, but don't need one? */
+}
+
+#define LOGLEVEL_GOTHERE LOG_DEBUG
+/****************************************************************************
+  This is ferry-enabled goto.  Should not normally be used for non-ferried 
+  units (i.e. planes or ships), use ai_unit_goto instead.
+
+  Return values: TRUE if got to or next to our destination, FALSE otherwise. 
+
+  TODO: A big one is rendez-vous points.  When this is implemented, we won't
+  have to be at the coast to ask for a boat to come to us.
+****************************************************************************/
+bool ai_gothere(struct player *pplayer, struct unit *punit,
+                         int dest_x, int dest_y)
+{
+  CHECK_UNIT(punit);
+
+  if (same_pos(dest_x, dest_y, punit->x, punit->y)) {
+    /* Nowhere to go */
+    return TRUE;
+  }
+
+  /* See if we need a bodyguard at our destination */
+  /* FIXME: If bodyguard is _really_ necessary, don't go anywhere */
+  ai_gothere_bodyguard(punit, dest_x, dest_y);
+
+  /* If we can, we will walk, take boat only if necessary */
+  if (punit->transported_by > 0 
+      || !goto_is_sane(punit, dest_x, dest_y, TRUE)) {
+    int boatid = punit->transported_by;
+    struct unit *ferryboat = NULL;
+
+    UNIT_LOG(LOGLEVEL_GOTHERE, punit, "will have to go to (%d,%d) by boat",
+             dest_x, dest_y);
+
+    if (boatid <= 0) {
+      int bx, by;
+      boatid = find_boat(pplayer, &bx, &by, 2);
+    } 
+    ferryboat = find_unit_by_id(boatid);
+
+    if (!ferryboat) {
+      UNIT_LOG(LOGLEVEL_GOTHERE, punit, "No boat found.");
+      if (is_at_coast(punit->x, punit->y)) {
+        br_request_boat(punit);
+      }
+      return FALSE;
+    } else {
+      UNIT_LOG(LOGLEVEL_GOTHERE, punit, "Found boat at (%d,%d)",
+               ferryboat->x, ferryboat->y);
+    }
+
+    punit->ai.ferryboat = boatid;
+    if (!same_pos(punit->x, punit->y, ferryboat->x, ferryboat->y)
+        && (!is_at_coast(punit->x, punit->y) 
+            || is_tiles_adjacent(punit->x, punit->y, 
+                                 ferryboat->x, ferryboat->y))) {
+      /* Go to the boat only if it cannot reach us or it's parked 
+       * next to us.  Otherwise just wait (boats are normally faster) */
+      /* FIXME: agree on a rendez-vous point */
+      /* FIXME: this can lose bodyguard */
+      UNIT_LOG(LOGLEVEL_GOTHERE, punit, "going to boat[%d](%d,%d).", boatid,
+               ferryboat->x, ferryboat->y);
+      if (!ai_unit_goto(punit, ferryboat->x, ferryboat->y)) { 
+        /* Died. */
+        return FALSE;
+      }
+    }
+    
+    if (!same_pos(punit->x, punit->y, ferryboat->x, ferryboat->y)) {
+      /* Didn't get to the boat */
+      if (is_at_coast(punit->x, punit->y)) {
+        /* At least got to the coast, wave to the boats! */
+        UNIT_LOG(LOGLEVEL_GOTHERE, punit, "asking a boat to come nearer");
+        br_request_boat(punit);
+      }
+      return FALSE;
+    }
+
+    /* Check if we are the passenger-in-charge */
+    if (ferryboat->ai.passenger <= 0
+        || ferryboat->ai.passenger == punit->id) {
+      struct tile *ptile;
+      int beach_x, beach_y;     /* Destination for the boat */
+      struct tile *dest_tile = map_get_tile(dest_x, dest_y);
+      struct unit *def;
+
+      UNIT_LOG(LOGLEVEL_GOTHERE, punit, "got boat[%d], going (%d,%d)",
+               ferryboat->id, dest_x, dest_y);
+      handle_unit_activity_request(punit, ACTIVITY_SENTRY);
+      ferryboat->ai.passenger = punit->id;
+
+      /* If the location is not accessible directly from sea
+       * or is defended and we are not marines, we will need a 
+       * landing beach */
+      if (!is_at_coast(dest_x, dest_y)
+          ||((is_non_allied_city_tile(dest_tile, pplayer) 
+              || is_non_allied_unit_tile(dest_tile, pplayer))
+             && !unit_flag(punit, F_MARINES))) {
+        if (!find_beachhead(punit, dest_x, dest_y, &beach_x, &beach_y)) {
+          /* Nowhere to go */
+          return FALSE;
+        }
+        UNIT_LOG(LOGLEVEL_GOTHERE, punit, 
+                 "Found beachhead (%d,%d)", beach_x, beach_y);
+      } else {
+        beach_x = dest_x;
+        beach_y = dest_y;
+      }
+
+      UNIT_LOG(LOGLEVEL_GOTHERE, punit, "All aboard!");
+      set_goto_dest(ferryboat, beach_x, beach_y);
+      set_goto_dest(punit, dest_x, dest_y);
+      /* Sentry the passengers */
+      ptile = map_get_tile(punit->x, punit->y);
+      unit_list_iterate(ptile->units, mypass) {
+        if (mypass->ai.ferryboat == ferryboat->id
+            && punit->owner == mypass->owner) {
+          handle_unit_activity_request(mypass, ACTIVITY_SENTRY);
+          def = unit_list_find(&ptile->units, mypass->ai.bodyguard);
+          if (def) {
+            handle_unit_activity_request(def, ACTIVITY_SENTRY);
+          }
+        }
+      } unit_list_iterate_end;
+
+      if (!ai_unit_goto(ferryboat, beach_x, beach_y)) {
+        /* died */
+        return FALSE;
+      }
+      if (!is_tiles_adjacent(ferryboat->x, ferryboat->y, beach_x, beach_y)
+          && !same_pos(ferryboat->x, ferryboat->y, beach_x, beach_y)) {
+        /* We are in still transit */
+        return FALSE;
+      }
+    } else {
+      /* Waiting for the boss to load and move us */
+      UNIT_LOG(LOGLEVEL_GOTHERE, punit, "Cannot command boat [%d],"
+               " its boss is [%d]", 
+               ferryboat->id, ferryboat->ai.passenger);
+      return FALSE;
+    }
+
+    UNIT_LOG(LOGLEVEL_GOTHERE, punit, "Our boat has arrived");
+    handle_unit_activity_request(punit, ACTIVITY_IDLE);
+  }
+
+  /* Go where we should be going if we can, and are at our destination 
+   * if we are on a ferry */
+  if (goto_is_sane(punit, dest_x, dest_y, TRUE) && punit->moves_left > 0) {
+    set_goto_dest(punit, dest_x, dest_y);
+    UNIT_LOG(LOGLEVEL_GOTHERE, punit, "Walking to (%d,%d)", dest_x, dest_y);
+    if (!ai_unit_goto(punit, dest_x, dest_y)) {
+      /* died */
+      return FALSE;
+    }
+    /* liable to bump into someone that will kill us.  Should avoid? */
+  } else {
+    UNIT_LOG(LOGLEVEL_GOTHERE, punit, "Not moving");
+    return FALSE;
+  }
+  
+  /* Dead unit shouldn't reach this point */
+  CHECK_UNIT(punit);
+  
+  return (same_pos(punit->x, punit->y, dest_x, dest_y) 
+          || is_tiles_adjacent(punit->x, punit->y, dest_x, dest_y));
+}
+
 /**************************************************************************
   Go to specified destination but do not disturb existing role or activity
   and do not clear the role's destination. Return FALSE iff we died.
@@ -482,6 +760,19 @@
   }
   
   return victim_cost;
+}
+
+/********************************************************************** 
+  Total attack power of the units in this square.
+***********************************************************************/
+static int stack_attack_value(int x, int y)
+{
+  int val = 0;
+  struct tile *ptile = map_get_tile(x, y);
+  unit_list_iterate(ptile->units, aunit)
+    val += unit_att_rating(aunit);
+  unit_list_iterate_end;
+  return val;
 }
 
 /**************************************************************************
Index: ai/aitools.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aitools.h,v
retrieving revision 1.39
diff -u -r1.39 aitools.h
--- ai/aitools.h        2003/09/27 11:22:40     1.39
+++ ai/aitools.h        2003/09/28 20:22:32
@@ -38,15 +38,24 @@
   BODYGUARD_NONE
 };
 
+#define FERRY_WANTED      -1      /* For passengers in need of a boat */
+#define FERRY_SEEKING     -1      /* For boats looking for a passenger */
+
+void br_request_cargo(struct unit *punit);
+int br_boat_demand(struct unit *punit);
+
 int military_amortize(struct player *pplayer, struct city *pcity, 
                       int value, int delay, int build_cost);
 int stack_cost(struct unit *pdef);
 
 bool ai_unit_execute_path(struct unit *punit, struct pf_path *path);
 bool ai_unit_gothere(struct unit *punit);
+bool ai_gothere(struct player *pplayer, struct unit *punit, 
+                int dest_x, int dest_y);
 bool ai_unit_goto(struct unit *punit, int x, int y);
-void ai_unit_new_role(struct unit *punit, enum ai_unit_task task, int x, int 
y);
 
+void ai_unit_new_role(struct unit *punit, enum ai_unit_task task, 
+                      int x, int y);
 bool ai_unit_make_homecity(struct unit *punit, struct city *pcity);
 bool ai_unit_attack(struct unit *punit, int x, int y);
 bool ai_unit_move(struct unit *punit, int x, int y);
Index: ai/aiunit.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aiunit.c,v
retrieving revision 1.297
diff -u -r1.297 aiunit.c
--- ai/aiunit.c 2003/09/28 09:33:21     1.297
+++ ai/aiunit.c 2003/09/28 20:22:33
@@ -59,8 +59,10 @@
 
 #include "aiunit.h"
 
+#define FERRY_WANTED      -1
 #define LOGLEVEL_RECOVERY LOG_DEBUG
 
+static void ai_manage_unit(struct player *pplayer, struct unit *punit);
 static void ai_manage_military(struct player *pplayer,struct unit *punit);
 static void ai_manage_caravan(struct player *pplayer, struct unit *punit);
 static void ai_manage_barbarian_leader(struct player *pplayer,
@@ -804,19 +806,6 @@
   return v * v;
 }
 
-/********************************************************************** 
-  Total attack power of the units in this square.
-***********************************************************************/
-static int stack_attack_value(int x, int y)
-{
-  int val = 0;
-  struct tile *ptile = map_get_tile(x, y);
-  unit_list_iterate(ptile->units, aunit)
-    val += unit_att_rating(aunit);
-  unit_list_iterate_end;
-  return val;
-}
-
 /**************************************************************************
 Compute how much we want to kill certain victim we've chosen, counted in
 SHIELDs.
@@ -1238,174 +1227,6 @@
   *y = best_y;
 }
 
-/**************************************************************************
-  A helper function for ai_military_gothere.  Estimates the dangers we will
-  be facing a out destination and tries to find/request a bodyguard if 
-  needed.
-**************************************************************************/
-static void ai_gothere_bodyguard(struct unit *punit, int dest_x, int dest_y)
-{
-  int danger;
-  struct city *dcity;
-  struct tile *ptile;
-
-  
-  if (is_barbarian(unit_owner(punit)))  {
-    /* barbarians must have more courage (= less brains) */
-    punit->ai.bodyguard = BODYGUARD_NONE;
-    return;
-  }
-
-  /* Estimate enemy attack power. */
-  danger = stack_attack_value(dest_x, dest_y);
-  if ((dcity = map_get_city(dest_x, dest_y))) {
-    /* Assume enemy will build another defender, add it's attack strength */
-    int d_type = ai_choose_defender_versus(dcity, punit->type);
-    danger += 
-      unittype_att_rating(d_type, do_make_unit_veteran(dcity, d_type), 
-                          SINGLE_MOVE, unit_types[d_type].hp);
-  }
-  danger *= POWER_DIVIDER;
-
-  /* If we are fast, there is less danger. */
-  danger /= (unit_type(punit)->move_rate / SINGLE_MOVE);
-  if (unit_flag(punit, F_IGTER)) {
-    danger /= 1.5;
-  }
-
-  ptile = map_get_tile(punit->x, punit->y);
-  /* We look for the bodyguard where we stand. */
-  if (!unit_list_find(&ptile->units, punit->ai.bodyguard)) {
-    int my_def = (punit->hp * (punit->veteran ? 15 : 10)
-                  * unit_type(punit)->defense_strength);
-    
-    /* FIXME: danger is multiplied by POWER_FACTOR, my_def isn't. */
-    if (danger >= my_def) {
-      UNIT_LOG(LOGLEVEL_BODYGUARD, punit, 
-               "wants a bodyguard, danger=%d, my_def=%d", danger, my_def);
-      punit->ai.bodyguard = BODYGUARD_WANTED;
-    } else {
-      punit->ai.bodyguard = BODYGUARD_NONE;
-    }
-  }
-
-  /* What if we have a bodyguard, but don't need one? */
-}
-
-/**************************************************************************
-  Return values: FALSE if died or stuck, TRUE otherwise. (This function
-  is not server-side autoattack safe.)
-**************************************************************************/
-static bool ai_military_gothere(struct player *pplayer, struct unit *punit,
-                                int dest_x, int dest_y)
-{
-  int id, x, y, boatid = 0, bx = -1, by = -1;
-  struct unit *ferryboat = NULL;
-  struct unit *def;
-  struct tile *ptile;
-  bool boat_arrived;
-
-  CHECK_UNIT(punit);
-
-  id = punit->id; x = punit->x; y = punit->y;
-
-  if (same_pos(dest_x, dest_y, x, y)) {
-    /* Nowhere to go */
-    return FALSE;
-  }
-
-  if (is_ground_unit(punit)) { 
-    boatid = find_boat(pplayer, &bx, &by, 2);
-    /* NB: ferryboat is set only if the boat is where we are */
-    ferryboat = unit_list_find(&(map_get_tile(x, y)->units), boatid);
-  }
-
-  /* See if we need a bodyguard at our destination */
-  ai_gothere_bodyguard(punit, dest_x, dest_y);
-  
-  if (!goto_is_sane(punit, dest_x, dest_y, TRUE) 
-      || (ferryboat && goto_is_sane(ferryboat, dest_x, dest_y, TRUE))) {
-    punit->ai.ferryboat = boatid;
-    UNIT_LOG(LOG_DEBUG, punit, "Looking for boat[%d].", boatid);
-    if (boatid > 0 && !same_pos(x, y, bx, by)) {
-      /* Go to the boat */
-      /* FIXME: this can lose bodyguard */
-      if (!ai_unit_goto(punit, bx, by)) {
-        /* Died. */
-        return FALSE;
-      }
-    }
-    ptile = map_get_tile(punit->x, punit->y);
-    ferryboat = unit_list_find(&ptile->units, punit->ai.ferryboat);
-
-    if (ferryboat && (ferryboat->ai.passenger == 0 
-                      || ferryboat->ai.passenger == punit->id)) {
-      int boat_x, boat_y;
-
-      UNIT_LOG(LOG_DEBUG, punit, "Found boat[%d], going (%d,%d)",
-               ferryboat->id, dest_x, dest_y);
-      handle_unit_activity_request(punit, ACTIVITY_SENTRY);
-      ferryboat->ai.passenger = punit->id;
-
-      /* Last ingredient: a beachhead. */
-      if (find_beachhead(punit, dest_x, dest_y, &boat_x, &boat_y)) {
-        UNIT_LOG(LOG_DEBUG, punit, "Found beachhead (%d,%d), all aboard", 
-                 boat_x, boat_y);
-       set_goto_dest(ferryboat, boat_x, boat_y);
-       set_goto_dest(punit, dest_x, dest_y);
-        unit_list_iterate(ptile->units, mypass) {
-          if (mypass->ai.ferryboat == ferryboat->id
-              && punit->owner == mypass->owner) {
-            handle_unit_activity_request(mypass, ACTIVITY_SENTRY);
-            def = unit_list_find(&ptile->units, mypass->ai.bodyguard);
-            if (def) {
-              handle_unit_activity_request(def, ACTIVITY_SENTRY);
-            }
-          }
-        } unit_list_iterate_end; /* passengers are safely stowed away */
-        if (!ai_unit_goto(ferryboat, dest_x, dest_y)) {
-          return FALSE; /* died */
-        }
-      }
-    } 
-  }
-
-  if (ferryboat && is_goto_dest_set(ferryboat)) {
-    /* we are on a ferry! did we arrive? */
-    boat_arrived = same_pos(ferryboat->x, ferryboat->y,
-                            goto_dest_x(ferryboat), goto_dest_y(ferryboat))
-      || is_tiles_adjacent(ferryboat->x, ferryboat->y,
-                           goto_dest_x(ferryboat), goto_dest_y(ferryboat));
-  } else {
-    boat_arrived = FALSE;
-  }
-
-  if (boat_arrived) {
-    handle_unit_activity_request(punit, ACTIVITY_IDLE);
-    UNIT_LOG(LOG_DEBUG, punit, "Our boat has arrived");
-  }
-
-  /* Go where we should be going if we can, and are at our destination 
-   * if we are on a ferry */
-  if (goto_is_sane(punit, dest_x, dest_y, TRUE) && punit->moves_left > 0
-      && (!ferryboat || boat_arrived)) {
-    set_goto_dest(punit, dest_x, dest_y);
-    UNIT_LOG(LOG_DEBUG, punit, "Attempt to walk to (%d,%d)", dest_x, dest_y);
-    if (!ai_unit_goto(punit, dest_x, dest_y)) {
-      /* died */
-      return FALSE;
-    }
-    /* liable to bump into someone that will kill us.  Should avoid? */
-  } else {
-    UNIT_LOG(LOG_DEBUG, punit, "Not moving");
-  }
-  
-  /* Dead unit shouldn't reach this point */
-  CHECK_UNIT(punit);
-  
-  return (!same_pos(punit->x, punit->y, x, y));
-}
-
 /*************************************************************************
   Does the unit with the id given have the flag L_DEFEND_GOOD?
 **************************************************************************/
@@ -2249,32 +2070,28 @@
     /* Then find enemies the hard way */
     find_something_to_kill(pplayer, punit, &dest_x, &dest_y);
     if (!same_pos(punit->x, punit->y, dest_x, dest_y)) {
-     int repeat;
-
-     for(repeat = 0; repeat < 2; repeat++) {
 
       if (!is_tiles_adjacent(punit->x, punit->y, dest_x, dest_y)
-          || !can_unit_attack_tile(punit, dest_x, dest_y)
-          || (could_unit_move_to_tile(punit, dest_x, dest_y) == 0)) {
-        /* Can't attack or move usually means we are adjacent but
-         * on a ferry. This fixes the problem (usually). */
+          || !can_unit_attack_tile(punit, dest_x, dest_y)) {
+        /* Adjacent and can't attack usually means we are not marines
+         * and on a ferry. This fixes the problem (usually). */
         UNIT_LOG(LOG_DEBUG, punit, "mil att gothere -> (%d,%d)", 
                  dest_x, dest_y);
-        if (!ai_military_gothere(pplayer, punit, dest_x, dest_y)) {
+        if (!ai_gothere(pplayer, punit, dest_x, dest_y)) {
           /* Died or got stuck */
           return;
         }
-      } else {
-        /* Close combat. fstk sometimes want us to attack an adjacent
-         * enemy that rampage wouldn't */
-        UNIT_LOG(LOG_DEBUG, punit, "mil att bash -> %d, %d", dest_x, dest_y);
-        if (!ai_unit_attack(punit, dest_x, dest_y)) {
-          /* Died */
+        /* Must be adjacent now. */
+      }
+      
+      /* Close combat. fstk sometimes want us to attack an adjacent
+       * enemy that rampage wouldn't */
+      UNIT_LOG(LOG_DEBUG, punit, "mil att bash -> %d, %d", dest_x, dest_y);
+      if (!ai_unit_attack(punit, dest_x, dest_y)) {
+        /* Died */
           return;
-        }
       }
 
-     } /* for-loop */
     } else {
       /* FIXME: This happens a bit too often! */
       UNIT_LOG(LOG_DEBUG, punit, "fstk didn't find us a worthy target!");
@@ -2304,14 +2121,14 @@
     if ((pc = dist_nearest_city(pplayer, punit->x, punit->y, FALSE, TRUE))) {
       if (!is_ocean(map_get_terrain(punit->x, punit->y))) {
         UNIT_LOG(LOG_DEBUG, punit, "Barbarian marching to conquer %s", 
pc->name);
-        (void) ai_military_gothere(pplayer, punit, pc->x, pc->y);
+        (void) ai_gothere(pplayer, punit, pc->x, pc->y);
       } else {
         /* sometimes find_beachhead is not enough */
         if (!find_beachhead(punit, pc->x, pc->y, &fx, &fy)) {
           find_city_beach(pc, punit, &fx, &fy);
         }
         UNIT_LOG(LOG_DEBUG, punit, "Barbarian sailing to %s", pc->name);
-        (void) ai_military_gothere(pplayer, punit, fx, fy);
+        (void) ai_gothere(pplayer, punit, fx, fy);
       }
     }
   }
@@ -2406,164 +2223,190 @@
   }
 }
 
-/**************************************************************************
-This seems to manage the ferryboat. When it carries units on their way
-to invade something, it goes there. If it carries other units, it returns home.
-When empty, it tries to find some units to carry or goes home or explores.
-Military units handled by ai_manage_military()
-**************************************************************************/
+#define LOGLEVEL_FERRY LOG_DEBUG
+/****************************************************************************
+  A helper for ai_manage_ferryboat.  Finds a passenger for the ferry.
+  Potential passengers signal the boats by setting their ai.ferry field to
+  FERRY_WANTED.
+
+  TODO: lift the path off the map
+****************************************************************************/
+static bool ai_ferry_findcargo(struct unit *punit)
+{
+  /* Path-finding stuff */
+  struct pf_map *map;
+  struct pf_parameter parameter;
+  
+  UNIT_LOG(LOGLEVEL_FERRY, punit, "Ferryboat is looking for cargo.");
+
+  pft_fill_default_parameter(&parameter);
+  pft_fill_unit_overlap_param(&parameter, punit);
+  /* We are looking for our units, no need to look into the unknown */
+  parameter.get_TB = no_fights_or_unknown;
+  parameter.omniscience = FALSE;
+  
+  map = pf_create_map(&parameter);
+  while (pf_next(map)) {
+    struct pf_position pos;
+    
+    pf_next_get_position(map, &pos);
+    
+    unit_list_iterate(map_get_tile(pos.x, pos.y)->units, aunit) {
+      if (aunit->ai.ferryboat == FERRY_WANTED) {
+        UNIT_LOG(LOGLEVEL_FERRY, punit, 
+                 "Found a potential cargo %s[%d](%d,%d), going there",
+                 unit_type(aunit)->name, aunit->id, aunit->x, aunit->y);
+        set_goto_dest(punit, aunit->x, aunit->y);
+        /* Exchange the phone numbers */
+        aunit->ai.ferryboat = punit->id;
+        punit->ai.passenger = aunit->id;
+        pf_destroy_map(map);
+        return TRUE;
+      }
+    } unit_list_iterate_end;
+  }
+  
+  pf_destroy_map(map);
+  return FALSE;
+}
+
+/****************************************************************************
+  It's about 12 feet square and has a capacity of almost 1000 pounds.
+  It is well constructed of teak, and looks seaworthy.
+
+  Manage ferryboat.  If there is a passenger-in-charge, we let it drive the 
+  boat.  If there isn't, appoint one from those we have on board.
+
+  If there is no one aboard, look for potential cargo.  If none found, 
+  explore and then go to the nearest port.
+****************************************************************************/
 static void ai_manage_ferryboat(struct player *pplayer, struct unit *punit)
-{ /* It's about 12 feet square and has a capacity of almost 1000 pounds.
-     It is well constructed of teak, and looks seaworthy. */
+{
   struct city *pcity;
-  struct city *port = NULL;
-  struct unit *bodyguard = NULL;
-  struct unit_type *punittype = get_unit_type(punit->type);
-  int best = 4 * punittype->move_rate, x = punit->x, y = punit->y;
-  int n = 0, p = 0;
+  struct tile *ptile = map_get_tile(punit->x, punit->y);
 
   CHECK_UNIT(punit);
-
-  if (!unit_list_find(&map_get_tile(punit->x, punit->y)->units, 
punit->ai.passenger)) {
-    punit->ai.passenger = 0;
-  }
-
-  unit_list_iterate(map_get_tile(punit->x, punit->y)->units, aunit)
-    if (punit->owner != aunit->owner) {
-      continue;
-    }
-    if (aunit->ai.ferryboat == punit->id) {
-      if (punit->ai.passenger == 0) {
-        punit->ai.passenger = aunit->id; /* oops */
-      }
-      if (is_military_unit(aunit) && punit->ai.bodyguard == BODYGUARD_NONE) {
-        /* Acquire some protection as we deliver an invasion army */
-        UNIT_LOG(LOG_DEBUG, punit, "shout out for a bodyguard");
-        punit->ai.bodyguard = BODYGUARD_WANTED;
-      }
-      p++;
-      bodyguard = unit_list_find(&map_get_tile(punit->x, punit->y)->units, 
aunit->ai.bodyguard);
-      if (is_goto_dest_set(aunit)) { /* HACK */
-       pcity = map_get_city(goto_dest_x(aunit), goto_dest_y(aunit));
-      } else {
-       pcity = NULL;
-      }
-      if (aunit->ai.bodyguard == BODYGUARD_NONE || bodyguard ||
-         (pcity && pcity->ai.invasion >= 2)) {
-       if (pcity) {
-         UNIT_LOG(LOG_DEBUG, punit, "Ferrying to %s to %s, invasion = %d, body 
= %d",
-                 unit_name(aunit->type), pcity->name,
-                 pcity->ai.invasion, aunit->ai.bodyguard);
-       }
-        n++;
-        handle_unit_activity_request(aunit, ACTIVITY_SENTRY);
-        if (bodyguard) {
-          handle_unit_activity_request(bodyguard, ACTIVITY_SENTRY);
-        }
-      }
-    }
-  unit_list_iterate_end;
 
-  /* we try to recover hitpoints if we are in a city, before we leave */
-  if (punit->hp < punittype->hp 
+  /* Try to recover hitpoints if we are in a city, before we do anything */
+  if (punit->hp < unit_type(punit)->hp 
       && (pcity = map_get_city(punit->x, punit->y))) {
-    /* Don't do anything, just wait in the city */
-    UNIT_LOG(LOG_DEBUG, punit, "waiting in %s to recover hitpoints "
-            "before ferrying", pcity->name);
+    UNIT_LOG(LOGLEVEL_FERRY, punit, "waiting in %s to recover hitpoints", 
+             pcity->name);
     return;
   }
 
-  if (p != 0) {
-    UNIT_LOG(LOG_DEBUG, punit, "in manage_ferryboat p=%d, n=%d", p, n);
-    if (is_goto_dest_set(punit) && punit->moves_left > 0 && n != 0) {
-      (void) ai_unit_gothere(punit);
-    } else if (n == 0 && !map_get_city(punit->x, punit->y)) { /* rest in a 
city, for unhap */
-      port = find_nearest_safe_city(punit);
-      if (port && !ai_unit_goto(punit, port->x, port->y)) {
-        return; /* oops! */
-      }
-      send_unit_info(pplayer, punit); /* to get the crosshairs right -- Syela 
*/
-    } else {
-      UNIT_LOG(LOG_DEBUG, punit, "Ferryboat %d@(%d,%d) stalling.",
-                   punit->id, punit->x, punit->y);
-      if(is_barbarian(pplayer)) /* just in case */
-        (void) ai_manage_explorer(punit);
-    }
+  /* Check if we are an empty barbarian boat and so not needed */
+  if (is_barbarian(pplayer) 
+      && unit_list_size(&ptile->units) < 2 ) {
+    wipe_unit(punit);
     return;
   }
 
-  /* check if barbarian boat is empty and so not needed - the crew has landed 
*/
-  if( is_barbarian(pplayer) && unit_list_size(&map_get_tile(punit->x, 
punit->y)->units)<2 ) {
-    wipe_unit(punit);
-    return;
+  /* Do we have the passenger-in-charge on board? */
+  if (punit->ai.passenger > 0 
+      && !unit_list_find(&ptile->units, punit->ai.passenger)) {
+    UNIT_LOG(LOGLEVEL_FERRY, punit, "lost psngr-in-charge [%d], resetting",
+             punit->ai.passenger);
+    punit->ai.passenger = 0;
   }
 
-  /* ok, not carrying anyone, even the ferryman */
-  punit->ai.passenger = 0;
-  UNIT_LOG(LOG_DEBUG, punit, "Ferryboat is lonely.");
-  handle_unit_activity_request(punit, ACTIVITY_IDLE);
-
-  /* Release bodyguard and let it roam */
-  ai_unit_new_role(punit, AIUNIT_NONE, -1, -1);
-  if (bodyguard) {
-    ai_military_attack(pplayer, bodyguard);
+  if (punit->ai.passenger <= 0) {
+    struct unit *bodyguard = NULL;
+    
+    /* Try to select passanger-in-charge from among their passengers */
+    unit_list_iterate(ptile->units, aunit) {
+      if (aunit->ai.ferryboat != punit->id) {
+        continue;
+      }
+      
+      if (aunit->ai.ai_role != AIUNIT_ESCORT) {
+        /* Bodyguards shouldn't be in charge of boats... */
+        UNIT_LOG(LOGLEVEL_FERRY, punit, 
+                 "appointed %s[%d] our psngr-in-charge",
+                 unit_type(aunit)->name, aunit->id);
+        punit->ai.passenger = aunit->id;
+        break;
+      } else {
+        bodyguard = aunit;
+      }
+    } unit_list_iterate_end;
+    
+    if (punit->ai.passenger <= 0 && bodyguard) {
+      UNIT_LOG(LOGLEVEL_FERRY, punit, 
+               "has to take %s[%d] as our psngr-in-charge",
+               unit_type(bodyguard)->name, bodyguard->id);
+      punit->ai.passenger = bodyguard->id;
+    }
   }
 
-  if (IS_ATTACKER(punit)) {
-    if (punit->moves_left > 0) ai_manage_military(pplayer, punit);
-    return;
-  } /* AI used to build frigates to attack and then use them as ferries -- 
Syela */
+  if (punit->ai.passenger > 0) {
+    int bossid = punit->ai.passenger;    /* For reference */
+    struct unit *boss = find_unit_by_id(bossid);
+    int id = punit->id;                  /* To check if survived */
+    int moves_left = punit->moves_left;  /* Loop prevention */
+    
+    assert(boss);
 
-  /*** Find work ***/
-  CHECK_UNIT(punit);
+    if (unit_flag(boss, F_SETTLERS) || unit_flag(boss, F_CITIES)) {
+      /* Temporary hack: settlers all go in the end, forcing them 
+       * earlier might mean uninitialised cache, so just wait for them */
+      return;
+    }
 
-  generate_warmap(map_get_city(punit->x, punit->y), punit);
-  p = 0; /* yes, I know it's already zero. -- Syela */
-  best = 9999;
-  x = -1; y = -1;
-  unit_list_iterate(pplayer->units, aunit) {
-    if (aunit->ai.ferryboat != 0
-       && WARMAP_SEACOST(aunit->x, aunit->y) < best
-       && ground_unit_transporter_capacity(aunit->x, aunit->y, pplayer) <= 0
-        && is_at_coast(aunit->x, aunit->y)) {
-      UNIT_LOG(LOG_DEBUG, punit, "Found a potential pickup %d@(%d, %d)",
-                   aunit->id, aunit->x, aunit->y);
-      x = aunit->x;
-      y = aunit->y;
-      best = WARMAP_SEACOST(x, y);
-    }
-    if (is_sailing_unit(aunit)
-       && is_ocean(map_get_terrain(aunit->x, aunit->y))) {
-      p++;
+    UNIT_LOG(LOGLEVEL_FERRY, punit, "passing control to %s[%d]",
+             unit_type(boss)->name, bossid);
+    ai_manage_unit(pplayer, boss);
+    
+    if (!find_unit_by_id(id) || punit->moves_left < moves_left) {
+      return;
     }
-  } unit_list_iterate_end;
-  if (best < 4 * unit_type(punit)->move_rate) {
-    /* Pickup is within 4 turns to grab, so move it! */
-    set_goto_dest(punit, x, y);
-    UNIT_LOG(LOG_DEBUG, punit, "Found a friend and going to him @(%d, %d)",
-             x, y);
-    (void) ai_unit_gothere(punit);
-    return;
-  }
+    /* We are alive and didn't spend any moves.  We are stuck! 
+     * NB: it can be that punit->ai.passenger has changed by now,
+     * for example if the boss has landed */
+    UNIT_LOG(LOGLEVEL_FERRY, punit, "taking control back from [%d]", 
+             bossid);
 
-  /* do cool stuff here */
-  CHECK_UNIT(punit);
+  } else {
+    /* Not carrying anyone, even the ferryman */
 
-  if (punit->moves_left == 0) return;
-  pcity = find_city_by_id(punit->homecity);
-  if (pcity) {
-    if (!ai_handicap(pplayer, H_TARGETS) ||
-        unit_move_turns(punit, pcity->x, pcity->y) < p) {
-      set_goto_dest(punit, pcity->x, pcity->y);
-      UNIT_LOG(LOG_DEBUG, punit, "No friends.  Going home.");
-      (void) ai_unit_gothere(punit);
+   if (IS_ATTACKER(punit) && punit->moves_left > 0) {
+      /* AI used to build frigates to attack and then use them as ferries 
+       * -- Syela */
+      ai_manage_military(pplayer, punit);
+      return;
+    }
+
+    UNIT_LOG(LOGLEVEL_FERRY, punit, "Ferryboat is not carrying anyone.");
+    br_request_cargo(punit);
+    handle_unit_activity_request(punit, ACTIVITY_IDLE);
+    CHECK_UNIT(punit);
+    
+    /* Only look for passengers if they are in existence.  Remeber we just
+     * registered as a seeking boat so there are (demand + 1) passengers out
+     * there */
+    if (br_boat_demand(punit) >= 0 && ai_ferry_findcargo(punit)) {
+      ai_unit_goto(punit, goto_dest_x(punit), goto_dest_y(punit));
       return;
     }
   }
-  if (is_ocean(map_get_terrain(punit->x, punit->y))) {
-    /* thanks, Tony */
-    (void) ai_manage_explorer(punit);
+    
+  CHECK_UNIT(punit);
+  
+  if (punit->moves_left == 0 
+      || !is_ocean(map_get_terrain(punit->x, punit->y))) {
+    return;
   }
+  
+  (void) ai_manage_explorer(punit);
+  if (punit->moves_left > 0) {
+    struct city *pcity = find_nearest_safe_city(punit);
+    if (pcity) {
+      set_goto_dest(punit, pcity->x, pcity->y);
+      UNIT_LOG(LOGLEVEL_FERRY, punit, "No work, going home");
+      (void) ai_unit_goto(punit, pcity->x, pcity->y);
+    }
+  }
+ 
   return;
 }
 
Index: server/settlers.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/settlers.c,v
retrieving revision 1.173
diff -u -r1.173 settlers.c
--- server/settlers.c   2003/09/23 15:59:05     1.173
+++ server/settlers.c   2003/09/28 20:22:33
@@ -54,6 +54,8 @@
 BV_DEFINE(enemy_mask, MAX_NUM_PLAYERS + MAX_NUM_BARBARIANS);
 static enemy_mask enemies[MAX_NUM_PLAYERS + MAX_NUM_BARBARIANS];
 
+static void auto_settler_findwork(struct player *pplayer, 
+                                  struct unit *punit);
 static void auto_settlers_player(struct player *pplayer); 
 static bool is_already_assigned(struct unit *myunit, struct player *pplayer,
                                int x, int y);
@@ -1188,75 +1190,6 @@
 }
 
 /**************************************************************************
-  Handles GOTO for settlers. Only ever used in auto_settler_findwork 
-  below.
-**************************************************************************/
-static bool ai_gothere(struct unit *punit, int gx, int gy, struct unit 
*ferryboat)
-{
-  struct player *pplayer = unit_owner(punit);
-
-  if (same_pos(gx, gy, punit->x, punit->y)) {
-    return TRUE;
-  }
-
-  /*
-   * Go by boat if we can't go by foot, or we have already found a boat
-   * (since boat travel is usually faster). 
-   */
-  if (!goto_is_sane(punit, gx, gy, TRUE)
-      || (ferryboat
-          && goto_is_sane(ferryboat, gx, gy, TRUE)
-          && (!is_tiles_adjacent(punit->x, punit->y, gx, gy)
-              || could_unit_move_to_tile(punit, gx, gy) == 0))) {
-    int x, y;
-
-    punit->ai.ferryboat = find_boat(pplayer, &x, &y, 1);
-    /* TODO: check if we found a boat */
-    freelog(LOG_DEBUG, "%d@(%d, %d): Looking for BOAT.",
-           punit->id, punit->x, punit->y);
-    if (!same_pos(x, y, punit->x, punit->y)) {
-      if (!ai_unit_goto(punit, x, y)) {
-        return FALSE; /* died */
-      }
-    }
-    ferryboat = unit_list_find(&(map_get_tile(punit->x, punit->y)->units),
-                               punit->ai.ferryboat);
-    set_goto_dest(punit, gx, gy);
-
-    if (ferryboat && (ferryboat->ai.passenger == 0
-                      || ferryboat->ai.passenger == punit->id)) {
-      UNIT_LOG(LOG_DEBUG, punit, "We have FOUND BOAT %d, ABOARD",
-               ferryboat->id);
-      handle_unit_activity_request(punit, ACTIVITY_SENTRY);
-      ferryboat->ai.passenger = punit->id;
-      set_goto_dest(ferryboat, gx, gy);
-      if (!ai_unit_goto(ferryboat, gx, gy)) {
-        return FALSE; /* died */
-      }
-      handle_unit_activity_request(punit, ACTIVITY_IDLE);
-    } /* need to zero pass & ferryboat at some point. */
-  }
-
-  /*
-   * Now check if we can walk by foot to our destination
-   * (possibly exiting our ferry)
-   */
-  if (goto_is_sane(punit, gx, gy, TRUE)
-      && punit->moves_left > 0
-      && (!ferryboat
-          || (is_tiles_adjacent(punit->x, punit->y, gx, gy)
-              && could_unit_move_to_tile(punit, gx, gy) != 0))) {
-    set_goto_dest(punit, gx, gy);
-    if (!ai_unit_goto(punit, gx, gy)) {
-      return FALSE; /* died */
-    }
-    punit->ai.ferryboat = 0;
-  }
-
-  return TRUE;
-}
-
-/**************************************************************************
   find some work for the settler
 **************************************************************************/
 static void auto_settler_findwork(struct player *pplayer, struct unit *punit)
@@ -1311,8 +1244,8 @@
   }
 
   /* We've now worked out what to do; go to it! */
-  if (!ai_gothere(punit, gx, gy, ferryboat)) {
-    /* died */
+  if (!ai_gothere(pplayer, punit, gx, gy)) {
+    /* Died or got stuck */
     return;
   }
 
@@ -1384,7 +1317,7 @@
   freelog(LOG_DEBUG, "Warmth = %d, game.globalwarming=%d",
          pplayer->ai.warmth, game.globalwarming);
   unit_list_iterate(pplayer->units, punit) {
-    if (punit->ai.control
+    if ((punit->ai.control || pplayer->ai.control)
        && (unit_flag(punit, F_SETTLERS)
            || unit_flag(punit, F_CITIES))) {
       freelog(LOG_DEBUG, "%s's settler at (%d, %d) is ai controlled.",

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