Complete.Org: Mailing Lists: Archives: freeciv-ai: December 2004:
[freeciv-ai] Re: (PR#10694) AI Builds Doomed Ferries and Passengers
Home

[freeciv-ai] Re: (PR#10694) AI Builds Doomed Ferries and Passengers

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
Subject: [freeciv-ai] Re: (PR#10694) AI Builds Doomed Ferries and Passengers
From: "Benedict Adamson" <badamson@xxxxxxxxxxx>
Date: Thu, 30 Dec 2004 15:23:47 -0800
Reply-to: bugs@xxxxxxxxxxx

<URL: http://bugs.freeciv.org/Ticket/Display.html?id=10694 >

I've implemented the change to the AI bodyguard and city guard code I 
outlined in early November. The attached patch is applicable to the CVS 
development version of 2004-12-29. This is a large change, so it will of 
course require review and amendment before being committed. I welcome 
your comments.

The patch incorporates Per's movemap patch. See PR#9610.
    http://bugs.freeciv.org/Ticket/Display.html?id=9610
I have amended the movemap code, to fix some memory leaks and to use a 
specvec for holding the unit IDs, rather than use a speclist of unit 
points, to prevent some dangling pointers.

This patch implements the improvements suggested by Per for PR#9887.
    http://bugs.freeciv.org/Ticket/Display.html?id=9887

Unlike the existing code, this code will assign guards to ferries (but 
only Cruisers are suitable in the default rule-set) and non military 
units, and often provides escorts for vulnerable attack units (escorting 
a Cannon with a Musketeer, for example) even when attacking overseas. It 
could, in theory, assign escorts for air units. It removes much of the 
cruft from the ai_military_findjob function, including all of the 
stay_and_defend function.

The code reassigns unit and city guards every turn. It tries to assign 
the best guards to the most vulnerable units and cities. It will assign 
several guards to a city, if necessary. The code therefore rearranges 
the guards of well connected cities (connected by rail roads or short 
roads) to provide the best possible defence. It tries to rationalise the 
home cities of city guards, so the units guarding a city have that as 
their home city.

The code computes 'danger' ratings for each of the AI's cities and 
units. It computes 'menace' ratings for each of the enemy units of the 
AI. Both ratings are carefully computed as the expected-loss in shields, 
rather than arbitrary WAG numbers, so sensible comparisons with unit 
costs are possible when making decisions. I assume these ratings could 
be useful to the code that decides whether to build defensive and hunter 
units, but it currently does not use them.

This code implements defensive attacks. That is, the AI's attack units 
will preferentially attack units that are menacing the AI's units. This 
is implemented by altering the stack_cost function used by the attack 
and rampage code for selecting targets. The amended function includes 
the (probable, estimated) value of our units saved by destroying the 
menacing unit. (It is a little more complex than that; see the code for 
the details).

This patch changes the semantics of the flag punit->ai.bodyguard == 
BODYGUARD_WANTED. This now indicates that the unit is on a dangerous 
mission, meaning that even if the unit is not currently in danger, it 
soon will be. The code assigns spare guards (those not required to deal 
with immediate dangers) to unguarded units flagged this way.

The code records which units belong to the 'garrison' of each city. The 
garrison are the units that have committed to defending the city. That 
includes defenders units that will arrive later in the turn, and 
excludes units that are merely passing through.

The code adds some convenience functions to unittype.[hc] and 
diplomats.[hc].

The increase in CPU time for this patch is modest. Most is in the 
movemap computation and the function aiguard_findcharge (about 3% each). 
We intend to use the movemap for other things, which will allow us to 
remove some dangermap computations and thus reduce the nett cost. We 
might be able to reduce the cost of aiguard_findcharge by improving the 
ai_military_findjob function to call it less often.

Note that this code tries to do the best it can with the available 
defensive units. It will still perform poorly if the AI fails to build 
enough defenders. Fixing that is the next stage of stopping the AI from 
building doomed attack units.

diff -ruN -Xvendor.freeciv.current/diff_ignore 
vendor.freeciv.current/ai/Makefile.am freeciv.PR10694-3/ai/Makefile.am
--- vendor.freeciv.current/ai/Makefile.am       2004-12-29 19:44:10.000000000 
+0000
+++ freeciv.PR10694-3/ai/Makefile.am    2004-12-30 21:02:37.000000000 +0000
@@ -17,6 +17,8 @@
                advspace.h      \
                aiair.c         \
                aiair.h         \
+               aiguard.h       \
+               aiguard.c       \
                aicity.c        \
                aicity.h        \
                aidata.c        \
@@ -35,6 +37,8 @@
                aisettler.h     \
                aitech.c        \
                aitech.h        \
+               aithreat.h      \
+               aithreat.c      \
                aitools.c       \
                aitools.h       \
                aiunit.c        \
diff -ruN -Xvendor.freeciv.current/diff_ignore 
vendor.freeciv.current/ai/advmilitary.c freeciv.PR10694-3/ai/advmilitary.c
--- vendor.freeciv.current/ai/advmilitary.c     2004-12-29 19:44:10.000000000 
+0000
+++ freeciv.PR10694-3/ai/advmilitary.c  2004-12-30 21:02:37.000000000 +0000
@@ -1335,7 +1335,9 @@
       choice->type = CT_BUILDING;
 
     } else if (danger > 0 && num_defenders <= urgency) {
-      /* Consider building defensive units units */
+      /* Consider building defensive units units.
+       * Assumes that defenders have comparable strength to attackers,
+       * so we can simply count numbers of units. */
       process_defender_want(pplayer, pcity, danger, choice);
       if (urgency == 0 && unit_types[choice->choice].defense_strength == 1) {
         if (get_city_bonus(pcity, EFT_LAND_REGEN) > 0) {
diff -ruN -Xvendor.freeciv.current/diff_ignore 
vendor.freeciv.current/ai/aiair.c freeciv.PR10694-3/ai/aiair.c
--- vendor.freeciv.current/ai/aiair.c   2004-12-29 19:44:10.000000000 +0000
+++ freeciv.PR10694-3/ai/aiair.c        2004-12-30 21:02:37.000000000 +0000
@@ -113,7 +113,7 @@
   unit_cost = unit_cost * unit_type(punit)->hp / punit->hp; 
 
   /* Determine cost of enemy units */
-  victim_cost = stack_cost(pdefender);
+  victim_cost = stack_cost(pdefender, 50, 0);
 
   /* Missile would die 100% so we adjust the victim_cost -- GB */
   if (unit_flag(punit, F_MISSILE)) {
diff -ruN -Xvendor.freeciv.current/diff_ignore 
vendor.freeciv.current/ai/aidata.c freeciv.PR10694-3/ai/aidata.c
--- vendor.freeciv.current/ai/aidata.c  2004-12-29 19:44:10.000000000 +0000
+++ freeciv.PR10694-3/ai/aidata.c       2004-12-30 21:02:37.000000000 +0000
@@ -31,6 +31,9 @@
 #include "settlers.h"
 #include "unittools.h"
 
+#include "path_finding.h"
+#include "pf_tools.h"
+
 #include "advdiplomacy.h"
 #include "advmilitary.h"
 #include "aicity.h"
@@ -45,6 +48,167 @@
 static struct ai_data aidata[MAX_NUM_PLAYERS + MAX_NUM_BARBARIANS];
 
 /**************************************************************************
+  Fill movemap with data.  This consumes a lot of CPU.
+**************************************************************************/
+void ai_data_movemap_init(void)
+{
+  movemap = fc_calloc(sizeof(*movemap), MAP_INDEX_SIZE);
+  whole_map_iterate(ptile) {
+    movemap_vector_init(&MOVEMAP(ptile).one_turn);
+    movemap_vector_init(&MOVEMAP(ptile).two_turn);
+  } whole_map_iterate_end;
+}
+
+/**************************************************************************
+  Dealloc.
+**************************************************************************/
+void ai_data_movemap_done(void)
+{
+  /* Clean the slate */
+  whole_map_iterate(ptile) {
+    movemap_vector_free(&MOVEMAP(ptile).one_turn);
+    movemap_vector_free(&MOVEMAP(ptile).two_turn);
+  } whole_map_iterate_end;
+
+  free(movemap);
+}
+
+/**************************************************************************
+  Insert unit into list unless it is already there. Expensive operation
+  but necessary.
+**************************************************************************/
+static inline void movemap_insert(struct movemap_vector *vector,
+                                 struct unit *punit)
+{
+  bool has_already = FALSE;
+  movemap_vector_iterate((*vector), check) {
+    if (check == punit->id) {
+      has_already = TRUE;
+      break;
+    }
+  } movemap_vector_iterate_end;
+  if (!has_already) {
+    movemap_vector_append(vector, &punit->id);
+  }
+}
+/**************************************************************************
+  Check where passengers onboard ferries can go.  turns is the number of
+  turns the passenger can walk on land in a two turn horizon, ie either 
+  one or two.
+
+  We have to check that we don't accidentially insert a unit into a vector
+  where it already exists. This is cumbersome but I see no other way.
+**************************************************************************/
+static void movemap_check_ferry(struct tile *ptile1, int id, int turns)
+{
+  struct unit *ferry = find_unit_by_id(id);
+
+  if (get_transporter_occupancy(ferry) > 0
+      && is_sailing_unit(ferry)) {
+    struct tile *ptile = ferry->tile;
+
+    unit_list_iterate(ptile->units, passenger) {
+      struct pf_map *pfmap;
+      struct pf_parameter parameter;
+      int moverate = unit_move_rate(passenger);
+
+      if (!is_ground_unit(passenger)
+          || passenger->transported_by != ferry->id) {
+        continue;
+      }
+      pft_fill_unit_attack_param(&parameter, passenger);
+      parameter.start_tile = ptile1; /* Suppose it landed right here... */
+      pfmap = pf_create_map(&parameter);
+      pf_iterator(pfmap, pos) {
+        if (turns == 1) {
+          /* We lost one turn landing on the beach or driving the ferry. */
+          if (pos.total_MC > moverate) {
+            /* This is too far */
+            break;
+          } else if (pos.total_MC <= moverate) {
+            movemap_insert(&MOVEMAP(pos.tile).two_turn, passenger);
+          }
+        } else { /* turns == 2 */
+          if (pos.total_MC > moverate * 2) {
+            /* This is too far */
+            break;
+          } else if (pos.total_MC <= moverate) {
+            movemap_insert(&MOVEMAP(pos.tile).one_turn, passenger);
+          } else {
+            movemap_insert(&MOVEMAP(pos.tile).two_turn, passenger);
+          }
+        }
+      } pf_iterator_end;
+      pf_destroy_map(pfmap);
+    } unit_list_iterate_end;
+  }
+}
+
+/**************************************************************************
+  The movemap structure is a quick way to find threats on the map.  Use
+  the iterators defined in the header file to iterate over units that
+  can reach a tile in one or two turns (as specified).  We correctly 
+  calculate in the possibility of travel by ferry.
+
+  This function fills the movemap with data.  It is rather CPU intensive.
+**************************************************************************/
+void ai_data_movemap_recalculate(void)
+{
+  /* Clean the slate */
+  whole_map_iterate(ptile) {
+    movemap_vector_free(&MOVEMAP(ptile).one_turn);
+    movemap_vector_free(&MOVEMAP(ptile).two_turn);
+  } whole_map_iterate_end;
+
+  players_iterate(pplayer) {
+    unit_list_iterate(pplayer->units, punit) {
+      struct pf_map *pfmap;
+      struct pf_parameter parameter;
+      int moverate = unit_move_rate(punit);
+
+      if (get_transporter_occupancy(punit) > 0
+          && is_sailing_unit(punit)) {
+        pft_fill_unit_overlap_param(&parameter, punit);
+      } else {
+        pft_fill_unit_attack_param(&parameter, punit);
+      }
+      pfmap = pf_create_map(&parameter);
+      pf_iterator(pfmap, pos) {
+        if (pos.total_MC > moverate * 2) {
+          /* This is too far */
+          break;
+        } else if (pos.total_MC <= moverate) {
+          movemap_vector_append(&MOVEMAP(pos.tile).one_turn, &punit->id);
+        } else {
+          movemap_vector_append(&MOVEMAP(pos.tile).two_turn, &punit->id);
+        }
+      } pf_iterator_end;
+      pf_destroy_map(pfmap);
+    } unit_list_iterate_end;
+  } players_iterate_end;
+
+  /* Now do ferries. This is ugly, but correct. I gave up on beating
+   * pf into submission. It is also probably very very slow. */
+  whole_map_iterate(ptile) {
+    if (is_ocean(map_get_terrain(ptile))) {
+      continue;
+    }
+    /* Check all ferries that can land on this spot in one turn horizon. */
+    movemap_vector_iterate(MOVEMAP(ptile).one_turn, id) {
+      movemap_check_ferry(ptile, id, 2 - game.slow_invasions);
+    } movemap_vector_iterate_end;
+    /* If game.slow_invasions is set, we do not need to fear ferries two
+     * turns away, since any units they drop off cannot move until the
+     * third turn, and the third turn is none of our responsibility. */
+    if (!game.slow_invasions) {
+      movemap_vector_iterate(MOVEMAP(ptile).two_turn, id) {
+        movemap_check_ferry(ptile, id, 1);
+      } movemap_vector_iterate_end;
+    }
+  } whole_map_iterate_end;
+}
+
+/**************************************************************************
   Precalculates some important data about the improvements in the game
   that we use later in ai/aicity.c.  We mark improvements as 'calculate'
   if we want to run a full test on them, as 'estimate' if we just want
diff -ruN -Xvendor.freeciv.current/diff_ignore 
vendor.freeciv.current/ai/aidata.h freeciv.PR10694-3/ai/aidata.h
--- vendor.freeciv.current/ai/aidata.h  2004-12-29 19:44:10.000000000 +0000
+++ freeciv.PR10694-3/ai/aidata.h       2004-12-30 21:02:37.000000000 +0000
@@ -16,6 +16,7 @@
 /* max size of a short */
 #define MAX_NUM_ID 32767
 
+#include "genlist.h"
 #include "shared.h"            /* bool type */
 
 #include "fc_types.h"
@@ -26,6 +27,16 @@
  * start of every turn. 
  */
 
+#define SPECVEC_TAG movemap
+#define SPECVEC_TYPE int
+#include "specvec.h"
+
+struct movemap_type {
+  struct movemap_vector one_turn;
+  struct movemap_vector two_turn;
+} *movemap;
+#define MOVEMAP(ptile) movemap[map_pos_to_index(ptile->x, ptile->y)]
+
 enum ai_improvement_status {
   AI_IMPR_CALCULATE, /* Calculate exactly its effect */
   AI_IMPR_ESTIMATE,  /* Estimate its effect using wild guesses */
@@ -149,4 +160,33 @@
 
 struct ai_data *ai_data_get(struct player *pplayer);
 
+void ai_data_movemap_recalculate(void);
+void ai_data_movemap_init(void);
+void ai_data_movemap_done(void);
+
+#define movemap_vector_iterate(movevec, num)             \
+  { \
+    unsigned int num_i; \
+    for(num_i = 0; num_i < movemap_vector_size(&(movevec)); \
+       num_i++) { \
+      int num = *movemap_vector_get(&(movevec), num_i);
+#define movemap_vector_iterate_end                      \
+  }}
+
+#define movemap_iterate_one_turn(ptile, punit)             \
+  movemap_vector_iterate(MOVEMAP(ptile).one_turn, miot) {       \
+    struct unit *punit = find_unit_by_id(miot);          \
+    if (punit) {
+#define movemap_iterate_one_turn_end                      \
+    }                                                     \
+  } movemap_vector_iterate_end
+
+#define movemap_iterate_two_turn(ptile, punit)             \
+  movemap_vector_iterate(MOVEMAP(ptile).two_turn, miot) {       \
+    struct unit *punit = find_unit_by_id(miot);          \
+    if (punit) {
+#define movemap_iterate_two_turn_end                      \
+    }                                                     \
+  } movemap_vector_iterate_end
+    
 #endif
diff -ruN -Xvendor.freeciv.current/diff_ignore 
vendor.freeciv.current/ai/aiferry.c freeciv.PR10694-3/ai/aiferry.c
--- vendor.freeciv.current/ai/aiferry.c 2004-12-29 19:44:10.000000000 +0000
+++ freeciv.PR10694-3/ai/aiferry.c      2004-12-30 21:02:37.000000000 +0000
@@ -27,6 +27,7 @@
 
 #include "aidata.h"
 #include "aiexplorer.h"
+#include "aiguard.h"
 #include "ailog.h"
 #include "aitools.h"
 #include "aiunit.h"
@@ -417,7 +418,7 @@
       boatid = aiferry_find_boat(punit, 2, &path_to_ferry);
       if (boatid <= 0) {
         UNIT_LOG(LOGLEVEL_GOBYBOAT, punit, 
-                "in ai_gothere cannot find any boats.");
+                "in aiferry_find_boat cannot find any boats.");
         return FALSE;
       }
 
@@ -511,7 +512,7 @@
         if (!goto_is_sane(bodyguard, punit->tile, TRUE)
             || !ai_unit_goto(punit, punit->tile)) {
           /* Bodyguard can't get there or died en route */
-          punit->ai.bodyguard = BODYGUARD_WANTED;
+         aiguard_request_guard(punit);
           bodyguard = NULL;
         } else if (bodyguard->moves_left <= 0) {
           /* Wait for me, I'm cooooming!! */
@@ -520,7 +521,7 @@
         } else {
           /* Crap bodyguard. Got stuck somewhere. Ditch it! */
           UNIT_LOG(LOGLEVEL_GOBYBOAT, punit, "ditching useless bodyguard");
-          punit->ai.bodyguard = BODYGUARD_WANTED;
+         aiguard_request_guard(punit);
           ai_unit_new_role(bodyguard, AIUNIT_NONE, NULL);
           bodyguard = NULL;
         }
diff -ruN -Xvendor.freeciv.current/diff_ignore 
vendor.freeciv.current/ai/aiguard.c freeciv.PR10694-3/ai/aiguard.c
--- vendor.freeciv.current/ai/aiguard.c 1970-01-01 01:00:00.000000000 +0100
+++ freeciv.PR10694-3/ai/aiguard.c      2004-12-30 21:02:37.000000000 +0000
@@ -0,0 +1,653 @@
+/********************************************************************** 
+ Freeciv - Copyright (C) 2004 - The Freeciv Project
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+***********************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "citytools.h"
+#include "log.h"
+#include "path_finding.h"
+#include "pf_tools.h"
+#include "rand.h"
+#include "unit.h"
+
+#include "aicity.h"
+#include "aidata.h"
+#include "ailog.h"
+#include "aitools.h"
+#include "aiunit.h"
+#include "aithreat.h"
+
+#include "aiguard.h"
+
+
+
+/**************************************************************************
+  Remove assignment of bodyguard for a unit.
+**************************************************************************/
+void aiguard_clear_guard(struct unit *punit)
+{
+  if (0 < punit->ai.bodyguard) {
+    struct unit *guard = find_unit_by_id(punit->ai.bodyguard);
+
+    if (guard && guard->ai.charge == punit->id) {
+      /* punit doesn't want us anymore */
+      guard->ai.charge = BODYGUARD_NONE;
+    }
+  }
+
+  punit->ai.bodyguard = BODYGUARD_NONE;
+}
+
+/**************************************************************************
+  Assign a bodyguard to a unit.
+**************************************************************************/
+void aiguard_assign_guard_unit(struct unit *charge, struct unit *guard)
+{
+  assert(charge);
+  assert(guard);
+  assert(guard != charge);
+  /* Usually, same_pos(charge->tile, guard->tile),
+   * but that need not be so. */
+
+  /* Remove previous assignment: */
+  aiguard_clear_guard(charge);
+
+  charge->ai.bodyguard = guard->id;
+  if (guard->ai.charge != BODYGUARD_NONE && guard->ai.charge != charge->id) {
+    struct unit *old_charge = find_unit_by_id(guard->ai.charge);
+    if (old_charge && old_charge->ai.bodyguard != BODYGUARD_NONE) {
+      /* Our previous charge will have to look for a new body guard */
+      aiguard_request_guard(old_charge);
+    }
+  }
+  guard->ai.charge = charge->id;
+  BODYGUARD_LOG(LOGLEVEL_BODYGUARD, guard, "assigned charge");
+}
+
+/**************************************************************************
+  Assign a bodyguard to a city.
+**************************************************************************/
+void aiguard_assign_guard_city(struct city *charge, struct unit *guard)
+{
+  assert(charge);
+  assert(guard);
+  if (guard->tile == charge->tile && guard->homecity != charge->id
+      && guard->owner == charge->owner) {
+    /* Try to rationalise home city assignments;
+     * may fail. */
+    (void) ai_unit_make_homecity(guard, charge);
+  }
+  if (guard->homecity == charge->id) {
+    ai_unit_new_role(guard, AIUNIT_DEFEND_HOME, charge->tile);
+    UNIT_LOG(LOGLEVEL_BODYGUARD, guard,
+            "will join homeguard garrison (d=%d,%d,%d,%d)",
+            get_city_danger(charge, TH_NEAR),
+            get_city_danger(charge, TH_FAR),
+            get_city_danger(charge, TH_NEAR_DIPLOMACY),
+            get_city_danger(charge, TH_FAR_DIPLOMACY));
+  } else {
+    /* temporary assignment */
+    ai_unit_new_role(guard, AIUNIT_ESCORT, charge->tile);
+    guard->ai.charge = charge->id;
+    BODYGUARD_LOG(LOGLEVEL_BODYGUARD, guard, "will reinforce garrison");
+  }
+}
+
+/**************************************************************************
+  Request a bodyguard for the unit.
+**************************************************************************/
+void aiguard_request_guard(struct unit *punit)
+{
+  UNIT_LOG(LOGLEVEL_BODYGUARD, punit, "requests a guard, d=%d,%d,%d,%d",
+          get_unit_danger(punit, TH_NEAR), get_unit_danger(punit, TH_FAR),
+          get_unit_danger(punit, TH_NEAR_DIPLOMACY),
+          get_unit_danger(punit, TH_FAR_DIPLOMACY));
+  punit->ai.bodyguard = BODYGUARD_WANTED;
+}
+
+/****************************************************************************
+  Remove all assignments of guards.
+****************************************************************************/
+void aiguard_clear_guards(struct player *pplayer)
+{
+  unit_list_iterate_safe(pplayer->units, punit) {
+    /* clear bodyguards */
+    punit->ai.charge = BODYGUARD_NONE;
+    punit->ai.bodyguard = BODYGUARD_NONE;
+
+    /* enable reassignment guards */
+    if (punit->ai.ai_role == AIUNIT_DEFEND_HOME
+       || punit->ai.ai_role == AIUNIT_ESCORT
+       || punit->ai.ai_role == AIUNIT_FORTIFY) {
+      punit->ai.ai_role = AIUNIT_NONE;
+    }
+  } unit_list_iterate_safe_end;
+
+  /* clear garrisons */
+  city_list_iterate(pplayer->cities, pcity) {
+    unit_list_unlink_all(&pcity->ai.garrison);
+    unit_list_init(&pcity->ai.garrison);
+  } city_list_iterate_end;
+}
+
+
+/**************************************************************************
+  Whether a unit should be considered part of a garrison of a city.
+**************************************************************************/
+bool is_garrison(const struct unit *punit)
+{
+  const struct city *pcity = punit->tile->city;
+  if (pcity) {
+    switch (punit->ai.ai_role) {
+    case AIUNIT_DEFEND_HOME:
+      return pcity->id == punit->homecity;
+    case AIUNIT_ESCORT:
+      return pcity->id == punit->ai.charge;
+    case AIUNIT_FORTIFY:
+    case AIUNIT_RECOVER:
+      return TRUE;
+    default:
+      return FALSE;
+    }
+  };
+  return FALSE;
+}
+
+/**************************************************************************
+  Update the records of whivch units form the garrisons of which cities.
+**************************************************************************/
+void aiguard_update_garrisons(struct player *pplayer)
+{
+  city_list_iterate(pplayer->cities, pcity) {
+    unit_list_unlink_all(&pcity->ai.garrison);
+    unit_list_init(&pcity->ai.garrison);
+
+    unit_list_iterate(pcity->tile->units, punit) {
+      if (is_garrison(punit)) {
+       unit_list_insert(&pcity->ai.garrison, punit);
+      }
+    } unit_list_iterate_end;
+  } city_list_iterate_end;
+}
+
+
+/****************************************************************************
+  Is a unit at all suitable as a guard for a charge?
+****************************************************************************/
+static bool aiguard_is_suitable_city(struct unit *guard, struct city *charge)
+{
+  return guard->owner == charge->owner
+      && (guard->ai.ai_role == AIUNIT_ESCORT
+         || guard->ai.ai_role == AIUNIT_FORTIFY
+         || guard->ai.ai_role == AIUNIT_NONE)
+      && (guard->ai.ai_role != AIUNIT_ESCORT
+         || guard->ai.charge == BODYGUARD_NONE)
+      && unit_type(guard)->move_type == LAND_MOVING
+      && !unit_flag(guard, F_NONMIL);
+}
+
+/****************************************************************************
+  Is a unit at all suitable as a guard for a charge?
+
+  We do not consider units with higher movement than us, or units that
+  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.
+****************************************************************************/
+bool aiguard_is_suitable(struct unit * guard, struct unit * charge)
+{
+  return unit_move_rate(charge) <= unit_move_rate(guard)
+      && unit_type(charge)->move_type == unit_type(guard)->move_type
+      && DEFENCE_POWER(charge) < DEFENCE_POWER(guard)
+      && (!is_military_unit(charge) || 0 < get_transporter_capacity(charge)
+         || ATTACK_POWER(guard) < ATTACK_POWER(charge));
+}
+
+/****************************************************************************
+  How suitable is a unit as a guard for a charge?
+  Larger values mean more suitable. Only positive values indicate that
+  the guard is at all suitable.
+****************************************************************************/
+int aiguard_suitability(struct unit *guard,
+                       struct unit *charge, const int dist_turns)
+{
+  int score = 0;
+  if (aiguard_is_suitable(guard, charge)) {
+    const int toughness = unit_def_rating_basic_sq(guard);
+    if (0 == toughness) {
+      /* useless */
+      return 0;
+    }
+    score = (toughness - unit_def_rating_basic_sq(charge));
+    if (score <= 0) {
+      return 0;
+    }
+    if (get_transporter_capacity(charge) == 0) {
+      /* Reduce want based on distance. We can't do this for
+       * transports since they move around all the time, leading
+       * to hillarious flip-flops */
+      score = score >> (dist_turns / 2);
+    }
+  }
+  return score;
+}
+
+#define CHOOSE_GOOD(G1, G2) \
+{ \
+  const bool good1 = (G1); \
+  const bool good2 = (G2); \
+  if (good1 && !good2) { \
+    return TRUE; \
+  } else if (!good1 && good2) { \
+    return FALSE; \
+  } \
+}
+
+/****************************************************************************
+  Is guard1 a better guard than guard2 for a particular charge?
+****************************************************************************/
+static bool aiguard_better_city_guard(struct city *charge,
+                                     struct unit *guard1,
+                                     struct unit *guard2,
+                                     const enum aithreat_class threat_class)
+{
+  unsigned int relative_defence;
+
+  /* something */
+  CHOOSE_GOOD(guard1 != NULL, guard2 != NULL);
+  if (guard1 == NULL && guard2 == NULL) {
+    /* whatever */
+    return FALSE;
+  }
+
+  /* suitable */
+  CHOOSE_GOOD(aiguard_is_suitable_city(guard1, charge),
+             aiguard_is_suitable_city(guard2, charge));
+
+
+  /* diplomacy */
+  switch (threat_class) {
+  case TH_NEAR:
+  case TH_FAR:
+    /* Try not to use diplomats as physical defenders */
+    CHOOSE_GOOD(!unit_flag(guard1, F_SPY), !unit_flag(guard2, F_SPY));
+    CHOOSE_GOOD(!unit_flag(guard1, F_DIPLOMAT),
+               !unit_flag(guard2, F_DIPLOMAT));
+    break;
+  case TH_NEAR_DIPLOMACY:
+  case TH_FAR_DIPLOMACY:
+    /* Try to use diplomats as diplomatic defenders */
+    CHOOSE_GOOD(unit_flag(guard1, F_SPY), unit_flag(guard2, F_SPY));
+    CHOOSE_GOOD(unit_flag(guard1, F_DIPLOMAT),
+               unit_flag(guard2, F_DIPLOMAT));
+    break;
+  default:
+    break;
+  }
+
+  /* stronger */
+  relative_defence = DEFENCE_POWER(guard1) - DEFENCE_POWER(guard2);
+  if (0 < relative_defence) {
+    return TRUE;
+  } else if (relative_defence < 0) {
+    return FALSE;
+  }
+
+  /* here */
+  CHOOSE_GOOD(guard1->tile == charge->tile, guard2->tile == charge->tile);
+
+  /* homeguard */
+  CHOOSE_GOOD(guard1->homecity == charge->id,
+             guard2->homecity == charge->id);
+
+  /* whatever */
+  return FALSE;
+}
+
+/****************************************************************************
+  How much danger is enough to warrant assignment of a guard?
+  This is somewhat greater than zero, so we hold some defensive units
+  in reserve.
+****************************************************************************/
+static Danger aiguard_plan_min_threat(struct player *pplayer)
+{
+  /* How important is it to guard cities?
+   * If building them is costly, very important. */
+  const Unit_Type_id rebuilder_type = best_role_unit_for_player(pplayer,
+                                                               F_CITIES);
+  const int rebuilder_value = unit_build_shield_cost(rebuilder_type);
+  /* WAG division, to implement a minimum conquest probabilty */
+  return MAX(1, rebuilder_value / 4);
+}
+
+/****************************************************************************
+  Assign guards to cities.
+
+  Assumes all homeguards set to AIUNIT_ESCORT or AIUNIT_NONE.
+
+  Try to assign guards to the most vulnerable cities,
+  if we have a shortge of guards.
+  Consider only guards that can arrive this turn.
+  Try to assign the strongest guards to the most vulnerable cities,
+  if we have a choice.
+  Assign multiple guards to cities, if their degree of danger warrants it.
+
+  This function requires a valid movemap.
+  This function requires that the recorded danger ratings
+  for cities are from the PoV of the given player and up to date.
+****************************************************************************/
+void aiguard_plan_cities(struct player *pplayer,
+                        const enum aithreat_class threat_class)
+{
+  const Danger min_city_threat = aiguard_plan_min_threat(pplayer);
+  unsigned int assigned = 0;
+
+  /* We assign in multiple rounds, so we can add multiple guards
+   * to cities */
+  do {
+    /* One round of assignments */
+    assigned = 0;
+
+    /* Consider the most vulnerable cities first,
+     * so they get the best guards */
+    sort_cities_decreasing_danger(&pplayer->cities, threat_class);
+
+    city_list_iterate(pplayer->cities, charge) {
+      struct unit *guard = NULL;
+      if (get_city_danger(charge, threat_class) <= min_city_threat) {
+       continue;
+      }
+      /* else find the best available guard for this city: */
+
+      /* Consider units that are already here */
+      unit_list_iterate(charge->tile->units, aunit) {
+       if (aiguard_better_city_guard(charge, aunit, guard, threat_class)) {
+         guard = aunit;
+       }
+      } unit_list_iterate_end;
+
+      /* Consider units that can arrive this turn */
+      movemap_iterate_one_turn(charge->tile, aunit) {
+       if (aiguard_better_city_guard(charge, aunit, guard, threat_class)) {
+         guard = aunit;
+       }
+      } movemap_iterate_one_turn_end;
+
+      if (guard && aiguard_is_suitable_city(guard, charge)) {
+       aiguard_assign_guard_city(charge, guard);
+       assigned++;
+       /* Has this guard made us safe enough? Reassess the defences. */
+       unit_list_insert(&charge->ai.garrison, guard);
+       aithreat_update_city_danger(charge);
+      }
+    } city_list_iterate_end;
+  } while (assigned);
+}
+
+/****************************************************************************
+  Is guard1 a better guard than guard2 for a particular charge?
+****************************************************************************/
+static bool aiguard_better_unit_guard(struct unit *charge,
+                                     struct unit *guard1,
+                                     struct unit *guard2)
+{
+  if (guard1 == charge) {
+    return FALSE;              /* do not guard self */
+  } else if (guard1->ai.ai_role != AIUNIT_ESCORT
+            || guard1->ai.charge != BODYGUARD_NONE
+            || !aiguard_is_suitable(guard1, charge)) {
+    /* not at all suitable */
+    return FALSE;
+  } else if (guard2 == NULL) {
+    /* better than nothing */
+    return TRUE;
+  } else if (DEFENCE_POWER(guard2) < DEFENCE_POWER(guard1)) {
+    /* stronger */
+    return TRUE;
+  } else if (DEFENCE_POWER(guard2) == DEFENCE_POWER(guard1)
+            && guard1->tile == charge->tile) {
+    /* here */
+    return TRUE;
+  }
+  return FALSE;
+}
+
+/****************************************************************************
+  Assign guards to charges.
+  Try to assign guards to the most vulnerable charges,
+  if we have a shortge of guards.
+  Consider only guards that can arrive this turn.
+  Try to assign the strongest guards to the most vulnerable charges,
+  if we have a choice.
+
+  This function requires a valid movemap.
+  This function requires that the recorded danger ratings
+  for units are from the PoV of the given player and up to date.
+****************************************************************************/
+void aiguard_plan_units(struct player *pplayer)
+{
+  const Danger min_unit_threat = aiguard_plan_min_threat(pplayer);
+
+  sort_units_decreasing_danger(&pplayer->units);
+
+  unit_list_iterate(pplayer->units, charge) {
+    struct unit *guard = NULL;
+    if ((charge->ai.bodyguard != BODYGUARD_WANTED
+        && charge->ai.bodyguard != BODYGUARD_NONE)
+       || get_unit_danger(charge, TH_NEAR) <= min_unit_threat) {
+      continue;
+    }
+
+    /* Consider units that are already here */
+    unit_list_iterate(charge->tile->units, aunit) {
+      if (aiguard_better_unit_guard(charge, aunit, guard)) {
+       guard = aunit;
+      }
+    } unit_list_iterate_end;
+
+    /* Consider units that can arrive this turn */
+    movemap_iterate_one_turn(charge->tile, aunit) {
+      if (aiguard_better_unit_guard(charge, aunit, guard)) {
+       guard = aunit;
+      }
+    } movemap_iterate_one_turn_end;
+
+    if (guard) {
+      aiguard_assign_guard_unit(charge, guard);
+    }
+  } unit_list_iterate_end;
+}
+
+/****************************************************************************
+  Is a unit on a dangerous mission that will require assignment of a gaurd
+  despite the current danger rating of the unit?
+****************************************************************************/
+static bool aiguard_need_guard(struct unit *punit)
+{
+  if (unit_has_role(punit->type, L_DEFEND_GOOD)) {
+    /* I can look after myself. */
+    return FALSE;
+  }
+  if (unit_has_role(punit->type, L_ATTACK_FAST)) {
+    /* I rely on my speed for safety,
+     * and guards will have difficulty keeping up. */
+    return FALSE;
+  }
+  if (unit_flag(punit, F_PARTIAL_INVIS)) {
+    /* An escort would only give away my position. */
+    return FALSE;
+  }
+  if (unit_has_role(punit->type, L_FERRYBOAT)
+      || unit_has_role(punit->type, L_GAMELOSS)
+      || unit_has_role(punit->type, L_DIPLOMAT)
+      || unit_flag(punit, F_DIPLOMAT)
+      || unit_flag(punit, F_GAMELOSS)
+      || unit_flag(punit, F_CARRIER)) {
+    /* Enemy hunters will target me. */
+    return TRUE;
+  }
+  if (unit_type(punit)->defense_strength < unit_type(punit)->attack_strength
+      && (punit->ai.ai_role == AIUNIT_ATTACK
+         || punit->ai.ai_role == AIUNIT_HUNTER
+         || punit->ai.ai_role == AIUNIT_PILLAGE)) {
+    /* I am on the offensive, but will be vulnerable to counter atttacks. */
+    return TRUE;
+  }
+  return FALSE;
+}
+
+
+/****************************************************************************
+  Decide which units should request bodyguards.
+****************************************************************************/
+void aiguard_request_unit_guards(struct player *pplayer)
+{
+  unit_list_iterate(pplayer->units, punit) {
+    if (aiguard_need_guard(punit)) {
+      aiguard_request_guard(punit);
+    }
+  } unit_list_iterate_end;
+}
+
+/*
+ * How many turns of movement to consider when searching for a charge.
+ * Although large values will increase the probability of finding a charge,
+ * by the time the guard arrives, the danger might be over or the charge
+ * might already be dead.
+ */
+#define MAX_FIND_CHARGE_RANGE 3
+#define MIN_FIND_CHARGE_RANGE 1
+/*
+ * Cut short the search once we have a charge that needs a guard this urgently,
+ * if we have searched at least MIN_FIND_CHARGE_RANGE turns ahead.
+ */
+#define URGENT_ENOUGH 10
+/*
+ * How important is long-term danger when considering a potential charge?
+ * For very small values, long-term danger is effectively only a tie breaker.
+ */
+#define FIND_CHARGE_LONG_TERM_WT_UNWANTED 5
+#define FIND_CHARGE_LONG_TERM_WT_WANTED 30
+/*
+ * Larger values favour guarding nearby units,
+ * and reduce yo-yoing of guards.
+ */
+#define NEAR_BIAS 2
+/****************************************************************************
+  Find a city or unit for a potential bodyguard to guard,
+  if possible.
+  Tries to find a nearby unit or city that is in danger,
+  perfering the most endangered  and closest.
+  Randomly breaks ties, so guards do not bunch together when everything
+  is equally endangered.
+  If sucessful, sets the found object as the charge of the guard.
+  Return value is whether was successful.
+  This function is relatively expensive, because it uses PF.
+****************************************************************************/
+bool aiguard_find_charge(struct unit *guard)
+{
+  const int owner = guard->owner;
+  const int move_rate = unit_move_rate(guard);
+
+  unsigned int greatest_urgency = 0;
+  unsigned int turns = 0;
+  bool good_enough = FALSE;
+  struct city *charge_city = NULL;
+  struct unit *charge_unit = NULL;
+
+  struct pf_parameter param;
+  struct pf_map *search_map;
+
+  assert(guard);
+  assert(guard->ai.charge == BODYGUARD_NONE);
+
+  pft_fill_unit_parameter(&param, guard);
+  param.turn_mode = TM_WORST_TIME;
+  param.get_TB = no_fights_or_unknown;
+  param.moves_left_initially += (MAX_FIND_CHARGE_RANGE - 1) * move_rate;
+
+  search_map = pf_create_map(&param);
+
+  /* We want to consider the place we are currently in too, hence the 
+   * do-while loop */
+  do {
+    /* all tiles nearby */
+    struct pf_position pos;
+    unsigned int time_divider;
+    pf_next_get_position(search_map, &pos);
+    turns = pos.total_MC / move_rate;
+    time_divider = 1 + NEAR_BIAS * pos.total_MC;
+
+    /* Consider guarding one of the units on this tile */
+    unit_list_iterate(pos.tile->units, punit) {
+      if (owner == punit->owner
+         && (punit->ai.bodyguard == BODYGUARD_WANTED
+             || punit->ai.bodyguard == BODYGUARD_NONE)
+         && guard != punit && aiguard_is_suitable(guard, punit)) {
+       const Danger d1 = get_unit_danger(punit, TH_NEAR);
+       const Danger d2 = get_unit_danger(punit, TH_FAR);
+       const unsigned int w = (punit->ai.bodyguard == BODYGUARD_WANTED)
+           ? FIND_CHARGE_LONG_TERM_WT_WANTED
+           : FIND_CHARGE_LONG_TERM_WT_UNWANTED;
+       const unsigned int urgency = (d1 + d2 * w / 100)
+           * NEAR_BIAS / time_divider;
+
+       if (greatest_urgency < urgency
+           || (greatest_urgency == urgency && myrand(1))) {
+         greatest_urgency = urgency;
+         charge_unit = punit;
+         charge_city = NULL;
+       }
+      }
+    } unit_list_iterate_end;
+
+    /* Consider guarding the city on this tile */
+    if (unit_type(guard)->move_type == LAND_MOVING
+       && pos.tile->city && owner == pos.tile->city->owner) {
+      const Danger d1 = get_city_danger(pos.tile->city, TH_NEAR);
+      const Danger d2 = get_city_danger(pos.tile->city, TH_FAR);
+      const unsigned int w = FIND_CHARGE_LONG_TERM_WT_UNWANTED;
+      const unsigned int urgency = (d1 + d2 * w / 100)
+         * NEAR_BIAS / time_divider;
+
+      if (greatest_urgency < urgency
+         || (greatest_urgency == urgency && myrand(1))) {
+       greatest_urgency = urgency;
+       charge_unit = NULL;
+       charge_city = pos.tile->city;
+      }
+    }
+
+    good_enough = (URGENT_ENOUGH <= greatest_urgency
+                  && MIN_FIND_CHARGE_RANGE < turns);
+  } while (!good_enough && pf_next(search_map));
+  pf_destroy_map(search_map);
+
+  if (charge_city) {
+    UNIT_LOG(LOGLEVEL_BODYGUARD, guard, "guard found charge, urgency=%d",
+            greatest_urgency);
+    aiguard_assign_guard_city(charge_city, guard);
+    return TRUE;
+  } else if (charge_unit) {
+    UNIT_LOG(LOGLEVEL_BODYGUARD, guard,
+            "guard found charge, urgency=%d, wanted=%d",
+            greatest_urgency,
+            charge_unit->ai.bodyguard == BODYGUARD_WANTED);
+    ai_unit_new_role(guard, AIUNIT_ESCORT, charge_unit->tile);
+    aiguard_assign_guard_unit(charge_unit, guard);
+    return TRUE;
+  }
+  return FALSE;
+}
diff -ruN -Xvendor.freeciv.current/diff_ignore 
vendor.freeciv.current/ai/aiguard.h freeciv.PR10694-3/ai/aiguard.h
--- vendor.freeciv.current/ai/aiguard.h 1970-01-01 01:00:00.000000000 +0100
+++ freeciv.PR10694-3/ai/aiguard.h      2004-12-30 21:02:37.000000000 +0000
@@ -0,0 +1,61 @@
+/********************************************************************** 
+ Freeciv - Copyright (C) 2002 - The Freeciv Project
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+***********************************************************************/
+#ifndef FC__AIGUARD_H
+#define FC__AIGUARD_H
+
+#include "shared.h"            /* bool type */
+
+#include "fc_types.h"
+#include "aithreat.h"
+
+enum bodyguard_enum {
+  /* A hint to the bodyguard code that a unit is on a dangerous mission,
+   * so that, even if its current danger rating is low,
+   * it will be moving in the near future to somewhere dangerous
+   * where it will need a guard.
+   * NOT an indication that a unit is somewhere dangerous;
+   * the bodyguard code can loog at aithreat values for that information.
+   */
+  BODYGUARD_WANTED = -1,
+  BODYGUARD_NONE
+};
+
+bool is_garrison(const struct unit *punit);
+
+void aiguard_clear_guard(struct unit *punit);
+
+void aiguard_assign_guard_unit(struct unit *charge, struct unit *guard);
+
+void aiguard_assign_guard_city(struct city *charge, struct unit *guard);
+
+void aiguard_request_guard(struct unit *punit);
+
+void aiguard_clear_guards(struct player *pplayer);
+
+bool aiguard_is_suitable(struct unit *guard, struct unit *charge);
+
+int aiguard_suitability(struct unit *guard,
+                       struct unit *charge, const int dist_turns);
+
+void aiguard_update_garrisons(struct player *pplayer);
+
+void aiguard_request_unit_guards(struct player *pplayer);
+
+void aiguard_plan_cities(struct player *pplayer,
+                        const enum aithreat_class threat_class);
+
+void aiguard_plan_units(struct player *pplayer);
+
+bool aiguard_find_charge(struct unit *guard);
+
+#endif /* FC__AIGUARD_H */
diff -ruN -Xvendor.freeciv.current/diff_ignore 
vendor.freeciv.current/ai/aihunt.c freeciv.PR10694-3/ai/aihunt.c
--- vendor.freeciv.current/ai/aihunt.c  2004-12-29 19:44:10.000000000 +0000
+++ freeciv.PR10694-3/ai/aihunt.c       2004-12-30 21:02:37.000000000 +0000
@@ -274,7 +274,7 @@
     /* Note that we need not (yet) be at war with aplayer */
     unit_list_iterate(aplayer->units, target) {
       struct tile *ptile = target->tile;
-      int dist1, dist2, stackthreat = 0, stackcost = 0;
+      int dist1, dist2, targetvalue = 0;
       struct unit *defender;
 
       if (ptile->city
@@ -311,34 +311,27 @@
                  TILE_XY(target->tile));
         continue;
       }
-      unit_list_iterate(ptile->units, sucker) {
-        stackthreat += ATTACK_POWER(sucker);
-        if (unit_flag(sucker, F_DIPLOMAT)) {
-          stackthreat += 500;
-        }
-        stackcost += unit_type(sucker)->build_cost;
-      } unit_list_iterate_end;
+      targetvalue = stack_cost(target, 80, 25);
       defender = get_defender(punit, target->tile);
-      if (stackcost < unit_type(punit)->build_cost
+      if (targetvalue < unit_type(punit)->build_cost
           && unit_win_chance(punit, defender) < 0.6) {
         UNIT_LOG(LOGLEVEL_HUNT, punit, "chickening out from attacking %s"
                  "(%d, %d)", unit_type(defender)->name,
                  TILE_XY(defender->tile));
         continue;
       }
-      stackthreat *= 9; /* WAG */
-      stackthreat += stackcost;
-      stackthreat /= real_map_distance(punit->tile, target->tile) + 1;
+      targetvalue = targetvalue
+                    / (real_map_distance(punit->tile, target->tile) + 1);
       UNIT_LOG(LOGLEVEL_HUNT, punit, "considering hunting %s's %s(%d, %d) id "
                "id %d with want %d, dist1 %d, dist2 %d", 
                unit_owner(defender)->name, unit_type(defender)->name, 
-               TILE_XY(defender->tile), defender->id, stackthreat, dist1,
+               TILE_XY(defender->tile), defender->id, targetvalue, dist1, 
                dist2);
       /* TO DO: probably ought to WAG down targets of players we are not (yet)
        * at war with */
       /* Ok, now we FINALLY have a candidate */
-      if (stackthreat > best_val) {
-        best_val = stackthreat;
+      if (targetvalue > best_val) {
+        best_val = targetvalue;
         best_id = target->id;
       }
     } unit_list_iterate_end;
diff -ruN -Xvendor.freeciv.current/diff_ignore 
vendor.freeciv.current/ai/ailog.c freeciv.PR10694-3/ai/ailog.c
--- vendor.freeciv.current/ai/ailog.c   2004-12-29 19:44:10.000000000 +0000
+++ freeciv.PR10694-3/ai/ailog.c        2004-12-30 21:02:37.000000000 +0000
@@ -28,6 +28,7 @@
 
 #include "aidata.h"
 #include "ailog.h"
+#include "aithreat.h"
 
 /* General AI logging functions */
 
@@ -180,6 +181,7 @@
   int id = -1;
   struct tile *ptile = NULL;
   const char *s = "none";
+  Dangers dangers;
 
   if (punit->debug) {
     minlevel = LOG_NORMAL;
@@ -193,16 +195,26 @@
     ptile = pcharge->tile;
     id = pcharge->id;
     s = unit_type(pcharge)->name;
+    dangers[TH_NEAR] = get_unit_danger(pcharge, TH_NEAR);
+    dangers[TH_FAR] = get_unit_danger(pcharge, TH_FAR);
+    dangers[TH_NEAR_DIPLOMACY] = get_unit_danger(pcharge, TH_NEAR_DIPLOMACY);
+    dangers[TH_FAR_DIPLOMACY] = get_unit_danger(pcharge, TH_FAR_DIPLOMACY);
   } else if (pcity) {
     ptile = pcity->tile;
     id = pcity->id;
     s = pcity->name;
+    dangers[TH_NEAR] = get_city_danger(pcity, TH_NEAR);
+    dangers[TH_FAR] = get_city_danger(pcity, TH_FAR);
+    dangers[TH_NEAR_DIPLOMACY] = get_city_danger(pcity, TH_NEAR_DIPLOMACY);
+    dangers[TH_FAR_DIPLOMACY] = get_city_danger(pcity, TH_FAR_DIPLOMACY);
   }
   my_snprintf(buffer, sizeof(buffer),
-              "%s's bodyguard %s[%d] (%d,%d){%s:%d@%d,%d} ",
+              "%s's bodyguard %s[%d] (%d,%d){%s:%d@%d,%d, d=%d,%d,%d,%d} ",
               unit_owner(punit)->name, unit_type(punit)->name,
               punit->id, punit->tile->x, punit->tile->y,
-             s, id, ptile->x, ptile->y);
+             s, id, ptile->x, ptile->y,
+             dangers[TH_NEAR], dangers[TH_FAR],
+             dangers[TH_NEAR_DIPLOMACY], dangers[TH_FAR_DIPLOMACY]);
   cat_snprintf(buffer, sizeof(buffer), msg);
   if (punit->debug) {
     notify_conn(&game.est_connections, buffer);
diff -ruN -Xvendor.freeciv.current/diff_ignore 
vendor.freeciv.current/ai/aithreat.c freeciv.PR10694-3/ai/aithreat.c
--- vendor.freeciv.current/ai/aithreat.c        1970-01-01 01:00:00.000000000 
+0100
+++ freeciv.PR10694-3/ai/aithreat.c     2004-12-30 21:02:37.000000000 +0000
@@ -0,0 +1,1065 @@
+/********************************************************************** 
+ Freeciv - Copyright (C) 2004 - The Freeciv Project
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+***********************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "citytools.h"
+#include "diplomats.h"
+#include "government.h"
+#include "log.h"
+#include "path_finding.h"
+#include "pf_tools.h"
+#include "unit.h"
+
+#include "aicity.h"
+#include "aidata.h"
+#include "ailog.h"
+#include "aisupport.h"
+#include "aitools.h"
+#include "aiunit.h"
+
+#include "aithreat.h"
+
+
+
+#define LOGLEVEL_THREAT LOG_DEBUG
+/*
+ * How much weight should we assign to threats that are 2 turns away,
+ * relative to that for threats 1 turn away?
+ * That '1 turn' might be *this* turn. A percentage;
+ * 50 corresponds to inverse-time weighting;
+ * 25 corresponds to inverse-square-time weighting.
+ */
+#define THREAT_WT_2 25
+
+
+/**********************************************************************
+  The probability that we will be at war with a player within one turn.
+  Compare with the is_player_dangerous function.
+***********************************************************************/
+double aithreat_player_danger(struct player *pplayer, struct player *aplayer)
+{
+  struct ai_data *ai = ai_data_get(pplayer);
+  struct ai_dip_intel *adip =
+      &ai->diplomacy.player_intel[aplayer->player_no];
+  const int reason = pplayer->diplstates[aplayer->player_no]
+      .has_reason_to_cancel;
+  double peace = 1;
+
+  if (pplayer == aplayer) {
+    /* Have to check if aplayer == pplayer explicitly because our reputation
+     * can be so low that we'd fear being stabbed in the back by ourselves */
+    return 0;
+  }
+
+  if (pplayers_at_war(pplayer, aplayer)) {
+    peace = 0;
+  }
+  /* WAGs */
+  if (ai->diplomacy.target == aplayer) {
+    peace = peace * 0.8;
+  }
+  if (reason != 0) {
+    peace = peace * 0.5;
+  }
+  if (ai->diplomacy.acceptable_reputation > aplayer->reputation) {
+    peace = peace * 0.9;
+  }
+  if (adip->is_allied_with_enemy) {
+    peace = peace * 0.6;
+  }
+
+  return 1.0 - peace;
+}
+
+/**************************************************************************
+  The danger that a given defender unit would be in if attacked
+  by a given attacker.
+  The units of menace are expected-loss in shields.
+**************************************************************************/
+static Danger unit_menace(struct unit *attacker, struct unit *defender)
+{
+  const double p_win = unit_win_chance(attacker, defender);
+  const double p_war = aithreat_player_danger(unit_owner(defender),
+                                             unit_owner(attacker));
+  const double value = stack_cost(defender, 0, 0);
+  const double menace = p_win * p_war * value;
+
+  return menace;
+}
+
+/*
+ * Unit conversion factor. Gold is more plentiful than shields,
+ * because of road building an Republic and Democracy governemnts.
+ */
+#define GOLD_PER_SHIELD 3
+/**************************************************************************
+  How important is it to defend a city?
+  We rate the importance of units by the number of shields it costs
+  to build them, so this function computes the analogous value for a city.
+**************************************************************************/
+static unsigned int get_city_value(struct city *pcity)
+{
+  return city_gold_worth(pcity) / GOLD_PER_SHIELD;
+}
+
+/**************************************************************************
+  The probability of an attacker conquouring a city.
+**************************************************************************/
+static double conquest_chance(struct unit *attacker, struct city *defender)
+{
+  const double p_war = aithreat_player_danger(city_owner(defender),
+                                             unit_owner(attacker));
+  double p_win;
+
+  if (!COULD_OCCUPY(attacker)) {
+    /* harmless unit */
+    return 0;
+  } else if (0 == p_war) {
+    /* harmless player */
+    return 0;
+  }
+
+  p_win = 1;
+
+  /* Compute the influence of defenders:
+   * they decrease the probability of success.
+   * Ignore the successive weakening of the attacker. */
+  unit_list_iterate_safe(defender->ai.garrison, guard) {
+    p_win *= unit_win_chance(attacker, guard);
+  } unit_list_iterate_safe_end;
+
+  return p_win * p_war;
+}
+
+#define PREFER(sx1, sx2 ) \
+{\
+  const bool s1 = sx1;\
+  const bool s2 = sx2;\
+  if (s1 && !s2) {\
+    return TRUE;\
+  } else if (!s1 && s2) {\
+    return FALSE;\
+  }\
+}
+
+#define PREFER_LARGER(sx1, sx2 ) \
+{\
+  const int s1 = sx1;\
+  const int s2 = sx2;\
+  const int diff = s1 - s2; \
+  if (0 < diff) {\
+    return TRUE;\
+  } else if (diff < 0) {\
+    return FALSE;\
+  }\
+}
+
+#define PREFER_SMALLER(sx1, sx2 ) \
+{\
+  const int s1 = sx1;\
+  const int s2 = sx2;\
+  const int diff = s1 - s2; \
+  if (diff < 0) {\
+    return TRUE;\
+  } else if (0 < diff) {\
+    return FALSE;\
+  }\
+}
+
+/**************************************************************************
+  Is punit1 a better city attacker than punit2?
+  That is, in an assault, should punit1 attack before punit2.
+  TODO: Probably ought to use this function in the AI attack code;
+  as it is, this is only worthwhile for assessing the danger from
+  player attackers.
+**************************************************************************/
+static bool better_attacker(struct unit *punit1, struct unit *punit2)
+{
+  int power1, power2;
+  assert(punit1);
+  assert(punit2);
+
+  /* Do not endanger valuable units in foolish attacks */
+  PREFER(!unit_flag(punit1, F_GAMELOSS), !unit_flag(punit2, F_GAMELOSS));
+  PREFER(!unit_flag(punit1, F_NONMIL), !unit_flag(punit2, F_NONMIL));
+  PREFER(!unit_flag(punit1, F_CARRIER), !unit_flag(punit2, F_CARRIER));
+  PREFER_SMALLER(get_transporter_capacity(punit1),
+                get_transporter_capacity(punit2));
+
+  /* Keep occupiers until last, so we can always take the city
+   * if we are victorious */
+  PREFER(!COULD_OCCUPY(punit1), !COULD_OCCUPY(punit2));
+
+  /* Blitzkrieg: artillery & air attacks first */
+  PREFER(unit_flag(punit1, F_NUCLEAR), unit_flag(punit2, F_NUCLEAR));
+  PREFER(unit_flag(punit1, F_CITYBUSTER), unit_flag(punit2, F_CITYBUSTER));
+  PREFER(unit_flag(punit1, F_MISSILE), unit_flag(punit2, F_MISSILE));
+  PREFER(unit_flag(punit1, F_IGWALL), unit_flag(punit2, F_IGWALL));
+  PREFER(unit_flag(punit1, F_FIGHTER), unit_flag(punit2, F_FIGHTER));
+
+  power1 = ATTACK_POWER(punit1);
+  power2 = ATTACK_POWER(punit1);
+  /* Assume the defenders have pikemen if we have horsemen. */
+  if (unit_flag(punit1, F_HORSE)) {
+    power1 /= 2;
+  }
+  if (unit_flag(punit2, F_HORSE)) {
+    power2 /= 2;
+  }
+  PREFER_LARGER(power1, power2);
+
+  /* Once we have the city, we will want to defend it,
+   * so keep good defenders until last */
+  PREFER_SMALLER(DEFENCE_POWER(punit1), DEFENCE_POWER(punit2));
+
+  /* whatever */
+  return FALSE;
+}
+
+/**************************************************************************
+  Is defender1 a better defender than defender2,
+  against a given attacker?
+**************************************************************************/
+static bool better_defender(struct unit *defender1,
+                           struct unit *defender2, struct unit *attacker)
+{
+  int unit_def1, build_cost1, defense_rating1;
+  int unit_def2, build_cost2, defense_rating2;
+  assert(defender1);
+  assert(defender2);
+  assert(attacker);
+
+  /* This will make units roughly evenly good defenders look alike. */
+  unit_def1 = (int) (100000 * (1 - unit_win_chance(attacker, defender1)));
+  unit_def2 = (int) (100000 * (1 - unit_win_chance(attacker, defender2)));
+  if (unit_def1 < unit_def2) {
+    return TRUE;
+  } else if (unit_def2 < unit_def1) {
+    return FALSE;
+  }
+
+  build_cost1 = unit_build_shield_cost(defender1->type);
+  build_cost2 = unit_build_shield_cost(defender2->type);
+  if (build_cost1 < build_cost2) {
+    return TRUE;
+  } else if (build_cost2 < build_cost1) {
+    return FALSE;
+  }
+
+  defense_rating1 = get_defense_rating(attacker, defender1);
+  defense_rating2 = get_defense_rating(attacker, defender2);
+  if (defense_rating2 < defense_rating1) {
+    return TRUE;
+  } else if (defense_rating1 < defense_rating2) {
+    return FALSE;
+  }
+
+  /* whatever */
+  return FALSE;
+}
+
+/* Reduce the computation cost at the expense of precision */
+#define PRUNE_LOW 0.1
+#define PRUNE_HIGH 0.98
+
+/**************************************************************************
+  The probability of a force of attackers conquouring a city.
+  The given lists may include dead units.
+**************************************************************************/
+static double assault_conquest_chance(struct unit_list *attackers,
+                                     struct unit_list *defenders,
+                                     struct city *pcity)
+{
+  struct player *owner = city_owner(pcity);
+  double p = 0;
+  struct unit *attacker = 0;
+  struct unit *defender = 0;
+  struct unit_list other_attackers;
+  struct unit_list other_defenders;
+
+  /* split the list of attackers */
+  unit_list_init(&other_attackers);
+  unit_list_iterate_safe(*attackers, aunit) {
+    if (!is_attack_unit(aunit)) {
+      ;
+    } else if (attacker && better_attacker(aunit, attacker)) {
+      unit_list_insert_back(&other_attackers, attacker);
+      attacker = aunit;
+    } else if (attacker) {
+      /* tail */
+      unit_list_insert_back(&other_attackers, aunit);
+    } else {
+      /* head */
+      attacker = aunit;
+    }
+  } unit_list_iterate_safe_end;
+
+  /* split the list of defenders */
+  unit_list_init(&other_defenders);
+  unit_list_iterate_safe(*defenders, aunit) {
+    if (defender) {
+      /* tail */
+      unit_list_insert_back(&other_defenders, aunit);
+    } else if (attacker && defender
+              && better_defender(aunit, defender, attacker)) {
+      unit_list_insert_back(&other_defenders, defender);
+      defender = aunit;
+    } else {
+      /* head */
+      /* Yes, even non-military units can defend cities. */
+      defender = aunit;
+    }
+  } unit_list_iterate_safe_end;
+
+  if (!attacker) {
+    p = 0;
+  } else if (unit_owner(attacker) == owner) {
+    /* We should not fear our self */
+    p = assault_conquest_chance(&other_attackers, defenders, pcity);
+  } else if (defender) {
+    /* Let battle commence */
+    double p_win;
+    double p_war;
+    struct tile *ptile = defender->tile;
+
+    /* The defender might not be at the city yet;
+     * move it for this calculation */
+    defender->tile = pcity->tile;
+    p_win = unit_win_chance(attacker, defender);
+    defender->tile = ptile;
+
+    /* Perhaps this unit will not attack? */
+    p_war = aithreat_player_danger(owner, unit_owner(attacker));
+    p_win *= p_war;
+
+    if (p_win < PRUNE_LOW) {
+      /* Pathetic attacker; he will not attack,
+       * or he will be repulsed without losses */
+      p = assault_conquest_chance(&other_attackers, defenders, pcity);
+    } else if (PRUNE_HIGH < p_win) {
+      /* Pathetic defender; he provides no real defence */
+      p = assault_conquest_chance(attackers, &other_defenders, pcity);
+    } else {                   /* equivocate */
+      /* Assume attackers can not attack again, but defenders
+       * holdout. Over-estimates defensive strength. */
+      const double p1 = assault_conquest_chance(&other_attackers,
+                                               &other_defenders, pcity);
+      const double p2 = assault_conquest_chance(&other_attackers, defenders,
+                                               pcity);
+      p = p_win * p1 + (1.0 - p_win) * p2;
+    }
+  } else if (COULD_OCCUPY(attacker)) {
+    /* No defenders; it could be ours! */
+    /* But do we want it? */
+    const double p_war = aithreat_player_danger(owner, unit_owner(attacker));
+
+    if (0 == unit_list_size(&other_attackers) || p_war == 1) {
+      /* If we want it, it is ours. */
+      p = p_war;
+    } else {
+      /* Even if we do not, somebody else might. */
+      /* Note that having many units of a questionably neutral player nearby
+       * increases the probability that that player will take the city.
+       * Having many units nearby IS suspiscious,
+       * so it is a feature, not a bug! */
+      p = 1.0 - (1.0 - p_war)
+         * (1.0 -
+            assault_conquest_chance(&other_attackers, defenders, pcity));
+    }
+  } else {
+    /* Perhaps one of the other attackers can take this defenceless city? */
+    p = assault_conquest_chance(&other_attackers, defenders, pcity);
+  }
+
+  unit_list_unlink_all(&other_attackers);
+  unit_list_unlink_all(&other_defenders);
+
+  return p;
+}
+
+/**************************************************************************
+  The probability of an attacker inciting a revolt in a city.
+**************************************************************************/
+static double incite_chance(struct unit *attacker, struct city *defender)
+{
+  const bool diplomacy_danger = (unit_flag(attacker, F_DIPLOMAT)
+                                &&
+                                !government_has_flag(get_gov_pcity
+                                                     (defender),
+                                                     G_UNBRIBABLE)
+                                && get_city_bonus(defender, EFT_NO_INCITE)
+                                <= 0);
+  const double p_war = aithreat_player_danger(city_owner(defender),
+                                             unit_owner(attacker));
+  double p_win;
+
+  if (!diplomacy_danger) {
+    /* harmless unit */
+    return 0;
+  } else if (0 == p_war) {
+    /* harmless player */
+    return 0;
+  }
+
+  p_win = 1;
+  unit_list_iterate_safe(defender->ai.garrison, guard) {
+    if (unit_flag(guard, F_DIPLOMAT) || unit_flag(guard, F_SUPERSPY)) {
+      p_win *= diplomat_win_chance(attacker, guard, defender->tile);
+    }
+  } unit_list_iterate_safe_end;
+
+  return p_win * p_war;
+}
+
+/**************************************************************************
+  The probability of a force of inciting a revolt in a city.
+**************************************************************************/
+static double assault_incite_chance(struct unit_list attackers,
+                                   struct city *defender)
+{
+  double p_win = 0;
+
+  /* Compute the influence of defenders:
+   * they decrease the probability of success */
+  unit_list_iterate_safe(attackers, attacker) {
+    p_win = 1.0 - (1.0 - p_win) * (1.0 - incite_chance(attacker, defender));
+  } unit_list_iterate_safe_end;
+
+  return p_win;
+}
+
+/**************************************************************************
+  The danger that a given defender city would be in if attacked
+  by a given attacker.
+  The computation includes only units that have committed
+  to defending the city. There might be additional units present,
+  which are only passing through.
+  The units of menace are expected-loss in shields.
+**************************************************************************/
+static Danger city_menace(struct unit *attacker, struct city *defender)
+{
+  double value = get_city_value(defender);
+  const double p_win = MAX(conquest_chance(attacker, defender),
+                          incite_chance(attacker, defender));
+  double menace;
+
+  /* Include the cost of lost defenders. */
+  unit_list_iterate_safe(defender->ai.garrison, guard) {
+    value += unit_build_shield_cost(guard->type);
+  } unit_list_iterate_safe_end;
+
+  menace = p_win * value;
+  return menace;
+}
+
+/**************************************************************************
+  A measure of how menacing an attacking unit is to (potential) defenders
+  of a particular type (role) belonging to a player.
+**************************************************************************/
+static Danger virtual_menace(struct player *pplayer,
+                            struct unit *attacker,
+                            const enum unit_role_id defender_role)
+{
+  double chance, menace;
+  const Unit_Type_id def_type_id = best_role_unit_for_player(pplayer,
+                                                            defender_role);
+  double defender_cost;
+  struct unit_type *def_type;
+  struct unit_type *att_type;
+  int def_power;
+  int att_power;
+
+  if (def_type_id == U_LAST) {
+    /* The attacker can not menace what we can not build */
+    return 0;
+  }
+
+  defender_cost = unit_build_shield_cost(def_type_id);
+  def_type = get_unit_type(def_type_id);
+  att_type = unit_type(attacker);
+  def_power = def_type->defense_strength * POWER_FACTOR
+      * def_type->veteran[0].power_fact;
+  att_power = att_type->attack_strength * POWER_FACTOR
+      * att_type->veteran[attacker->veteran].power_fact;
+
+  chance = win_chance(att_power, att_type->hp, att_type->firepower,
+                     def_power, att_type->hp, def_type->firepower);
+  menace = chance * defender_cost;
+
+  return menace;
+}
+
+/**************************************************************************
+  A measure of how much a unit could endanger units belonging to a player.
+  The units of menace are expected-loss in shields in a typical attack.
+
+  The function uses the unit-roles information so it will produce reasonable
+  results for a wide range of rules sets and technology differences.
+  For example, Caravels are little danger to Galleons.
+
+  In the function we assume that the unit owner will direct the unit to
+  suitable targets, so we choose the target for which it is most menacing.
+  We determinee which is most menacing by trying them all.
+**************************************************************************/
+static void aithreat_assess_threat_enemy_longterm(struct player *pplayer,
+                                                 struct unit *attacker)
+{
+  Danger menace = 0;
+
+  if (is_diplomat_unit(attacker)) {
+    /* Assume the attacker will try to take a city */
+    /* The value of a city is the cost in settlers it would take
+     * to rebuild it */
+    const Unit_Type_id rebuilder_type = best_role_unit_for_player(pplayer,
+                                                                 F_CITIES);
+    const int rebuilder_value = unit_build_shield_cost(rebuilder_type);
+    /* WAG -- we will defend our big cities,
+     * so consider only smallish cities */
+    const unsigned int city_size = 3;
+    const int city_value = rebuilder_value * city_size;
+    attacker->ai.threat.menace[TH_FAR_DIPLOMACY] = city_value;
+  } else {
+    attacker->ai.threat.menace[TH_FAR_DIPLOMACY] = 0;
+  }
+
+  if (is_attack_unit(attacker)) {
+    /* Is it a menace to our settlers? */
+    menace = MAX(menace, virtual_menace(pplayer, attacker, F_CITIES));
+    /* Is it a menace to our city defenders? */
+    menace = MAX(menace, virtual_menace(pplayer, attacker, L_DEFEND_GOOD));
+  }
+
+  if (is_sailing_unit(attacker) || is_air_unit(attacker)
+      || is_heli_unit(attacker)) {
+    /* Is it a menace to our ferries? */
+    menace = MAX(menace, virtual_menace(pplayer, attacker, L_FERRYBOAT));
+  }
+
+  /* Is it a menace by virtue of its ability to ferry units that endanger
+   * our cities? */
+  if (is_ground_units_transport(attacker)) {
+    struct player *owner = unit_owner(attacker);
+    const Unit_Type_id psngr_type = best_role_unit_for_player(owner,
+                                                             L_ATTACK_STRONG);
+    if (psngr_type < U_LAST) {
+      struct unit *psngr = create_unit_virtual(owner, NULL,
+                                              psngr_type, FALSE);
+      menace = MAX(menace, virtual_menace(pplayer, psngr, L_DEFEND_GOOD));
+      destroy_unit_virtual(psngr);
+    }
+    /* else enemy can not (yet) build good attackers */
+  }
+
+  attacker->ai.threat.menace[TH_FAR] = menace;
+}
+
+/****************************************************************************
+  Determine the best attacker a player could use for attacking a unit or city.
+  Returns a new virtual unit; may return NULL, indicating the player
+  could not build a suitable attacker.
+****************************************************************************/
+static struct unit *aithreat_best_attacker(struct player *attacker_owner,
+                                          const enum unit_move_type
+                                          move_type)
+{
+  Unit_Type_id attacker_type =
+      best_movement_role_unit_for_player(attacker_owner, L_ATTACK_STRONG,
+                                        move_type);
+  if (attacker_type == U_LAST) {
+    attacker_type =
+       best_movement_role_unit_for_player(attacker_owner, L_ATTACK_FAST,
+                                          move_type);
+  }
+  if (attacker_type == U_LAST) {
+    attacker_type =
+       best_movement_role_unit_for_player(attacker_owner, L_DEFEND_OK,
+                                          move_type);
+  }
+  if (attacker_type < U_LAST) {
+    return create_unit_virtual(attacker_owner, NULL, attacker_type, FALSE);
+  }
+  return NULL;
+}
+
+/****************************************************************************
+  Compute the danger rating of a unit, due to a potential attack by
+  an enemy unit.
+  Also, update the menace rating of enemy unit.
+  The units of danger are expected-loss in shields.
+****************************************************************************/
+static void aithreat_update_unit_danger_1(struct unit *attacker,
+                                         struct unit *defender,
+                                         const unsigned int
+                                         short_term_weight)
+{
+  struct player *attacker_owner = unit_owner(attacker);
+  struct player *defender_owner = unit_owner(defender);
+  const bool soldier = is_attack_unit(attacker);
+  Danger danger = 0;
+
+  if (!soldier || defender_owner == attacker_owner) {
+    return;                    /* harmless */
+  }
+  /* Normally, is_player_dangerous(defender_owner, attacker_owner),
+   * but sometimes our allies are untrustworthy */
+
+  danger = unit_menace(attacker, defender);
+
+  /* long-term danger */
+  attacker->ai.threat.menace[TH_FAR] =
+      MAX(attacker->ai.threat.menace[TH_FAR], danger);
+  defender->ai.threat.danger[TH_FAR] =
+      MAX(defender->ai.threat.danger[TH_FAR], danger);
+
+  /* short-term danger */
+  danger = danger * short_term_weight / 100;
+  attacker->ai.threat.menace[TH_NEAR] =
+      MAX(attacker->ai.threat.menace[TH_NEAR], danger);
+  defender->ai.threat.danger[TH_NEAR] =
+      MAX(defender->ai.threat.danger[TH_NEAR], danger);
+}
+
+/****************************************************************************
+  Compute the long-term danger rating of a unit, due to potential attacks by
+  enemy units of a given movement type sent by a given evenmy.
+****************************************************************************/
+static void virtual_danger(struct unit *defender,
+                          struct player *aplayer,
+                          const enum unit_move_type move_type)
+{
+  struct unit *attacker = aithreat_best_attacker(aplayer, move_type);
+  if (attacker) {
+    aithreat_update_unit_danger_1(attacker, defender, 0);
+    destroy_unit_virtual(attacker);
+  }
+}
+
+/****************************************************************************
+  Compute the danger rating of a unit, due to potential attacks by
+  enemy units.
+  Also, updates the menace ratings of enemy units that can attack this unit.
+  The units of danger are expected-loss in shields.
+****************************************************************************/
+void aithreat_update_unit_danger(struct unit *defender)
+{
+  struct player *defender_owner = unit_owner(defender);
+  assert(defender);
+  defender->ai.threat.danger[TH_NEAR] = 0;
+  defender->ai.threat.danger[TH_FAR] = 0;
+  /* At present, we ignore diplomacy threats against units */
+  defender->ai.threat.danger[TH_NEAR_DIPLOMACY] = 0;
+  defender->ai.threat.danger[TH_FAR_DIPLOMACY] = 0;
+
+  /* We do not expect enemies on our tiles,
+   * but our allies might be untrustworthy */
+  unit_list_iterate(defender->tile->units, attacker) {
+    aithreat_update_unit_danger_1(attacker, defender, 100);
+  } unit_list_iterate_end;
+
+  movemap_iterate_one_turn(defender->tile, attacker) {
+    aithreat_update_unit_danger_1(attacker, defender, 100);
+  } movemap_iterate_one_turn_end;
+
+  movemap_iterate_two_turn(defender->tile, attacker) {
+    aithreat_update_unit_danger_1(attacker, defender, THREAT_WT_2);
+  } movemap_iterate_two_turn_end;
+
+  /* long-term danger */
+  players_iterate(aplayer) {
+    if (defender_owner != aplayer) {
+      virtual_danger(defender, aplayer, unit_type(defender)->move_type);
+      if (unit_type(defender)->move_type == LAND_MOVING) {
+       /* Land bombardment */
+       virtual_danger(defender, aplayer, SEA_MOVING);
+      }
+      if (unit_type(defender)->move_type != AIR_MOVING) {
+       /* Air to ground atack */
+       virtual_danger(defender, aplayer, AIR_MOVING);
+       virtual_danger(defender, aplayer, HELI_MOVING);
+      }
+    }
+  } players_iterate_end;
+}
+
+/****************************************************************************
+  Compute the danger rating of a city, due to a potential attack by
+  an enemy unit.
+  The computation includes only units that have committed
+  to defending the city. There might be additional units present,
+  which are only passing through.
+  Also, update the menace rating of the attacker.
+  The units of danger are expected-loss in shields.
+****************************************************************************/
+static void aithreat_update_city_danger_1(struct unit *attacker,
+                                         struct city *defender,
+                                         const unsigned int weight)
+{
+  struct player *defender_owner = city_owner(defender);
+  struct player *attacker_owner = unit_owner(attacker);
+  const bool soldier = is_attack_unit(attacker);
+  const bool diplomat = unit_flag(attacker, F_DIPLOMAT);
+  Danger danger = 0;
+
+  if ((!soldier && !diplomat) || defender_owner == attacker_owner) {
+    return;                    /* harmless */
+  }
+  /* Normally, is_player_dangerous(defender_owner, attacker_owner),
+   * but sometimes our allies are untrustworthy */
+
+  danger = city_menace(attacker, defender);
+
+  /* long-term */
+  if (soldier) {
+    defender->ai.new_danger[TH_FAR] = MAX(defender->ai.new_danger[TH_FAR],
+                                         danger);
+  } else {
+    /* diplomat */
+    defender->ai.new_danger[TH_FAR_DIPLOMACY] =
+       MAX(defender->ai.new_danger[TH_FAR_DIPLOMACY], danger);
+  }
+
+  /* short-term */
+  danger = danger * weight / 100;
+  if (soldier) {
+    attacker->ai.threat.menace[TH_NEAR] =
+       MAX(attacker->ai.threat.menace[TH_NEAR], danger);
+  } else {
+    /* diplomat */
+    defender->ai.new_danger[TH_NEAR_DIPLOMACY] =
+       MAX(defender->ai.new_danger[TH_NEAR_DIPLOMACY], danger);
+  }
+}
+
+/****************************************************************************
+  Compute the danger rating of a city, due to a potential attack by
+  a force of attackers.
+  The computation includes only units that have committed
+  to defending the city. There might be additional units present,
+  which are only passing through.
+  Also, update the menace rating of the attackers.
+  The units of danger are expected-loss in shields.
+****************************************************************************/
+static void aithreat_update_city_danger_n(struct unit_list *attackers,
+                                         struct city *defender,
+                                         const unsigned int weight)
+{
+  struct player *defender_owner = city_owner(defender);
+  const double value = get_city_value(defender);
+  const double p_conquest = assault_conquest_chance(attackers,
+                                                   &defender->ai.garrison,
+                                                   defender);
+  const double p_incite = assault_incite_chance(*attackers, defender);
+  double danger_conquest = p_conquest * value;
+  double danger_incite = p_incite * value;
+
+  /* long-term danger */
+  defender->ai.new_danger[TH_FAR] = MAX(defender->ai.new_danger[TH_FAR],
+                                       danger_conquest);
+  defender->ai.new_danger[TH_FAR_DIPLOMACY] =
+      MAX(defender->ai.new_danger[TH_FAR_DIPLOMACY], danger_incite);
+
+  /* short-term danger */
+  danger_conquest = danger_conquest * weight / 100;
+  danger_incite = danger_incite * weight / 100;
+
+  defender->ai.new_danger[TH_NEAR] = MAX(defender->ai.new_danger[TH_NEAR],
+                                        danger_conquest);
+  defender->ai.new_danger[TH_NEAR_DIPLOMACY] =
+      MAX(defender->ai.new_danger[TH_NEAR_DIPLOMACY], danger_incite);
+
+  /* short-term menace */
+  unit_list_iterate_safe(*attackers, attacker) {
+    struct player *attacker_owner = unit_owner(attacker);
+    const double p_war = aithreat_player_danger(defender_owner,
+                                               attacker_owner);
+    double p_win = 0;
+
+    /* How much contribution does this unit make to an assault on the city ? */
+    if (0 == p_war) {
+      /* Optimisation; this unit will never attack,
+       * so we need not bother calculating p_win */
+      ;
+    } else if (is_attack_unit(attacker)
+              && unit_list_size(&defender->ai.garrison)) {
+      /* Use the danger to the weakest defender;
+       * this ensures we will not over estimate the menace from
+       * pathetic enemy units */
+      unit_list_iterate_safe(defender->ai.garrison, guard) {
+       /* The guard might not be at the city yet;
+        * move it for this calculation */
+       struct tile *ptile = guard->tile;
+       guard->tile = defender->tile;
+
+       p_win = MAX(p_win, unit_win_chance(attacker, guard));
+       guard->tile = ptile;
+      } unit_list_iterate_safe_end;
+    } else if (COULD_OCCUPY(attacker)) {
+      /* The city is defenceless */
+      p_win = 1;
+    }
+
+    attacker->ai.threat.menace[TH_NEAR] = MAX(attacker->
+                                             ai.threat.menace[TH_NEAR],
+                                             p_war * p_win *
+                                             danger_conquest);
+
+    if (unit_flag(attacker, F_DIPLOMAT)) {
+      /* TO DO: calculate a more accurate  p_win */
+      p_win = 1;
+      attacker->ai.threat.menace[TH_NEAR_DIPLOMACY] =
+         MAX(attacker->ai.threat.menace[TH_NEAR_DIPLOMACY],
+             p_war * p_win * danger_incite);
+    }
+  } unit_list_iterate_safe_end;
+}
+
+/****************************************************************************
+  Compute the danger rating of a city, due to potential attacks by
+  enemy units.
+  The computation includes only units that have committed
+  to defending the city. There might be additional units present,
+  which are only passing through.
+  Also, updates the menace ratings of enemy units that can attack this city.
+  The units of danger are expected-loss in shields.
+****************************************************************************/
+void aithreat_update_city_danger(struct city *defender)
+{
+  struct player *defender_owner = city_owner(defender);
+  struct unit_list attackers;
+  assert(defender);
+  defender->ai.new_danger[TH_NEAR] = 0;
+  defender->ai.new_danger[TH_FAR] = 0;
+  defender->ai.new_danger[TH_NEAR_DIPLOMACY] = 0;
+  defender->ai.new_danger[TH_FAR_DIPLOMACY] = 0;
+  unit_list_init(&attackers);
+
+  /* Assault by units that attack this turn */
+  /* We do not expect enemies on our tiles,
+   * but our allies might be untrustworthy */
+  unit_list_iterate(defender->tile->units, attacker) {
+    if (unit_owner(attacker) != defender_owner) {
+      unit_list_insert(&attackers, attacker);
+    }
+  } unit_list_iterate_end;
+
+  movemap_iterate_one_turn(defender->tile, attacker) {
+    if (unit_owner(attacker) != defender_owner) {
+      unit_list_insert(&attackers, attacker);
+    }
+  } movemap_iterate_one_turn_end;
+
+  aithreat_update_city_danger_n(&attackers, defender, 100);
+
+  /* Assault by a combined force next turn */
+  movemap_iterate_two_turn(defender->tile, attacker) {
+    if (unit_owner(attacker) != defender_owner) {
+      unit_list_insert(&attackers, attacker);
+    }
+  } movemap_iterate_two_turn_end;
+
+  aithreat_update_city_danger_n(&attackers, defender, THREAT_WT_2);
+
+  /* long-term danger */
+  players_iterate(aplayer) {
+    if (defender_owner != aplayer) {
+      struct unit *attacker = aithreat_best_attacker(aplayer, LAND_MOVING);
+      if (attacker) {
+       aithreat_update_city_danger_1(attacker, defender, 0);
+       destroy_unit_virtual(attacker);
+      }
+    }
+  } players_iterate_end;
+
+  unit_list_unlink_all(&attackers);
+}
+
+/****************************************************************************
+  Initialise all the menace ratings, to 0.
+****************************************************************************/
+void aithreat_clear_threats(struct player *pplayer)
+{
+  players_iterate(aplayer) {
+    if (pplayer == aplayer) {
+      continue;
+    }
+    unit_list_iterate(aplayer->units, punit) {
+      punit->ai.threat.menace[TH_NEAR] = 0;
+      punit->ai.threat.menace[TH_FAR] = 0;
+      punit->ai.threat.menace[TH_NEAR_DIPLOMACY] = 0;
+      punit->ai.threat.menace[TH_FAR_DIPLOMACY] = 0;
+    } unit_list_iterate_end;
+  } players_iterate_end;
+}
+
+/****************************************************************************
+  Initialise all the short-term menace ratings, to 0.
+****************************************************************************/
+void aithreat_clear_threats_shortterm(struct player *pplayer)
+{
+  /* Clear the fields */
+  players_iterate(aplayer) {
+    if (pplayer == aplayer) {
+      continue;
+    }
+    unit_list_iterate(aplayer->units, punit) {
+      punit->ai.threat.menace[TH_NEAR] = 0;
+      punit->ai.threat.menace[TH_NEAR_DIPLOMACY] = 0;
+    } unit_list_iterate_end;
+  } players_iterate_end;
+}
+
+/****************************************************************************
+  Update the short-term menace and danger values for units.
+****************************************************************************/
+void aithreat_update_unit_dangers(struct player *pplayer)
+{
+  unit_list_iterate(pplayer->units, defender) {
+    aithreat_update_unit_danger(defender);
+  } unit_list_iterate_end;
+}
+
+/****************************************************************************
+  Update the short-term menace and danger values for cities.
+****************************************************************************/
+void aithreat_update_city_dangers(struct player *pplayer)
+{
+  city_list_iterate(pplayer->cities, defender) {
+    aithreat_update_city_danger(defender);
+  } city_list_iterate_end;
+}
+
+/****************************************************************************
+  Update the long-term menace values.
+****************************************************************************/
+void aithreat_update_longterm_dangers(struct player *pplayer)
+{
+  players_iterate(aplayer) {
+    if (!is_player_dangerous(pplayer, aplayer)) {
+      continue;
+    }
+
+    unit_list_iterate(aplayer->units, attacker) {
+      aithreat_assess_threat_enemy_longterm(pplayer, attacker);
+      UNIT_LOG(LOGLEVEL_THREAT, attacker, "against %s: menace=%d, %d",
+              pplayer->name, attacker->ai.threat.menace[TH_NEAR],
+              attacker->ai.threat.menace[TH_FAR]);
+    } unit_list_iterate_end;
+  } players_iterate_end;
+}
+
+/****************************************************************************
+  Accessor
+****************************************************************************/
+Danger get_unit_danger(const struct unit *defender,
+                      const enum aithreat_class threat_class)
+{
+  return defender->ai.threat.danger[threat_class];
+}
+
+/****************************************************************************
+  Accessor
+****************************************************************************/
+Danger get_city_danger(const struct city * defender,
+                      const enum aithreat_class threat_class)
+{
+  return defender->ai.new_danger[threat_class];
+}
+
+/****************************************************************************
+  Sorter helper function,
+  for sorting in decreasing TH_NEAR danger order, using TH_FAR danger
+  as a tie-breaker.
+****************************************************************************/
+static int city_decreasing_danger_near(const void *v1, const void *v2)
+{
+  const struct city *const *cp1 = v1;
+  const struct city *const *cp2 = v2;
+  const struct city *c1 = *cp1;
+  const struct city *c2 = *cp2;
+  int diff = c2->ai.new_danger[TH_NEAR] - c1->ai.new_danger[TH_NEAR];
+  if (0 == diff) {
+    diff = c2->ai.new_danger[TH_FAR] - c1->ai.new_danger[TH_FAR];
+  }
+  return diff;
+}
+
+/****************************************************************************
+  Sorter helper function,
+  for sorting in decreasing TH_NEAR_DIPLOMACY danger order,
+  using TH_FAR_DIPLOMACY danger
+  as a tie-breaker.
+****************************************************************************/
+static int city_decreasing_danger_near_diplomacy(const void *v1,
+                                                const void *v2)
+{
+  const struct city *const *cp1 = v1;
+  const struct city *const *cp2 = v2;
+  const struct city *c1 = *cp1;
+  const struct city *c2 = *cp2;
+  int diff = c2->ai.new_danger[TH_NEAR_DIPLOMACY]
+      - c1->ai.new_danger[TH_NEAR_DIPLOMACY];
+  if (0 == diff) {
+    diff = c2->ai.new_danger[TH_FAR_DIPLOMACY]
+       - c1->ai.new_danger[TH_FAR_DIPLOMACY];
+  }
+  return diff;
+}
+
+/****************************************************************************
+  Sorter
+****************************************************************************/
+void sort_cities_decreasing_danger(struct city_list *cities,
+                                  const enum aithreat_class threat_class)
+{
+  switch (threat_class) {
+  case TH_NEAR:
+    city_list_sort(cities, city_decreasing_danger_near);
+    return;
+  case TH_NEAR_DIPLOMACY:
+    city_list_sort(cities, city_decreasing_danger_near_diplomacy);
+    return;
+  case TH_FAR:
+  case TH_FAR_DIPLOMACY:
+    /* Not supported */
+  default:
+    die("Illegal threat class");
+    return;
+  }
+}
+
+/****************************************************************************
+  Sorter helper function
+****************************************************************************/
+static int unit_decreasing_danger(const void *v1, const void *v2)
+{
+  const struct unit *const *up1 = v1;
+  const struct unit *const *up2 = v2;
+  const struct unit *u1 = *up1;
+  const struct unit *u2 = *up2;
+  int diff = u2->ai.threat.danger[TH_NEAR] - u1->ai.threat.danger[TH_NEAR];
+  if (0 == diff) {
+    diff = u2->ai.threat.danger[TH_FAR] - u1->ai.threat.danger[TH_FAR];
+  }
+  return diff;
+}
+
+/****************************************************************************
+  Sorter
+****************************************************************************/
+void sort_units_decreasing_danger(struct unit_list *units)
+{
+  unit_list_sort(units, unit_decreasing_danger);
+}
diff -ruN -Xvendor.freeciv.current/diff_ignore 
vendor.freeciv.current/ai/aithreat.h freeciv.PR10694-3/ai/aithreat.h
--- vendor.freeciv.current/ai/aithreat.h        1970-01-01 01:00:00.000000000 
+0100
+++ freeciv.PR10694-3/ai/aithreat.h     2004-12-30 21:02:37.000000000 +0000
@@ -0,0 +1,50 @@
+/********************************************************************** 
+ Freeciv - Copyright (C) 2002 - The Freeciv Project
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+***********************************************************************/
+#ifndef FC__AITHREAT_H
+#define FC__AITHREAT_H
+
+#include "shared.h"            /* bool type */
+#include "fc_types.h"
+#include "aithreattype.h"
+
+
+void aithreat_update_unit_danger(struct unit *defender);
+
+void aithreat_update_city_danger(struct city *defender);
+
+void aithreat_clear_threats(struct player *pplayer);
+
+void aithreat_clear_threats_shortterm(struct player *pplayer);
+
+void aithreat_update_unit_dangers(struct player *pplayer);
+
+void aithreat_update_city_dangers(struct player *pplayer);
+
+void aithreat_update_longterm_dangers(struct player *pplayer);
+
+double aithreat_player_danger(struct player *pplayer,
+                             struct player *aplayer);
+
+Danger get_unit_danger(const struct unit *defender,
+                      const enum aithreat_class threat_class);
+
+Danger get_city_danger(const struct city *defender,
+                      const enum aithreat_class threat_class);
+
+void sort_cities_decreasing_danger(struct city_list *cities,
+                                  const enum aithreat_class threat_class);
+
+void sort_units_decreasing_danger(struct unit_list *units);
+
+
+#endif /* FC__AITHREAT_H */
diff -ruN -Xvendor.freeciv.current/diff_ignore 
vendor.freeciv.current/ai/aitools.c freeciv.PR10694-3/ai/aitools.c
--- vendor.freeciv.current/ai/aitools.c 2004-12-29 19:44:10.000000000 +0000
+++ freeciv.PR10694-3/ai/aitools.c      2004-12-30 21:02:37.000000000 +0000
@@ -49,6 +49,7 @@
 #include "aicity.h"
 #include "aidata.h"
 #include "aiferry.h"
+#include "aiguard.h"
 #include "ailog.h"
 #include "aiunit.h"
 
@@ -142,68 +143,6 @@
   return TRUE;
 }
 
-/****************************************************************************
-  A helper function for ai_gothere.  Estimates the dangers we will
-  be facing at our destination and tries to find/request a bodyguard if 
-  needed.
-****************************************************************************/
-static void ai_gothere_bodyguard(struct unit *punit, struct tile *dest_tile)
-{
-  struct player *pplayer = unit_owner(punit);
-  struct ai_data *ai = ai_data_get(pplayer);
-  unsigned int danger = 0;
-  struct city *dcity;
-  struct tile *ptile;
-  
-  if (is_barbarian(unit_owner(punit))) {
-    /* barbarians must have more courage (ie less brains) */
-    punit->ai.bodyguard = BODYGUARD_NONE;
-    return;
-  }
-
-  /* Estimate enemy attack power. */
-  unit_list_iterate(dest_tile->units, aunit) {
-    if (HOSTILE_PLAYER(pplayer, ai, unit_owner(aunit))) {
-      danger += unit_att_rating(aunit);
-    }
-  } unit_list_iterate_end;
-  dcity = map_get_city(dest_tile);
-  if (dcity && HOSTILE_PLAYER(pplayer, ai, city_owner(dcity))) {
-    /* Assume enemy will build another defender, add it's attack strength */
-    int d_type = ai_choose_defender_versus(dcity, punit->type);
-    danger += 
-      unittype_att_rating(d_type, do_make_unit_veteran(dcity, d_type), 
-                          SINGLE_MOVE, unit_types[d_type].hp);
-  }
-  danger *= POWER_DIVIDER;
-
-  /* If we are fast, there is less danger. */
-  danger /= (unit_type(punit)->move_rate / SINGLE_MOVE);
-  if (unit_flag(punit, F_IGTER)) {
-    danger /= 1.5;
-  }
-
-  ptile = punit->tile;
-  /* We look for the bodyguard where we stand. */
-  if (!unit_list_find(&ptile->units, punit->ai.bodyguard)) {
-    int my_def = (punit->hp 
-                  * unit_type(punit)->veteran[punit->veteran].power_fact
-                 * unit_type(punit)->defense_strength
-                  * POWER_FACTOR);
-    
-    if (danger >= my_def) {
-      UNIT_LOG(LOGLEVEL_BODYGUARD, punit, 
-               "want bodyguard @(%d, %d) danger=%d, my_def=%d", 
-               TILE_XY(dest_tile), danger, my_def);
-      punit->ai.bodyguard = BODYGUARD_WANTED;
-    } else {
-      punit->ai.bodyguard = BODYGUARD_NONE;
-    }
-  }
-
-  /* What if we have a bodyguard, but don't need one? */
-}
-
 #define LOGLEVEL_GOTHERE LOG_DEBUG
 /****************************************************************************
   This is ferry-enabled goto.  Should not normally be used for non-ferried 
@@ -227,10 +166,6 @@
     return TRUE;
   }
 
-  /* See if we need a bodyguard at our destination */
-  /* FIXME: If bodyguard is _really_ necessary, don't go anywhere */
-  ai_gothere_bodyguard(punit, dest_tile);
-
   if (punit->transported_by > 0 
       || !goto_is_sane(punit, dest_tile, TRUE)) {
     /* Must go by boat, call an aiferryboat function */
@@ -310,8 +245,8 @@
   /* If the unit is under (human) orders we shouldn't control it. */
   assert(!unit_has_orders(punit));
 
-  UNIT_LOG(LOG_DEBUG, punit, "changing role from %d to %d",
-           punit->activity, task);
+  UNIT_LOG(LOG_DEBUG, punit, "changing role from %d(%d) to %d",
+           punit->ai.ai_role, punit->activity, task);
 
   /* Free our ferry.  Most likely it has been done already. */
   if (task == AIUNIT_NONE || task == AIUNIT_DEFEND_HOME) {
@@ -595,26 +530,61 @@
   return(pc);
 }
 
-
 /**************************************************************************
-  Calculate the value of the target unit including the other units which
-  will die in a successful attack
-**************************************************************************/
-int stack_cost(struct unit *pdef)
+  Calculate the value of the target unit, including the other units which
+  will die in a successful attack, and the benefit of destroying units that
+  endanger our units.
+
+  The units of menace are expected-loss, in shields, so we can simply add
+  menace values to build costs to get a total value.
+
+  The menace_weights (percentages) give how important destroying endangering
+  units is.
+
+  The menace ratings, punit->ai.threat.menace.*, record the expected loss
+  in shields *if* we provide no bodyguards, take no evasive action,
+  and the units are certain to attack their best possible target.
+  In practice the situation is not so pessimistic,
+  so have menace weights < 100.
+
+  The long-term menaces values are not amortised by the delay until the
+  menace manifests itself. In practice, that could be a long time,
+  so the menace_weight_long_term is a means of reducing the importance
+  of the long-term menace to account for that.
+
+  Some attackers (hunters, for example), specifically attack menacing units.
+  Those attackers can use larger weights to favour menacing units.
+**************************************************************************/
+int stack_cost(struct unit *pdef,
+              const int menace_weight_short_term,
+              const int menace_weight_long_term)
 {
   int victim_cost = 0;
+  /* The danger to our units: */
+  Danger menace_short_term = 0;
+  /* The potential danger to our units: */
+  Danger menace_long_term = 0;
 
   if (is_stack_vulnerable(pdef->tile)) {
     /* lotsa people die */
     unit_list_iterate(pdef->tile->units, aunit) {
       victim_cost += unit_build_shield_cost(aunit->type);
+      menace_short_term += MAX(aunit->ai.threat.menace[TH_NEAR],
+                              aunit->ai.threat.menace[TH_NEAR_DIPLOMACY]);
+      menace_long_term += MAX(aunit->ai.threat.menace[TH_FAR],
+                             aunit->ai.threat.menace[TH_FAR_DIPLOMACY]);
     } unit_list_iterate_end;
   } else {
     /* Only one unit dies if attack is successful */
     victim_cost = unit_build_shield_cost(pdef->type);
+    menace_short_term = MAX(pdef->ai.threat.menace[TH_NEAR],
+                           pdef->ai.threat.menace[TH_NEAR_DIPLOMACY]);
+    menace_long_term = MAX(pdef->ai.threat.menace[TH_FAR],
+                          pdef->ai.threat.menace[TH_FAR_DIPLOMACY]);
   }
   
-  return victim_cost;
+  return victim_cost + MAX(menace_weight_short_term * (int)menace_short_term,
+                       menace_weight_long_term * (int)menace_long_term) / 100;
 }
 
 /**************************************************************************
diff -ruN -Xvendor.freeciv.current/diff_ignore 
vendor.freeciv.current/ai/aitools.h freeciv.PR10694-3/ai/aitools.h
--- vendor.freeciv.current/ai/aitools.h 2004-12-29 19:44:10.000000000 +0000
+++ freeciv.PR10694-3/ai/aitools.h      2004-12-30 21:02:37.000000000 +0000
@@ -32,14 +32,12 @@
 #define CHECK_UNIT(punit) assert(TRUE)
 #endif
 
-enum bodyguard_enum {
-  BODYGUARD_WANTED=-1,
-  BODYGUARD_NONE
-};
-
 int military_amortize(struct player *pplayer, struct city *pcity, 
                       int value, int delay, int build_cost);
-int stack_cost(struct unit *pdef);
+
+int stack_cost(struct unit *pdef,
+              const int menace_weight_short_term,
+              const int menace_weight_long_term);
 
 bool ai_unit_execute_path(struct unit *punit, struct pf_path *path);
 bool ai_gothere(struct player *pplayer, struct unit *punit, 
diff -ruN -Xvendor.freeciv.current/diff_ignore 
vendor.freeciv.current/ai/aiunit.c freeciv.PR10694-3/ai/aiunit.c
--- vendor.freeciv.current/ai/aiunit.c  2004-12-29 19:44:10.000000000 +0000
+++ freeciv.PR10694-3/ai/aiunit.c       2004-12-30 21:02:37.000000000 +0000
@@ -53,9 +53,11 @@
 #include "aidiplomat.h"
 #include "aiexplorer.h"
 #include "aiferry.h"
+#include "aiguard.h"
 #include "aihand.h"
 #include "aihunt.h"
 #include "ailog.h"
+#include "aithreat.h"
 #include "aitools.h"
 
 #include "aiunit.h"
@@ -70,14 +72,22 @@
 #define RAMPAGE_HUT_OR_BETTER        99998
 #define RAMPAGE_FREE_CITY_OR_BETTER  99999
 #define BODYGUARD_RAMPAGE_THRESHOLD (SHIELD_WEIGHTING * 4)
+
+#define BODYGUARD_RAMPAGE_MENACE_WT_NEAR 25
+#define BODYGUARD_RAMPAGE_MENACE_WT_FAR 0
+#define ATTACK_RAMPAGE_MENACE_WT_NEAR 15
+#define ATTACK_RAMPAGE_MENACE_WT_FAR 5
+#define CAUTIOUS_RAMPAGE_MENACE_WT_NEAR 10
+#define CAUTIOUS_RAMPAGE_MENACE_WT_FAR 0
+
 static bool ai_military_rampage(struct unit *punit, int thresh_adj, 
-                                int thresh_move);
+                                int thresh_move, const int menace_wt_near,
+                               const int menace_wt_far);
 static void ai_military_findjob(struct player *pplayer,struct unit *punit);
 static void ai_military_gohome(struct player *pplayer,struct unit *punit);
 static void ai_military_attack(struct player *pplayer,struct unit *punit);
 
 static int unit_move_turns(struct unit *punit, struct tile *ptile);
-static bool unit_role_defender(Unit_Type_id type);
 static int unit_def_rating_sq(struct unit *punit, struct unit *pdef);
 
 /*
@@ -105,7 +115,7 @@
 static void ai_airlift(struct player *pplayer)
 {
   struct city *most_needed;
-  int comparison;
+  Danger comparison;
   struct unit *transported;
 
   do {
@@ -114,21 +124,13 @@
     transported = NULL;
 
     city_list_iterate(pplayer->cities, pcity) {
-      if (pcity->ai.urgency > comparison && pcity->airlift) {
-        comparison = pcity->ai.urgency;
+      const Danger danger = get_city_danger(pcity, TH_NEAR);
+      if (danger > comparison && pcity->airlift) {
+        comparison = danger;
         most_needed = pcity;
       }
     } city_list_iterate_end;
     if (!most_needed) {
-      comparison = 0;
-      city_list_iterate(pplayer->cities, pcity) {
-        if (pcity->ai.danger > comparison && pcity->airlift) {
-          comparison = pcity->ai.danger;
-          most_needed = pcity;
-        }
-      } city_list_iterate_end;
-    }
-    if (!most_needed) {
       return;
     }
     comparison = 0;
@@ -136,13 +138,13 @@
       struct tile *ptile = (punit->tile);
 
       if (ptile->city && ptile->city->ai.urgency == 0
-          && ptile->city->ai.danger - DEFENCE_POWER(punit) < comparison
+          && get_city_danger(ptile->city, TH_NEAR) < comparison
           && unit_can_airlift_to(punit, most_needed)
           && DEFENCE_POWER(punit) > 2
           && (punit->ai.ai_role == AIUNIT_NONE
               || punit->ai.ai_role == AIUNIT_DEFEND_HOME)
           && IS_ATTACKER(punit)) {
-        comparison = ptile->city->ai.danger;
+        comparison = get_city_danger(ptile->city, TH_NEAR);
         transported = punit;
       }
     } unit_list_iterate_end;
@@ -327,54 +329,6 @@
 }
 
 /**************************************************************************
-  Return whether we should stay and defend a square, usually a city. Will
-  protect allied cities temporarily in case of grave danger.
-
-  FIXME: We should check for fortresses here.
-**************************************************************************/
-static bool stay_and_defend(struct unit *punit)
-{
-  struct city *pcity = map_get_city(punit->tile);
-  bool has_defense = FALSE;
-  int mydef;
-  int units = -2; /* WAG for grave danger threshold, seems to work */
-
-  if (!pcity) {
-    return FALSE;
-  }
-  mydef = assess_defense_unit(pcity, punit, FALSE);
-
-  unit_list_iterate((pcity->tile)->units, pdef) {
-    if (assess_defense_unit(pcity, pdef, FALSE) >= mydef
-       && pdef != punit
-       && pdef->homecity == pcity->id) {
-      has_defense = TRUE;
-    }
-    units++;
-  } unit_list_iterate_end;
- 
-  /* Guess I better stay / you can live at home now */
-  if (!has_defense && pcity->ai.danger > 0 && punit->owner == pcity->owner) {
-    /* Change homecity to this city */
-    if (ai_unit_make_homecity(punit, pcity)) {
-      /* Very important, or will not stay -- Syela */
-      ai_unit_new_role(punit, AIUNIT_DEFEND_HOME, pcity->tile);
-      return TRUE;
-    } /* else city cannot upkeep us! */
-  }
-
-  /* Treat grave danger anyway if danger is over threshold, which is the
-   * number of units currently in the city.  However, to avoid AI panics
-   * (this is common when enemy is huge), add a ceiling. */
-  if (pcity->ai.grave_danger > units && units <= 2) {
-    ai_unit_new_role(punit, AIUNIT_DEFEND_HOME, pcity->tile);
-    return TRUE;
-  }
-
-  return FALSE;
-}
-
-/**************************************************************************
   Attack rating of this kind of unit.
 **************************************************************************/
 int unittype_att_rating(Unit_Type_id type, int veteran,
@@ -591,7 +545,8 @@
   Here the minus indicates that you need to enter the target tile (as 
   opposed to attacking it, which leaves you where you are).
 **************************************************************************/
-static int ai_rampage_want(struct unit *punit, struct tile *ptile)
+static int ai_rampage_want(struct unit *punit, struct tile *ptile,
+                          const int menace_wt_near, const int menace_wt_far)
 {
   struct player *pplayer = unit_owner(punit);
   struct unit *pdef = get_defender(punit, ptile);
@@ -607,7 +562,7 @@
     {
       /* See description of kill_desire() about these variables. */
       int attack = unit_att_rating_now(punit);
-      int benefit = stack_cost(pdef);
+      int benefit = stack_cost(pdef, menace_wt_near, menace_wt_far);
       int loss = unit_build_shield_cost(punit->type);
 
       attack *= attack;
@@ -662,7 +617,9 @@
   Look for worthy targets within a one-turn horizon.
 *************************************************************************/
 static struct pf_path *find_rampage_target(struct unit *punit, 
-                                           int thresh_adj, int thresh_move)
+                                           int thresh_adj, int thresh_move,
+                                          const int menace_wt_near,
+                                          const int menace_wt_far)
 {
   struct pf_map *tgt_map;
   struct pf_path *path = NULL;
@@ -695,7 +652,7 @@
       continue;
     }
     
-    want = ai_rampage_want(punit, pos.tile);
+    want = ai_rampage_want(punit, pos.tile, menace_wt_near, menace_wt_far);
 
     /* Negative want means move needed even though the tiles are adjacent */
     move_needed = (!is_tiles_adjacent(punit->tile, pos.tile)
@@ -731,14 +688,15 @@
   attacking distant (but within reach) targets.
 
   For example, if unit is a bodyguard on duty, it should call
-    ai_military_rampage(punit, 100, RAMPAGE_FREE_CITY_OR_BETTER)
+    ai_military_rampage(punit, 100, RAMPAGE_FREE_CITY_OR_BETTER, 25, 0)
   meaning "we will move _only_ to pick up a free city but we are happy to
   attack adjacent squares as long as they are worthy of it".
 
   Returns TRUE if survived the rampage session.
 **************************************************************************/
 static bool ai_military_rampage(struct unit *punit, int thresh_adj, 
-                                int thresh_move)
+                                int thresh_move, const int menace_wt_near,
+                               const int menace_wt_far)
 {
   int count = punit->moves_left + 1; /* break any infinite loops */
   struct pf_path *path = NULL;
@@ -750,7 +708,8 @@
   thresh_adj += ((thresh_move - thresh_adj) * game.occupychance / 100);
 
   while (count-- > 0 && punit->moves_left > 0
-         && (path = find_rampage_target(punit, thresh_adj, thresh_move))) {
+         && (path = find_rampage_target(punit, thresh_adj, thresh_move,
+                                       menace_wt_near, menace_wt_far))) {
     if (!ai_unit_execute_path(punit, path)) {
       /* Died */
       count = -1;
@@ -765,6 +724,20 @@
 }
 
 /*************************************************************************
+  Does a list include a given element?
+  TODO: ought to move this to speclist.h
+**************************************************************************/
+static bool unit_list_has(struct unit_list *units, struct unit *punit)
+{
+  unit_list_iterate_safe(*units, aunit) {
+    if (punit == aunit) {
+      return TRUE;
+    }
+  } unit_list_iterate_safe_end;
+  return FALSE;
+}
+
+/*************************************************************************
   If we are not covering our charge's ass, go do it now. Also check if we
   can kick some ass, which is always nice.
 **************************************************************************/
@@ -773,16 +746,13 @@
   struct unit *aunit = player_find_unit_by_id(pplayer, punit->ai.charge);
   struct city *acity = find_city_by_id(punit->ai.charge);
   struct tile *ptile;
+  bool alive = TRUE;
 
   CHECK_UNIT(punit);
 
   if (aunit && aunit->owner == punit->owner) {
     /* protect a unit */
-    if (is_sailing_unit(aunit)) {
-      ptile = aunit->goto_tile;
-    } else {
-      ptile = aunit->tile;
-    }
+    ptile = aunit->tile;
   } else if (acity && acity->owner == punit->owner) {
     /* protect a city */
     ptile = acity->tile;
@@ -793,17 +763,9 @@
     return;
   }
 
-  if (aunit) {
-    UNIT_LOG(LOGLEVEL_BODYGUARD, punit, "to meet charge %s#%d@(%d,%d){%d}",
-             unit_type(aunit)->name, aunit->id, aunit->tile->x,
-            aunit->tile->y,
-             aunit->ai.bodyguard);
-  } else if (acity) {
-    UNIT_LOG(LOGLEVEL_BODYGUARD, punit, "to guard %s", acity->name);
-  }
-
   if (!same_pos(punit->tile, ptile)) {
     if (goto_is_sane(punit, ptile, TRUE)) {
+      BODYGUARD_LOG(LOGLEVEL_BODYGUARD, punit, "going to charge");
       if (!ai_unit_goto(punit, ptile)) {
         /* We died */
         return;
@@ -816,8 +778,25 @@
   /* I had these guys set to just fortify, which is so dumb. -- Syela
    * Instead we can attack adjacent units and maybe even pick up some free 
    * cities! */
-  (void) ai_military_rampage(punit, BODYGUARD_RAMPAGE_THRESHOLD,
-                             RAMPAGE_FREE_CITY_OR_BETTER);
+  alive = ai_military_rampage(punit, BODYGUARD_RAMPAGE_THRESHOLD,
+                             RAMPAGE_FREE_CITY_OR_BETTER,
+                             BODYGUARD_RAMPAGE_MENACE_WT_NEAR,
+                             BODYGUARD_RAMPAGE_MENACE_WT_FAR);
+
+  /* Does our charge now feel safer? */
+  if (alive && aunit && same_pos(punit->tile, ptile)) {
+    assert(aunit->ai.bodyguard == punit->id);
+    aithreat_update_unit_danger(aunit);
+    BODYGUARD_LOG(LOGLEVEL_BODYGUARD, punit, "is with charge");
+  } else if (alive && acity && same_pos(punit->tile, ptile)) {
+    if (!unit_list_has(&acity->ai.garrison, punit)) {
+      /* If we don't do this, other potential guards might continue
+       * to hear the cry for help, creating a useless swarm of units.  */
+      unit_list_insert(&acity->ai.garrison, punit);
+      aithreat_update_city_danger(acity);
+    }
+    BODYGUARD_LOG(LOGLEVEL_BODYGUARD, punit, "is with charge");
+  }
 }
 
 /*************************************************************************
@@ -899,33 +878,28 @@
 }
 
 /*************************************************************************
-  Does the unit with the id given have the flag L_DEFEND_GOOD?
-**************************************************************************/
-static bool unit_role_defender(Unit_Type_id type)
-{
-  if (unit_types[type].move_type != LAND_MOVING) {
-    return FALSE; /* temporary kluge */
-  }
-  return (unit_has_role(type, L_DEFEND_GOOD));
-}
-
-/*************************************************************************
-  See if we can find something to defend. Called both by wannabe
-  bodyguards and building want estimation code. Returns desirability
+  See if we can find something to defend. Called by
+  building want estimation code. Returns desirability
   for using this unit as a bodyguard or for defending a city.
 
-  We do not consider units with higher movement than us, or units that
-  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.
+  In the event of ties, we choose the most endangered charge,
+  or the closest charge. If no charges really need a bodyguard,
+  we choose the closest city.
 
   Requires an initialized warmap!
 **************************************************************************/
 int look_for_charge(struct player *pplayer, struct unit *punit, 
                     struct unit **aunit, struct city **acity)
 {
-  int dist, def, best = 0;
+  int want = 0;
+  int dist, def, best_dist = FC_INFINITY, best = 0;
+  Danger best_danger = 0;
   int toughness = unit_def_rating_basic_sq(punit);
+  struct tile *charge_tile = NULL;
+  const char *charge_name = "";
+
+  *aunit = NULL;
+  *acity = NULL; 
 
   if (toughness == 0) {
     /* useless */
@@ -933,30 +907,22 @@
   }
 
   /* Unit bodyguard */
+  /* TODO: make this more dependent on danger than bodyguard suitability */
   unit_list_iterate(pplayer->units, buddy) {
+    Danger danger;
     if (buddy->ai.bodyguard != BODYGUARD_WANTED
-        || !goto_is_sane(punit, buddy->tile, TRUE)
-        || unit_move_rate(buddy) > unit_move_rate(punit)
-        || DEFENCE_POWER(buddy) >= DEFENCE_POWER(punit)
-        || (is_military_unit(buddy) && get_transporter_capacity(buddy) == 0
-            && ATTACK_POWER(buddy) <= ATTACK_POWER(punit))
-        || unit_type(buddy)->move_type != unit_type(punit)->move_type) { 
+       || !goto_is_sane(punit, buddy->tile, TRUE)) {
       continue;
     }
     dist = unit_move_turns(punit, buddy->tile);
-    def = (toughness - unit_def_rating_basic_sq(buddy));
-    if (def <= 0) {
-      continue; /* This should hopefully never happen. */
-    }
-    if (get_transporter_capacity(buddy) == 0) {
-      /* Reduce want based on distance. We can't do this for
-       * transports since they move around all the time, leading
-       * to hillarious flip-flops */
-      def = def >> (dist/2);
-    }
-    if (def > best) { 
+    def = aiguard_suitability(punit, buddy, dist);
+    danger = get_unit_danger(buddy, TH_NEAR);
+    if (def > best || (0 < def && def == best && best_danger < danger)
+       || (0 < def && def == best && dist < best_dist)) { 
       *aunit = buddy; 
       best = def; 
+      best_dist = dist;
+      best_danger = danger;
     }
   } unit_list_iterate_end;
 
@@ -973,22 +939,30 @@
       continue;
     }
     def = def >> dist;
-    if (def > best && ai_fuzzy(pplayer, TRUE)) { 
+    if ((def > best || (def == best && dist < best_dist))
+       && ai_fuzzy(pplayer, TRUE)) { 
+      *aunit = NULL;
       *acity = mycity; 
       best = def; 
+      best_dist = dist;
     }
    } city_list_iterate_end;
   }
 
-  UNIT_LOG(LOGLEVEL_BODYGUARD, punit, "look_for_charge, best=%d, "
-           "type=%s(%d,%d)", best * 100 / toughness, *acity ? (*acity)->name
-           : (*aunit ? unit_name((*aunit)->type) : ""), 
-           *acity ? (*acity)->tile->x : (*aunit
-                                             ? (*aunit)->tile->y : 0),
-           *acity ? (*acity)->tile->x : (*aunit
-                                             ? (*aunit)->tile->y : 0));
+  want = ((best * 100) / toughness);
+  if (*acity) {
+    charge_tile = (*acity)->tile;
+    charge_name = (*acity)->name;
+  }else if (*aunit) {
+    charge_tile = (*aunit)->tile;
+    charge_name = unit_name((*aunit)->type);
+  }
+  UNIT_LOG(LOGLEVEL_BODYGUARD, punit,
+          "look_for_charge, best=%d, type=%s(%d,%d)",
+          want, charge_name,
+          (charge_tile? charge_tile->x: 0), (charge_tile? charge_tile->y: 0));
   
-  return ((best * 100) / toughness);
+  return want;
 }
 
 /********************************************************************** 
@@ -997,51 +971,11 @@
 ***********************************************************************/
 static void ai_military_findjob(struct player *pplayer,struct unit *punit)
 {
-  struct city *pcity = NULL, *acity = NULL;
-  struct unit *aunit;
-  int val, def;
-  int q = 0;
   struct unit_type *punittype = get_unit_type(punit->type);
+  const bool is_defender = unit_has_role(punit->type, L_DEFEND_GOOD);
+  struct city *homecity = find_city_by_id(punit->homecity); /* may be NULL */
 
   CHECK_UNIT(punit);
-
-/* tired of AI abandoning its cities! -- Syela */
-  if (punit->homecity != 0 && (pcity = find_city_by_id(punit->homecity))) {
-    if (pcity->ai.danger != 0) { /* otherwise we can attack */
-      def = assess_defense(pcity);
-      if (same_pos(punit->tile, pcity->tile)) {
-        /* I'm home! */
-        val = assess_defense_unit(pcity, punit, FALSE); 
-        def -= val; /* old bad kluge fixed 980803 -- Syela */
-/* only the least defensive unit may leave home */
-/* and only if this does not jeopardize the city */
-/* def is the defense of the city without punit */
-        if (unit_flag(punit, F_FIELDUNIT)) val = -1;
-        unit_list_iterate((pcity->tile)->units, pdef)
-          if (is_military_unit(pdef) 
-              && pdef != punit 
-              && !unit_flag(pdef, F_FIELDUNIT)
-              && pdef->owner == punit->owner) {
-            if (assess_defense_unit(pcity, pdef, FALSE) >= val) val = 0;
-          }
-        unit_list_iterate_end; /* was getting confused without the is_military 
part in */
-        if (unit_def_rating_basic_sq(punit) == 0) {
-          /* thanks, JMT, Paul */
-          q = 0;
-        } else { 
-          /* this was a WAG, but it works, so now it's just good code! 
-           * -- Syela */
-          q = (pcity->ai.danger * 2 
-               - (def * unit_type(punit)->attack_strength /
-                  unit_type(punit)->defense_strength));
-        }
-        if (val > 0 || q > 0) { /* Guess I better stay */
-          ;
-        } else q = 0;
-      } /* end if home */
-    } /* end if home is in danger */
-  } /* end if we have a home */
-
   /* keep barbarians aggresive and primitive */
   if (is_barbarian(pplayer)) {
     if (can_unit_do_activity(punit, ACTIVITY_PILLAGE)
@@ -1054,36 +988,25 @@
     return;
   }
 
+  if (punit->ai.ai_role == AIUNIT_DEFEND_HOME) {
+    /* We have already been given a simple job to do; continue it */
+    return;
+  }
+
   if (punit->ai.charge != BODYGUARD_NONE) { /* I am a bodyguard */
-    aunit = player_find_unit_by_id(pplayer, punit->ai.charge);
-    acity = find_city_by_id(punit->ai.charge);
+    struct unit *aunit = player_find_unit_by_id(pplayer, punit->ai.charge);
+    struct city *acity = find_city_by_id(punit->ai.charge);
 
-    /* Check if city we are on our way to rescue is still in danger,
-     * or unit we should protect is still alive */
-    if ((aunit && aunit->ai.bodyguard != BODYGUARD_NONE 
-         && unit_def_rating_basic(punit) > unit_def_rating_basic(aunit)) 
-        || (acity && acity->owner == punit->owner && acity->ai.urgency != 0 
-            && acity->ai.danger > assess_defense_quadratic(acity))) {
+    if (aunit || (acity && acity->owner == punit->owner)) {
+      /* we still have a job to do -- continue on mission */
       assert(punit->ai.ai_role == AIUNIT_ESCORT);
       return;
     } else {
+      /* Charge destroyed or conquered */
       ai_unit_new_role(punit, AIUNIT_NONE, NULL);
     }
   }
 
-  /* ok, what if I'm somewhere new? - ugly, kludgy code by Syela */
-  if (stay_and_defend(punit)) {
-    UNIT_LOG(LOG_DEBUG, punit, "stays to defend %s",
-             map_get_city(punit->tile)->name);
-    return;
-  }
-
-  if (pcity && q > 0 && pcity->ai.urgency > 0) {
-    UNIT_LOG(LOG_DEBUG, punit, "decides to camp at home in %s", pcity->name);
-    ai_unit_new_role(punit, AIUNIT_DEFEND_HOME, pcity->tile);
-    return;
-  }
-
   /* Is the unit badly damaged? */
   if ((punit->ai.ai_role == AIUNIT_RECOVER
        && punit->hp < punittype->hp)
@@ -1095,7 +1018,8 @@
 
   /* Make unit a seahunter? */
   if (punit->ai.ai_role == AIUNIT_HUNTER) {
-    return; /* Continue mission. */
+    /* Continue mission. */
+    return;
   }
   if (ai_hunter_qualify(pplayer, punit)) {
     UNIT_LOG(LOGLEVEL_HUNT, punit, "is qualified as hunter");
@@ -1106,47 +1030,40 @@
     }
   }
 
-/* I'm not 100% sure this is the absolute best place for this... -- Syela */
-  generate_warmap(map_get_city(punit->tile), punit);
-/* I need this in order to call unit_move_turns, here and in look_for_charge */
-
-  if (pcity && q > 0) {
-    q *= 100;
-    q /= unit_def_rating_basic_sq(punit);
-    q >>= unit_move_turns(punit, pcity->tile);
-  }
-
-  val = 0; acity = NULL; aunit = NULL;
-  if (unit_role_defender(punit->type)) {
-    /* 
-     * This is a defending unit that doesn't need to stay put.
-     * It needs to defend something, but not necessarily where it's at.
-     * Therefore, it will consider becoming a bodyguard. -- Syela 
-     */
-    val = look_for_charge(pplayer, punit, &aunit, &acity);
-  }
-  if (pcity && q > val) {
-    UNIT_LOG(LOG_DEBUG, punit, "decided not to go anywhere, sits in %s",
-             pcity->name);
-    ai_unit_new_role(punit, AIUNIT_DEFEND_HOME, pcity->tile);
-    return;
-  }
-  /* this is bad; riflemen might rather attack if val is low -- Syela */
-  if (acity) {
-    ai_unit_new_role(punit, AIUNIT_ESCORT, acity->tile);
-    punit->ai.charge = acity->id;
-    BODYGUARD_LOG(LOG_DEBUG, punit, "going to defend city");
-  } else if (aunit) {
-    ai_unit_new_role(punit, AIUNIT_ESCORT, aunit->tile);
-    punit->ai.charge = aunit->id;
-    BODYGUARD_LOG(LOG_DEBUG, punit, "going to defend unit");
+  /* TODO: aiguard_find_charge is rather expensive, so try harder before
+   * calling it */
+  if (is_defender && unit_type(punit)->move_type == LAND_MOVING) {
+    if (aiguard_find_charge(punit)) {
+      /* We have been recruited as a guard by someone fearful */
+      return;
+    } else if (punit->tile->city && punit->owner == punit->tile->city->owner) {
+      /* Defending any city is better than nothing,
+       * even if it is not currently endangered. */
+       aiguard_assign_guard_city(punit->tile->city, punit);
+       return;
+    } else {
+      /* Defending any city is better than nothing,
+       * even if it is not currently endangered. */
+      struct city *pcity = find_nearest_safe_city(punit);
+      if (pcity) {
+       aiguard_assign_guard_city(pcity, punit);
+       return;
+      } else if (homecity) {
+       aiguard_assign_guard_city(homecity, punit);
+       return;
+      }
+    }
+  } else if (is_defender && aiguard_find_charge(punit)) {
+    /* We have been recruited as a sea or air guard by someone fearful */
+    return;
   } else if (ai_unit_attack_desirability(punit->type) != 0 ||
-      (pcity && !same_pos(pcity->tile, punit->tile))) {
-     ai_unit_new_role(punit, AIUNIT_ATTACK, NULL);
-  } else {
-    UNIT_LOG(LOG_DEBUG, punit, "nothing to do, sit where we are");
-    ai_unit_new_role(punit, AIUNIT_DEFEND_HOME, NULL); /* for default */
+            (homecity && !same_pos(homecity->tile, punit->tile))) {
+    ai_unit_new_role(punit, AIUNIT_ATTACK, NULL);
+    return;
   }
+
+  UNIT_LOG(LOG_DEBUG, punit, "nothing to do, sit where we are");
+  ai_unit_new_role(punit, AIUNIT_DEFEND_HOME, NULL); /* for default */
 }
 
 /********************************************************************** 
@@ -1162,22 +1079,56 @@
      * it looks silly. */
     pcity = find_closest_owned_city(pplayer, punit->tile, FALSE, NULL);
   }
+  if (!pcity) {
+    return;
+  }
 
   CHECK_UNIT(punit);
+  if (!same_pos(punit->tile, pcity->tile)) {
+    UNIT_LOG(LOG_DEBUG, punit, "going home to %s(%d,%d)",
+            pcity->name, TILE_XY(pcity->tile)); 
+    if (!ai_gothere(pplayer, punit, pcity->tile)) {
+      /* Died, or still travelling */
+      return;
+    }
+  }
 
-  if (pcity) {
-    UNIT_LOG(LOG_DEBUG, punit, "go home to %s(%d,%d)",
-             pcity->name, TILE_XY(pcity->tile)); 
-    if (same_pos(punit->tile, pcity->tile)) {
-      UNIT_LOG(LOG_DEBUG, punit, "go home successful; role AI_NONE");
+  if (same_pos(punit->tile, pcity->tile)) {
+    UNIT_LOG(LOG_DEBUG, punit, "at home");
+    if (punit->ai.ai_role == AIUNIT_DEFEND_HOME) {
+      /* We have a responsibility to guard the city */
+      bool alive;
+      const int attack_strength = unit_type(punit)->attack_strength;
+      const int defense_strength = unit_type(punit)->defense_strength;
+      if (attack_strength > defense_strength
+         || (attack_strength == defense_strength
+             && 1 < unit_list_size(&pcity->ai.garrison))
+         ) {
+       /* Try an aggressive defence; attack adjacent targets. */
+       alive = ai_military_rampage(punit, BODYGUARD_RAMPAGE_THRESHOLD, 
+                                   RAMPAGE_FREE_CITY_OR_BETTER,
+                                   BODYGUARD_RAMPAGE_MENACE_WT_NEAR,
+                                   BODYGUARD_RAMPAGE_MENACE_WT_FAR);
+      } else {
+       /* We are better at providing a passive defence
+        * than an aggressive defence, so do not attack anything. */
+       alive = true;
+      }
+      if (alive && !unit_list_has(&pcity->ai.garrison, punit)) {
+       /* Has this guard made the city safe enough? Reassess the defences. */
+       unit_list_insert(&pcity->ai.garrison, punit);
+       aithreat_update_city_danger(pcity);
+      }
+    } else {
+      /* We are free to do whatever we want */
+      /* Find a (new) job to do */
       ai_unit_new_role(punit, AIUNIT_NONE, NULL);
-
-      /* aggro defense goes here -- Syela */
+    
       /* Attack anything that won't kill us */
       (void) ai_military_rampage(punit, RAMPAGE_ANYTHING, 
-                                 RAMPAGE_ANYTHING);
-    } else {
-      (void) ai_gothere(pplayer, punit, pcity->tile);
+                                RAMPAGE_ANYTHING,
+                                ATTACK_RAMPAGE_MENACE_WT_NEAR,
+                                ATTACK_RAMPAGE_MENACE_WT_FAR);
     }
   }
 }
@@ -1769,7 +1720,9 @@
    * conquered cities.
    * FIXME: 2. would be more convenient if it returned FALSE if we run out 
    * of moves too.*/
-  if (!ai_military_rampage(punit, RAMPAGE_ANYTHING, RAMPAGE_ANYTHING)) {
+  if (!ai_military_rampage(punit, RAMPAGE_ANYTHING, RAMPAGE_ANYTHING,
+                          ATTACK_RAMPAGE_MENACE_WT_NEAR,
+                          ATTACK_RAMPAGE_MENACE_WT_FAR)) {
     return; /* we died */
   }
   
@@ -1779,14 +1732,7 @@
 
   /* Main attack loop */
   do {
-    if (stay_and_defend(punit)) {
-      /* This city needs defending, don't go outside! */
-      UNIT_LOG(LOG_DEBUG, punit, "stayed to defend %s", 
-               map_get_city(punit->tile)->name);
-      return;
-    }
-
-    /* Then find enemies the hard way */
+    /* find enemies the hard way */
     find_something_to_kill(pplayer, punit, &dest_tile);
     if (!same_pos(punit->tile, dest_tile)) {
 
@@ -1970,7 +1916,9 @@
     /* rest in city until the hitpoints are recovered, but attempt
        to protect city from attack (and be opportunistic too)*/
     if (ai_military_rampage(punit, RAMPAGE_ANYTHING, 
-                            RAMPAGE_FREE_CITY_OR_BETTER)) {
+                            RAMPAGE_FREE_CITY_OR_BETTER,
+                           CAUTIOUS_RAMPAGE_MENACE_WT_NEAR,
+                           CAUTIOUS_RAMPAGE_MENACE_WT_FAR)) {
       UNIT_LOG(LOGLEVEL_RECOVERY, punit, "recovering hit points.");
     } else {
       return; /* we died heroically defending our city */
@@ -1979,7 +1927,9 @@
     /* goto to nearest city to recover hit points */
     /* just before, check to see if we can occupy an undefended enemy city */
     if (!ai_military_rampage(punit, RAMPAGE_FREE_CITY_OR_BETTER, 
-                             RAMPAGE_FREE_CITY_OR_BETTER)) { 
+                             RAMPAGE_FREE_CITY_OR_BETTER,
+                            CAUTIOUS_RAMPAGE_MENACE_WT_NEAR,
+                            CAUTIOUS_RAMPAGE_MENACE_WT_FAR)) { 
       return; /* oops, we died */
     }
 
@@ -2150,11 +2100,11 @@
    * fine. If we had one and lost it, ask for a new one. */
   if (!bodyguard && punit->ai.bodyguard > BODYGUARD_NONE) {
     UNIT_LOG(LOGLEVEL_BODYGUARD, punit, "lost bodyguard, asking for new");
-    punit->ai.bodyguard = BODYGUARD_WANTED;
+    aiguard_request_guard(punit);
   }  
 
   if (punit->moves_left <= 0) {
-    /* Can do nothing */
+    UNIT_LOG(LOG_DEBUG, punit, "no more moves");
     return;
   }
 
@@ -2202,11 +2152,48 @@
 }
 
 /**************************************************************************
+  Do unit management activites that do not move any units.
+  The function uses a movemap. That movemap remains valid
+  throughout the function because the units do not move.
+**************************************************************************/
+static void ai_manage_units_plan(struct player *pplayer) 
+{
+  aiguard_clear_guards(pplayer);
+  aithreat_clear_threats(pplayer);
+
+  if (!is_barbarian(pplayer)) {
+    /* Compute the danger and menace ratings,
+     * assuming no guards are assigned to cities or units */
+    aithreat_update_city_dangers(pplayer);
+    aithreat_update_unit_dangers(pplayer);
+    aithreat_update_longterm_dangers(pplayer);
+
+    /* Assign guards */
+    aiguard_plan_cities(pplayer, TH_NEAR);
+    aiguard_plan_cities(pplayer, TH_NEAR_DIPLOMACY);
+    aiguard_request_unit_guards(pplayer);
+    aiguard_plan_units(pplayer);
+
+    /* Update the danger and menace ratings,
+     * to take into account the assigned city and unit guards.
+     * This ensures that rampage, hunting, etc. decisions,
+     * which use menace ratings, will be sensible. */
+    aithreat_clear_threats_shortterm(pplayer);
+    aithreat_update_city_dangers(pplayer);
+    aithreat_update_unit_dangers(pplayer);
+  }
+  /* else barbarians: must have more courage (ie less brains) */
+}
+
+/**************************************************************************
   Master manage unit function.
 **************************************************************************/
 void ai_manage_units(struct player *pplayer) 
 {
+  ai_manage_units_plan(pplayer);
+
   ai_airlift(pplayer);
+
   unit_list_iterate_safe(pplayer->units, punit) {
     ai_manage_unit(pplayer, punit);
   } unit_list_iterate_safe_end;
diff -ruN -Xvendor.freeciv.current/diff_ignore 
vendor.freeciv.current/common/Makefile.am freeciv.PR10694-3/common/Makefile.am
--- vendor.freeciv.current/common/Makefile.am   2004-12-29 19:44:10.000000000 
+0000
+++ freeciv.PR10694-3/common/Makefile.am        2004-12-30 21:02:36.000000000 
+0000
@@ -9,6 +9,7 @@
 ## Above, note -I../intl instead of -I$(top_srcdir/intl) is deliberate.
 
 libcivcommon_a_SOURCES = \
+               aithreattype.h  \
                capstr.c        \
                capstr.h        \
                city.c          \
diff -ruN -Xvendor.freeciv.current/diff_ignore 
vendor.freeciv.current/common/aicore/pf_tools.c 
freeciv.PR10694-3/common/aicore/pf_tools.c
--- vendor.freeciv.current/common/aicore/pf_tools.c     2004-12-29 
19:44:10.000000000 +0000
+++ freeciv.PR10694-3/common/aicore/pf_tools.c  2004-12-30 21:02:36.000000000 
+0000
@@ -515,6 +515,10 @@
   case SEA_MOVING:
     parameter->get_MC = sea_attack_move;
     break;
+  case AIR_MOVING:
+  case HELI_MOVING:
+    parameter->get_MC = single_airmove; /* very crude */
+    break;    
   default:
     die("Unsupported move_type");
   }
diff -ruN -Xvendor.freeciv.current/diff_ignore 
vendor.freeciv.current/common/aithreattype.h 
freeciv.PR10694-3/common/aithreattype.h
--- vendor.freeciv.current/common/aithreattype.h        1970-01-01 
01:00:00.000000000 +0100
+++ freeciv.PR10694-3/common/aithreattype.h     2004-12-30 21:02:36.000000000 
+0000
@@ -0,0 +1,33 @@
+/**********************************************************************
+ Freeciv - Copyright (C) 2004 - The Freeciv Project
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+***********************************************************************/
+
+#ifndef FC__AITHREATTYPE_H
+#define FC__AITHREATTYPE_H
+
+enum aithreat_class {
+  TH_NEAR,
+  TH_FAR,
+  TH_NEAR_DIPLOMACY,
+  TH_FAR_DIPLOMACY,
+  TH_LAST /* keep this last */
+};
+
+/* Danger values measured in expected-loss in shields.
+ * For these values, beware of applying WAGs, because we need to
+ * to retain correspondance between dangers ratings and unit values,
+ * so we can make sensible cost-benefit comparisons. */
+typedef unsigned int Danger;
+
+typedef Danger Dangers[TH_LAST];
+
+#endif /* FC__AITHREATTYPE_H */
diff -ruN -Xvendor.freeciv.current/diff_ignore 
vendor.freeciv.current/common/city.c freeciv.PR10694-3/common/city.c
--- vendor.freeciv.current/common/city.c        2004-12-29 19:44:10.000000000 
+0000
+++ freeciv.PR10694-3/common/city.c     2004-12-30 21:02:36.000000000 +0000
@@ -2423,6 +2423,12 @@
   pcity->ai.attack = 0;
   pcity->ai.next_recalc = 0;
 
+  pcity->ai.new_danger[TH_NEAR] = 0;
+  pcity->ai.new_danger[TH_FAR] = 0;
+  pcity->ai.new_danger[TH_NEAR_DIPLOMACY] = 0;
+  pcity->ai.new_danger[TH_FAR_DIPLOMACY] = 0;
+  unit_list_init(&pcity->ai.garrison);
+
   memset(pcity->surplus, 0, O_COUNT * sizeof(*pcity->surplus));
   memset(pcity->waste, 0, O_COUNT * sizeof(*pcity->waste));
   memset(pcity->prod, 0, O_COUNT * sizeof(*pcity->prod));
@@ -2448,5 +2454,6 @@
 void remove_city_virtual(struct city *pcity)
 {
   unit_list_unlink_all(&pcity->units_supported);
+  unit_list_unlink_all(&pcity->ai.garrison);
   free(pcity);
 }
diff -ruN -Xvendor.freeciv.current/diff_ignore 
vendor.freeciv.current/common/city.h freeciv.PR10694-3/common/city.h
--- vendor.freeciv.current/common/city.h        2004-12-29 19:44:10.000000000 
+0000
+++ freeciv.PR10694-3/common/city.h     2004-12-30 21:02:36.000000000 +0000
@@ -14,6 +14,7 @@
 #define FC__CITY_H
 
 #include "fc_types.h"
+#include "aithreattype.h"
 #include "improvement.h"
 #include "unit.h"              /* struct unit_list */
 #include "worklist.h"
@@ -188,6 +189,10 @@
                                    avoiding paradox */
   bool celebrate;               /* try to celebrate in this city */
 
+  /* new-style danger calculation: */
+  Dangers new_danger;
+  struct unit_list garrison;
+
   /* Used for caching when settlers evalueate which tile to improve,
      and when we place workers. */
   signed short int detox[CITY_MAP_SIZE][CITY_MAP_SIZE];
diff -ruN -Xvendor.freeciv.current/diff_ignore 
vendor.freeciv.current/common/combat.c freeciv.PR10694-3/common/combat.c
--- vendor.freeciv.current/common/combat.c      2004-12-29 19:44:10.000000000 
+0000
+++ freeciv.PR10694-3/common/combat.c   2004-12-30 21:02:36.000000000 +0000
@@ -507,7 +507,7 @@
 Unlike the one got from win chance this doesn't potentially get insanely
 small if the units are unevenly matched, unlike win_chance.
 **************************************************************************/
-static int get_defense_rating(struct unit *attacker, struct unit *defender)
+int get_defense_rating(struct unit *attacker, struct unit *defender)
 {
   int afp, dfp;
 
diff -ruN -Xvendor.freeciv.current/diff_ignore 
vendor.freeciv.current/common/combat.h freeciv.PR10694-3/common/combat.h
--- vendor.freeciv.current/common/combat.h      2004-12-29 19:44:10.000000000 
+0000
+++ freeciv.PR10694-3/common/combat.h   2004-12-30 21:02:36.000000000 +0000
@@ -46,6 +46,7 @@
 int get_attack_power(struct unit *punit);
 int base_get_attack_power(Unit_Type_id type, int veteran, int moves_left);
 int base_get_defense_power(struct unit *punit);
+int get_defense_rating(struct unit *attacker, struct unit *defender);
 int get_defense_power(struct unit *punit);
 int get_total_defense_power(struct unit *attacker, struct unit *defender);
 int get_virtual_defense_power(Unit_Type_id att_type, Unit_Type_id def_type,
diff -ruN -Xvendor.freeciv.current/diff_ignore 
vendor.freeciv.current/common/unit.h freeciv.PR10694-3/common/unit.h
--- vendor.freeciv.current/common/unit.h        2004-12-29 19:44:10.000000000 
+0000
+++ freeciv.PR10694-3/common/unit.h     2004-12-30 21:02:36.000000000 +0000
@@ -16,6 +16,7 @@
 #include "mem.h"               /* unit_list_iterate_safe */
 
 #include "fc_types.h"
+#include "aithreattype.h"
 #include "terrain.h"           /* enum tile_special_type */
 #include "unittype.h"
 
@@ -107,6 +108,14 @@
 struct unit_ai {
   bool control; /* 0: not automated    1: automated */
   enum ai_unit_task ai_role;
+
+  union threat_ai {
+    Dangers danger; /* In how much danger is this unit? (for our units) */
+    Dangers menace; /* How much danger does this unit present to us?
+                    * (for enemy units). */
+  } threat;
+    
+
   /* The following are unit ids or special indicator values (<=0) */
   int ferryboat; /* the ferryboat assigned to us */
   int passenger; /* the unit assigned to this ferryboat */
diff -ruN -Xvendor.freeciv.current/diff_ignore 
vendor.freeciv.current/common/unittype.c freeciv.PR10694-3/common/unittype.c
--- vendor.freeciv.current/common/unittype.c    2004-12-29 19:44:10.000000000 
+0000
+++ freeciv.PR10694-3/common/unittype.c 2004-12-30 21:02:36.000000000 +0000
@@ -653,6 +653,34 @@
 }
 
 /**************************************************************************
+Return "best" unit the player can build, with given role/flag and
+movement type.
+Returns U_LAST if none match. "Best" means highest unit type id.
+
+TODO: Cache the result per player?
+**************************************************************************/
+Unit_Type_id best_movement_role_unit_for_player(struct player *pplayer,
+                                               int role,
+                                               const enum unit_move_type 
move_type)
+{
+  int j;
+
+  assert((role >= 0 && role < F_LAST) || (role >= L_FIRST && role < L_LAST));
+
+  for(j = n_with_role[role]-1; j >= 0; j--) {
+    Unit_Type_id utype = with_role[role][j];
+
+    if (get_unit_type(j)->move_type != move_type) {
+      continue;
+    } else if (can_player_build_unit(pplayer, utype)) {
+      return utype;
+    }
+  }
+
+  return U_LAST;
+}
+
+/**************************************************************************
   Return first unit the player can build, with given role/flag.
   Returns U_LAST if none match.  Used eg when placing starting units.
 **************************************************************************/
diff -ruN -Xvendor.freeciv.current/diff_ignore 
vendor.freeciv.current/common/unittype.h freeciv.PR10694-3/common/unittype.h
--- vendor.freeciv.current/common/unittype.h    2004-12-29 19:44:10.000000000 
+0000
+++ freeciv.PR10694-3/common/unittype.h 2004-12-30 21:02:36.000000000 +0000
@@ -272,6 +272,9 @@
 Unit_Type_id get_role_unit(int role, int index);
 Unit_Type_id best_role_unit(const struct city *pcity, int role);
 Unit_Type_id best_role_unit_for_player(struct player *pplayer, int role);
+Unit_Type_id best_movement_role_unit_for_player(struct player *pplayer,
+                                               int role,
+                                               const enum unit_move_type 
move_type);
 Unit_Type_id first_role_unit_for_player(struct player *pplayer, int role);
 
 void unit_types_free(void);
diff -ruN -Xvendor.freeciv.current/diff_ignore 
vendor.freeciv.current/server/diplomats.c freeciv.PR10694-3/server/diplomats.c
--- vendor.freeciv.current/server/diplomats.c   2004-12-29 19:43:45.000000000 
+0000
+++ freeciv.PR10694-3/server/diplomats.c        2004-12-30 21:02:04.000000000 
+0000
@@ -1086,8 +1086,8 @@
 }
 
 /**************************************************************************
-  This determines if a diplomat/spy succeeds against some defender,
-  who is also a diplomat or spy.
+  This determines the diplomacy attack and defence factors for a diplomacy
+  contest.
   (Note: This is weird in order to try to conform to Civ2 rules.)
 
   - Depends entirely upon game.diplchance and the defender:
@@ -1096,39 +1096,79 @@
 
   - Return TRUE if the "attacker" succeeds.
 **************************************************************************/
-static bool diplomat_success_vs_defender (struct unit *pattacker, 
-       struct unit *pdefender, struct tile *pdefender_tile)
+static void diplomat_contest_strengths(int *att, int *def,
+                                      struct unit *pattacker, 
+                                      struct unit *pdefender,
+                                      struct tile *pdefender_tile)
 {
-  int att = game.diplchance;
-  int def = 100 - game.diplchance;
+  *att = game.diplchance;
+  *def = 100 - game.diplchance;
 
   if (unit_flag(pdefender, F_SUPERSPY)) {
-    return TRUE;
+    *def = 0;
   }
   if (unit_flag (pattacker, F_SPY)) {
-    att *= 2;
+    *att *= 2;
   }
   if (unit_flag (pdefender, F_SPY)) {
-    def *= 2;
+    *def *= 2;
   }
 
-  att += (att/5.0) * pattacker->veteran;
-  def += (def/5.0) * pdefender->veteran;
+  *att += (*att/5.0) * pattacker->veteran;
+  *def += (*def/5.0) * pdefender->veteran;
 
   if (pdefender_tile->city) {
-    def = def * (100 + get_city_bonus(pdefender_tile->city,
-                                     EFT_SPY_RESISTANT)) / 100;
+    *def = *def * (100 + get_city_bonus(pdefender_tile->city,
+                                       EFT_SPY_RESISTANT)) / 100;
   } else {
     if (tile_has_special(pdefender_tile, S_FORTRESS)
        || tile_has_special(pdefender_tile, S_AIRBASE)) {
-       def = (def * 5) / 4;/* +25% */ 
+      *def = (*def * 5) / 4;/* +25% */ 
     }
   }
+}
+
+/**************************************************************************
+  This determines if a diplomat/spy succeeds against some defender,
+  who is also a diplomat or spy.
+
+  - Return TRUE if the "attacker" succeeds.
+**************************************************************************/
+static bool diplomat_success_vs_defender (struct unit *pattacker, 
+       struct unit *pdefender, struct tile *pdefender_tile)
+{
+  int att;
+  int def;
+  diplomat_contest_strengths(&att, &def, pattacker, pdefender,pdefender_tile);
+
+  if (0 == def) {
+    return TRUE;
+  }
   
   return myrand(att) > myrand(def);
 }
 
 /**************************************************************************
+  This gives the probability of success of a diplomat/spy
+  against some defender, who is also a diplomat or spy.
+**************************************************************************/
+double diplomat_win_chance(struct unit *pattacker, 
+                          struct unit *pdefender, struct tile *pdefender_tile)
+{
+  int att;
+  int def;
+  double p;
+  diplomat_contest_strengths(&att, &def, pattacker, pdefender,pdefender_tile);
+  
+  if (def < att) {
+    p = 1.0 - def / (2 * att);
+  } else {
+    p = att / (2 * def);
+  }
+  return p;
+}
+
+/**************************************************************************
   This determines if a diplomat/spy succeeds in infiltrating a tile.
 
   - The infiltrator must go up against each defender.
diff -ruN -Xvendor.freeciv.current/diff_ignore 
vendor.freeciv.current/server/diplomats.h freeciv.PR10694-3/server/diplomats.h
--- vendor.freeciv.current/server/diplomats.h   2004-12-29 19:43:45.000000000 
+0000
+++ freeciv.PR10694-3/server/diplomats.h        2004-12-30 21:02:04.000000000 
+0000
@@ -40,4 +40,8 @@
 int count_diplomats_on_tile(struct tile *ptile);
 int unit_bribe_cost(struct unit *punit);
 
+double diplomat_win_chance(struct unit *pattacker, 
+                          struct unit *pdefender,
+                          struct tile *pdefender_tile);
+
 #endif  /* FC__DIPLOMATS_H */
diff -ruN -Xvendor.freeciv.current/diff_ignore 
vendor.freeciv.current/server/srv_main.c freeciv.PR10694-3/server/srv_main.c
--- vendor.freeciv.current/server/srv_main.c    2004-12-29 19:43:45.000000000 
+0000
+++ freeciv.PR10694-3/server/srv_main.c 2004-12-30 21:02:04.000000000 +0000
@@ -514,6 +514,7 @@
 
   conn_list_do_buffer(&game.game_connections);
 
+  ai_data_movemap_recalculate();
   players_iterate(pplayer) {
     freelog(LOG_DEBUG, "beginning player turn for #%d (%s)",
            pplayer->player_no, pplayer->name);
@@ -1852,6 +1853,7 @@
 
   initialize_move_costs(); /* this may be the wrong place to do this */
   init_settlers(); /* create minimap and other settlers.c data */
+  ai_data_movemap_init();
 
   if (!game.is_new_game) {
     players_iterate(pplayer) {
@@ -1888,6 +1890,9 @@
 
   /*** Where the action is. ***/
   main_loop();
+
+  /* Clean up some AI game data. */
+  ai_data_movemap_done();
 }
 
 /**************************************************************************
diff -ruN -Xvendor.freeciv.current/diff_ignore 
vendor.freeciv.current/server/unittools.c freeciv.PR10694-3/server/unittools.c
--- vendor.freeciv.current/server/unittools.c   2004-12-29 19:43:45.000000000 
+0000
+++ freeciv.PR10694-3/server/unittools.c        2004-12-30 21:02:04.000000000 
+0000
@@ -1495,6 +1495,16 @@
 }
 
 /**************************************************************************
+  Prevent a dangling pointer in a city garrison list.
+**************************************************************************/
+static void remove_from_garrison(struct unit *punit, struct city *pcity)
+{
+  if (pcity) {
+    unit_list_unlink(&pcity->ai.garrison, punit);
+  }
+}
+
+/**************************************************************************
 We remove the unit and see if it's disappearance has affected the homecity
 and the city it was in.
 **************************************************************************/
@@ -1538,6 +1548,13 @@
     unit_owner(punit)->is_dying = TRUE;
   }
 
+  /* The unit is no longer in any garrisons.
+   * The unit can be in at most one garrison,
+   * but this covers all the possibilities */
+  remove_from_garrison(punit, phomecity);
+  remove_from_garrison(punit, unit_tile->city);
+  remove_from_garrison(punit, find_city_by_id(punit->ai.charge));
+
   game_remove_unit(punit);
   punit = NULL;
 
diff -ruN -Xvendor.freeciv.current/diff_ignore 
vendor.freeciv.current/utility/specvec.h freeciv.PR10694-3/utility/specvec.h
--- vendor.freeciv.current/utility/specvec.h    2004-12-29 19:43:40.000000000 
+0000
+++ freeciv.PR10694-3/utility/specvec.h 2004-12-30 21:02:00.000000000 +0000
@@ -33,6 +33,7 @@
       void foo_vector_copy(struct foo_vector *to, 
                 const struct foo_vector *from);
       void foo_vector_free(struct foo_vector *tthis);
+      void foo_vector_append(struct foo_vector *tthis, foo_t *pfoo);
 
    Note this is not protected against multiple inclusions; this is
    so that you can have multiple different specvectors.  For each

[Prev in Thread] Current Thread [Next in Thread]
  • [freeciv-ai] Re: (PR#10694) AI Builds Doomed Ferries and Passengers, Benedict Adamson <=