Complete.Org: Mailing Lists: Archives: freeciv-ai: September 2003:
[freeciv-ai] (PR#6199) even huger fstk/ksw/paw/ferry cleanup
Home

[freeciv-ai] (PR#6199) even huger fstk/ksw/paw/ferry cleanup

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
To: undisclosed-recipients: ;
Subject: [freeciv-ai] (PR#6199) even huger fstk/ksw/paw/ferry cleanup
From: "Per I. Mathisen" <per@xxxxxxxxxxx>
Date: Sun, 28 Sep 2003 05:48:00 -0700
Reply-to: rt@xxxxxxxxxxxxxx

This is getting much too big :-(

The problem is that everything here is so bloody interconnected, so that
in order to get the stuff working properly, I had to touch almost
everything in the relevant functions.

You need to apply rrpf1.diff before this patch if you want to test it.

The patch uses the cached pf iterator heavily, but does not depend on it.
I can easily write that out of the patch. Even if the punit->pf framework
is thrown out, all if not lost. But it is really urgent that we soon make
a decision on which it will be.

Ok, so features:
  - fstk hunts down (inland) units on other continents
  - fstk hunts inland cities on other continents
  - fstk has no range restrictions, being 12 tiles away will not save you
  - no more beachhead problems
  - correct estimation of time to inland targets
  - paw, fstk and gothere used to get like a dozen parameters, no longer
  - much cleaner code

And probably some more.

  - Per

Index: ai/advmilitary.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/advmilitary.c,v
retrieving revision 1.154
diff -u -r1.154 advmilitary.c
--- ai/advmilitary.c    2003/09/23 18:43:23     1.154
+++ ai/advmilitary.c    2003/09/28 12:28:23
@@ -24,6 +24,9 @@
 #include "log.h"
 #include "map.h"
 
+#include "path_finding.h"
+#include "pf_tools.h"
+
 #include "citytools.h"
 #include "cityturn.h"
 #include "gotohand.h"          /* warmap has been redeployed */
@@ -716,43 +719,26 @@
   We will also set increase the technology want to get units which could 
   perform the job better.
 
-  I decided this funct wasn't confusing enough, so I made 
-  kill_something_with send it some more variables for it to meddle with. 
-  -- Syela
+  FIXME: We should add cost of building ferry to total cost and
+  time to build it to total time.
 
-  (x,y) is location of the target.
   best_choice is pre-filled with our current choice, we only 
   consider units of the same move_type as best_choice
 **************************************************************************/
-static void process_attacker_want(struct city *pcity,
-                                  int value, Unit_Type_id victim_unit_type,
-                                  bool veteran, int x, int y,
-                                  struct ai_choice *best_choice,
-                                  struct unit *boat, Unit_Type_id boattype)
+static void process_attacker_want(struct city *pcity, int value, 
+                                  struct target *ptarget,
+                                  struct ai_choice *best_choice)
 {
   struct player *pplayer = city_owner(pcity);
   /* The enemy city.  acity == NULL means stray enemy unit */
-  struct city *acity = map_get_city(x, y);
+  struct city *acity = map_get_city(ptarget->x, ptarget->y);
   bool shore = is_ocean_near_tile(pcity->x, pcity->y);
   int orig_move_type = unit_types[best_choice->choice].move_type;
-  int victim_count = 1;
-  int needferry = 0;
   bool unhap = ai_assess_military_unhappiness(pcity,
                                               get_gov_pplayer(pplayer));
 
   assert(orig_move_type == SEA_MOVING || orig_move_type == LAND_MOVING);
-
-  if (orig_move_type == LAND_MOVING && !boat && boattype < U_LAST) {
-    /* cost of ferry */
-    needferry = unit_types[boattype].build_cost;
-  }
   
-  if (!is_stack_vulnerable(x,y)) {
-    /* If it is a city, a fortress or an air base,
-     * we may have to whack it many times */
-    victim_count += unit_list_size(&(map_get_tile(x, y)->units));
-  }
-
   simple_ai_unit_type_iterate (unit_type) {
     Tech_Type_id tech_req = unit_types[unit_type].tech_requirement;
     int move_type = unit_types[unit_type].move_type;
@@ -780,7 +766,6 @@
                         unit_types[unit_type].tech_requirement) / 4
                       / city_list_size(&pplayer->cities);
       int move_rate = unit_types[unit_type].move_rate;
-      int move_time;
       int bcost_balanced = build_cost_balanced(unit_type);
       /* See description of kill_desire() for info about this variables. */
       int bcost = unit_types[unit_type].build_cost;
@@ -801,21 +786,15 @@
         move_rate *= SINGLE_MOVE;
       }
 
-      /* Set the move_time appropriatelly. */
-      if (acity) {
-        move_time = turns_to_enemy_city(unit_type, acity, move_rate,
-                                        (boattype < U_LAST), boat, boattype);
+      /* Estimate strength of the enemy. */
+      if (ptarget->defender) {
+       vuln = unittype_def_rating_sq(unit_type, ptarget->defender->type,
+                                     ptarget->x, ptarget->y, FALSE, 
+                                     ptarget->defender->veteran);
       } else {
-        /* Target is in the field */
-        move_time = turns_to_enemy_unit(unit_type, move_rate, x, y, 
-                                        victim_unit_type);
+        vuln = 0;
       }
 
-      /* Estimate strength of the enemy. */
-      
-      vuln = unittype_def_rating_sq(unit_type, victim_unit_type,
-                                    x, y, FALSE, veteran);
-
       /* Not bothering to s/!vuln/!pdef/ here for the time being. -- Syela
        * (this is noted elsewhere as terrible bug making warships yoyoing) 
        * as the warships will go to enemy cities hoping that the enemy builds 
@@ -829,31 +808,41 @@
         
       } else {
         if (!acity) {
-          desire = kill_desire(value, attack, bcost, vuln, victim_count);
+          desire = kill_desire(value, attack, bcost, vuln, 
+                               ptarget->defender_count);
         } else {
           int city_attack = acity->ai.attack * acity->ai.attack;
 
           /* See aiunit.c:find_something_to_kill() for comments. */
           
           desire = kill_desire(value, attack, (bcost + acity->ai.bcost), vuln,
-                               victim_count);
+                               ptarget->defender_count);
           
           if (value * city_attack > acity->ai.bcost * vuln) {
             desire -= kill_desire(value, city_attack, acity->ai.bcost, vuln,
-                                  victim_count);
+                                  ptarget->defender_count);
           }
         }
       }
-      
+
+      /* Only pass ferryboat below if we NEED to go by boat */
+      (void) target_assign_ferry(ptarget, ptarget->ferry);
+
+      /* Check if we can go there with this unit type, and how fast */
+      if (!target_turns(ptarget, move_rate)) {
+        continue;
+      }
+
       desire -= tech_cost * SHIELD_WEIGHTING;
       /* We can be possibly making some people of our homecity unhappy - then
        * we don't want to travel too far away to our victims. */
       /* TODO: Unify the 'unhap' dependency to some common function. */
-      desire -= move_time * (unhap ? SHIELD_WEIGHTING + 2 * TRADE_WEIGHTING
-                             : SHIELD_WEIGHTING);
+      desire -= ptarget->ETA.turns 
+                * (unhap ? SHIELD_WEIGHTING + 2 * TRADE_WEIGHTING
+                         : SHIELD_WEIGHTING);
 
-      want = military_amortize(pplayer, pcity, desire, MAX(1, move_time),
-                               bcost_balanced + needferry);
+      want = military_amortize(pplayer, pcity, desire, 
+                               MAX(1, ptarget->ETA.turns), bcost_balanced);
       
       if (want > 0) {
         if (tech_dist > 0) {
@@ -863,17 +852,17 @@
           CITY_LOG(LOG_DEBUG, pcity, "wants %s to build %s to punish 
%s@(%d,%d)"
                    " with desire %d", advances[tech_req].name, 
                    unit_name(unit_type), (acity ? acity->name : "enemy"),
-                   x, y, want);
+                   ptarget->x, ptarget->y, want);
 
         } else if (want > best_choice->want) {
           if (can_build_unit(pcity, unit_type)) {
             /* This is a real unit and we really want it */
 
             CITY_LOG(LOG_DEBUG, pcity, "overriding %s(%d) with %s(%d)"
-                     " [attack=%d,value=%d,move_time=%d,vuln=%d,bcost=%d]",
+                     " [attack=%d,value=%d,ETA.turns=%d,vuln=%d,bcost=%d]",
                      unit_name(best_choice->choice), best_choice->want,
-                     unit_name(unit_type), want, attack, value, move_time,
-                     vuln, bcost);
+                     unit_name(unit_type), want, attack, value,
+                     ptarget->ETA.turns, vuln, bcost);
 
             best_choice->choice = unit_type;
             best_choice->want = want;
@@ -911,30 +900,15 @@
 static void kill_something_with(struct player *pplayer, struct city *pcity, 
                                struct unit *myunit, struct ai_choice *choice)
 {
-  struct ai_data *ai = ai_data_get(pplayer);
-  /* Our attack rating (with reinforcements) */
-  int attack;
-  /* Benefit from fighting the target */
-  int benefit;
-  /* Enemy defender type */
-  Unit_Type_id def_type;
-  /* Target coordinates */
-  int x, y;
-  /* Our transport */
   struct unit *ferryboat = NULL;
-  /* Out target */
-  struct city *acity;
-  /* Defender of the target city/tile */
-  struct unit *pdef; 
-  /* Coordinates of the boat */
-  int bx = 0, by = 0;
-  /* Type of the boat (real or a future one) */
-  Unit_Type_id boattype = U_LAST;
-  int boatspeed;
-  bool go_by_boat;
-  /* Is the defender veteran? */
-  bool def_vet;
+  int attack; /* Our attack rating (with reinforcements) */
+  int benefit; /* Benefit from fighting the target */
+  Unit_Type_id def_type; /* Enemy defender type */
+  int boatspeed = 1;
+  bool def_vet; /* Is the defender veteran? */
   struct ai_choice best_choice;
+  struct target mytarget;
+  bool virtualferry = FALSE;
 
   init_choice(&best_choice);
   best_choice.choice = myunit->type;
@@ -954,42 +928,58 @@
     return;
   }
 
-  if (is_ground_unit(myunit)) {
+  if (is_ground_unit(myunit)) { /* find or fake ferry */
+    int bx = 0, by = 0; /* Coordinates of the boat */
     int boatid = find_boat(pplayer, &bx, &by, 2);
-    ferryboat = player_find_unit_by_id(pplayer, boatid);
-  }
+    Unit_Type_id boattype = U_LAST;
 
-  if (ferryboat) {
-    boattype = ferryboat->type;
-  } else {
-    boattype = best_role_unit_for_player(pplayer, L_FERRYBOAT);
-    if (boattype == U_LAST) {
-      /* We pretend that we can have the simplest boat -- to stimulate tech */
-      boattype = get_role_unit(L_FERRYBOAT, 0);
+    if (boatid != 0) {
+      ferryboat = player_find_unit_by_id(pplayer, boatid);
+      /* we need to create a pf_map for it here */
+      if (ferryboat) {
+        struct pf_parameter parm;
+
+        pft_fill_default_parameter(&parm);
+        pft_fill_unit_parameter(&parm, ferryboat);
+        ferryboat->pf = pf_create_map(&parm);
+      }
+    } else if (myunit->id == 0 && is_at_coast(pcity->x, pcity->y)) {
+      /* has no boat, found no boat, so make virtual boat */
+      boattype = best_role_unit_for_player(pplayer, L_FERRYBOAT);
+      if (boattype == U_LAST) {
+        /* We pretend that we can have the simplest boat -- to stimulate tech 
*/
+        boattype = get_role_unit(L_FERRYBOAT, 0);
+      }
+      if (boattype != U_LAST) { /* we really have any boats in this ruleset */
+        ferryboat = make_ai_unit(pplayer, pcity, boattype);
+        virtualferry = TRUE;
+      }
+    }
+    if (ferryboat) {
+      boatspeed = unit_types[boattype].move_rate;
+      myunit->ai.ferryboat = ferryboat->id;
     }
   }
-  boatspeed = unit_types[boattype].move_rate;
 
-  best_choice.want = find_something_to_kill(pplayer, myunit, &x, &y);
+  /* Initialize target */
+  target_set(myunit, &mytarget, 0, 0, NULL);
 
-  acity = map_get_city(x, y);
+  find_something_to_kill(pplayer, &mytarget);
+  best_choice.want = mytarget.want;
 
   if (myunit->id != 0) {
-    freelog(LOG_ERROR, "ERROR: Non-virtual unit in kill_something_with!");
-    return;
+    die("ERROR: Non-virtual unit in kill_something_with!");
   }
-  
+
   attack = unit_att_rating(myunit);
-  if (acity) {
-    attack += acity->ai.attack;
+  if (mytarget.city) {
+    attack += mytarget.city->ai.attack;
   }
   attack *= attack;
-  
-  if (acity) {
+
+  if (mytarget.city) {
     /* Our move rate */
     int move_rate = unit_types[myunit->type].move_rate;
-    /* Distance to target (in turns) */
-    int move_time;
     /* Rating of enemy defender */
     int vuln;
 
@@ -997,22 +987,12 @@
       /* See comment in unit_move_turns */
       move_rate *= 3;
     }
-    
-    if (!HOSTILE_PLAYER(pplayer, ai, city_owner(acity))) {
-      /* Not a valid target */
-      return;
-    }
 
-    go_by_boat = !(goto_is_sane(myunit, acity->x, acity->y, TRUE) 
-                  && WARMAP_COST(x, y) <= (MIN(6, move_rate) * THRESHOLD));
-    move_time = turns_to_enemy_city(myunit->type, acity, move_rate, 
-                                    go_by_boat, ferryboat, boattype);
-
-    def_type = ai_choose_defender_versus(acity, myunit->type);
-    if (move_time > 1) {
-      def_vet = do_make_unit_veteran(acity, def_type);
+    def_type = ai_choose_defender_versus(mytarget.city, myunit->type);
+    if (mytarget.ETA.turns > 1) {
+      def_vet = do_make_unit_veteran(mytarget.city, def_type);
       vuln = unittype_def_rating_sq(myunit->type, def_type,
-                                    x, y, FALSE, def_vet);
+                                    mytarget.x, mytarget.y, FALSE, def_vet);
       benefit = unit_types[def_type].build_cost;
     } else {
       vuln = 0;
@@ -1020,56 +1000,53 @@
       def_vet = FALSE;
     }
 
-    pdef = get_defender(myunit, x, y);
-    if (pdef) {
-      int m = unittype_def_rating_sq(myunit->type, pdef->type,
-                                     x, y, FALSE, pdef->veteran);
+    if (mytarget.defender) {
+      int m = unittype_def_rating_sq(myunit->type, mytarget.defender->type,
+                                     mytarget.x, mytarget.y, FALSE, 
+                                     mytarget.defender->veteran);
       if (vuln < m) {
         vuln = m;
-        benefit = unit_type(pdef)->build_cost;
-        def_vet = pdef->veteran;
-        def_type = pdef->type; 
+        benefit = unit_type(mytarget.defender)->build_cost;
+        def_vet = mytarget.defender->veteran;
+        def_type = mytarget.defender->type; 
       }
     }
-    if (COULD_OCCUPY(myunit) || TEST_BIT(acity->ai.invasion, 0)) {
+    if (COULD_OCCUPY(myunit) || TEST_BIT(mytarget.city->ai.invasion, 0)) {
       /* bonus for getting the city */
       benefit += 40;
     }
 
     /* end dealing with cities */
   } else {
-
-    pdef = get_defender(myunit, x, y);
-    if (!pdef) {
+    if (!mytarget.defender) {
       /* Nobody to attack! */
-      return;
+      goto cleanup;
     }
 
-    benefit = unit_type(pdef)->build_cost;
-    go_by_boat = FALSE;
+    benefit = unit_type(mytarget.defender)->build_cost;
 
-    def_type = pdef->type;
-    def_vet = pdef->veteran;
+    def_type = mytarget.defender->type;
+    def_vet = mytarget.defender->veteran;
     /* end dealing with units */
   }
-  
-  if (!go_by_boat) {
-    process_attacker_want(pcity, benefit, def_type, def_vet, x, y, 
-                          &best_choice, NULL, U_LAST);
-  } else { 
-    process_attacker_want(pcity, benefit, def_type, def_vet, x, y, 
-                          &best_choice, ferryboat, boattype);
-  }
+
+  /* Try to figure out which attacker we want against this enemy */  
+  process_attacker_want(pcity, benefit, &mytarget, &best_choice);
 
   if (best_choice.want > choice->want) {
     /* We want attacker more that what we have selected before */
     copy_if_better_choice(&best_choice, choice);
-    if (go_by_boat && !ferryboat) {
-      ai_choose_ferryboat(pplayer, pcity, choice);
+    if (mytarget.ferry && !ferryboat) {
+      ai_choose_ferryboat(pplayer, pcity, choice); /* build ferry */
     }
     freelog(LOG_DEBUG, "%s has chosen attacker, %s, want=%d",
             pcity->name, unit_types[choice->choice].name, choice->want);
-  } 
+  }
+
+  cleanup:
+  if (virtualferry) {
+    destroy_ai_unit(ferryboat);
+  }
 }
 
 /**********************************************************************
@@ -1086,8 +1063,7 @@
   struct unit *aunit = NULL;
   struct city *acity = NULL;
 
-  virtualunit = create_unit_virtual(pplayer, pcity, unit_type,
-                                    do_make_unit_veteran(pcity, unit_type));
+  virtualunit = make_ai_unit(pplayer, pcity, unit_type);
 
   if (choice->want < 100) {
     int want = look_for_charge(pplayer, virtualunit, &aunit, &acity);
@@ -1097,7 +1073,7 @@
       choice->type = CT_DEFENDER;
     }
   }
-  destroy_unit_virtual(virtualunit);
+  destroy_ai_unit(virtualunit);
 }
 
 /*********************************************************************
@@ -1309,18 +1285,17 @@
      before we mung the seamap */
   unit_type = ai_choose_attacker(pcity, SEA_MOVING);
   if (unit_type >= 0) {
-    virtualunit = create_unit_virtual(pplayer, pcity, unit_type,
-                              player_knows_improvement_tech(pplayer, B_PORT));
+    virtualunit = make_ai_unit(pplayer, pcity, unit_type);
     kill_something_with(pplayer, pcity, virtualunit, choice);
-    destroy_unit_virtual(virtualunit);
+    destroy_ai_unit(virtualunit);
   }
 
   /* Consider a land attacker */
   unit_type = ai_choose_attacker(pcity, LAND_MOVING);
   if (unit_type >= 0) {
-    virtualunit = create_unit_virtual(pplayer, pcity, unit_type, TRUE);
+    virtualunit = make_ai_unit(pplayer, pcity, unit_type);
     kill_something_with(pplayer, pcity, virtualunit, choice);
-    destroy_unit_virtual(virtualunit);
+    destroy_ai_unit(virtualunit);
   }
 
   /* Consider veteran level enhancing buildings before non-urgent units */
Index: ai/aidiplomat.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aidiplomat.c,v
retrieving revision 1.24
diff -u -r1.24 aidiplomat.c
--- ai/aidiplomat.c     2003/08/08 22:11:41     1.24
+++ ai/aidiplomat.c     2003/09/28 12:28:23
@@ -40,7 +40,6 @@
 #include "citytools.h"
 #include "cityturn.h"
 #include "diplomats.h"
-#include "gotohand.h"
 #include "maphand.h"
 #include "settlers.h"
 #include "unithand.h"
@@ -158,7 +157,7 @@
     int want, loss, p_success, p_failure, time_to_dest;
     int gain_incite = 0, gain_theft = 0, gain = 1;
     int incite_cost;
-    struct unit *punit = create_unit_virtual(pplayer, pcity, u, FALSE);
+    struct unit *punit = make_ai_unit(pplayer, pcity, u);
 
     find_city_to_diplomat(pplayer, punit, &acity, &time_to_dest);
 
@@ -232,7 +231,7 @@
       choice->choice = u;
       BV_SET(ai->stats.diplomat_reservations, acity->id);
     }
-    destroy_unit_virtual(punit);
+    destroy_ai_unit(punit);
   }
 }
 
@@ -319,16 +318,20 @@
   bool has_embassy;
   int incite_cost = 0; /* incite cost */
   bool dipldef; /* whether target is protected by diplomats */
+  struct pf_position pos;
 
   assert(punit != NULL);
   *ctarget = NULL;
   *move_dist = -1;
 
-  simple_unit_path_iterator(punit, pos) {
-    struct city *acity = map_get_city(pos.x, pos.y);
+  pf_cache_reset(punit->pf);
+  while (pf_cached_next(punit->pf, &pos)) {
+    struct city *acity;
     struct player *aplayer;
     bool can_incite;
 
+    acity = map_get_city(pos.x, pos.y);
+
     if (!acity) {
       continue;
     }
@@ -362,7 +365,7 @@
       *move_dist = pos.total_MC;
       break;
     }
-  } simple_unit_path_iterator_end;
+  }
 }
 
 /**************************************************************************
@@ -376,6 +379,7 @@
   int best_urgency = 0;
   struct city *ctarget = NULL;
   struct city *pcity = map_get_city(punit->x, punit->y);
+  struct pf_position pos;
 
   if (pcity 
       && count_diplomats_on_tile(pcity->x, pcity->y) == 1
@@ -384,11 +388,13 @@
     return pcity;
   }
 
-  simple_unit_path_iterator(punit, pos) {
-    struct city *acity = map_get_city(pos.x, pos.y);
+  pf_cache_reset(punit->pf);
+  while (pf_cached_next(punit->pf, &pos)) {
+    struct city *acity;
     struct player *aplayer;
     int dipls, urgency;
 
+    acity = map_get_city(pos.x, pos.y);
     if (!acity) {
       continue;
     }
@@ -421,7 +427,7 @@
       /* squelch divide-by-zero */
       best_dist = MAX(pos.total_MC, 1);
     }
-  } simple_unit_path_iterator_end;
+  }
 
   return ctarget;
 }
@@ -437,8 +443,11 @@
   struct packet_diplomat_action packet;
   int gold_avail = pplayer->economic.gold - pplayer->ai.est_upkeep;
   struct ai_data *ai = ai_data_get(pplayer);
+  struct pf_position pos;
 
-  simple_unit_overlap_path_iterator(punit, pos) {
+  update_unit_map(punit, UNIT_MAP_NORMAL);
+  pf_cache_reset(punit->pf);
+  while (pf_cached_next(punit->pf, &pos)) {
     struct tile *ptile = map_get_tile(pos.x, pos.y);
     bool threat = FALSE;
     int newval, bestval = 0, cost;
@@ -496,10 +505,11 @@
     /* Found someone! */
     {
       int x, y;
+      struct pf_path *path;
 
       MAPSTEP(x, y, pos.x, pos.y, DIR_REVERSE(pos.dir_to_here));
-
-      if (!ai_unit_goto(punit, x, y) || punit->moves_left <= 0) {
+      path = pf_get_path(punit->pf, pos.x, pos.y);
+      if (!ai_unit_execute_path(punit, path) || punit->moves_left <= 0) {
         return FALSE;
       }
     }
@@ -521,7 +531,7 @@
                " %d moves left", pos.x, pos.y, punit->moves_left);
       return FALSE;
     }
-  } simple_unit_overlap_path_iterator_end;
+  }
 
   return (punit->moves_left > 0);
 }
@@ -549,6 +559,7 @@
     /* Died or ran out of moves */
     return;
   }
+  update_unit_map(punit, UNIT_MAP_NORMAL);
 
   /* If we are the only diplomat in a threatened city, then stay to defend */
   pcity = map_get_city(punit->x, punit->y); /* we may have moved */
@@ -627,27 +638,23 @@
 
   /* GOTO unless we want to stay */
   if (!same_pos(punit->x, punit->y, ctarget->x, ctarget->y)) {
-    if (!ai_unit_gothere(punit)) {
-      return;
-    } 
-  }
+    struct pf_path *path;
 
-  /* Have we run out of moves? */
-  if (punit->moves_left <= 0) {
-    return;
-  }
-
-  /* Check if we can do something with our destination now. */
-  if (punit->ai.ai_role == AIUNIT_ATTACK) {
-    int dist  = real_map_distance(punit->x, punit->y,
-                                 goto_dest_x(punit), goto_dest_y(punit));
-    UNIT_LOG(LOG_DIPLOMAT, punit, "attack, dist %d to %s (%s goto)",
-             dist, ctarget ? ctarget->name : "(none)", 
-             punit->activity == ACTIVITY_GOTO ? "has" : "no");
-    if (dist == 1) {
-      /* Do our stuff */
-      ai_unit_new_role(punit, AIUNIT_NONE, -1, -1);
-      ai_diplomat_city(punit, ctarget);
+    path = pf_get_path(punit->pf, goto_dest_x(punit), goto_dest_y(punit));
+    if (ai_unit_execute_path(punit, path) && punit->moves_left > 0) {
+      /* Check if we can do something with our destination now. */
+      if (punit->ai.ai_role == AIUNIT_ATTACK) {
+        int dist  = real_map_distance(punit->x, punit->y,
+                                  goto_dest_x(punit), goto_dest_y(punit));
+        UNIT_LOG(LOG_DIPLOMAT, punit, "attack, dist %d to %s (%s goto)",
+                 dist, ctarget ? ctarget->name : "(none)", 
+                 punit->activity == ACTIVITY_GOTO ? "has" : "no");
+        if (dist == 1) {
+          /* Do our stuff */
+          ai_unit_new_role(punit, AIUNIT_NONE, -1, -1);
+          ai_diplomat_city(punit, ctarget);
+        }
+      }
     }
   }
 }
Index: ai/aitools.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aitools.c,v
retrieving revision 1.90
diff -u -r1.90 aitools.c
--- ai/aitools.c        2003/09/27 11:22:40     1.90
+++ ai/aitools.c        2003/09/28 12:28:24
@@ -29,6 +29,7 @@
 #include "player.h"
 #include "shared.h"
 #include "unit.h"
+#include "unittype.h"
 
 #include "path_finding.h"
 #include "pf_tools.h"
@@ -39,6 +40,8 @@
 #include "cityturn.h"
 #include "gotohand.h"
 #include "maphand.h"
+#include "path_finding.h"
+#include "pf_tools.h"
 #include "plrhand.h"
 #include "settlers.h"
 #include "unithand.h"
@@ -52,6 +55,36 @@
 #include "aitools.h"
 
 /**************************************************************************
+  Specialized create unit function that creates a unit with a pf_map
+  and veteran status.
+**************************************************************************/
+struct unit *make_ai_unit(struct player *pplayer, struct city *pcity,
+                          Unit_Type_id unit_type)
+{
+  struct pf_parameter pf_param;
+  struct unit *punit = create_unit_virtual(pplayer, pcity, unit_type,
+                                       do_make_unit_veteran(pcity, unit_type));
+
+  pft_fill_default_parameter(&pf_param);
+  pft_fill_unit_parameter(&pf_param, punit);
+  pf_param.cached = TRUE;
+  punit->pf = pf_create_map(&pf_param);
+  assert(punit->pf);
+
+  return punit;
+}
+
+/**************************************************************************
+  Undo above.
+**************************************************************************/
+struct unit *destroy_ai_unit(struct unit *punit)
+{
+  pf_destroy_map(punit->pf);
+  punit->pf = NULL;
+  destroy_unit_virtual(punit);
+}
+
+/**************************************************************************
   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
   build time is claculated as the build cost divided by the production
@@ -91,6 +124,56 @@
           || pplayer->diplstates[aplayer->player_no].has_reason_to_cancel
           || ai->diplomacy.acceptable_reputation > aplayer->reputation
           || adip->is_allied_with_enemy);
+}
+
+ /**************************************************************************
+  Ensure that our map is sane. If not, recreate. Call this always before 
+  you reuse a punit->pf pf_map for path-execution unless you *know* where 
+  it has been before and that punit has not moved. Note that all 
+  parameters in the map may be reset here.
+**************************************************************************/
+void update_unit_map(struct unit *punit, enum ai_unit_map maptype)
+{
+  struct pf_parameter *param = NULL;
+
+  if (punit->pf) {
+    param = pf_get_parameter(punit->pf);
+    if ((maptype == UNIT_MAP_NORMAL && param->get_TB == no_fights_or_unknown)
+#ifdef TEST_WITHOUT_CACHE
+        || TRUE
+#endif
+        || (maptype == UNIT_MAP_EXPLORER 
+            && param->get_TB != no_fights_or_unknown)) {
+      /* We have the wrong type of map. Recreate it. */
+      param = NULL;
+      pf_destroy_map(punit->pf);
+      punit->pf = NULL;
+    }
+  }
+
+  CHECK_UNIT(punit);
+  if (!param || param->start_x != punit->x || param->start_y != punit->y
+      || param->moves_left_initially != punit->moves_left) {
+    /* We have moved since we created this pf_map. So recreate it. */
+    struct pf_parameter new_param;
+
+    if (punit->pf) {
+      pf_destroy_map(punit->pf);
+    }
+    pft_fill_default_parameter(&new_param);
+    if (is_sailing_unit(punit)) {
+      pft_fill_unit_overlap_param(&new_param, punit);
+    } else {
+      pft_fill_unit_parameter(&new_param, punit);
+    }
+    if (maptype == UNIT_MAP_EXPLORER) {
+      new_param.get_TB = no_fights_or_unknown;
+      /* When exploring, even AI should pretend to not cheat. */
+      new_param.omniscience = FALSE;
+    }
+    new_param.cached = TRUE;
+    punit->pf = pf_create_map(&new_param);
+  }
 }
 
 /*************************************************************************
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 12:28:24
@@ -23,6 +23,10 @@
 struct player;
 struct pf_path;
 
+enum ai_unit_map {
+  UNIT_MAP_NORMAL, UNIT_MAP_EXPLORER
+};
+
 #ifdef DEBUG
 #define CHECK_UNIT(punit)                                        \
  (assert(punit),                                                 \
@@ -42,6 +46,10 @@
                       int value, int delay, int build_cost);
 int stack_cost(struct unit *pdef);
 
+struct unit *make_ai_unit(struct player *pplayer, struct city *pcity,
+                          Unit_Type_id unit_type);
+struct unit *destroy_ai_unit(struct unit *punit);
+void update_unit_map(struct unit *punit, enum ai_unit_map maptype);
 bool ai_unit_execute_path(struct unit *punit, struct pf_path *path);
 bool ai_unit_gothere(struct unit *punit);
 bool ai_unit_goto(struct unit *punit, int x, int y);
Index: ai/aiunit.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aiunit.c,v
retrieving revision 1.296
diff -u -r1.296 aiunit.c
--- ai/aiunit.c 2003/09/27 11:22:40     1.296
+++ ai/aiunit.c 2003/09/28 12:28:24
@@ -60,6 +60,7 @@
 #include "aiunit.h"
 
 #define LOGLEVEL_RECOVERY LOG_DEBUG
+#define LOGLEVEL_FSTK LOG_DEBUG
 
 static void ai_manage_military(struct player *pplayer,struct unit *punit);
 static void ai_manage_caravan(struct player *pplayer, struct unit *punit);
@@ -229,7 +230,7 @@
 }
 
 /**********************************************************************
- Precondition: A warmap must already be generated for the punit.
+ Precondition: A pf_map must already be generated for the punit.
 
  Returns the minimal amount of turns required to reach the given
  destination position. The actual turn at which the unit will
@@ -258,9 +259,11 @@
 **************************************************************************/
 static int unit_move_turns(struct unit *punit, int x, int y)
 {
-  int move_time;
-  int move_rate = unit_type(punit)->move_rate;
+  struct pf_position pos;
+  int move_rate = unit_move_rate(punit);
 
+  assert(punit->pf);
+
   switch (unit_type(punit)->move_type) {
   case LAND_MOVING:
   
@@ -272,39 +275,27 @@
     if (unit_flag(punit, F_IGTER)) {
       move_rate *= 3;
     }
-    move_time = WARMAP_COST(x, y) / move_rate;
     break;
  
   case SEA_MOVING:
-   
-    if (player_owns_active_wonder(unit_owner(punit), B_LIGHTHOUSE)) {
-        move_rate += SINGLE_MOVE;
-    }
-    
-    if (player_owns_active_wonder(unit_owner(punit), B_MAGELLAN)) {
-        move_rate += (improvement_variant(B_MAGELLAN) == 1) 
-                       ?  SINGLE_MOVE : 2 * SINGLE_MOVE;
-    }
-      
-    if (player_knows_techs_with_flag(unit_owner(punit), TF_BOAT_FAST)) {
-        move_rate += SINGLE_MOVE;
-    }
-      
-    move_time = WARMAP_SEACOST(x, y) / move_rate;
     break;
  
   case HELI_MOVING:
   case AIR_MOVING:
-     move_time = real_map_distance(punit->x, punit->y, x, y) 
-                   * SINGLE_MOVE / move_rate;
+     return (real_map_distance(punit->x, punit->y, x, y) 
+             * SINGLE_MOVE / move_rate);
      break;
  
   default:
     die("ai/aiunit.c:unit_move_turns: illegal move type %d",
        unit_type(punit)->move_type);
-    move_time = 0;
   }
-  return move_time;
+
+  if (pf_get_position(punit->pf, x, y, &pos)) {
+    return (pos.total_MC / move_rate);
+  } else {
+    return 255; /* kludge */
+  }
 }
  
 /**************************************************************************
@@ -548,24 +539,15 @@
   int best_x = -1, best_y = -1;
 
   /* Path-finding stuff */
-  struct pf_map *map;
-  struct pf_parameter parameter;
+  struct pf_position pos;
 
 #define DIST_FACTOR   0.6
 
-  pft_fill_default_parameter(&parameter);
-  pft_fill_unit_parameter(&parameter, punit);
-  parameter.get_TB = no_fights_or_unknown;
-  /* When exploring, even AI should pretend to not cheat. */
-  parameter.omniscience = FALSE;
-
-  map = pf_create_map(&parameter);
-  while (pf_next(map)) {
+  update_unit_map(punit, UNIT_MAP_EXPLORER);
+  pf_cache_reset(punit->pf);
+  while (pf_cached_next(punit->pf, &pos)) {
     float desirable;
-    struct pf_position pos;
 
-    pf_next_get_position(map, &pos);
-    
     /* Our callback should insure this. */
     assert(map_is_known(pos.x, pos.y, pplayer));
     
@@ -594,7 +576,6 @@
       break;
     }
   }
-  pf_destroy_map(map);
 
   /* Go to the best tile found. */
   if (most_desirable > 0) {
@@ -1007,27 +988,21 @@
 static struct pf_path *find_rampage_target(struct unit *punit, 
                                            int thresh_adj, int thresh_move)
 {
-  struct pf_map *tgt_map;
   struct pf_path *path = NULL;
-  struct pf_parameter parameter;
   /* Coordinates of the best target (initialize to silence compiler) */
   int x = punit->x, y = punit->y;
   /* Want of the best target */
   int max_want = 0;
   struct player *pplayer = unit_owner(punit);
- 
-  pft_fill_default_parameter(&parameter);  
-  pft_fill_unit_attack_param(&parameter, punit);
-  
-  tgt_map = pf_create_map(&parameter);
-  while (pf_next(tgt_map)) {
-    struct pf_position pos;
+  struct pf_position pos;
+
+  update_unit_map(punit, UNIT_MAP_NORMAL);
+  pf_cache_reset(punit->pf);
+  while (pf_cached_next(punit->pf, &pos)) {
     int want;
     bool move_needed;
     int thresh;
  
-    pf_next_get_position(tgt_map, &pos);
-    
     if (pos.total_MC > punit->moves_left) {
       /* This is too far */
       break;
@@ -1059,12 +1034,10 @@
 
   if (max_want > 0) {
     /* We found something */
-    path = pf_get_path(tgt_map, x, y);
+    path = pf_get_path(punit->pf, x, y);
     assert(path);
   }
 
-  pf_destroy_map(tgt_map);
-  
   return path;
 }
 
@@ -1159,83 +1132,57 @@
 }
 
 /*************************************************************************
-  Tries to find a land tile adjacent to water and to our target 
-  (dest_x, dest_y).  Prefers tiles which are more defensible and/or
-  where we will have moves left.
-  FIXME: It checks if the ocean tile is in our Zone of Control?!
-**************************************************************************/
-bool find_beachhead(struct unit *punit, int dest_x, int dest_y, int *x, int *y)
-{
-  int ok, best = 0;
-  enum tile_terrain_type t;
-
-  CHECK_UNIT(punit);
-
-  adjc_iterate(dest_x, dest_y, x1, y1) {
-    ok = 0;
-    t = map_get_terrain(x1, y1);
-    if (WARMAP_SEACOST(x1, y1) <= 6 * THRESHOLD && !is_ocean(t)) {
-      /* accessible beachhead */
-      adjc_iterate(x1, y1, x2, y2) {
-       if (is_ocean(map_get_terrain(x2, y2))
-           && is_my_zoc(unit_owner(punit), x2, y2)) {
-         ok++;
-         goto OK;
-
-         /* FIXME: The above used to be "break; out of adjc_iterate",
-         but this is incorrect since the current adjc_iterate() is
-         implemented as two nested loops.  */
-       }
-      } adjc_iterate_end;
-    OK:
-
-      if (ok > 0) {
-       /* accessible beachhead with zoc-ok water tile nearby */
-        ok = get_tile_type(t)->defense_bonus;
-       if (map_has_special(x1, y1, S_RIVER))
-         ok += (ok * terrain_control.river_defense_bonus) / 100;
-        if (get_tile_type(t)->movement_cost * SINGLE_MOVE <
-            unit_type(punit)->move_rate)
-         ok *= 8;
-        ok += (6 * THRESHOLD - WARMAP_SEACOST(x1, y1));
-        if (ok > best) {
-         best = ok;
-         *x = x1;
-         *y = y1;
-       }
-      }
+  Find a valid beachhead for punit to destination. When we find a 
+  beachhead backtracked from target to an ocean where ferry can reach
+  us, we assume this is the best spot for speed reasons. This works well 
+  in (WAG) 95% of the cases.
+
+  FIXME: We should instruct pf here to 1) check for danger, 2) prefer 
+  defensive terrain, and 3) smarter stacking check.
+**************************************************************************/
+bool target_beachhead(struct target *pt, struct unit *ferry)
+{
+  struct pf_map *land_map;
+  struct pf_parameter punitpf;
+
+  assert(pt->attacker->pf && ferry && ferry->pf);
+
+  pt->beach.x = -1; pt->beach.y = -1;
+  pft_fill_default_parameter(&punitpf);
+  pft_fill_unit_parameter(&punitpf, pt->attacker);
+  punitpf.start_x = pt->x; /* Backtrack punit from target */
+  punitpf.start_y = pt->y;
+  punitpf.get_zoc = NULL;
+  land_map = pf_create_map(&punitpf);
+  while (pf_next(land_map)) {
+    struct pf_position land_pos, sea_pos;
+    struct tile *ptile;
+
+    pf_next_get_position(land_map, &land_pos);
+    ptile = map_get_tile(land_pos.x, land_pos.y);
+    /* Don't stack */
+    if (unit_list_size(&ptile->units) > 0) {
+      continue;
     }
-  } adjc_iterate_end;
-
-  return (best > 0);
-}
-
-/**************************************************************************
-find_beachhead() works only when city is not further that 1 tile from
-the sea. But Sea Raiders might want to attack cities inland.
-So this finds the nearest land tile on the same continent as the city.
-**************************************************************************/
-static void find_city_beach( struct city *pc, struct unit *punit, int *x, int 
*y)
-{
-  int best_x = punit->x;
-  int best_y = punit->y;
-  int dist = 100;
-  int search_dist = real_map_distance(pc->x, pc->y, punit->x, punit->y) - 1;
-
-  CHECK_UNIT(punit);
-  
-  square_iterate(punit->x, punit->y, search_dist, xx, yy) {
-    if (map_get_continent(xx, yy) == map_get_continent(pc->x, pc->y)
-        && real_map_distance(punit->x, punit->y, xx, yy) < dist) {
-
-      dist = real_map_distance(punit->x, punit->y, xx, yy);
-      best_x = xx;
-      best_y = yy;
+    if (is_ocean(ptile->terrain)) {
+      continue; /* A transporter is on this tile, ignore it */
     }
-  } square_iterate_end;
-
-  *x = best_x;
-  *y = best_y;
+    adjc_iterate(land_pos.x, land_pos.y, adjx, adjy) {
+      ptile = map_get_tile(adjx, adjy);
+      if (is_ocean(ptile->terrain)) {
+        if (pf_get_position(ferry->pf, adjx, adjy, &sea_pos)) {
+          pt->beach.x = land_pos.x;
+          pt->beach.y = land_pos.y;
+          pt->ETA.final = land_pos.total_MC;
+          pt->ETA.ferry = sea_pos.total_MC;
+          pf_destroy_map(land_map);
+          return TRUE;
+        }
+      }
+    } adjc_iterate_end;
+  }
+  pf_destroy_map(land_map);
+  return FALSE;
 }
 
 /**************************************************************************
@@ -1296,114 +1243,111 @@
   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)
+static bool ai_military_gothere(struct player *pplayer, struct target *ptarget)
 {
-  int id, x, y, boatid = 0, bx = -1, by = -1;
-  struct unit *ferryboat = NULL;
-  struct unit *def;
-  struct tile *ptile;
-  bool boat_arrived;
+  struct unit *punit = ptarget->attacker; /* shorthand */
 
   CHECK_UNIT(punit);
 
-  id = punit->id; x = punit->x; y = punit->y;
-
-  if (same_pos(dest_x, dest_y, x, y)) {
+  if (same_pos(ptarget->x, ptarget->y, punit->x, punit->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)) {
+  ai_gothere_bodyguard(punit, ptarget->x, ptarget->y);
+
+  /* We have a ferry, go to it and use it */
+  if (ptarget->ferry) {
+    struct unit *body = find_unit_by_id(punit->ai.bodyguard);
+
+    assert(punit->ai.ferryboat == ptarget->ferry->id);
+    if (!same_pos(punit->x, punit->y, ptarget->ferry->x, ptarget->ferry->y)) {
       /* Go to the boat */
       /* FIXME: this can lose bodyguard */
-      if (!ai_unit_goto(punit, bx, by)) {
+      if (!ai_unit_goto(punit, ptarget->ferry->x, ptarget->ferry->y)) {
         /* Died. */
         return FALSE;
       }
+    }
+    if (!same_pos(punit->x, punit->y, ptarget->ferry->x, ptarget->ferry->y)) {
+      if (punit->moves_left == 0) {
+        return TRUE; /* good excuse */
+      } else {
+        UNIT_LOG(LOGLEVEL_FSTK, punit, "got stuck going to ferry.");
+        return FALSE; /* stuck */
+      }
     }
-    ptile = map_get_tile(punit->x, punit->y);
-    ferryboat = unit_list_find(&ptile->units, punit->ai.ferryboat);
+    ptarget->ferry->ai.passenger = punit->id;
 
-    if (ferryboat && (ferryboat->ai.passenger == 0 
-                      || ferryboat->ai.passenger == punit->id)) {
-      int boat_x, boat_y;
+    set_goto_dest(ptarget->ferry, ptarget->beach.x, ptarget->beach.y);
+    set_goto_dest(punit, ptarget->x, ptarget->y);
 
-      UNIT_LOG(LOG_DEBUG, punit, "Found boat[%d], going (%d,%d)",
-               ferryboat->id, dest_x, dest_y);
+    if (get_transporter_occupancy(ptarget->ferry) 
+        < get_transporter_capacity(ptarget->ferry)) {
       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);
-            }
+      punit->transported_by = ptarget->ferry->id;
+      UNIT_LOG(LOG_DEBUG, punit, "boarding %d", ptarget->ferry->id);
+    } else {
+      UNIT_LOG(LOGLEVEL_FSTK, punit, "our ferry[%d] was full!", 
+               ptarget->ferry->id);
+    }
+    if (body) {
+      if (get_transporter_occupancy(ptarget->ferry)
+          < get_transporter_capacity(ptarget->ferry)) {
+        if (same_pos(punit->x, punit->y, body->x, body->y)) {
+          handle_unit_activity_request(punit, ACTIVITY_SENTRY);
+          punit->transported_by = ptarget->ferry->id;
+          BODYGUARD_LOG(LOG_DEBUG, body, "boarding with charge");
+        } else {
+          if (!ai_unit_goto(body, punit->x, punit->y)) {
+            UNIT_LOG(LOGLEVEL_FSTK, punit, "bodyguard died en route to ferry");
+          } else if (!same_pos(punit->x, punit->y, body->x, body->y)) {
+            UNIT_LOG(LOGLEVEL_FSTK, punit, "waiting for bodyguard");
+            return TRUE;
+          } else {
+            BODYGUARD_LOG(LOG_DEBUG, body, "rendezvous at ferry, boarding");
+            handle_unit_activity_request(punit, ACTIVITY_SENTRY);
+            punit->transported_by = ptarget->ferry->id;
           }
-        } unit_list_iterate_end; /* passengers are safely stowed away */
-        if (!ai_unit_goto(ferryboat, dest_x, dest_y)) {
-          return FALSE; /* died */
         }
+      } else {
+        BODYGUARD_LOG(LOGLEVEL_FSTK, body, "ferry full - cannot fit charge!");
       }
-    } 
+    }
+    UNIT_LOG(LOG_DEBUG, ptarget->ferry, "moving cargo");
+    if (!ai_unit_gothere(ptarget->ferry)) {
+      return FALSE; /* died */
+    }
+    if (punit->moves_left == 0) {
+      UNIT_LOG(LOG_DEBUG, punit, "Cannot disembark (no moves left)");
+      return TRUE;
+    }
   }
+
+  if (ptarget->ferry) {
+    int x = ptarget->ferry->x, y = ptarget->ferry->y;
+    int bx = goto_dest_x(ptarget->ferry), by = goto_dest_y(ptarget->ferry);
 
-  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 (!same_pos(x, y, bx, by) && !is_tiles_adjacent(x, y, bx, by)) {
+      UNIT_LOG(LOG_DEBUG, punit, "Cannot disembark (not there yet)");
+      return TRUE; /* No. We assume we have moved. */
+    } else {
+      handle_unit_activity_request(punit, ACTIVITY_IDLE);
+      UNIT_LOG(LOG_DEBUG, punit, "Our boat has arrived");
+    }
   }
 
-  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? */
+  /* Go where we should be going if we can, and we are at our destination */
+  if (goto_is_sane(punit, ptarget->x, ptarget->y, TRUE)) {
+    set_goto_dest(punit, ptarget->x, ptarget->y);
+    UNIT_LOG(LOG_DEBUG, punit, "gothere");
+    return ai_unit_gothere(punit);
   } else {
-    UNIT_LOG(LOG_DEBUG, punit, "Not moving");
+    UNIT_LOG(LOGLEVEL_FSTK, punit, "gothere failed (goto not sane)!");
+    return FALSE;
   }
-  
-  /* Dead unit shouldn't reach this point */
-  CHECK_UNIT(punit);
-  
-  return (!same_pos(punit->x, punit->y, x, y));
 }
 
 /*************************************************************************
@@ -1426,8 +1370,6 @@
   have different move type than us, as potential charges. Nor do we 
   attempt to bodyguard units with higher defence than us, or military
   units with higher attack than us.
-
-  Requires an initialized warmap!
 **************************************************************************/
 int look_for_charge(struct player *pplayer, struct unit *punit, 
                     struct unit **aunit, struct city **acity)
@@ -1588,10 +1530,6 @@
     return;
   }
 
-/* I'm not 100% sure this is the absolute best place for this... -- Syela */
-  generate_warmap(map_get_city(punit->x, punit->y), punit);
-/* I need this in order to call unit_move_turns, here and in look_for_charge */
-
   if (q > 0) {
     q *= 100;
     q /= unit_def_rating_basic_sq(punit);
@@ -1654,90 +1592,115 @@
 }
 
 /***************************************************************************
- A rough estimate of time (measured in turns) to get to the enemy city, 
- taking into account ferry transfer.
- If boat == NULL, we will build a boat of type boattype right here, so
- we wouldn't have to walk to it.
+  Check if we want ferry for target. If we want it, we assign it to 
+  target and set beachhead coordinates appropriately. We do NOT assign
+  the ferry to us in a real sense, and we do not change ferryboat.
 
- Requires ready warmap(s).  Assumes punit is ground or sailing.
+  FIXME: Check if ferry is faster! Right now we choose ferryboat as a
+  last option.
 ***************************************************************************/
-int turns_to_enemy_city(Unit_Type_id our_type,  struct city *acity,
-                        int speed, bool go_by_boat, 
-                        struct unit *boat, Unit_Type_id boattype)
+bool target_assign_ferry(struct target *pt, struct unit *ferryboat)
 {
-  switch(unit_types[our_type].move_type) {
-  case LAND_MOVING:
-    if (go_by_boat) {
-      int boatspeed = unit_types[boattype].move_rate;
-      int move_time = (WARMAP_SEACOST(acity->x, acity->y)) / boatspeed;
-      
-      if (unit_type_flag(boattype, F_TRIREME) && move_time > 2) {
-        /* FIXME: Should also check for LIGHTHOUSE */
-        /* Return something prohibitive */
-        return 999;
-      }
-      if (boat) {
-        /* Time to get to the boat */
-        move_time += (WARMAP_COST(boat->x, boat->y) + speed - 1) / speed;
-      }
-      
-      if (!unit_type_flag(our_type, F_MARINES)) {
-        /* Time to get off the boat (Marines do it from the vessel) */
-        move_time += 1;
-      }
-      
-      return move_time;
-    } else {
-      /* We are walking */
-      return (WARMAP_COST(acity->x, acity->y) + speed - 1) / speed;
+  bool g = TRUE, b = TRUE; /* for debugging */
+
+  if (is_ground_unit(pt->attacker) && ferryboat
+      && (g = !goto_is_sane(pt->attacker, pt->x, pt->y, TRUE))
+      && (b = target_beachhead(pt, ferryboat))) {
+    UNIT_LOG(LOG_DEBUG, pt->attacker, "assigning us ferry %d for target"
+             "@(%d, %d)", ferryboat->id, pt->x, pt->y);
+    pt->ferry = ferryboat;
+    return TRUE;
+  } else {
+    if (is_ground_unit(pt->attacker)) {
+      UNIT_LOG(LOG_DEBUG, pt->attacker, "unassigning ferry %d for "
+               "target@(%d,%d) because %s %s %s", 
+               ferryboat ? ferryboat->id : 0, ferryboat ? "" : "no ferry",
+               pt->x, pt->y, !g ? "can walk" : "", 
+               !b ? "no beachhead found": "");
+    } else if (ferryboat) {
+      UNIT_LOG(LOG_ERROR, pt->attacker, "why try to assign me a ferry??");
     }
-  case SEA_MOVING:
-    /* We are a boat: time to sail */
-    return (WARMAP_SEACOST(acity->x, acity->y) + speed - 1) / speed;
-  default: 
-    freelog(LOG_ERROR, "ERROR: Unsupported move_type in time_to_enemy_city");
-    /* Return something prohibitive */
-    return 999;
+    pt->ferry = NULL;
+    return FALSE;
   }
-
 }
+
+/***************************************************************************
+  A rough estimate of time (measured in turns) to get to the enemy, 
+  taking into account ferry transfer. target_assign_ferry() must be 
+  run first!
+
+  FIXME: Take enemy speed into account in a more sensible way.
 
-/************************************************************************
- Rough estimate of time (in turns) to catch up with the enemy unit.  
- FIXME: Take enemy speed into account in a more sensible way
+  Requires punit->pf.  Assumes punit is ground or sailing. speed is set
+  separately, since we might want to try out alternatives with different
+  speed in process_attacker_want().
 
- Requires precomputed warmap.  Assumes punit is ground or sailing. 
-************************************************************************/ 
-int turns_to_enemy_unit(Unit_Type_id our_type, int speed, int x, int y, 
-                        Unit_Type_id enemy_type)
+  Returns TRUE if we can go there at all, FALSE otherwise.
+***************************************************************************/
+bool target_turns(struct target *pt, int speed)
 {
-  int dist;
+  struct pf_position pos;
+  struct player *pplayer = unit_owner(pt->attacker);
 
-  switch(unit_types[our_type].move_type) {
-  case LAND_MOVING:
-    dist = WARMAP_COST(x, y);
-    break;
-  case SEA_MOVING:
-    dist = WARMAP_SEACOST(x, y);
-    break;
-  default:
-    /* Compiler warning */
-    dist = 0; 
-    freelog(LOG_ERROR, "ERROR: Unsupported unit_type in time_to_enemy_city");
-    /* Return something prohibitive */
-    return 999;
+  pt->ETA.turns = 0;
+
+  if (ai_handicap(pplayer, H_TARGETS)
+      && !map_is_known(pt->x, pt->y, pplayer)) {
+    /* Does not know it is there. */
+    return FALSE;
   }
+
+  switch(unit_types[pt->attacker->type].move_type) {
+  case LAND_MOVING:
+    if (pt->ferry) {
+      int ferryspeed = unit_move_rate(pt->ferry);
 
-  /* if dist <= move_rate, we hit the enemy right now */    
-  if (dist > speed) { 
-    /* Weird attempt to take into account enemy running away... */
-    dist *= unit_types[enemy_type].move_rate;
-    if (unit_type_flag(enemy_type, F_IGTER)) {
-      dist *= 3;
+      /* Time to get to the boat */
+      if (!pf_get_position(pt->attacker->pf, pt->ferry->x, pt->ferry->y, 
&pos)) {
+        UNIT_LOG(LOG_DEBUG, pt->attacker, "target_turns: Cannot walk to ferry"
+                 "at (%d, %d)", pt->ferry->x, pt->ferry->y);
+        return FALSE;
+      }
+      pt->ETA.basic = pos.total_MC;
+      pt->ETA.turns += pt->ETA.basic / speed;
+      /* FIXME: Horrible WAG for trireme */
+      if (unit_flag(pt->ferry, F_TRIREME) && pt->ETA.turns > 2
+          && !player_owns_active_wonder(pplayer, B_LIGHTHOUSE)) {
+        UNIT_LOG(LOG_DEBUG, pt->attacker, "target_turns: Ferry unsafe");
+        return FALSE;
+      }
+      /* Time for boat to get there. This info is set by target_beachhead. */
+      pt->ETA.turns += (pt->ETA.ferry + ferryspeed - 1) / ferryspeed;
+      /* Time to get from beachhead to target. Info set by target_beachhead. */
+      pt->ETA.turns += pt->ETA.final / speed;
+      return TRUE;
+    } else {
+      /* We are walking */
+      if (!pf_get_position(pt->attacker->pf, pt->x, pt->y, &pos)) {
+        UNIT_LOG(LOG_DEBUG, pt->attacker, "target_turns: Cannot walk to "
+                 "(%d, %d)", pt->x, pt->y);
+        return FALSE;
+      }
+      pt->ETA.basic = pos.total_MC;
+      pt->ETA.turns = (pt->ETA.basic + speed - 1) / speed;
+      return TRUE;
+    }
+  case SEA_MOVING:
+    /* We are a boat: time to sail */
+    if (!pf_get_position(pt->attacker->pf, pt->x, pt->y, &pos)) {
+      UNIT_LOG(LOG_DEBUG, pt->attacker, "target_turns: Boat cannot reach "
+               "(%d, %d)", pt->x, pt->y);
+      return FALSE;
     }
+    pt->ETA.basic = pos.total_MC;
+    pt->ETA.turns = (pt->ETA.basic + speed - 1) / speed;
+    return TRUE;
+  default: 
+    freelog(LOG_ERROR, "ERROR: Unsupported move_type in time_to_enemy");
+    /* Return something prohibitive */
+    return FALSE;
   }
-  
-  return (dist + speed - 1) / speed;
 }
 
 /*************************************************************************
@@ -1781,44 +1744,52 @@
 }
 
 /*************************************************************************
+  Set target with minimum options, and assure the rest is reset. Always
+  set the other options AFTER calling this function.
+**************************************************************************/
+void target_set(struct unit *punit, struct target *ptarget, 
+                int x, int y, struct unit *pdefender)
+{
+  ptarget->attacker = punit;
+  ptarget->ferry = find_unit_by_id(punit->ai.ferryboat);
+  ptarget->x = x;
+  ptarget->y = y;
+  ptarget->want = 0;
+  ptarget->defender = pdefender;
+  ptarget->ETA.basic = 0;
+  ptarget->ETA.ferry = 0;
+  ptarget->ETA.final = 0;
+  ptarget->ETA.turns = 0;
+  ptarget->beach.x = -1;
+  ptarget->beach.y = -1;
+  ptarget->city = NULL;
+  if (ptarget->defender && !is_stack_vulnerable(x, y)) {
+    ptarget->defender_count = unit_list_size(&(map_get_tile(x, y)->units)) + 1;
+  } else {
+    ptarget->defender_count = 0;
+  }
+}
+
+/*************************************************************************
   Find something to kill! This function is called for units to find 
   targets to destroy and for cities that want to know if they should
-  build offensive units. Target location returned in (x, y), want as
-  function return value.
+  build offensive units.
 
   punit->id == 0 means that the unit is virtual (considered to be built).
+
+  Ferry must already by arranged before this function is called.
 **************************************************************************/
-int find_something_to_kill(struct player *pplayer, struct unit *punit, 
-                           int *x, int *y)
+void find_something_to_kill(struct player *pplayer, struct target *ptarget)
 {
+  struct unit *punit = ptarget->attacker;
+  struct unit *ferryboat = ptarget->ferry;
   struct ai_data *ai = ai_data_get(pplayer);
-  /* basic attack */
-  int attack_value = unit_att_rating(punit);
-  /* Enemy defence rating */
-  int vuln;
-  /* Benefit from killing the target */
-  int benefit;
-  /* Number of enemies there */
-  int victim_count;
-  /* Want (amortized) of the operaton */
-  int want;
-  /* Best of all wants */
-  int best = 0;
-  /* Our total attack value with reinforcements */
-  int attack;
-  int move_time, move_rate;
-  int con = map_get_continent(punit->x, punit->y);
-  struct unit *pdef;
-  int maxd, needferry;
-  /* Do we have access to sea? */
-  bool harbor = FALSE;
-  int bx = 0, by = 0;
-  /* Build cost of the attacker (+adjustments) */
-  int bcost, bcost_bal;
-  bool handicap = ai_handicap(pplayer, H_TARGETS);
-  struct unit *ferryboat = NULL;
-  /* Type of our boat (a future one if ferryboat == NULL) */
-  Unit_Type_id boattype = U_LAST;
+  int attack_value = unit_att_rating(punit); /* basic attack */
+  int vuln; /* Enemy defence rating */
+  int benefit; /* Benefit from killing the target */
+  int attack; /* Our total attack value with reinforcements */
+  int move_rate = unit_move_rate(punit);
+  int bcost, bcost_bal; /* Build cost of the attacker (+adjustments) */
   bool unhap = FALSE;
   struct city *pcity;
   /* this is a kluge, because if we don't set x and y with !punit->id,
@@ -1826,19 +1797,20 @@
    * never learning steam engine, even though ironclads would be very 
    * useful. -- Syela */
   int bk = 0; 
+  struct target est; /* target estimate */
 
-  /*** Very preliminary checks */
-  *x = punit->x; 
-  *y = punit->y;
+  /* Do not reserve this ferry if we don't need to. From now on use
+   * ferryboat as pointer to ferry. */
+  ptarget->ferry = NULL;
 
+  /*** Very preliminary checks */
   if (!is_ground_unit(punit) && !is_sailing_unit(punit)) {
     /* Don't know what to do with them! */
-    return 0;
+    return;
   }
-
   if (attack_value == 0) {
     /* A very poor attacker... */
-    return 0;
+    return;
   }
 
   /*** Part 1: Calculate targets ***/
@@ -1902,39 +1874,24 @@
     unhap = ai_assess_military_unhappiness(pcity, get_gov_pplayer(pplayer));
   }
 
-  move_rate = unit_type(punit)->move_rate;
-  if (unit_flag(punit, F_IGTER)) {
-    move_rate *= 3;
-  }
-
-  maxd = MIN(6, move_rate) * THRESHOLD + 1;
-
   bcost = unit_type(punit)->build_cost;
   bcost_bal = build_cost_balanced(punit->type);
-
-  /* most flexible but costs milliseconds */
-  generate_warmap(map_get_city(*x, *y), punit);
-
-  if (is_ground_unit(punit)) {
-    int boatid = find_boat(pplayer, &bx, &by, 2);
-    ferryboat = player_find_unit_by_id(pplayer, boatid);
-  }
 
+  /* Sanity check for ferry. */
   if (ferryboat) {
-    boattype = ferryboat->type;
-    really_generate_warmap(map_get_city(ferryboat->x, ferryboat->y),
-                           ferryboat, SEA_MOVING);
-  } else {
-    boattype = best_role_unit_for_player(pplayer, L_FERRYBOAT);
-    if (boattype == U_LAST) {
-      /* We pretend that we can have the simplest boat -- to stimulate tech */
-      boattype = get_role_unit(L_FERRYBOAT, 0);
-    }
-  }
+    struct pf_position pos;
 
-  if (is_ground_unit(punit) && punit->id == 0 
-      && is_ocean_near_tile(punit->x, punit->y)) {
-    harbor = TRUE;
+    if (!pf_get_position(punit->pf, ferryboat->x, ferryboat->y, &pos)) {
+      /* This is an error: We have this boat by mistake. Can often happen
+       * though, due to old savegames. */
+      UNIT_LOG(LOG_ERROR, punit, "Disowning bad ferry[%d]", ferryboat->id);
+      UNIT_LOG(LOG_ERROR, ferryboat, "Disowned by %d", punit->id);
+      punit->ai.passenger = 0;
+      if (ferryboat->ai.passenger == punit->id) {
+        ferryboat->ai.passenger = 0;
+      }
+      ferryboat = NULL;
+    }
   }
 
   players_iterate(aplayer) {
@@ -1944,46 +1901,30 @@
     }
 
     city_list_iterate(aplayer->cities, acity) {
-      bool go_by_boat = (is_ground_unit(punit)
-                         && !(goto_is_sane(punit, acity->x, acity->y, TRUE) 
-                              && WARMAP_COST(acity->x, acity->y) < maxd));
-
+      target_set(ptarget->attacker, &est, acity->x, acity->y,
+                 get_defender(ptarget->attacker, acity->x, acity->y));
+      est.city = acity;
       if (!is_ocean(map_get_terrain(acity->x, acity->y))
           && unit_flag(punit, F_NO_LAND_ATTACK)) {
         /* Can't attack this city. It is on land. */
         continue;
       }
 
-      if (handicap && !map_is_known(acity->x, acity->y, pplayer)) {
-        /* Can't see it */
-        continue;
-      }
+      (void) target_assign_ferry(&est, ferryboat);
 
-      if (go_by_boat 
-          && (!(ferryboat || harbor)
-              || WARMAP_SEACOST(acity->x, acity->y) > 6 * THRESHOLD)) {
-        /* Too far or impossible to go by boat */
-        continue;
-      }
-      
-      if (is_sailing_unit(punit) 
-          && WARMAP_SEACOST(acity->x, acity->y) >= maxd) {
-        /* Too far to sail */
-        continue;
+      if (!target_turns(&est, move_rate)) {
+        continue; /* Cannot reach target */
       }
-      
-      if ((pdef = get_defender(punit, acity->x, acity->y))) {
-        vuln = unit_def_rating_sq(punit, pdef);
-        benefit = unit_type(pdef)->build_cost;
+
+      if (est.defender) {
+        vuln = unit_def_rating_sq(punit, est.defender);
+        benefit = unit_type(est.defender)->build_cost;
       } else { 
         vuln = 0; 
         benefit = 0; 
       }
-      
-      move_time = turns_to_enemy_city(punit->type, acity, move_rate, 
-                                      go_by_boat, ferryboat, boattype);
 
-      if (move_time > 1) {
+      if (est.ETA.turns > 1) {
         Unit_Type_id def_type = ai_choose_defender_versus(acity, punit->type);
         int v = unittype_def_rating_sq(punit->type, def_type,
                                        acity->x, acity->y, FALSE,
@@ -2000,31 +1941,22 @@
         benefit += 40;
       }
 
-      attack = (attack_value + acity->ai.attack) 
-        * (attack_value + acity->ai.attack);
       /* Avoiding handling upkeep aggregation this way -- Syela */
+      attack = (attack_value + acity->ai.attack) 
+               * (attack_value + acity->ai.attack);
       
-      /* AI was not sending enough reinforcements to totally wipe out a city
-       * and conquer it in one turn.  
-       * This variable enables total carnage. -- Syela */
-      victim_count 
-        = unit_list_size(&(map_get_tile(acity->x, acity->y)->units)) + 1;
-
-      if (!COULD_OCCUPY(punit) && !pdef) {
+      if (!COULD_OCCUPY(punit) && !est.defender) {
         /* Nothing there to bash and we can't occupy! 
          * Not having this check caused warships yoyoing */
-        want = 0;
-      } else if (move_time > THRESHOLD) {
-        /* Too far! */
-        want = 0;
+        est.want = 0;
       } else if (COULD_OCCUPY(punit) && acity->ai.invasion == 2) {
         /* Units able to occupy really needed there! */
-        want = bcost * SHIELD_WEIGHTING;
+        est.want = bcost * SHIELD_WEIGHTING;
       } else {
         int a_squared = acity->ai.attack * acity->ai.attack;
         
-        want = kill_desire(benefit, attack, (bcost + acity->ai.bcost), 
-                           vuln, victim_count);
+        est.want = kill_desire(benefit, attack, (bcost + acity->ai.bcost), 
+                               vuln, est.defender_count);
         if (benefit * a_squared > acity->ai.bcost * vuln) {
           /* If there're enough units to do the job, we don't need this
            * one. */
@@ -2033,63 +1965,51 @@
            * cavalries to take over a city, we have four (which is not
            * enough), then we will be severely discouraged to build the
            * fifth one.  Where is logic in this??!?! --GB */
-          want -= kill_desire(benefit, a_squared, acity->ai.bcost, 
-                              vuln, victim_count);
+          est.want -= kill_desire(benefit, a_squared, acity->ai.bcost, 
+                                  vuln, est.defender_count);
         }
       }
-      want -= move_time * (unhap ? SHIELD_WEIGHTING + 2 * TRADE_WEIGHTING 
+      est.want -= est.ETA.turns 
+                  * (unhap ? SHIELD_WEIGHTING + 2 * TRADE_WEIGHTING 
                            : SHIELD_WEIGHTING);
-      /* build_cost of ferry */
-      needferry = (go_by_boat && !ferryboat ? unit_value(boattype) : 0);
+      /* Want for city in itself */
+      /* FIXME: Add want for wonders and buildings also. */
+      est.want += SHIELD_WEIGHTING * acity->size 
+                  * unit_list_size(&acity->units_supported) 
+                  * (!est.defender && COULD_OCCUPY(punit) ? 4 : 1);
       /* FIXME: add time to build the ferry? */
-      want = military_amortize(pplayer, find_city_by_id(punit->homecity),
-                               want, MAX(1, move_time), bcost_bal + needferry);
+      est.want = military_amortize(pplayer, find_city_by_id(punit->homecity),
+                                   est.want, MAX(1, est.ETA.turns), bcost_bal);
 
       /* BEGIN STEAM-ENGINES-ARE-OUR-FRIENDS KLUGE */
-      if (want <= 0 && punit->id == 0 && best == 0) {
+      /* If we have no targets, at least set (x, y) to best potential
+       * target. This primes our tech want for aggressive units. */
+      if (est.want <= 0 && punit->id == 0 && ptarget->want <= 0) {
         int bk_e = military_amortize(pplayer, find_city_by_id(punit->homecity),
                                      benefit * SHIELD_WEIGHTING, 
-                                     MAX(1, move_time), bcost_bal + needferry);
+                                     MAX(1, est.ETA.turns), bcost_bal);
 
         if (bk_e > bk) {
-          *x = acity->x;
-          *y = acity->y;
+          ptarget->x = acity->x;
+          ptarget->y = acity->y;
           bk = bk_e;
         }
       }
       /* END STEAM-ENGINES KLUGE */
-      
-      if (punit->id != 0 && ferryboat && is_ground_unit(punit)) {
-        UNIT_LOG(LOG_DEBUG, punit, "in fstk with boat %s@(%d, %d) -> %s@(%d, 
%d)"
-                 " (go_by_boat=%d, move_time=%d, want=%d, best=%d)",
-                 unit_type(ferryboat)->name, bx, by,
-                 acity->name, acity->x, acity->y, 
-                 go_by_boat, move_time, want, best);
-      }
-      
-      if (want > best && ai_fuzzy(pplayer, TRUE)) {
+UNIT_LOG(LOG_DEBUG, punit, "want for %d, %d is %d (ferry%d)", est.x, est.y, 
est.want, est.ferry ? est.ferry->id : 0);
+
+      if (est.want > ptarget->want && ai_fuzzy(pplayer, TRUE)) {
+        if (punit->id != 0 && est.ferry) {
+          UNIT_LOG(LOGLEVEL_FSTK, punit, "to %s(%d,%d) using %s[%d]@(%d, %d)"
+                   "->(%d, %d), time (%d:%d:%d)%d), want %d ?", acity->name, 
+                   acity->x, acity->y, unit_type(ferryboat)->name, 
+                   ferryboat->id, ferryboat->x, ferryboat->y, est.beach.x, 
+                   est.beach.y, est.ETA.basic, est.ETA.ferry, est.ETA.final,
+                   est.ETA.turns, est.want);
+        }
+
         /* Yes, we like this target */
-        if (punit->id != 0 && is_ground_unit(punit) 
-            && !unit_flag(punit, F_MARINES)
-            && map_get_continent(acity->x, acity->y) != con) {
-          /* a non-virtual ground unit is trying to attack something on 
-           * another continent.  Need a beachhead which is adjacent 
-           * to the city and an available ocean tile */
-          int xx, yy;
-
-          if (find_beachhead(punit, acity->x, acity->y, &xx, &yy)) { 
-            best = want;
-            *x = acity->x;
-            *y = acity->y;
-            /* the ferryboat needs to target the beachhead, but the unit 
-             * needs to target the city itself.  This is a little weird, 
-             * but it's the best we can do. -- Syela */
-          } /* else do nothing, since we have no beachhead */
-        } else {
-          best = want;
-          *x = acity->x;
-          *y = acity->y;
-        } /* end need-beachhead else */
+        *ptarget = est;
       }
     } city_list_iterate_end;
 
@@ -2103,73 +2023,47 @@
         continue;
       }
 
-      if (handicap && !map_is_known(aunit->x, aunit->y, pplayer)) {
-        /* Can't see the target */
-        continue;
-      }
-
       if ((unit_flag(aunit, F_HELP_WONDER) || unit_flag(aunit, F_TRADE_ROUTE))
           && punit->id == 0) {
         /* We will not build units just to chase caravans */
         continue;
       }
 
+      target_set(ptarget->attacker, &est, aunit->x, aunit->y,
+                 get_defender(ptarget->attacker, aunit->x, aunit->y));
+
       /* We have to assume the attack is diplomatically ok.
        * We cannot use can_player_attack_tile, because we might not
        * be at war with aplayer yet */
       if (!can_unit_attack_all_at_tile(punit, aunit->x, aunit->y)
-          || !(aunit == get_defender(punit, aunit->x, aunit->y))) {
+          || aunit != est.defender) {
         /* We cannot attack it, or it is not the main defender. */
         continue;
       }
 
-      if (is_ground_unit(punit) 
-          && (map_get_continent(aunit->x, aunit->y) != con 
-              || WARMAP_COST(aunit->x, aunit->y) >= maxd)) {
-        /* Impossible or too far to walk */
-        continue;
-      }
-
-      if (is_sailing_unit(punit)
-          && (!goto_is_sane(punit, aunit->x, aunit->y, TRUE)
-              || WARMAP_SEACOST(aunit->x, aunit->y) >= maxd)) {
-        /* Impossible or too far to sail */
-        continue;
-      }
-
       vuln = unit_def_rating_sq(punit, aunit);
       benefit = unit_type(aunit)->build_cost;
- 
-      move_time = turns_to_enemy_unit(punit->type, move_rate, 
-                                      aunit->x, aunit->y, aunit->type);
 
-      if (!COULD_OCCUPY(punit) && vuln == 0) {
-        /* FIXME: There is something with defence 0 there, maybe a diplomat.
-         * What's wrong in killing a diplomat? -- GB */
-        want = 0;
-      } else if (move_time > THRESHOLD) {
-        /* Too far */
-        want = 0;
-      } else {
-        want = kill_desire(benefit, attack, bcost, vuln, 1);
-          /* Take into account maintainance of the unit */
-          /* FIXME: Depends on the government */
-        want -= move_time * SHIELD_WEIGHTING;
-        /* Take into account unhappiness 
-         * (costs 2 luxuries to compensate) */
-        want -= (unhap ? 2 * move_time * TRADE_WEIGHTING : 0);
-      }
-      want = military_amortize(pplayer, find_city_by_id(punit->homecity),
-                               want, MAX(1, move_time), bcost_bal);
-      if (want > best && ai_fuzzy(pplayer, TRUE)) {
-        best = want;
-        *x = aunit->x;
-        *y = aunit->y;
+      (void) target_assign_ferry(&est, ferryboat);
+
+      if (!target_turns(&est, move_rate)) {
+        continue; /* Cannot reach target */
+      }
+
+      est.want = kill_desire(benefit, attack, bcost, vuln, 1);
+      /* Take into account maintainance of the unit */
+      /* FIXME: Depends on the government */
+      est.want -= est.ETA.turns * SHIELD_WEIGHTING;
+      /* Take into account unhappiness 
+       * (costs 2 luxuries to compensate) */
+      est.want -= (unhap ? 2 * est.ETA.turns * TRADE_WEIGHTING : 0);
+      est.want = military_amortize(pplayer, find_city_by_id(punit->homecity),
+                                   est.want, MAX(1, est.ETA.turns), bcost_bal);
+      if (est.want > ptarget->want && ai_fuzzy(pplayer, TRUE)) {
+        *ptarget = est;
       }
     } unit_list_iterate_end;
   } players_iterate_end;
-
-  return(best);
 }
 
 /**********************************************************************
@@ -2221,6 +2115,47 @@
 }
 
 /*************************************************************************
+  Same idea as find_boat() but with pf.
+**************************************************************************/
+static struct unit *find_ferry(struct unit *punit)
+{
+  struct pf_position pos;
+
+  assert(is_ground_unit(punit));
+
+  /* First try our own stack */
+  unit_list_iterate(map_get_tile(punit->x, punit->y)->units, aunit) {
+    if (punit->owner == aunit->owner
+        && is_ground_units_transport(aunit)
+        && get_transporter_occupancy(aunit) < get_transporter_capacity(aunit)
+        && aunit->ai.passenger == 0) {
+      return aunit;
+    }
+  } unit_list_iterate_end;
+
+  /* That failed. Reset iterator and try to find one nearby. */
+  pf_get_position(punit->pf, punit->x, punit->y, &pos);
+
+  pf_cache_reset(punit->pf);
+  while (pf_cached_next(punit->pf, &pos)) {
+
+    if (pos.total_MC > unit_move_rate(punit)) {
+      return NULL; /* one turn horizon */
+    }
+
+    unit_list_iterate(map_get_tile(pos.x, pos.y)->units, aunit) {
+      if (punit->owner == aunit->owner
+          && is_ground_units_transport(aunit)
+          && get_transporter_occupancy(aunit) < get_transporter_capacity(aunit)
+          && aunit->ai.passenger == 0) {
+        return aunit;
+      }
+    } unit_list_iterate_end;
+  }
+  return NULL;
+}
+
+/*************************************************************************
   This does the attack until we have used up all our movement, unless we
   should safeguard a city.  First we rampage nearby, then we go
   looking for trouble elsewhere. If there is nothing to kill, sailing units 
@@ -2228,15 +2163,23 @@
 **************************************************************************/
 static void ai_military_attack(struct player *pplayer, struct unit *punit)
 {
-  int dest_x, dest_y; 
   int id = punit->id;
   int ct = 10;
   struct city *pcity = NULL;
+  struct target mytarget;
+
+  target_set(punit, &mytarget, punit->x, punit->y, NULL);
+  if (is_ground_unit(punit) && !mytarget.ferry) {
+    /* Find us a potential ferry */
+    mytarget.ferry = find_ferry(punit);
+  }
 
   CHECK_UNIT(punit);
 
   /* Main attack loop */
   do {
+    int x = punit->x, y = punit->y; /* anti-rust */
+
     /* First find easy nearby enemies, anything better than pillage goes */
     if (!ai_military_rampage(punit, RAMPAGE_ANYTHING, 
                              RAMPAGE_ANYTHING)) {
@@ -2251,34 +2194,35 @@
     }
 
     /* 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)) {
+    find_something_to_kill(pplayer, &mytarget);
+    if (mytarget.ferry) {
+      /* Reserve ferry */
+      mytarget.ferry->ai.passenger = punit->id;
+      punit->ai.ferryboat = mytarget.ferry->id;
+    }
+    if (!same_pos(punit->x, punit->y, mytarget.x, mytarget.y)) {
+      if (!is_tiles_adjacent(punit->x, punit->y, mytarget.x, mytarget.y)
+          || !can_unit_attack_tile(punit, mytarget.x, mytarget.y)
+          || (could_unit_move_to_tile(punit, mytarget.x, mytarget.y) == 0)) {
         /* Can't attack or move usually means we are adjacent but
          * 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)) {
+        UNIT_LOG(LOG_DEBUG, punit, "mil att gothere -> (%d, %d)", 
+                 mytarget.x, mytarget.y);
+        if (!ai_military_gothere(pplayer, &mytarget)
+            || (punit->x == x && punit->y == 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)) {
+        UNIT_LOG(LOG_DEBUG, punit, "mil att bash -> %d, %d", 
+                 mytarget.x, mytarget.y);
+        if (!ai_unit_attack(punit, mytarget.x, mytarget.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!");
@@ -2286,6 +2230,12 @@
       ct = 0;
     }
 
+    /* Ensure that our references are still valid */
+    if ((mytarget.ferry || punit->ai.ferryboat)
+        && !find_unit_by_id(punit->ai.ferryboat)) {
+      mytarget.ferry = NULL;
+      punit->ai.ferryboat = 0;
+    }
     ct--; /* infinite loops from railroads must be stopped */
   } while (punit->moves_left > 0 && ct > 0);
 
@@ -2300,24 +2250,6 @@
   } else if (!is_barbarian(pplayer)) {
     /* Nothing else to do, so try exploring. */
     (void) ai_manage_explorer(punit);
-  } else {
-    /* You can still have some moves left here, but barbarians should
-       not sit helplessly, but advance towards nearest known enemy city */
-    struct city *pc;
-    int fx, fy;
-    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);
-      } 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);
-      }
-    }
   }
   if ((punit = find_unit_by_id(id)) && punit->moves_left > 0) {
     struct city *pcity = map_get_city(punit->x, punit->y);
@@ -2454,10 +2386,10 @@
       }
       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",
+       if (pcity && pplayer->player_no == 0) {
+         UNIT_LOG(LOGLEVEL_FSTK, punit, "Ferrying ->%s, invasion = %d",
                  unit_name(aunit->type), pcity->name,
-                 pcity->ai.invasion, aunit->ai.bodyguard);
+                 pcity->ai.invasion);
        }
         n++;
         handle_unit_activity_request(aunit, ACTIVITY_SENTRY);
@@ -2488,7 +2420,7 @@
       }
       send_unit_info(pplayer, punit); /* to get the crosshairs right -- Syela 
*/
     } else {
-      UNIT_LOG(LOG_DEBUG, punit, "Ferryboat %d@(%d,%d) stalling.",
+      UNIT_LOG(LOGLEVEL_FSTK, punit, "Ferryboat %d@(%d,%d) stalling.",
                    punit->id, punit->x, punit->y);
       if(is_barbarian(pplayer)) /* just in case */
         (void) ai_manage_explorer(punit);
@@ -2504,7 +2436,7 @@
 
   /* ok, not carrying anyone, even the ferryman */
   punit->ai.passenger = 0;
-  UNIT_LOG(LOG_DEBUG, punit, "Ferryboat is lonely.");
+  UNIT_LOG(LOGLEVEL_FSTK, punit, "Ferryboat is lonely.");
   handle_unit_activity_request(punit, ACTIVITY_IDLE);
 
   /* Release bodyguard and let it roam */
@@ -2521,13 +2453,14 @@
   /*** Find work ***/
   CHECK_UNIT(punit);
 
-  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) {
+    struct pf_position pos;
     if (aunit->ai.ferryboat != 0
-       && WARMAP_SEACOST(aunit->x, aunit->y) < best
+        && pf_get_position(punit->pf, aunit->x, aunit->y, &pos)
+       && pos.total_MC < 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)",
@@ -2684,6 +2617,7 @@
   if ((punit = find_unit_by_id(id))) {
     if (unit_list_find(&(map_get_tile(punit->x, punit->y)->units),
         punit->ai.ferryboat)) {
+      UNIT_LOG(LOG_DEBUG, punit, "sentry on ferry at end of ai_manage_unit");
       handle_unit_activity_request(punit, ACTIVITY_SENTRY);
     } else if (punit->activity == ACTIVITY_IDLE) {
       handle_unit_activity_request(punit, ACTIVITY_FORTIFYING);
@@ -2797,6 +2731,19 @@
 **************************************************************************/
 void ai_manage_units(struct player *pplayer) 
 {
+  unit_list_iterate(pplayer->units, punit) {
+    if (is_ground_unit(punit) || is_sailing_unit(punit)) {
+      /* Allocate a pf_map to the unit here, so that we can avoid
+       * doing it twice over and also can look at the pf maps of
+       * other units without worrying about them not existing. 
+       * This might consume some memory, but really saves CPU time. */
+      if (!unit_has_role(punit->type, L_EXPLORER)) {
+        update_unit_map(punit, UNIT_MAP_NORMAL);
+      } else {
+        update_unit_map(punit, UNIT_MAP_EXPLORER);
+      }
+    }
+  } unit_list_iterate_end;
   ai_airlift(pplayer);
   unit_list_iterate_safe(pplayer->units, punit) {
     ai_manage_unit(pplayer, punit);
Index: ai/aiunit.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aiunit.h,v
retrieving revision 1.49
diff -u -r1.49 aiunit.h
--- ai/aiunit.h 2003/09/27 11:22:40     1.49
+++ ai/aiunit.h 2003/09/28 12:28:24
@@ -43,25 +43,44 @@
 struct unit;
 struct ai_choice;
 struct pf_path;
+struct map_position;
 
+struct target {
+  struct unit *attacker;
+  struct unit *ferry;
+  int x, y, want;
+  struct map_position beach;
+  struct city *city;
+  struct unit *defender;
+  struct {
+    int basic;      /* Time to get to closer of target or ferry pickup */
+    int ferry;      /* Time it takes ferry to takes us to beachhead */
+    int final;      /* Time it takes ut to go from beachhead to target */
+    int turns;      /* Sum of above divided by speed */
+  } ETA;
+  int defender_count; /* Number of units that will actively defend tile */
+};
+
 extern Unit_Type_id simple_ai_types[U_LAST];
 
 void ai_manage_units(struct player *pplayer); 
 int could_unit_move_to_tile(struct unit *punit, int dest_x, int dest_y);
+bool ai_unit_execute_path(struct unit *punit, struct pf_path *path);
 int look_for_charge(struct player *pplayer, struct unit *punit,
                     struct unit **aunit, struct city **acity);
 
 bool ai_manage_explorer(struct unit *punit);
+
+void find_something_to_kill(struct player *pplayer, struct target *ptarget);
+
+bool target_beachhead(struct target *pt, struct unit *ferry);
+bool target_turns(struct target *pt, int speed);
+bool target_assign_ferry(struct target *pt, struct unit *ferryboat);
+void target_set(struct unit *punit, struct target *ptarget,
+                int x, int y, struct unit *pdefender);
 
-int turns_to_enemy_city(Unit_Type_id our_type, struct city *acity,
-                        int speed, bool go_by_boat, 
-                        struct unit *boat, Unit_Type_id boattype);
-int turns_to_enemy_unit(Unit_Type_id our_type, int speed, int x, int y, 
-                        Unit_Type_id enemy_type);
-int find_something_to_kill(struct player *pplayer, struct unit *punit, 
-                            int *x, int *y);
-bool find_beachhead(struct unit *punit, int dest_x, int dest_y, 
-                    int *x, int *y);
+int turns_to_enemy(struct unit *punit, struct unit *ferry, int x, int y,
+                   int speed);
 
 int build_cost_balanced(Unit_Type_id type);
 int unittype_att_rating(Unit_Type_id type, bool veteran,
Index: common/unit.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/unit.c,v
retrieving revision 1.183
diff -u -r1.183 unit.c
--- common/unit.c       2003/09/22 16:54:09     1.183
+++ common/unit.c       2003/09/28 12:28:25
@@ -1477,6 +1477,7 @@
   punit->bribe_cost = -1; /* flag value */
   punit->transported_by = -1;
   punit->pgr = NULL;
+  punit->pf = NULL;
   punit->focus_status = FOCUS_AVAIL;
   punit->ord_map = 0;
   punit->ord_city = 0;
@@ -1495,7 +1496,9 @@
   if (punit->pgr) {
     free(punit->pgr->pos);
     free(punit->pgr);
-    punit->pgr = NULL;
+  }
+  if (punit->pf) {
+    free(punit->pf);
   }
   free(punit);
 }
Index: common/unit.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/unit.h,v
retrieving revision 1.100
diff -u -r1.100 unit.h
--- common/unit.h       2003/09/22 16:54:09     1.100
+++ common/unit.h       2003/09/28 12:28:25
@@ -139,6 +139,7 @@
   int transported_by;
   int occupy; /* number of units that occupy transporter */
   struct goto_route *pgr;
+  struct pf_map *pf; /* path-finding map */
 };
 
 /* Wrappers for accessing the goto destination of a unit.  This goto_dest
Index: common/aicore/pf_tools.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/aicore/pf_tools.h,v
retrieving revision 1.4
diff -u -r1.4 pf_tools.h
--- common/aicore/pf_tools.h    2003/09/21 09:06:43     1.4
+++ common/aicore/pf_tools.h    2003/09/28 12:28:25
@@ -28,47 +28,4 @@
                                         enum known_type known,
                                         struct pf_parameter *param);
 
-/*
- * Below iterator is mostly for use by AI, iterates through all positions
- * on the map, reachable by punit, in order of their distance from punit.
- * Returns info about these positions via the second field.
- */
-#define simple_unit_path_iterator(punit, position) {                          \
-  struct pf_map *UPI_map;                                                     \
-  struct pf_parameter UPI_parameter;                                          \
-                                                                              \
-  pft_fill_default_parameter(&UPI_parameter);                                 \
-  pft_fill_unit_parameter(&UPI_parameter, punit);                             \
-  UPI_map = pf_create_map(&UPI_parameter);                                    \
-  while (pf_next(UPI_map)) {                                                  \
-    struct pf_position position;                                              \
-                                                                              \
-    pf_next_get_position(UPI_map, &position);
-
-#define simple_unit_path_iterator_end                                         \
-  }                                                                           \
-  pf_destroy_map(UPI_map);                                                    \
-}
-
-/* 
- * Below iterator is to be used when a land unit needs to consider going one
- * step into the sea (to consider boarding, say) or a sea unit needs to
- * consider going one step into the land (land bombardment)
- */
-#define simple_unit_overlap_path_iterator(punit, position) {                  \
-  struct pf_map *UPI_map;                                                     \
-  struct pf_parameter UPI_parameter;                                          \
-                                                                              \
-  pft_fill_unit_overlap_param(&UPI_parameter, punit);                         \
-  UPI_map = pf_create_map(&UPI_parameter);                                    \
-  while (pf_next(UPI_map)) {                                                  \
-    struct pf_position position;                                              \
-                                                                              \
-    pf_next_get_position(UPI_map, &position);
-
-#define simple_unit_overlap_path_iterator_end                                 \
-  }                                                                           \
-  pf_destroy_map(UPI_map);                                                    \
-}
-
 #endif                         /* FC__PF_TOOLS_H */

[Prev in Thread] Current Thread [Next in Thread]
  • [freeciv-ai] (PR#6199) even huger fstk/ksw/paw/ferry cleanup, Per I. Mathisen <=