Complete.Org: Mailing Lists: Archives: freeciv-ai: May 2004:
[freeciv-ai] (PR#8777) Find ferry
Home

[freeciv-ai] (PR#8777) Find ferry

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
To: undisclosed-recipients: ;
Subject: [freeciv-ai] (PR#8777) Find ferry
From: "Gregory Berkolaiko" <Gregory.Berkolaiko@xxxxxxxxxxxxx>
Date: Wed, 19 May 2004 11:37:21 -0700
Reply-to: rt@xxxxxxxxxxx

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

Here is the implementation of find_ferry function torn out from the new
settler code.  It is tested using the old settler code and it works, see
settler4.gz.  Now even settlers inland can find a ferry far away in the
seas to take them to a new continent.

New find_ferry should eventually replace all uses of find_boat.
Changes to server/settler.c are for testing only and can be removed.

G.
? ttt.gz
Index: ai/aitools.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aitools.c,v
retrieving revision 1.103
diff -u -r1.103 aitools.c
--- ai/aitools.c        4 May 2004 17:40:25 -0000       1.103
+++ ai/aitools.c        19 May 2004 17:41:46 -0000
@@ -97,8 +97,8 @@
   This is a function to execute paths returned by the path-finding engine.
   It is analogous to goto_route_execute, only much simpler.
 
-  We use ai_unit_attack which means "move if target unoccupied, attack
-  otherwise" and also brings our bodyguard along.
+  Brings our bodyguard along.
+  Returns FALSE only if died.
 *************************************************************************/
 bool ai_unit_execute_path(struct unit *punit, struct pf_path *path)
 {
@@ -107,7 +107,6 @@
   /* We start with i = 1 for i = 0 is our present position */
   for (i = 1; i < path->length; i++) {
     int x = path->positions[i].x, y = path->positions[i].y;
-    bool result;
     int id = punit->id;
 
     /* We use ai_unit_move() for everything but the last step
@@ -115,12 +114,11 @@
      * shows up. Any enemy on the target tile is expected to
      * be our target and any attack there intentional. */
     if (i == path->length - 1) {
-      result = ai_unit_attack(punit, x, y);
+      (void) ai_unit_attack(punit, x, y);
     } else {
-      ai_unit_move(punit, x, y);
-      result = (find_unit_by_id(id) != NULL);
+      (void) ai_unit_move(punit, x, y);
     }
-    if (!result) {
+    if (!find_unit_by_id(id)) {
       /* Died... */
       return FALSE;
     }
@@ -206,7 +204,126 @@
   /* What if we have a bodyguard, but don't need one? */
 }
 
-#define LOGLEVEL_GOTHERE LOG_DEBUG
+/****************************************************************************
+  Combined cost function for a land unit looking for a ferry.  The path 
+  finding first goes over the continent and then into the ocean where we 
+  actually look for ferry.  Thus moves land-to-sea are allowed and moves
+  sea-to-land are not.  A consequence is that we don't get into the cities
+  on other continent, which might station boats.  This defficiency seems to
+  be impossible to fix with the current PF structure, so it has to be
+  accounted for in the actual ferry search function.
+
+  For movements sea-to-sea the cost is collected via the extra cost 
+  call-back.  Doesn't care for enemy/neutral tiles, these should be excluded
+  using a TB call-back.
+****************************************************************************/
+static int combined_land_sea_move(int x, int y, enum direction8 dir,
+                                  int x1, int y1, 
+                                  struct pf_parameter *param)
+{
+  struct tile *src_tile = map_get_tile(x, y);
+  struct tile *tgt_tile = map_get_tile(x1, y1);
+  int move_cost;
+
+  if (is_ocean(tgt_tile->terrain)) {
+    /* Any-to-Sea */
+    move_cost = 0;
+  } else if (src_tile->terrain == T_OCEAN) {
+    /* Sea-to-Land */
+    move_cost = PF_IMPOSSIBLE_MC;
+  } else {
+    /* Land-to-Land */
+    move_cost = src_tile->move_cost[dir];
+  }
+
+  return move_cost;
+}
+
+/****************************************************************************
+  EC callback to account for the cost of sea moves by a ferry hurrying to 
+  pick our unit up.
+****************************************************************************/
+static int sea_move(int x, int y, enum known_type known,
+                    struct pf_parameter *param)
+{
+  if (is_ocean(map_get_tile(x, y)->terrain)) {
+    /* Approximately TURN_FACTOR / average ferry move rate */
+    return SINGLE_MOVE * PF_TURN_FACTOR / 16;
+  } else {
+    return 0;
+  }
+}
+
+/****************************************************************************
+  Proper and real PF function for finding a boat.
+
+  WRANING: Due to the nature of this function and PF (see the comment of 
+  combined_land_sea_move), the path won't lead onto the boat itself.
+
+  FIXME: Actuall check the capacity.
+
+  FIXME: Lift the path off the PF map, otherwise ai_unit_got in ai_gothere 
+  fails to move us (thinks that if the boat is off the coast, goto is 
+  insane -- need to give it a beach instead). 
+****************************************************************************/
+int find_ferry(struct unit *punit, int cap, struct pf_path **path)
+{
+  int best_turns = FC_INFINITY;
+  int best_id = 0;
+  struct pf_parameter param;
+  struct pf_map *search_map;
+
+  pft_fill_unit_parameter(&param, punit);
+  param.turn_mode = TM_WORST_TIME;
+  param.get_TB = no_fights_or_unknown;
+  param.get_EC = sea_move;
+  param.get_MC = combined_land_sea_move;
+
+  search_map = pf_create_map(&param);
+
+  pf_iterator(search_map, pos) {
+    int radius = (is_ocean(map_get_tile(pos.x, pos.y)->terrain) ? 1 : 0);
+
+    if (pos.turn + pos.total_EC/PF_TURN_FACTOR > best_turns) {
+      /* Won't find anything better */
+      /* FIXME: This condiion is somewhat dodgy */
+      break;
+    }
+    
+    square_iterate(pos.x, pos.y, radius, x, y) {
+      struct tile *ptile = map_get_tile(x, y);
+      
+      unit_list_iterate(ptile->units, aunit) {
+        if (is_ground_units_transport(aunit)
+            && (aunit->ai.passenger == FERRY_AVAILABLE
+                || aunit->ai.passenger == punit->id)) {
+          /* Turns for the unit to get to rendezvous pnt */
+          int u_turns = pos.turn;
+          /* Turns for the boat to get to the rendezvous pnt */
+          int f_turns = ((pos.total_EC / PF_TURN_FACTOR * 16 
+                          - aunit->moves_left) 
+                         / unit_type(aunit)->move_rate);
+          int turns = MAX(u_turns, f_turns);
+          
+          if (turns < best_turns) {
+            UNIT_LOG(LOG_DEBUG, punit, 
+                     "Found a potential boat %s[%d](%d,%d)",
+                     unit_type(aunit)->name, aunit->id, aunit->x, aunit->y);
+           if (path) {
+             *path = pf_next_get_path(search_map);
+           }
+            best_turns = turns;
+            best_id = aunit->id;
+          }
+        }
+      } unit_list_iterate_end;
+    } square_iterate_end;
+  } pf_iterator_end;
+  pf_destroy_map(search_map);
+  return(best_id);
+}
+
+#define LOGLEVEL_GOTHERE LOG_NORMAL
 /****************************************************************************
   This is ferry-enabled goto.  Should not normally be used for non-ferried 
   units (i.e. planes or ships), use ai_unit_goto instead.
@@ -220,6 +337,7 @@
                 int dest_x, int dest_y)
 {
   struct unit *bodyguard = find_unit_by_id(punit->ai.bodyguard);
+  struct pf_path *path_to_ferry;
 
   CHECK_UNIT(punit);
 
@@ -242,8 +360,7 @@
              dest_x, dest_y);
 
     if (boatid <= 0) {
-      int bx, by;
-      boatid = find_boat(pplayer, &bx, &by, 2);
+      boatid = find_ferry(punit, 2, &path_to_ferry);
     } 
     ferryboat = find_unit_by_id(boatid);
 
@@ -259,21 +376,28 @@
     }
 
     ai_set_ferry(punit, ferryboat);
-    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))) {
+
+    if (!is_tiles_adjacent(punit->x, punit->y, ferryboat->x, ferryboat->y)
+       && !same_pos(punit->x, punit->y, ferryboat->x, ferryboat->y)
+       && !is_at_coast(punit->x, punit->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 */
+      /* TODO: agree on a rendez-vous point */
       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)) { 
+      /* The path can be amphibious so we will stop at the coast.  
+       * It might not lead _onto_ the boat. */
+      if (!ai_unit_execute_path(punit, path_to_ferry)) { 
         /* Died. */
+       pf_destroy_path(path_to_ferry);
         return FALSE;
       }
     }
+    pf_destroy_path(path_to_ferry);
+
+    if (is_tiles_adjacent(punit->x, punit->y, ferryboat->x, ferryboat->y)) {
+      (void) ai_unit_move(punit, ferryboat->x, ferryboat->y);
+    }
     
     if (!same_pos(punit->x, punit->y, ferryboat->x, ferryboat->y)) {
       /* Didn't get to the boat */
Index: ai/aitools.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aitools.h,v
retrieving revision 1.41
diff -u -r1.41 aitools.h
--- ai/aitools.h        9 Jan 2004 16:59:49 -0000       1.41
+++ ai/aitools.h        19 May 2004 17:41:47 -0000
@@ -78,5 +78,7 @@
 bool ai_wants_no_science(struct player *pplayer);
 
 bool is_player_dangerous(struct player *pplayer, struct player *aplayer);
+int find_ferry(struct unit *punit, int cap, struct pf_path **path);
+
 
 #endif  /* FC__AITOOLS_H */
Index: ai/aiunit.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aiunit.c,v
retrieving revision 1.314
diff -u -r1.314 aiunit.c
--- ai/aiunit.c 4 May 2004 17:40:25 -0000       1.314
+++ ai/aiunit.c 19 May 2004 17:41:58 -0000
@@ -2320,8 +2320,6 @@
 static void ai_manage_ferryboat(struct player *pplayer, struct unit *punit)
 {
   struct city *pcity;
-  int oldbossid = -1;  /* Loop prevention. If boss doesn't want to move,
-                        * neither do we. */
 
   CHECK_UNIT(punit);
 
@@ -2343,17 +2341,20 @@
     /* Do we have the passenger-in-charge on board? */
     struct tile *ptile = map_get_tile(punit->x, punit->y);
 
-    if (punit->ai.passenger > 0 
-        && !unit_list_find(&ptile->units, punit->ai.passenger)) {
-      UNIT_LOG(LOGLEVEL_FERRY, punit, 
-              "lost passenger-in-charge[%d], resetting",
-               punit->ai.passenger);
-      ai_set_passenger(punit, NULL);
-    } else if (oldbossid > 0) {
-      /* Need to look for a new boss */
-      UNIT_LOG(LOGLEVEL_FERRY, punit, "taking control back from [%d]", 
-              oldbossid);
-      ai_set_passenger(punit, NULL);
+    if (punit->ai.passenger > 0) {
+      struct unit *psngr = find_unit_by_id(punit->ai.passenger);
+      
+      /* If the passenger-in-charge is adjacent, we should wait for it to 
+       * board.  We will pass control to it later.
+       * FIXME: A possible side-effect: a boat will linger near a passenger 
+       * which already landed. */
+      if (!psngr 
+         || real_map_distance(punit->x, punit->y, psngr->x, psngr->y) > 1) {
+       UNIT_LOG(LOGLEVEL_FERRY, punit, 
+                "lost passenger-in-charge[%d], resetting",
+                punit->ai.passenger);
+       punit->ai.passenger = 0;
+      }
     }
 
     if (punit->ai.passenger <= 0) {
@@ -2375,11 +2376,6 @@
         }
       } unit_list_iterate_end;
       
-      if (candidate && candidate->id == oldbossid) {
-       /* The boss decided to stay put on the ferry. We aren't moving. */
-       return;
-      }
-
       if (candidate) {
         UNIT_LOG(LOGLEVEL_FERRY, punit, 
                  "appointed %s[%d] our passenger-in-charge",
@@ -2392,11 +2388,11 @@
     }
 
     if (punit->ai.passenger > 0) {
+      int bossid = punit->ai.passenger;    /* Loop prevention */
       struct unit *boss = find_unit_by_id(punit->ai.passenger);
       int id = punit->id;                  /* To check if survived */
 
       assert(boss != NULL);
-      oldbossid = punit->ai.passenger;
 
       if (unit_flag(boss, F_SETTLERS) || unit_flag(boss, F_CITIES)) {
         /* Temporary hack: settlers all go in the end, forcing them 
@@ -2411,6 +2407,11 @@
       if (!find_unit_by_id(id) || punit->moves_left <= 0) {
         return;
       }
+      if (find_unit_by_id(bossid) 
+         && same_pos(punit->x, punit->y, boss->x, boss->y)) {
+       /* The boss decided to stay put on the ferry. We aren't moving. */
+       return;
+      }
     } else {
       /* Cannot select a passenger-in-charge */
       break;
Index: server/settlers.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/settlers.c,v
retrieving revision 1.180
diff -u -r1.180 settlers.c
--- server/settlers.c   26 Feb 2004 03:24:16 -0000      1.180
+++ server/settlers.c   19 May 2004 17:42:14 -0000
@@ -928,11 +928,11 @@
   int food_upkeep        = unit_food_upkeep(punit);
   int food_cost          = unit_foodbox_cost(punit);
 
-  int boatid, bx = 0, by = 0;  /* as returned by find_boat */
+  int boatid, bx = punit->x, by = punit->y;    /* as returned by find_boat */
   enemy_mask my_enemies = enemies[pplayer->player_no]; /* optimalization */
 
   if (pplayer->ai.control)
-    boatid = find_boat(pplayer, &bx, &by, 1); /* might need 2 for bodyguard */
+    boatid = find_ferry(punit, 2, NULL); /* might need 2 for bodyguard */
   else
     boatid = 0;
   *ferryboat = unit_list_find(&(map_get_tile(punit->x, punit->y)->units), 
boatid);
@@ -969,9 +969,7 @@
       } else if (!goto_is_sane(punit, x, y, TRUE) ||
                 WARMAP_COST(x, y) > THRESHOLD * mv_rate) {
        /* for Rome->Carthage */
-       if (!is_ocean_near_tile(x, y)) {
-         mv_cost = 9999;
-       } else if (boatid != 0) {
+       if (boatid != 0) {
          if (punit->id == 0 && mycity->id == boatid) {
            w_virtual = TRUE;
          }
@@ -1028,7 +1026,7 @@
       }
 #endif
 
-      if (map_get_continent(x, y) != ucont && !nav_known && near >= 8) {
+      if (map_get_continent(x, y) != ucont && !nav_known && near >= 10) {
 #ifdef REALLY_DEBUG_THIS
        freelog(LOG_DEBUG,
                "%s (%d, %d) rejected city at (%d, %d) to %d, newv = %d, moves 
= %d" \

Attachment: settle4.gz
Description: GNU Zip compressed data


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