Complete.Org: Mailing Lists: Archives: freeciv-ai: September 2002:
[freeciv-ai] Re: definitely last version of active diplomats patch
Home

[freeciv-ai] Re: definitely last version of active diplomats patch

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
To: "Per I. Mathisen" <per@xxxxxxxxxxx>
Cc: freeciv-ai@xxxxxxxxxxx
Subject: [freeciv-ai] Re: definitely last version of active diplomats patch
From: Raimar Falke <rf13@xxxxxxxxxxxxxxxxx>
Date: Fri, 6 Sep 2002 09:48:04 +0200

On Tue, Sep 03, 2002 at 08:54:41PM +0000, Per I. Mathisen wrote:
> This version cleans up all Mike's complaints, then cleans up even more,
> and replaces the kludgy use of real_map_distance with real use of warmaps,
> including in the bribery function, where we now not just consider adjacent
> targets, but any target we can reach, up to and including ships in the
> coastline!
> 
> You must be thinking, "this got to be slow". But it isn't! Since warmaps
> are cached and we create them anyway in do_unit_goto(), the actual impact
> is not even measurable with five seconds of inaccuracy.
> 
> Warmaps rock!!
> 
> Issues that can still be considered before inclusion:
>  - "medium" difficulty unchanged. It should probably not have active
>    diplomats. They are nasty.
>  - Now that we are so good at bribing, we may want to keep more diplomats
>    in defense. Tony's previous approach (never send a diplomat out on
>    action unless we have one in defence), may actually be a good idea now.
> 
> Another issue:
>  - We should _seriously_ upgrade our want to Courthouse now. That would be
>    a good idea anyway (my calculations show them to be better than
>    Marketplace in most situations). I'll look at that once Ross'
>    ai_eval_buildings() cleanup hits cvs.
> 
> But:
> Please don't complain about the incite algorithm. Once the code is in cvs,
> it is much easier to test out new, experimental algorithms. I've tested
> this one for many, many hours and it seems to work, so I don't want to
> change it now.
> 
> I've attached a small savegame created in civworld, which demonstrates the
> use of warmap bribery. Login as David Ben-Gurion and watch your diplomat
> travel around the island by rail to snatch up the coastal destroyer.

Can you please describe how the AI uses diplomats (which functions are
called in which order and do which tasks)? Can you also write down the
limitations and assumptions?

BTW: the patch is big. IMHO too big to really read is carefully.

> diff -uNrX freeciv/diff_ignore freeciv-phased/ai/advdomestic.c 
> freeciv/ai/advdomestic.c
> --- freeciv-phased/ai/advdomestic.c   2002-08-31 17:16:20.000000000 +0200
> +++ freeciv/ai/advdomestic.c  2002-09-03 18:56:38.000000000 +0200
> @@ -943,11 +943,6 @@
>      /* Oh dear, better think of something! */
>      unit_type = best_role_unit(pcity, F_TRADE_ROUTE);
>      
> -    if (unit_type == U_LAST) {
> -      unit_type = best_role_unit(pcity, F_DIPLOMAT);
> -      /* Someday, real diplomat code will be here! */
> -    }
> -    
>      if (unit_type != U_LAST) {
>        choice->want = 1;
>        choice->type = CT_NONMIL;
> diff -uNrX freeciv/diff_ignore freeciv-phased/ai/advmilitary.c 
> freeciv/ai/advmilitary.c
> --- freeciv-phased/ai/advmilitary.c   2002-09-03 22:22:36.000000000 +0200
> +++ freeciv/ai/advmilitary.c  2002-09-03 22:14:09.000000000 +0200
> @@ -23,10 +23,13 @@
>  #include "cityturn.h"
>  #include "gotohand.h"                /* warmap has been redeployed */
>  #include "settlers.h"
> +#include "diplomats.h"
> +#include "player.h"
>  
>  #include "aicity.h"
>  #include "aitools.h"
>  #include "aiunit.h"
> +#include "aidiplomat.h"
>  
>  #include "advmilitary.h"
>  
> @@ -287,8 +290,6 @@
>    int danger5 = 0; /* linear for SDI */
>    struct player *pplayer;
>    bool pikemen = FALSE;
> -  bool diplomat = FALSE; /* TRUE mean that this town can defend
> -                  * against diplomats or spies */
>    int urgency = 0;
>    bool igwall;
>    int badmojo = 0;
> @@ -305,10 +306,11 @@
>  
>    pcity->ai.grave_danger = 0;
>    pcity->ai.diplomat_threat = FALSE;
> +  pcity->ai.has_diplomat = FALSE;
>  
>    unit_list_iterate(map_get_tile(pcity->x, pcity->y)->units, punit)
>      if (unit_flag(punit, F_PIKEMEN)) pikemen = TRUE;
> -    if (unit_flag(punit, F_DIPLOMAT)) diplomat = TRUE;
> +    if (unit_flag(punit, F_DIPLOMAT)) pcity->ai.has_diplomat = TRUE;
>    unit_list_iterate_end;
>  
>    players_iterate(aplayer) {
> @@ -356,8 +358,9 @@
>                                   (v * m / (dist*2)));
>            }
>  
> -        if (unit_flag(funit, F_DIPLOMAT) && (dist <= 2 * m)) 
> -          pcity->ai.diplomat_threat = !diplomat;
> +          if (unit_flag(funit, F_DIPLOMAT) && (dist <= 2 * m)) {
> +          pcity->ai.diplomat_threat = TRUE;
> +          }
>  
>            v *= v;
>  
> @@ -399,7 +402,7 @@
>          }
>  
>          if (unit_flag(punit, F_DIPLOMAT) && (dist <= 2 * m))
> -          pcity->ai.diplomat_threat = !diplomat;
> +          pcity->ai.diplomat_threat = TRUE;
>  
>          v *= v;
>  
> @@ -1135,6 +1138,7 @@
>  
>  /* TODO: recognize units that can DEFEND_HOME but are in the field. -- Syela 
> */
>  
> +  /* Note: assess_danger() creates a warmap for us */
>    urgency = assess_danger(pcity); /* calling it now, rewriting old wall code 
> */
>    def = assess_defense_quadratic(pcity); /* has to be AFTER assess_danger 
> thanks to wallvalue */
>  /* changing to quadratic to stop AI from building piles of small units -- 
> Syela */
> @@ -1142,22 +1146,7 @@
>    freelog(LOG_DEBUG, "Assessed danger for %s = %d, Def = %d",
>         pcity->name, danger, def);
>  
> -  if (pcity->ai.diplomat_threat && def != 0){
> -  /* It's useless to build a diplomat as the last defender of a town. --nb 
> */ 
> -
> -    Unit_Type_id u = best_role_unit(pcity, F_DIPLOMAT);
> -    if (u<U_LAST) {
> -       freelog(LOG_DEBUG, "A diplomat will be built in city %s.", 
> pcity->name);
> -       choice->want = 16000; /* diplomat more important than soldiers */ 
> -       pcity->ai.urgency = 1;
> -       choice->type = CT_DEFENDER;
> -       choice->choice = u;
> -       return;
> -    } else if (num_role_units(F_DIPLOMAT)>0) {
> -      u = get_role_unit(F_DIPLOMAT, 0);
> -      pplayer->ai.tech_want[get_unit_type(u)->tech_requirement] += 16000;
> -    }
> -  } 
> +  ai_choose_diplomat_defensive(pplayer, pcity, choice, def);
>  
>    if (danger != 0) { /* otherwise might be able to wait a little longer to 
> defend */
>  /* old version had danger -= def in it, which was necessary before 
> disband/upgrade
> @@ -1264,6 +1253,7 @@
>        virtualunit.hp = unit_types[v].hp;
>        kill_something_with(pplayer, pcity, &virtualunit, choice);
>      } /* ok.  can now mung seamap for ferryboat code.  Proceed! */
> +    ai_choose_diplomat_offensive(pplayer, pcity, choice);
>      v = ai_choose_attacker_ground(pcity);
>      virtualunit.type = v;
>  /*    virtualunit.veteran = do_make_unit_veteran(pcity, v);*/
> diff -uNrX freeciv/diff_ignore freeciv-phased/ai/aidiplomat.c 
> freeciv/ai/aidiplomat.c
> --- freeciv-phased/ai/aidiplomat.c    1970-01-01 01:00:00.000000000 +0100
> +++ freeciv/ai/aidiplomat.c   2002-09-03 22:38:25.000000000 +0200
> @@ -0,0 +1,639 @@
> +/********************************************************************** 

> + Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold

Needs updating.

> +   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.
> +***********************************************************************/
> +
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <assert.h>
> +
> +#include "city.h"
> +#include "combat.h"
> +#include "game.h"
> +#include "government.h"
> +#include "log.h"
> +#include "map.h"
> +#include "mem.h"
> +#include "packets.h"
> +#include "player.h"
> +#include "rand.h"
> +#include "shared.h"
> +#include "timing.h"
> +#include "unit.h"
> +
> +#include "barbarian.h"
> +#include "citytools.h"
> +#include "cityturn.h"
> +#include "diplomats.h"
> +#include "gotohand.h"
> +#include "maphand.h"
> +#include "settlers.h"
> +#include "unithand.h"
> +#include "unittools.h"
> +
> +#include "advmilitary.h"
> +#include "aicity.h"
> +#include "aihand.h"
> +#include "aitools.h"
> +#include "aiunit.h"
> +
> +#include "aidiplomat.h"
> +

> +#define LOG_DIPLOMAT LOG_DEBUG

You use below LOGLEVEL_FOOBAR. This isn't consistent.

> +/******************************************************************************
> +  ...
> +******************************************************************************/
> +static int count_sabotagable_improvements(struct city *pcity)
> +{
> +  int count = 0;
> +
> +  built_impr_iterate(pcity, index) {
> +    if (!is_wonder(index) && index != B_PALACE) {
> +      count++;
> +    }
> +  } built_impr_iterate_end;
> +
> +  return count;
> +}
> +
> +/******************************************************************************
> +  ...
> +******************************************************************************/
> +static int count_stealable_techs(struct player *pplayer, struct player 
> *tplayer)
> +{
> +  int index, count = 0;
> +
> +  for (index = A_FIRST; index < game.num_tech_types; index++) {
> +    if ((get_invention(pplayer, index) != TECH_KNOWN)
> +        && (get_invention(tplayer, index) == TECH_KNOWN)) {
> +      count++;
> +    }
> +  }
> +
> +  return count;
> +}
> +
> +/**********************************************************************
> +  Calculates our need for diplomats as defensive units. May replace

> +  values in choice. The values 16000 and 3000 used below are totally
> +  arbitrary but seem to work.
> +***********************************************************************/
> +void ai_choose_diplomat_defensive(struct player *pplayer,
> +                                  struct city *pcity,
> +                                  struct ai_choice *choice, int def)
> +{
> +  /* Build a diplomat if our city is threatened by enemy diplomats, and
> +     we have other defensive troops, and we don't already have a diplomat
> +     to protect us. If we see an enemy diplomat and we don't have diplomat
> +     tech... race it! */
> +  if (def != 0 && pcity->ai.diplomat_threat && !pcity->ai.has_diplomat) {
> +    Unit_Type_id u = best_role_unit(pcity, F_DIPLOMAT);
> +
> +    if (u < U_LAST) {
> +       freelog(LOG_VERBOSE, "A defensive diplomat will be built in city %s.",
> +           pcity->name);
> +       choice->want = 16000; /* diplomat more important than soldiers */
> +       pcity->ai.urgency = 1;
> +       choice->type = CT_DEFENDER;
> +       choice->choice = u;
> +    } else if (num_role_units(F_DIPLOMAT) > 0) {
> +      /* We don't know diplomats yet... */
> +      freelog(LOG_VERBOSE, "A defensive diplomat is wanted badly in city 
> %s.",
> +           pcity->name);
> +      u = get_role_unit(F_DIPLOMAT, 0);
> +      /* 3000 is a just a large number, but not hillariously large as the
> +         previously used one. This is important for diplomacy later - Per */
> +      pplayer->ai.tech_want[get_unit_type(u)->tech_requirement] += 3000;

Please don't do this (using arbitrary values). State what conditions
these values have to fulfill. This way just recreates Syela.

> +    }
> +  }
> +}
> +
> +/**********************************************************************
> +  Calculates our need for diplomats as offensive units. May replace
> +  values in choice.
> +
> +  Requires an initialized warmap!
> +***********************************************************************/
> +void ai_choose_diplomat_offensive(struct player *pplayer,
> +                                  struct city *pcity,
> +                                  struct ai_choice *choice)
> +{
> +  Unit_Type_id u = best_role_unit(pcity, F_DIPLOMAT);
> +
> +  if (u >= U_LAST) {
> +    /* We don't know diplomats yet! */
> +    return;
> +  }
> +
> +  if (ai_handicap(pplayer, H_DIPLOMAT)) {
> +    /* Diplomats are too tough on newbies */
> +    return;
> +  }
> +
> +  /* Do we have a good reason for building diplomats? */
> +  {
> +    struct unit_type *ut = get_unit_type(u);
> +    struct city *acity = find_city_to_diplomat(pplayer, pcity->x,
> +                                               pcity->y, FALSE);
> +    int want, loss, p_success, p_failure, time_to_dest;
> +    int gain_incite = 0, gain_theft = 0, gain = 1;
> +    int incite_cost;
> +
> +    if (acity == NULL || acity->ai.already_considered_for_diplomat) {
> +      /* Found no target or city already considered */
> +      return;
> +    }
> +    city_incite_cost(acity); /* prime variable */
> +    incite_cost = acity->incite_revolt_cost;

> +    if (pplayer->player_no == acity->original) {
> +      incite_cost /= 2;
> +    }

Why isn't this included in city_incite_cost? It should be.

> +    if (pplayers_at_war(pplayer, city_owner(acity))

> +        && (find_palace(city_owner(acity)) != acity)

Compare incite_cost against INCITE_IMPOSSIBLE_COST.

> +        && !government_has_flag(get_gov_pplayer(city_owner(acity)),
> +                                G_UNBRIBABLE)
> +        && (acity->incite_revolt_cost <
> +            pplayer->economic.gold - pplayer->ai.est_upkeep)) {

> +      /* incite gain (FIXME: we should count wonders too but need to
> +         cache that somehow to avoid CPU hog -- Per) */

What is the problem? Updating the cached value every turn is enough.

> +      gain_incite = acity->food_prod * FOOD_WEIGHTING
> +                    + acity->shield_prod * SHIELD_WEIGHTING
> +                    + (acity->luxury_total
> +                       + acity->tax_total
> +                       + acity->science_total) * TRADE_WEIGHTING;

> +      gain_incite *= SHIELD_WEIGHTING; /* cost to take city otherwise */

???

> +      gain_incite -= acity->incite_revolt_cost;
> +    }
> +    if (city_owner(acity)->research.techs_researched <
> +             pplayer->research.techs_researched
> +             && !pplayers_allied(pplayer, city_owner(acity))) {
> +      /* tech theft gain */
> +      gain_theft = total_bulbs_required(pplayer) * TRADE_WEIGHTING;
> +    }
> +    gain = MAX(gain_incite, gain_theft);
> +    loss = ut->build_cost * SHIELD_WEIGHTING;
> +
> +    /* Probability to succeed, assuming no defending diplomat */
> +    p_success = game.diplchance;
> +    /* Probability to lose our unit */
> +    p_failure = (unit_type_flag(u, F_SPY) ? 100 - p_success : 100);
> +
> +    time_to_dest = warmap.cost[acity->x][acity->y]
> +                   * SINGLE_MOVE / ut->move_rate;
> +    time_to_dest *= (time_to_dest/2); /* No long treks, please */
> +
> +    /* Almost kill_desire */
> +    want = (p_success * gain - p_failure * loss) / 100
> +           - SHIELD_WEIGHTING * time_to_dest;
> +    if (want <= 0) {
> +      return;
> +    }
> +
> +    want = military_amortize(want, time_to_dest, ut->build_cost);
> +
> +    if (!player_has_embassy(pplayer, city_owner(acity))) {
> +        freelog(LOG_VERBOSE, "A diplomat desired in %s to establish an "
> +                          "embassy with %s in %s", pcity->name,
> +                          city_owner(acity)->name, acity->name);
> +        want = MAX(want, 99);
> +    }
> +    if (want > choice->want) {
> +      freelog(LOG_DEBUG, "%s,%s: %s is desired with want %d (was %d) to spy "
> +              "in %s (incite desire %d, tech theft desire %d)",
> +              pplayer->name, pcity->name, ut->name, want, choice->want,
> +              acity->name, gain_incite, gain_theft);
> +      choice->want = want; 
> +      choice->type = CT_ATTACKER;
> +      choice->choice = u;
> +      acity->ai.already_considered_for_diplomat = TRUE;
> +    }
> +  }
> +}
> +
> +/**************************************************************************
> +  Check if something is on our receiving end for some nasty diplomat
> +  business! Note that pdiplomat may die or be moved during this function.
> +
> +  We try to make embassy first, and abort if we already have one and target
> +  is allied. Then we incite, steal, sabotage or poison the city, in that
> +  order of priority.
> +**************************************************************************/
> +static void ai_diplomat_city(struct unit *pdiplomat, struct city *ctarget)
> +{
> +  struct packet_diplomat_action packet;
> +  struct player *pplayer = unit_owner(pdiplomat);
> +  struct player *tplayer = city_owner(ctarget);
> +  int count_impr = count_sabotagable_improvements(ctarget);
> +  int count_tech = count_stealable_techs(pplayer, tplayer);
> +  int gold_avail = pplayer->economic.gold;
> +  int incite_cost;
> +
> +  if (pdiplomat->moves_left == 0) {
> +    UNIT_LOG(LOG_ERROR, pdiplomat, "no moves left in ai_diplomat_city()!");
> +  }
> +
> +  if (pplayer->ai.control) {
> +    gold_avail -= pplayer->ai.est_upkeep;
> +  }
> +  packet.diplomat_id = pdiplomat->id;
> +  packet.target_id = ctarget->id;
> +
> +#define T(my_act,my_val)                                            \
> +  if (diplomat_can_do_action(pdiplomat, my_act, ctarget->x,         \
> +                             ctarget->y)) {                         \
> +    packet.action_type = my_act;                                    \
> +    packet.value = my_val;                                          \
> +    freelog(LOG_DIPLOMAT, "Player %s's diplomat %d does " #my_act   \
> +            " on %s", pplayer->name, pdiplomat->id, ctarget->name); \
> +    handle_diplomat_action(pplayer, &packet);                       \
> +    return;                                                         \
> +  }
> +
> +  if (!pdiplomat->foul) T(DIPLOMAT_EMBASSY,0);
> +  if (pplayers_allied(pplayer, tplayer)) return;
> +  city_incite_cost(ctarget);
> +  incite_cost = ctarget->incite_revolt_cost;
> +  if (pplayer->player_no == ctarget->original) {
> +    incite_cost /= 2;
> +  }
> +  if (incite_cost + pplayer->ai.est_upkeep <= gold_avail) {
> +    T(DIPLOMAT_INCITE,0);
> +  } else {
> +    UNIT_LOG(LOG_DIPLOMAT, pdiplomat, "%s too expensive!", ctarget->name);
> +  }
> +  if (count_tech > 0 
> +      && (ctarget->steal == 0 || unit_flag(pdiplomat, F_SPY))) {
> +    T(DIPLOMAT_STEAL,0);
> +  } else {
> +    UNIT_LOG(LOG_DIPLOMAT, pdiplomat, "We have already stolen from %s!",
> +             ctarget->name);
> +  }
> +  if (count_impr > 0) T(DIPLOMAT_SABOTAGE, B_LAST+1);
> +  T(SPY_POISON, 0); /* absolutely last resort */
> +#undef T
> +

> +  /* This can happen for a number of odd and esoteric reasons  */

???

> +  UNIT_LOG(LOG_DIPLOMAT, pdiplomat, "decides to stand idle outside "
> +           "enemy city %s!", ctarget->name);
> +  ai_unit_ready(pdiplomat);
> +}
> +
> +/**************************************************************************
> +  Find a city to send diplomats against, or NULL if none available on
> +  this continent. x,y are coordinates of diplomat or city which wishes
> +  to build diplomats, and foul is TRUE if diplomat has done something bad
> +  before.
> +
> +
> +  Requires an initialized warmap!
> +**************************************************************************/
> +struct city *find_city_to_diplomat(struct player *pplayer, int x, int y, 
> +                                   bool foul)
> +{
> +  bool has_embassy;

> +  int incite_cost = 0; /* incite cost */
> +  int move_cost = 0; /* move cost */

Use indent.

> +  int best_dist = MAX(map.xsize, map.ysize);
> +  int continent = map_get_continent(x, y);

> +  bool dipldef; /* whether target is protected by diplomats */

Same.

> +  bool handicap = ai_handicap(pplayer, H_TARGETS);
> +  struct city *ctarget = NULL;
> +
> +  players_iterate(aplayer) {
> +    /* sneaky way of avoiding foul diplomat capture  -AJS */

> +    has_embassy = player_has_embassy(pplayer, aplayer) || foul;

Move the bool here.

> +    /* Note: it is possible to lose an embassy to an allied player 
> +       if we have Marco's and lose it */
> +    if (aplayer == pplayer || is_barbarian(aplayer)
> +        || (pplayers_allied(pplayer,aplayer) && has_embassy)) {
> +      continue; 
> +    }
> +
> +    city_list_iterate(aplayer->cities, acity) {
> +      struct city *capital = find_palace(city_owner(acity));
> +
> +      if (handicap && !map_get_known(acity->x, acity->y, pplayer)) { 
> +        /* Target is not visible */
> +        continue; 
> +      }
> +      if (continent != map_get_continent(acity->x, acity->y)) { 
> +        continue; 
> +      }
> +
> +      /* Figure out incite cost */
> +      city_incite_cost(acity); /* FIXME: remove need for this */
> +      incite_cost = acity->incite_revolt_cost;
> +      if (pplayer->player_no == acity->original) {
> +        incite_cost /= 2;
> +      }
> +
> +      move_cost = warmap.cost[acity->x][acity->y];
> +      dipldef = (count_diplomats_on_tile(acity->x, acity->y) > 0);
> +      if (!ctarget || (best_dist > move_cost)) {
> +        if (!has_embassy
> +            || (acity->steal == 0 && pplayer->research.techs_researched < 
> +                city_owner(acity)->research.techs_researched && !dipldef)
> +            || (incite_cost < (pplayer->economic.gold - 
> pplayer->ai.est_upkeep)
> +                && !government_has_flag(get_gov_pplayer(city_owner(acity)),
> +                                        G_UNBRIBABLE)
> +                && acity != capital && !dipldef)) {
> +          /* We have the closest enemy city so far on the same continent */
> +          ctarget = acity;
> +          best_dist = move_cost;
> +        }
> +      }
> +    } city_list_iterate_end;
> +  } players_iterate_end;
> +
> +  return ctarget;
> +}
> +
> +/**************************************************************************
> +  Go to nearest/most threatened city.
> +
> +  Requires an initialized warmap!
> +**************************************************************************/
> +static struct city *ai_diplomat_defend(struct player *pplayer, int x, int y,
> +                                       Unit_Type_id utype)
> +{
> +  int dist, urgency;
> +  int best_dist = 30; /* any city closer than this is better than none */
> +  int best_urgency = 0;
> +  struct city *ctarget = NULL;
> +  int continent = map_get_continent(x, y);
> +
> +  city_list_iterate(pplayer->cities, acity) {

Move the stuff into the loop.

> +    if (continent != map_get_continent(acity->x, acity->y)) {
> +      continue;
> +    }
> +    urgency = acity->ai.urgency + 1;
> +    if (unit_type_flag(utype, F_DIPLOMAT)) {
> +      if (!acity->ai.has_diplomat && acity->ai.diplomat_threat) {
> +        urgency *= 5; /* we can help */
> +      } else if (acity->ai.has_diplomat) {
> +        urgency /= 3; /* we are not really needed there */
> +      }
> +    }
> +    dist = warmap.cost[acity->x][acity->y];
> +    /* I don't know if this formula is optimal, but it works. */
> +    if (dist > best_dist) {
> +      /* punish city for being so far away */
> +      urgency /= (float)(dist/best_dist);
> +    }
> +    if (urgency > best_urgency) {
> +      ctarget = acity;
> +      best_urgency = urgency;
> +      best_dist = MAX(dist,1); /* squelch divide-by-zero */
> +    }
> +  } city_list_iterate_end;
> +  return ctarget;
> +}
> +
> +/**************************************************************************
> +  Find units that we can reach, and bribe them. Returns TRUE if survived
> +  the ordeal, FALSE if not or we expended all our movement.
> +
> +  Requires an initialized warmap!
> +**************************************************************************/
> +static bool ai_diplomat_bribe_nearby(struct player *pplayer, 
> +                                     struct unit *pdiplomat)
> +{
> +  struct packet_diplomat_action packet;
> +  int move_rate = unit_type(pdiplomat)->move_rate;
> +  struct unit *pvictim;
> +  struct tile *ptile;
> +  int gold_avail = pplayer->economic.gold - pplayer->ai.est_upkeep;
> +  int cost, destx, desty;
> +  bool target = FALSE;
> +  enum goto_result result;
> +
> +  /* Check ALL possible targets */
> +  do {

Same here.

> +   whole_map_iterate(x, y) {
> +    ptile = map_get_tile(x, y);
> +    if (warmap.cost[x][y] > move_rate && ptile->terrain != T_OCEAN) {
> +      /* Can't get there */
> +      continue;
> +    }
> +    destx = x;
> +    desty = y;
> +    if (ptile->terrain == T_OCEAN) {
> +      /* Try to bribe a ship on the coast */
> +      int best = 9999;
> +      adjc_iterate(x, y, x2, y2) {
> +        if (best > warmap.cost[x2][y2]) {
> +          best = warmap.cost[x2][y2];
> +          destx = x2;
> +          desty = y2;
> +        }
> +      } adjc_iterate_end;
> +      if (best >= move_rate) {
> +        /* Can't get there, either */
> +        continue;
> +      }
> +    }
> +    pvictim = unit_list_get(&ptile->units, 0);
> +    if (!pvictim || !pplayers_at_war(pplayer, unit_owner(pvictim))) {
> +      continue;
> +    }
> +    if (unit_list_size(&ptile->units) > 1) {
> +      /* Can't do a stack */
> +      continue;
> +    }
> +    if (map_get_city(x, y)) {
> +      /* Can't do city */
> +      continue;
> +    }
> +    if (government_has_flag(get_gov_pplayer(unit_owner(pvictim)), 
> +                            G_UNBRIBABLE)) {
> +      /* Can't bribe */
> +      continue;
> +    }
> +    cost = pvictim->bribe_cost = unit_bribe_cost(pvictim);
> +    if (cost > gold_avail) {
> +      /* Can't afford */
> +      continue;
> +    }
> +    /* Found someone! */
> +    pdiplomat->goto_dest_x = destx;
> +    pdiplomat->goto_dest_y = desty;
> +    result = do_unit_goto(pdiplomat, GOTO_MOVE_ANY, FALSE);
> +    if (result == GR_DIED || pdiplomat->moves_left == 0) {
> +      return FALSE;
> +    }
> +    if (diplomat_can_do_action(pdiplomat, DIPLOMAT_BRIBE, x, y)) {
> +      packet.diplomat_id = pdiplomat->id;
> +      packet.target_id = pvictim->id;
> +      packet.action_type = DIPLOMAT_BRIBE;
> +      handle_diplomat_action(pplayer, &packet);
> +    } else {
> +      /* usually because we ended move early due to another unit */
> +      UNIT_LOG(LOG_DIPLOMAT, pdiplomat, "could not bribe target "
> +               " (%d, %d), %d moves left", x, y, pdiplomat->moves_left);
> +    }
> +   } whole_map_iterate_end;
> +  } while (target);
> +
> +  return (pdiplomat->moves_left > 0);
> +}
> +
> +/**************************************************************************
> +  If we are the only diplomat in a threatened city, defend against enemy 
> +  actions. The passive defense is set by game.diplchance.  The active 
> +  defense is to bribe units which end their move nearby. Our next trick is 
> +  to look for enemy cities on our continent and do our diplomat things.
> +
> +  FIXME: It is important to establish contact with all civilizations, so
> +  we should send diplomats by boat eventually. I just don't know how that
> +  part of the code works, yet - Per
> +**************************************************************************/
> +void ai_manage_diplomat(struct player *pplayer, struct unit *pdiplomat)
> +{
> +  struct city *pcity, *ctarget = NULL;
> +  Unit_Type_id sanity = pdiplomat->id;

> +  assert((pplayer != NULL) && (pdiplomat != NULL));

These extra () aren't used in about 80% of the code. I'm for
specifying if and under which conditions they should be used.

> +  pcity = map_get_city(pdiplomat->x, pdiplomat->y);
> +  generate_warmap(pcity, pdiplomat);
> +
> +  /* Look for someone to bribe */
> +  if (!ai_diplomat_bribe_nearby(pplayer, pdiplomat)) {
> +    /* Died or ran out of moves */
> +    return;
> +  }

> +  /* Note that the warmap may now be slightly wrong, but we
> +   * don't care, its accuracy is good enough for our purposes */

This is no longer true if the diplomat has a lot of moves compared to
the map size.

> +  /* If we are the only diplomat in a threatened city, then stay to defend */
> +  pcity = map_get_city(pdiplomat->x, pdiplomat->y); /* we may have moved */
> +  if (pcity && (count_diplomats_on_tile(pdiplomat->x, pdiplomat->y) == 1)
> +      && (pcity->ai.diplomat_threat || pcity->ai.grave_danger)) {
> +    UNIT_LOG(LOG_DIPLOMAT, pdiplomat, "stays to defend %s", pcity->name);
> +    return;
> +  }
> +
> +  if (pdiplomat->activity == ACTIVITY_GOTO) {
> +    /* Goto coordinates should always be normalized */
> +    if (!is_normal_map_pos(pdiplomat->goto_dest_x, pdiplomat->goto_dest_y)) {
> +      UNIT_LOG(LOG_FATAL, pdiplomat, "had non-normalized map pos");
> +      assert(FALSE);
> +      exit(EXIT_FAILURE);
> +    }
> +    ctarget = map_get_city(pdiplomat->goto_dest_x, pdiplomat->goto_dest_y);
> +  }
> +
> +  /* Check if we can do something with our destination now. */
> +  if (ctarget && pdiplomat->ai.ai_role == AIUNIT_ATTACK) {
> +   int dist = real_map_distance(pdiplomat->x, pdiplomat->y,
> +                                pdiplomat->goto_dest_x, 
> +                                pdiplomat->goto_dest_y);
> +
> +    UNIT_LOG(LOG_DIPLOMAT, pdiplomat, "attack, dist %d to %s (%s goto)"
> +             "[%d mc]", dist, ctarget ? ctarget->name : "(none)", 
> +             pdiplomat->activity == ACTIVITY_GOTO ? "has" : "no",
> +             warmap.cost[pdiplomat->goto_dest_x][pdiplomat->goto_dest_y]);
> +    if (dist == 1 && pdiplomat->moves_left > 0) {
> +      /* Do our stuff */
> +      ai_unit_ready(pdiplomat);
> +      ai_diplomat_city(pdiplomat, ctarget);
> +    } else if (warmap.cost[ctarget->x][ctarget->y]
> +               <= unit_type(pdiplomat)->move_rate * 2) {
> +      /* Prepare to do our stuff (ie raise taxes) */
> +      int incite_cost;
> +
> +      city_incite_cost(ctarget);
> +      incite_cost = ctarget->incite_revolt_cost;
> +      if (pplayer->player_no == ctarget->original) {
> +        incite_cost /= 2;
> +      }
> +      pplayer->ai.maxbuycost = MAX(incite_cost, pplayer->ai.maxbuycost);
> +    }
> +  }
> +
> +  /* Sanity check */
> +  if (find_unit_by_id(sanity) == NULL || pdiplomat->moves_left == 0) {
> +    return; 
> +  }
> +
> +  /* Check if existing target still makes sense */
> +  if (ctarget && pdiplomat->activity == ACTIVITY_GOTO
> +      && pdiplomat->ai.ai_role == AIUNIT_ATTACK) {
> +    /* We probably incited this city with another diplomat */
> +    if (pplayers_allied(pplayer, city_owner(ctarget))
> +        && player_has_embassy(pplayer, city_owner(ctarget))) {
> +      ai_unit_ready(pdiplomat);
> +    }
> +  }
> +
> +  /* If we are not busy, acquire a target. */
> +  if (pdiplomat->activity != ACTIVITY_GOTO) {
> +    if ((ctarget = find_city_to_diplomat(pplayer, pdiplomat->x, 
> pdiplomat->y, 
> +         pdiplomat->foul)) != NULL) {
> +      ai_unit_new_role(pdiplomat, AIUNIT_ATTACK);
> +      pdiplomat->ai.bodyguard = -1; /* want one */
> +    } else if ((ctarget = ai_diplomat_defend(pplayer, pdiplomat->x, 
> pdiplomat->y,
> +               pdiplomat->type)) != NULL) {
> +      ai_unit_new_role(pdiplomat, AIUNIT_DEFEND_HOME);
> +    } else {
> +       /* 
> +        * This should only happen if the entire continent was suddenly
> +        * conquered. So we head for closest coastal city and wait for someone
> +        * to code ferrying for diplomats, or hostile attacks from the sea. 
> +        */
> +      ctarget = find_closest_owned_city(pplayer, pdiplomat->x, pdiplomat->y, 
> +                                        TRUE, NULL);
> +    }
> +  }
> +
> +  /* GOTO (unless we want to stay) */
> +  if (ctarget && pdiplomat->moves_left > 0
> +      && !same_pos(pdiplomat->x, pdiplomat->y, ctarget->x, ctarget->y)) {
> +    enum goto_result result;
> +
> +    pdiplomat->goto_dest_x = ctarget->x;
> +    pdiplomat->goto_dest_y = ctarget->y;
> +    handle_unit_activity_request(pdiplomat, ACTIVITY_GOTO);
> +    result = do_unit_goto(pdiplomat, GOTO_MOVE_ANY, FALSE);
> +    switch (result) {
> +      case GR_ARRIVED:
> +        UNIT_LOG(LOG_DIPLOMAT, pdiplomat, "arrived at target");
> +        ai_unit_ready(pdiplomat);
> +        break;
> +      case GR_DIED:
> +      case GR_OUT_OF_MOVEPOINTS:
> +        break;
> +      case GR_FAILED:
> +        if (pdiplomat->moves_left == 0) {
> +          return; /* this can happen */
> +        }
> +        handle_unit_activity_request(pdiplomat, ACTIVITY_IDLE);
> +        /* check if we can achieve something */
> +        if (real_map_distance(pdiplomat->x, pdiplomat->y,
> +            pdiplomat->goto_dest_x, pdiplomat->goto_dest_y) == 1) {
> +          ai_diplomat_city(pdiplomat, ctarget);
> +        } else {
> +          ai_diplomat_bribe_nearby(pplayer, pdiplomat);
> +        }
> +        break;
> +      case GR_FOUGHT:
> +        UNIT_LOG(LOG_DIPLOMAT, pdiplomat, "diplomat fought??");
> +        break;
> +    }
> +  } else {
> +    /* Very unlikely but remotely possible */
> +    UNIT_LOG(LOG_DIPLOMAT, pdiplomat, "could not find a target, "
> +        "a city to defend or even a coastal city!");
> +  }
> +}
> diff -uNrX freeciv/diff_ignore freeciv-phased/ai/aidiplomat.h 
> freeciv/ai/aidiplomat.h
> --- freeciv-phased/ai/aidiplomat.h    1970-01-01 01:00:00.000000000 +0100
> +++ freeciv/ai/aidiplomat.h   2002-09-03 20:19:00.000000000 +0200
> @@ -0,0 +1,30 @@
> +/********************************************************************** 
> + Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
> +   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__AIDIPLOMAT_H
> +#define FC__AIDIPLOMAT_H
> +
> +struct player;
> +struct unit;
> +
> +struct city *find_city_to_diplomat(struct player *pplayer, int x, int y,
> +                                   bool foul);
> +void ai_manage_diplomat(struct player *pplayer, struct unit *pdiplomat);
> +void ai_choose_diplomat_defensive(struct player *pplayer,
> +                                  struct city *pcity,
> +                                  struct ai_choice *choice, int def);
> +void ai_choose_diplomat_offensive(struct player *pplayer,
> +                                  struct city *pcity,
> +                                  struct ai_choice *choice);
> +
> +
> +#endif  /* FC__AIDIPLOMAT_H */
> diff -uNrX freeciv/diff_ignore freeciv-phased/ai/aitools.c 
> freeciv/ai/aitools.c
> --- freeciv-phased/ai/aitools.c       2002-08-31 17:16:20.000000000 +0200
> +++ freeciv/ai/aitools.c      2002-09-03 20:23:39.000000000 +0200
> @@ -37,6 +37,19 @@
>  #include "aitools.h"
>  
>  /**************************************************************************
> +  When a manage function is done with a unit and cannot or will not use
> +  it anymore, call this to free its tasks so that another manage function
> +  can sanely take it over.
> +**************************************************************************/
> +void ai_unit_ready(struct unit *punit)
> +{
> +  ai_unit_new_role(punit, AIUNIT_NONE); /* also takes care of charge */
> +  handle_unit_activity_request(punit, ACTIVITY_IDLE);
> +  punit->goto_dest_x = -1;
> +  punit->goto_dest_y = -1;
> +}
> +
> +/**************************************************************************
>    Ensure unit sanity
>  **************************************************************************/
>  void ai_unit_new_role(struct unit *punit, enum ai_unit_task task)
> @@ -70,6 +83,9 @@
>    punit = find_unit_by_id(bodyguard->ai.charge);
>    assert(punit);
>  
> +  assert(punit->ai.bodyguard == bodyguard->id);
> +  assert(bodyguard->ai.charge == punit->id);
> +
>    if (!is_tiles_adjacent(x, y, bodyguard->x, bodyguard->y)) {
>      BODYGUARD_LOG(LOG_DEBUG, bodyguard, "is too far from its charge");
>      return;
> @@ -105,7 +121,7 @@
>        return TRUE;
>      } else {
>        punit->ai.bodyguard = BODYGUARD_NONE;
> -      UNIT_LOG(LOG_VERBOSE, punit, "bodyguard disappeared!");
> +      UNIT_LOG(LOGLEVEL_BODYGUARD, punit, "bodyguard disappeared!");
>      }
>    }
>    return FALSE;
> @@ -154,7 +170,11 @@
>    assert(punit);
>    assert(unit_owner(punit)->ai.control);
>    assert(is_normal_map_pos(x, y));
> -  assert(is_tiles_adjacent(punit->x, punit->y, x, y));
> +  if (!is_tiles_adjacent(punit->x, punit->y, x, y)) {
> +    freelog(LOG_ERROR, "ai_unit_move: move to non-adjacent coords");
> +    assert(FALSE);
> +    return FALSE;
> +  }
>  
>    /* if enemy, stop and let ai attack function take this case */
>    if (is_enemy_unit_tile(ptile, pplayer)
> @@ -168,7 +188,7 @@
>    }
>  
>    /* don't leave bodyguard behind */
> -  if (has_bodyguard(punit) 
> +  if (has_bodyguard(punit)
>        && (bodyguard = find_unit_by_id(punit->ai.bodyguard))
>        && same_pos(punit->x, punit->y, bodyguard->x, bodyguard->y)
>        && bodyguard->moves_left == 0) {
> @@ -247,13 +267,15 @@
>  }
>  
>  /**************************************************************************
> -... Credits the AI wants to have in reserves.
> +  Credits the AI wants to have in reserves. We need some gold to bribe
> +  and incite cities.
> +
> +  "I still don't trust this function" -- Syela
>  **************************************************************************/
>  int ai_gold_reserve(struct player *pplayer)
>  {
> -  int i = total_player_citizens(pplayer)*2;
> +  int i = total_player_citizens(pplayer) * 5;
>    return MAX(pplayer->ai.maxbuycost, i);
> -/* I still don't trust this function -- Syela */
>  }
>  
>  /**************************************************************************
> diff -uNrX freeciv/diff_ignore freeciv-phased/ai/aitools.h 
> freeciv/ai/aitools.h
> --- freeciv-phased/ai/aitools.h       2002-08-31 18:11:22.000000000 +0200
> +++ freeciv/ai/aitools.h      2002-09-03 18:56:38.000000000 +0200
> @@ -16,6 +16,10 @@
>  #include "shared.h"          /* bool type */
>  #include "unit.h"
>  
> +/* Map sanity */
> +#define HAS_GOTO(punit) \
> +  (punit->goto_dest_x != -1 && punit->goto_dest_y != -1)
> +
>  /* 
>   * Change these and remake to watch logs from a specific 
>   * part of the AI code.
> @@ -42,10 +46,11 @@
>  #define UNIT_LOG(level, punit, msg...)                       \
>    {                                                          \
>      char buffer[500];                                        \
> -    sprintf(buffer, "%s's %s[%d] (%d,%d)->(%d,%d) ",         \
> +    sprintf(buffer, "%s's %s[%d] (%d,%d)->(%d,%d){%d} ",     \
>              unit_owner(punit)->name, unit_type(punit)->name, \
>            punit->id, punit->x, punit->y,                     \
> -          punit->goto_dest_x, punit->goto_dest_y);           \
> +          punit->goto_dest_x, punit->goto_dest_y,            \
> +          punit->ai.bodyguard);                              \
>      cat_snprintf(buffer, sizeof(buffer), msg);               \
>      freelog(MIN(LOGLEVEL_UNIT, level), buffer);              \
>    }
> @@ -67,12 +72,17 @@
>    {                                                             \
>      char buffer[500];                                           \
>      struct unit *pcharge = find_unit_by_id(punit->ai.charge);   \
> -    sprintf(buffer, "%s's bodyguard %s[%d] (%d,%d)[%d@%d,%d]->(%d,%d) ",\
> +    struct city *pcity = find_city_by_id(punit->ai.charge);     \
> +    int x = -1, y = -1, id = -1; char *s = "none";              \
> +    if (pcharge) {                                              \
> +      x = pcharge->x; y = pcharge->y; id = pcharge->id;         \
> +      s = unit_type(pcharge)->name;                             \
> +    } else if (pcity) {                                         \
> +      x = pcity->x; y = pcity->y; id = pcity->id; s = pcity->name; \
> +    }                                                           \
> +    sprintf(buffer, "%s's bodyguard %s[%d] (%d,%d){%s:%d@%d,%d} ",\
>            unit_owner(punit)->name, unit_type(punit)->name,      \
> -          punit->id, punit->x, punit->y,                        \
> -          pcharge ? pcharge->id : -1, pcharge ? pcharge->x : -1,\
> -          pcharge ? pcharge->y : -1,                            \
> -          punit->goto_dest_x, punit->goto_dest_y);              \
> +          punit->id, punit->x, punit->y, s, id, x, y);          \
>      cat_snprintf(buffer, sizeof(buffer), msg);                  \
>      freelog(MIN(LOGLEVEL_BODYGUARD, level), buffer);            \
>    }
> @@ -87,6 +97,7 @@
>    BODYGUARD_NONE
>  };
>  
> +void ai_unit_ready(struct unit *punit);
>  void ai_unit_new_role(struct unit *punit, enum ai_unit_task utask);
>  void ai_unit_attack(struct unit *punit, int x, int y);
>  bool ai_unit_move(struct unit *punit, int x, int y);
> diff -uNrX freeciv/diff_ignore freeciv-phased/ai/aiunit.c freeciv/ai/aiunit.c
> --- freeciv-phased/ai/aiunit.c        2002-09-03 22:22:36.000000000 +0200
> +++ freeciv/ai/aiunit.c       2002-09-03 20:26:09.000000000 +0200
> @@ -42,13 +42,13 @@
>  
>  #include "advmilitary.h"
>  #include "aicity.h"
> +#include "aidata.h"
> +#include "aidiplomat.h"
>  #include "aihand.h"
>  #include "aitools.h"
> -#include "aidata.h"
>  
>  #include "aiunit.h"
>  
> -static void ai_manage_diplomat(struct player *pplayer, struct unit 
> *pdiplomat);
>  static void ai_manage_military(struct player *pplayer,struct unit *punit);
>  static void ai_manage_caravan(struct player *pplayer, struct unit *punit);
>  static void ai_manage_barbarian_leader(struct player *pplayer,
> @@ -1070,19 +1070,20 @@
>  {
>    struct unit *aunit = player_find_unit_by_id(pplayer, punit->ai.charge);
>    struct city *acity = find_city_by_id(punit->ai.charge);
> -  int x, y, id = punit->id;
> +  int x, y;
>  
>    if (aunit && aunit->owner == punit->owner) { 
>      /* protect a unit */
> -    x = aunit->x; 
> -    y = aunit->y; 
> +    x = aunit->x;
> +    y = aunit->y;
> +    aunit->ai.bodyguard = punit->id; /* always notify charge */
>    } else if (acity && acity->owner == punit->owner) { 
>      /* protect a city */
> -    x = acity->x; 
> -    y = acity->y; 
> -  } else { 
> +    x = acity->x;
> +    y = acity->y;
> +  } else {
>      /* should be impossible */
> -    ai_unit_new_role(punit, AIUNIT_NONE);
> +    ai_unit_ready(punit);
>      return;
>    }
>    
> @@ -1099,12 +1100,10 @@
>        punit->goto_dest_x = x;
>        punit->goto_dest_y = y;
>        result = do_unit_goto(punit, GOTO_MOVE_ANY, FALSE);
> -      if (result != GR_DIED) {
> -        GOTO_LOG(LOGLEVEL_BODYGUARD, punit, result, "bodyguard meet");
> -      }
> +      GOTO_LOG(LOGLEVEL_BODYGUARD, punit, result, "bodyguard meet");
>      } else {
>        /* can't possibly get there to help */
> -      ai_unit_new_role(punit, AIUNIT_NONE);
> +      ai_unit_ready(punit);
>      }
>    } else {
>      /* I had these guys set to just fortify, which is so dumb. -- Syela */
> @@ -1115,12 +1114,6 @@
>      /* otherwise don't bother, but free cities are free cities and must be 
>         snarfed. -- Syela */
>    }
> -
> -  /* is this insanity supposed to be a sanity check? -- per */
> -  if (aunit && unit_list_find(&map_get_tile(x, y)->units, id) 
> -      && aunit->ai.bodyguard != BODYGUARD_NONE) {
> -    aunit->ai.bodyguard = id;
> -  }
>  }
>  
>  /*************************************************************************
> @@ -1480,20 +1473,24 @@
>      return;
>    }
>  
> -  if (punit->ai.charge != BODYGUARD_NONE) { /* I am a bodyguard */
> +  /* Bodyguard sanity */
> +  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);
>  
> -    /* another crazy duct-tape sanity check to ensure we don't do something 
> stupid */
> -    if ((aunit && aunit->ai.bodyguard != BODYGUARD_NONE 
> -         && unit_vulnerability_virtual(punit) >
> -         unit_vulnerability_virtual(aunit)) ||
> -         (acity && acity->owner == punit->owner && acity->ai.urgency != 0 &&
> -          acity->ai.danger > assess_defense_quadratic(acity))) {
> -      punit->ai.ai_role = AIUNIT_ESCORT; /* do not use ai_unit_new_role() */
> +    /* Check if city we are on our way to rescue is still in danger,
> +     * or unit we should protect is still alive */
> +    if ((acity && acity->owner == punit->owner && acity->ai.urgency != 0
> +         && acity->ai.danger > assess_defense_quadratic(acity))
> +        || aunit) {
> +      if (punit->ai.ai_role != AIUNIT_ESCORT) {
> +        /* We forgot what we were doing. Incredible! */
> +        BODYGUARD_LOG(LOG_ERROR, punit, "lost role as bodyguard in findjob");
> +        punit->ai.ai_role = AIUNIT_ESCORT; /* do not use ai_unit_new_role() 
> */
> +      }
>        return;
>      } else {
> -      ai_unit_new_role(punit, AIUNIT_NONE);
> +      ai_unit_ready(punit);
>      }
>    }
>  
> @@ -1538,15 +1535,11 @@
>    if (acity) {
>      ai_unit_new_role(punit, AIUNIT_ESCORT);
>      punit->ai.charge = acity->id;
> -    freelog(LOG_DEBUG, "%s@(%d, %d) going to defend %s@(%d, %d)",
> -               unit_type(punit)->name, punit->x, punit->y,
> -               acity->name, acity->x, acity->y);
> +    BODYGUARD_LOG(LOG_DEBUG, punit, "going to defend city");
>    } else if (aunit) {
>      ai_unit_new_role(punit, AIUNIT_ESCORT);
>      punit->ai.charge = aunit->id;
> -    freelog(LOG_DEBUG, "%s@(%d, %d) going to defend %s@(%d, %d)",
> -               unit_type(punit)->name, punit->x, punit->y,
> -               unit_type(aunit)->name, aunit->x, aunit->y);
> +    BODYGUARD_LOG(LOG_DEBUG, punit, "going to defend unit");
>    } else if (ai_unit_attack_desirability(punit->type) != 0 ||
>        (pcity && !same_pos(pcity->x, pcity->y, punit->x, punit->y)))
>       ai_unit_new_role(punit, AIUNIT_ATTACK);
> @@ -1628,7 +1621,7 @@
>  
>    unit_list_iterate(pplayer->units, aunit)
>      if (aunit == punit) continue;
> -    if (aunit->activity == ACTIVITY_GOTO &&
> +    if (aunit->activity == ACTIVITY_GOTO && HAS_GOTO(aunit) &&
>         (pcity = map_get_city(aunit->goto_dest_x, aunit->goto_dest_y))) {
>        if (unit_type(aunit)->attack_strength >
>          unit_type(aunit)->transport_capacity) { /* attacker */
> @@ -1927,10 +1920,9 @@
>      }
>    } players_iterate_end;
>    if (best > 6 * THRESHOLD) return FALSE;
> -  freelog(LOG_DEBUG, "Friendly port nearest to (%d,%d) is %s@(%d,%d) [%d]",
> -             punit->x, punit->y,
> -             map_get_city(punit->goto_dest_x, punit->goto_dest_y)->name,
> -             punit->goto_dest_x, punit->goto_dest_y, best);
> +  freelog(LOG_DEBUG, "Friendly port nearest to (%d,%d) is at (%d,%d) [%d]",
> +             punit->x, punit->y, 
> +                punit->goto_dest_x, punit->goto_dest_y, best);
>    return TRUE;
>  }
>  
> @@ -2088,7 +2080,7 @@
>  static void ai_manage_ferryboat(struct player *pplayer, struct unit *punit)
>  { /* It's about 12 feet square and has a capacity of almost 1000 pounds.
>       It is well constructed of teak, and looks seaworthy. */
> -  struct city *pcity;
> +  struct city *pcity = NULL;
>    struct unit *bodyguard;
>    int best = 4 * unit_type(punit)->move_rate, x = punit->x, y = punit->y;
>    int n = 0, p = 0;
> @@ -2101,7 +2093,9 @@
>        if (punit->ai.passenger == 0) punit->ai.passenger = aunit->id; /* oops 
> */
>        p++;
>        bodyguard = unit_list_find(&map_get_tile(punit->x, punit->y)->units, 
> aunit->ai.bodyguard);
> -      pcity = map_get_city(aunit->goto_dest_x, aunit->goto_dest_y);
> +      if (HAS_GOTO(aunit)) {
> +        pcity = map_get_city(aunit->goto_dest_x, aunit->goto_dest_y);
> +      }
>        if (aunit->ai.bodyguard == BODYGUARD_NONE || bodyguard ||
>           (pcity && pcity->ai.invasion >= 2)) {
>       if (pcity) {
> @@ -2207,29 +2201,15 @@
>  **************************************************************************/
>  static void ai_manage_military(struct player *pplayer, struct unit *punit)
>  {
> -  int id;
> +  int id = punit->id;
>  
> -  id = punit->id;
> -
> -  if (punit->activity != ACTIVITY_IDLE)
> +  /* FIXME: This should be pushed further back */
> +  if (punit->activity != ACTIVITY_IDLE) {
>      handle_unit_activity_request(punit, ACTIVITY_IDLE);
> +  }
>  
> -  punit->ai.ai_role = AIUNIT_NONE; /* this can't be right -- Per */
> -  /* was getting a bad bug where a settlers caused a defender to leave home 
> */
> -  /* and then all other supported units went on DEFEND_HOME/goto */
>    ai_military_findjob(pplayer, punit);
>  
> -#ifdef DEBUG
> -  {
> -    struct city *pcity;
> -    if (unit_flag(punit, F_FIELDUNIT)
> -     && (pcity = map_get_city(punit->x, punit->y))) {
> -      freelog(LOG_DEBUG, "%s in %s is going to %d", unit_name(punit->type),
> -           pcity->name, punit->ai.ai_role);
> -    }
> -  }
> -#endif
> -  
>    switch (punit->ai.ai_role) {
>    case AIUNIT_AUTO_SETTLER:
>    case AIUNIT_BUILD_CITY:
> @@ -2325,14 +2305,8 @@
>      }
>    }
>  
> -  if ((unit_flag(punit, F_DIPLOMAT))
> -      || (unit_flag(punit, F_SPY))) {
> +  if (unit_flag(punit, F_DIPLOMAT)) {
>      ai_manage_diplomat(pplayer, punit);
> -    /* the right test if the unit is in a city and 
> -       there is no other diplomat it musn't move.
> -       This unit is a bodyguard against enemy diplomats.
> -       Right now I don't know how to use bodyguards! (17/12/98) (--NB)
> -    */
>      return;
>    } else if (unit_flag(punit, F_SETTLERS)
>            ||unit_flag(punit, F_CITIES)) {
> @@ -2473,148 +2447,6 @@
>    return FALSE;
>  }
>  
> -/**************************************************************************
> - If we are the only diplomat in a city, defend against enemy actions.
> - The passive defense is set by game.diplchance.  The active defense is
> - to bribe units which end their move nearby.
> - Our next trick is to look for enemy units and cities on our continent.
> - If we find a city, we look first to establish an embassy.  The
> - information gained this way is assumed to be more useful than anything
> - else we could do.  Making this come true is for future code.  -AJS
> -**************************************************************************/
> -static void ai_manage_diplomat(struct player *pplayer, struct unit 
> *pdiplomat)
> -{
> -  bool handicap, has_emb;
> -  int continent, dist, rmd, oic, did;
> -  struct packet_unit_request req;
> -  struct packet_diplomat_action dact;
> -  struct city *pcity, *ctarget;
> -  struct tile *ptile;
> -  struct unit *ptres;
> -
> -  if (pdiplomat->activity != ACTIVITY_IDLE)
> -    handle_unit_activity_request(pdiplomat, ACTIVITY_IDLE);
> -
> -  pcity = map_get_city(pdiplomat->x, pdiplomat->y);
> -
> -  if (pcity && count_diplomats_on_tile(pdiplomat->x, pdiplomat->y) == 1) {
> -    /* We're the only diplomat in a city.  Defend it. */
> -    if (pdiplomat->homecity != pcity->id) {
> -      /* this may be superfluous, but I like it.  -AJS */
> -      req.unit_id=pdiplomat->id;
> -      req.city_id=pcity->id;
> -      req.name[0]='\0';
> -      handle_unit_change_homecity(pplayer, &req);
> -    }
> -  } else {
> -    if (pcity) {
> -      /*
> -       * More than one diplomat in a city: may try to bribe trespassers.
> -       * We may want this to be a city_map_iterate, or a warmap distance
> -       * check, but this will suffice for now.  -AJS
> -       */
> -      adjc_iterate(pcity->x, pcity->y, x, y) {
> -     if (diplomat_can_do_action(pdiplomat, DIPLOMAT_BRIBE, x, y)) {
> -       /* A lone trespasser! Seize him! -AJS */
> -       ptile = map_get_tile(x, y);
> -       ptres = unit_list_get(&ptile->units, 0);
> -       ptres->bribe_cost = unit_bribe_cost(ptres);
> -       if (ptres->bribe_cost <
> -           (pplayer->economic.gold - pplayer->ai.est_upkeep)) {
> -         dact.diplomat_id = pdiplomat->id;
> -         dact.target_id = ptres->id;
> -         dact.action_type = DIPLOMAT_BRIBE;
> -         handle_diplomat_action(pplayer, &dact);
> -         return;
> -       }
> -     }
> -      }
> -      adjc_iterate_end;
> -    }
> -    /*
> -     *  We're wandering in the desert, or there is more than one diplomat
> -     *  here.  Go elsewhere.
> -     *  First, we look for an embassy, to steal a tech, or for a city to
> -     *  subvert.  Then we look for a city of our own without a defending
> -     *  diplomat.  This may not prove to work so very well, but it's
> -     *  possible otherwise that all diplomats we ever produce are home
> -     *  guard, and that's kind of silly.  -AJS, 20000130
> -     */
> -    ctarget = NULL;
> -    dist=MAX(map.xsize, map.ysize);
> -    continent=map_get_continent(pdiplomat->x, pdiplomat->y);
> -    handicap = ai_handicap(pplayer, H_TARGETS);
> -    players_iterate(aplayer) {
> -      /* don't target ourselves or friendly players that we already
> -         have embassies with */
> -      if ((!pplayers_at_war(pplayer, aplayer))
> -          && (player_has_embassy(pplayer, aplayer))) continue;
> -      /* sneaky way of avoiding foul diplomat capture  -AJS */
> -      has_emb=player_has_embassy(pplayer, aplayer) || pdiplomat->foul;
> -      city_list_iterate(aplayer->cities, acity)
> -     if (handicap && !map_get_known(acity->x, acity->y, pplayer)) continue;
> -     if (continent != map_get_continent(acity->x, acity->y)) continue;
> -     city_incite_cost(acity);
> -     /* figure our incite cost */
> -     oic = acity->incite_revolt_cost;
> -     if (pplayer->player_no == acity->original) oic = oic / 2;
> -     rmd=real_map_distance(pdiplomat->x, pdiplomat->y, acity->x, acity->y);
> -     if (!ctarget || (dist > rmd)) {
> -       if (!has_emb || acity->steal == 0 || (oic < 
> -               pplayer->economic.gold - pplayer->ai.est_upkeep)) {
> -         /* We have the closest enemy city so far on the same continent */
> -         ctarget = acity;
> -         dist = rmd;
> -       }
> -     }
> -      city_list_iterate_end;
> -    } players_iterate_end;
> -
> -    if (!ctarget) {
> -      /* No enemy cities are useful.  Check our own. -AJS */
> -      city_list_iterate(pplayer->cities, acy)
> -     if (continent != map_get_continent(acy->x, acy->y)) continue;
> -     if (count_diplomats_on_tile(acy->x, acy->y) == 0) {
> -       ctarget=acy;
> -       dist=real_map_distance(pdiplomat->x, pdiplomat->y, acy->x, acy->y);
> -       /* keep dist's integrity, and we can use it later. -AJS */
> -     }
> -      city_list_iterate_end;
> -    }
> -    if (ctarget) {
> -      /* Otherwise, we just kinda sit here.  -AJS */
> -      did = -1;
> -      if ((dist == 1) && (pplayer->player_no != ctarget->owner)) {
> -     dact.diplomat_id=pdiplomat->id;
> -     dact.target_id=ctarget->id;
> -     if (!pdiplomat->foul && diplomat_can_do_action(pdiplomat,
> -          DIPLOMAT_EMBASSY, ctarget->x, ctarget->y)) {
> -         did=pdiplomat->id;
> -         dact.action_type=DIPLOMAT_EMBASSY;
> -         handle_diplomat_action(pplayer, &dact);
> -     } else if (ctarget->steal == 0 && diplomat_can_do_action(pdiplomat,
> -                 DIPLOMAT_STEAL, ctarget->x, ctarget->y)) {
> -         did=pdiplomat->id;
> -         dact.action_type=DIPLOMAT_STEAL;
> -         handle_diplomat_action(pplayer, &dact);
> -     } else if (diplomat_can_do_action(pdiplomat, DIPLOMAT_INCITE,
> -                ctarget->x, ctarget->y)) {
> -         did=pdiplomat->id;
> -         dact.action_type=DIPLOMAT_INCITE;
> -         handle_diplomat_action(pplayer, &dact);
> -     }
> -      }
> -      if ((did < 0) || find_unit_by_id(did)) {
> -     pdiplomat->goto_dest_x=ctarget->x;
> -     pdiplomat->goto_dest_y=ctarget->y;
> -     set_unit_activity(pdiplomat, ACTIVITY_GOTO);
> -     do_unit_goto(pdiplomat, GOTO_MOVE_ANY, FALSE);
> -      }
> -    }
> -  }
> -  return;
> -}
> -
>  /*************************************************************************
>  Barbarian leader tries to stack with other barbarian units, and if it's
>  not possible it runs away. When on coast, it may disappear with 33% chance.
> diff -uNrX freeciv/diff_ignore freeciv-phased/ai/Makefile.am 
> freeciv/ai/Makefile.am
> --- freeciv-phased/ai/Makefile.am     2002-08-31 17:16:20.000000000 +0200
> +++ freeciv/ai/Makefile.am    2002-09-03 18:56:38.000000000 +0200
> @@ -36,4 +36,6 @@
>               aitools.c       \
>               aitools.h       \
>               aiunit.c        \
> -             aiunit.h
> +             aiunit.h        \
> +             aidiplomat.c    \
> +             aidiplomat.h
> diff -uNrX freeciv/diff_ignore freeciv-phased/client/control.c 
> freeciv/client/control.c
> --- freeciv-phased/client/control.c   2002-08-31 17:16:20.000000000 +0200
> +++ freeciv/client/control.c  2002-09-03 18:56:38.000000000 +0200
> @@ -513,8 +513,10 @@
>      pcity = find_city_by_id(victim_id);
>      punit = find_unit_by_id(victim_id);
>  
> -    if (!pdiplomat || !unit_flag(pdiplomat, F_DIPLOMAT))
> +    if (!pdiplomat || !unit_flag(pdiplomat, F_DIPLOMAT)) {
> +      assert(0); /* this is always an error */
>        continue;
> +    }
>  
>      if (punit
>       && is_diplomat_action_available(pdiplomat, DIPLOMAT_ANY_ACTION,
> diff -uNrX freeciv/diff_ignore freeciv-phased/common/city.h 
> freeciv/common/city.h
> --- freeciv-phased/common/city.h      2002-08-31 17:16:20.000000000 +0200
> +++ freeciv/common/city.h     2002-09-03 18:56:38.000000000 +0200
> @@ -186,8 +186,8 @@
>    /* building desirabilities - easiest to handle them here -- Syela */
>    int building_want[B_LAST]; /* not sure these will always be < 256 */
>    int danger; /* danger to be compared to assess_defense */
> -  bool diplomat_threat; /* an enemy diplomat or spy is near the city,
> -                       and this city has no diplomat or spy defender */
> +  bool diplomat_threat; /* an enemy diplomat or spy is near the city */
> +  bool has_diplomat; /* this city has diplomat or spy defender */
>    int urgency; /* how close the danger is; if zero, bodyguards can leave */
>    int grave_danger; /* danger that is upon us, should show positive feedback 
> */
>    int wallvalue; /* how much it helps for defenders to be ground units */
> @@ -212,6 +212,9 @@
>    int invasion; /* who's coming to kill us, for attack co-ordination */
>    int attack, bcost; /* This is also for invasion - total power and value of
>                        * all units coming to kill us. */
> +
> +  /* Used by _other_ cities temporarily while assigning diplomat targets */
> +  bool already_considered_for_diplomat;
>  };
>  
>  struct city {
> diff -uNrX freeciv/diff_ignore freeciv-phased/common/player.h 
> freeciv/common/player.h
> --- freeciv-phased/common/player.h    2002-08-31 17:16:20.000000000 +0200
> +++ freeciv/common/player.h   2002-09-03 18:56:38.000000000 +0200
> @@ -40,7 +40,7 @@
>  
>  enum handicap_type {
>    H_NONE=0, /* no handicaps */
> -  H_RIGIDPROD=1, /* can't switch to/from building_unit without penalty */
> +  H_DIPLOMAT=1, /* can't build offensive diplomats */
>    H_MAP=2, /* only knows map_get_known tiles */
>    H_TECH=4, /* doesn't know what enemies have researched */
>    H_CITYBUILDINGS=8, /* doesn't know what buildings are in enemy cities */
> diff -uNrX freeciv/diff_ignore freeciv-phased/server/cityturn.c 
> freeciv/server/cityturn.c
> --- freeciv-phased/server/cityturn.c  2002-08-31 17:16:20.000000000 +0200
> +++ freeciv/server/cityturn.c 2002-09-03 18:56:50.000000000 +0200
> @@ -397,6 +397,7 @@
>  void begin_cities_turn(struct player *pplayer)
>  {
>    city_list_iterate(pplayer->cities, pcity)
> +     pcity->ai.already_considered_for_diplomat = FALSE; /* ai hack */
>       define_orig_production_values(pcity);
>    city_list_iterate_end;
>  }
> diff -uNrX freeciv/diff_ignore freeciv-phased/server/settlers.c 
> freeciv/server/settlers.c
> --- freeciv-phased/server/settlers.c  2002-08-31 17:16:20.000000000 +0200
> +++ freeciv/server/settlers.c 2002-09-03 18:56:50.000000000 +0200
> @@ -33,6 +33,7 @@
>  #include "aicity.h"
>  #include "aiunit.h"
>  #include "aidata.h"
> +#include "aitools.h"
>  
>  #include "settlers.h"
>  
> @@ -905,7 +906,7 @@
>  
>    generate_warmap(mycity, punit);
>  
> -  if (punit->ai.ai_role == AIUNIT_BUILD_CITY) {
> +  if (punit->ai.ai_role == AIUNIT_BUILD_CITY && HAS_GOTO(punit)) {
>      remove_city_from_minimap(punit->goto_dest_x, punit->goto_dest_y);
>    }
>    punit->ai.ai_role = AIUNIT_AUTO_SETTLER; /* here and not before! -- Syela 
> */
> @@ -1410,7 +1411,7 @@
>    unit_list_iterate(pplayer->units, punit)
>      if (unit_flag(punit, F_SETTLERS)
>       || unit_flag(punit, F_CITIES)) {
> -      if (punit->activity == ACTIVITY_GOTO) {
> +      if (punit->activity == ACTIVITY_GOTO && HAS_GOTO(punit)) {
>          ptile = map_get_tile(punit->goto_dest_x, punit->goto_dest_y);
>          ptile->assigned = ptile->assigned | i; /* assigned for us only */
>        } else {
> diff -uNrX freeciv/diff_ignore freeciv-phased/server/stdinhand.c 
> freeciv/server/stdinhand.c
> --- freeciv-phased/server/stdinhand.c 2002-09-03 22:22:55.000000000 +0200
> +++ freeciv/server/stdinhand.c        2002-09-03 18:56:50.000000000 +0200
> @@ -1612,7 +1612,7 @@
>    int h[11] = { -1,
>               H_NONE,
>               H_NONE,
> -             H_RATES | H_TARGETS | H_HUTS | H_DEFENSIVE,
> +             H_RATES | H_TARGETS | H_HUTS | H_DEFENSIVE | H_DIPLOMAT,
>               H_NONE,
>               H_RATES | H_TARGETS | H_HUTS,
>               H_NONE,


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