Complete.Org: Mailing Lists: Archives: freeciv-ai: January 2003:
[freeciv-ai] AI diplomacy v6 (PR#2413)
Home

[freeciv-ai] AI diplomacy v6 (PR#2413)

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
Cc: freeciv-ai@xxxxxxxxxxx
Subject: [freeciv-ai] AI diplomacy v6 (PR#2413)
From: "Per I. Mathisen via RT" <rt@xxxxxxxxxxxxxx>
Date: Mon, 13 Jan 2003 09:49:25 -0800
Reply-to: rt@xxxxxxxxxxxxxx

Lots of changes in this one. All but one feature implemented. That is an
AI "away" mode, in which the AI will do minimal changes, meant for human
players going away for a few minutes. I'm not sure if this should be done
in this patch, though, since this feature is more general.

Notable changes in this version:
  - Tech sharing with allies, shared vision
  - Hatred (remember betrayals and backstabbings)
  - Will sometimes offer gold for treaty, better haggling for them too
  - Talk now is like  *Per(AI)* Greetings friend, may we suggest ...
  - Removed AI personalities. They were just making the code harder to
    read, will readd later, maybe.
  - Code refactored and cleaned up some
  - More intelligent handling of space race; since space race gives
    individual (not allied) victory, we *will* destroy allies that build
    space ships
  - Make peace with everyone when we build a spaceship and lead the space
    race
  - Lots of bugs killed
  - Contains some bugfixes already posted as separate patches

That's it for now. I've done a lot of playtesting, but only skimmed the
surface of what will be necessary. Since it'll require so much
playtesting, I think it should be put into cvs soon, once someone has
reviewed it. That is the only way it can be tested enough before next
release.

  - Per

? ai/advdiplomacy.c
? ai/advdiplomacy.h
Index: ai/Makefile.am
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/Makefile.am,v
retrieving revision 1.12
diff -u -r1.12 Makefile.am
--- ai/Makefile.am      2002/11/25 19:18:08     1.12
+++ ai/Makefile.am      2003/01/13 17:38:09
@@ -21,6 +21,8 @@
                advmilitary.h   \
                advscience.c    \
                advscience.h    \
+               advdiplomacy.c  \
+               advdiplomacy.h  \
                advspace.c      \
                advspace.h      \
                advtrade.c      \
Index: ai/aidata.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aidata.c,v
retrieving revision 1.9
diff -u -r1.9 aidata.c
--- ai/aidata.c 2003/01/09 02:36:36     1.9
+++ ai/aidata.c 2003/01/13 17:38:09
@@ -22,14 +22,17 @@
 #include "game.h"
 #include "government.h"
 #include "map.h"
+#include "rand.h"
 #include "unit.h"
 #include "mem.h"
 
 #include "citytools.h"
+#include "diplhand.h"
+#include "maphand.h"
 #include "settlers.h"
 #include "unittools.h"
-#include "maphand.h"
 
+#include "advdiplomacy.h"
 #include "advmilitary.h"
 #include "aicity.h"
 #include "aitools.h"
@@ -195,6 +198,12 @@
     }
   } unit_list_iterate_end;
 
+  /* Diplomacy */
+
+  if (pplayer->ai.control && !is_barbarian(pplayer)) {
+    ai_diplomacy_calculate(pplayer, ai);
+  }
+
   /* 
    * Priorities. NEVER set these to zero! Weight values are usually
    * multiplied by these values, so be careful with them. They are
@@ -222,6 +231,36 @@
   free(ai->threats.continent); ai->threats.continent = NULL;
   free(ai->stats.workers);     ai->stats.workers = NULL;
   free(ai->stats.cities);      ai->stats.cities = NULL;
+
+  /* Otherwise our meeting schedule will be so full we won't have time
+   * for new offerings! Note that I find this acceptable for human players
+   * trying to talk to AI players, since AI players respond immediately. */
+  /* FIXME: I am not really sure this is necessary. -- Per */
+  if (pplayer->ai.control) {
+    cancel_all_meetings(pplayer);
+  }
+}
+
+/**************************************************************************
+  Initialize with sane values.
+**************************************************************************/
+void ai_data_init(struct player *pplayer) {
+  struct ai_data *ai = &aidata[pplayer->player_no];
+  int i;
+
+  ai->diplomacy.target = NULL;
+  ai->diplomacy.strategy = WIN_OPEN;
+  ai->diplomacy.timer = 0;
+  ai->diplomacy.mil_strength = 0;
+
+  for (i = 0; i < MAX_NUM_PLAYERS; i++) {
+    ai->diplomacy.other[i].war_desire = 0;
+    ai->diplomacy.other[i].war_fear = 0;
+    ai->diplomacy.other[i].spam = 0;
+    ai->diplomacy.other[i].distance = 0;
+    ai->diplomacy.other[i].ally_patience = 0;
+    ai->diplomacy.other[i].hatred = 0;
+  }
 }
 
 /**************************************************************************
Index: ai/aidata.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aidata.h,v
retrieving revision 1.5
diff -u -r1.5 aidata.h
--- ai/aidata.h 2003/01/02 11:59:29     1.5
+++ ai/aidata.h 2003/01/13 17:38:09
@@ -24,7 +24,30 @@
  * start of every turn. 
  */
 
+enum winning_strategy {
+  WIN_OPEN,     /* still undetermined */
+  WIN_WAR,      /* we have no other choice than to crush all opposition */
+  WIN_SPACE,    /* we will race for space, peace very important */
+  WIN_CAPITAL   /* we cannot win unless we take war_target's capital */
+};
+
 struct ai_data {
+  /* AI diplomacy */
+  struct {
+    enum winning_strategy strategy;
+    int timer; /* pursue our goals with some stubbornness, in turns */
+    struct {
+      int war_desire; /* desire for war or peace */
+      int war_fear;   /* fear of this player's military might */
+      int spam;       /* timer to avoid spamming a player with chat */
+      int distance;   /* average distance to that player's cities */
+      int ally_patience; /* we EXPECT our allies to help us! */
+      int hatred;     /* like negative reputation, only worse */
+    } other[MAX_NUM_PLAYERS];
+    struct player *target;    /* concentrate on this player */
+    int mil_strength;         /* Estimate of our military strength */
+  } diplomacy;
+
   /* Long-term threats, not to be confused with short-term danger */
   struct {
     bool invasions;   /* check if we need to consider invasions */
@@ -63,6 +86,8 @@
   int angry_priority;
   int pollution_priority;
 };
+
+void ai_data_init(struct player *pplayer);
 
 void ai_data_turn_init(struct player *pplayer);
 void ai_data_turn_done(struct player *pplayer);
Index: ai/aiunit.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aiunit.c,v
retrieving revision 1.253
diff -u -r1.253 aiunit.c
--- ai/aiunit.c 2003/01/12 22:24:04     1.253
+++ ai/aiunit.c 2003/01/13 17:38:09
@@ -973,7 +973,8 @@
      
       /* ...and free foreign city waiting for us. Who would resist! */
       if (pcity && pplayers_at_war(pplayer, city_owner(pcity))
-          && is_ground_unit(punit)
+          && could_unit_move_to_tile(punit, x1, y1) != 0
+          && COULD_OCCUPY(punit)
           && !is_ocean(map_get_terrain(punit->x, punit->y))) {
         SET_BEST(99999);
         continue;
Index: client/civclient.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/civclient.c,v
retrieving revision 1.160
diff -u -r1.160 civclient.c
--- client/civclient.c  2003/01/05 20:51:36     1.160
+++ client/civclient.c  2003/01/13 17:38:09
@@ -823,7 +823,7 @@
   return (pplayer->is_alive
           && pplayer != game.player_ptr
           && player_has_embassy(game.player_ptr, pplayer)
-          && pplayer->is_connected
+          && (pplayer->is_connected || pplayer->ai.control)
           && can_client_issue_orders());
 }
 
Index: common/player.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/player.c,v
retrieving revision 1.107
diff -u -r1.107 player.c
--- common/player.c     2003/01/05 23:24:52     1.107
+++ common/player.c     2003/01/13 17:38:09
@@ -555,6 +555,23 @@
 }
 
 /***************************************************************
+  Returns true iff players are allied or at peace.
+***************************************************************/
+bool pplayers_in_peace(const struct player *pplayer,
+                       const struct player *pplayer2)
+{
+  enum diplstate_type ds = pplayer_get_diplstate(pplayer, pplayer2)->type;
+
+  if (pplayer == pplayer2) {
+    return TRUE;
+  }
+  if (is_barbarian(pplayer) || is_barbarian(pplayer2)) {
+    return FALSE;
+  }
+  return (ds == DS_ALLIANCE || ds == DS_PEACE);
+}
+
+/***************************************************************
 returns true iff players have peace or cease-fire
 ***************************************************************/
 bool pplayers_non_attack(const struct player *pplayer,
Index: common/player.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/player.h,v
retrieving revision 1.91
diff -u -r1.91 player.h
--- common/player.h     2003/01/05 23:24:52     1.91
+++ common/player.h     2003/01/13 17:38:09
@@ -250,6 +250,8 @@
                    const struct player *pplayer2);
 bool pplayers_allied(const struct player *pplayer,
                    const struct player *pplayer2);
+bool pplayers_in_peace(const struct player *pplayer,
+                    const struct player *pplayer2);
 bool pplayers_non_attack(const struct player *pplayer,
                        const struct player *pplayer2);
 
Index: server/diplhand.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/diplhand.c,v
retrieving revision 1.65
diff -u -r1.65 diplhand.c
--- server/diplhand.c   2002/11/15 21:24:30     1.65
+++ server/diplhand.c   2003/01/13 17:38:09
@@ -37,6 +37,8 @@
 #include "settlers.h"
 #include "unittools.h"
 
+#include "advdiplomacy.h"
+
 #include "diplhand.h"
 
 #define SPECLIST_TAG treaty
@@ -286,6 +288,8 @@
       pgiver = pclause->from;
       pdest = (plr0==pgiver) ? plr1 : plr0;
 
+      ai_treaty_react(pgiver, pdest, pclause);
+
       switch (pclause->type) {
       case CLAUSE_ADVANCE:
        notify_player_ex(pdest, -1, -1, E_TECH_GAIN,
@@ -425,6 +429,12 @@
                                 PACKET_DIPLOMACY_REMOVE_CLAUSE, packet);
       lsend_packet_diplomacy_info(&plr1->connections, 
                                 PACKET_DIPLOMACY_REMOVE_CLAUSE, packet);
+      if (plr0->ai.control) {
+        ai_treaty_evaluate(plr0, plr1, ptreaty);
+      }
+      if (plr1->ai.control) {
+        ai_treaty_evaluate(plr1, plr0, ptreaty);
+      }
     }
   }
 
@@ -468,6 +478,12 @@
       lsend_packet_diplomacy_info(&plr1->connections, 
                                 PACKET_DIPLOMACY_CREATE_CLAUSE, 
                                 packet);
+      if (plr0->ai.control) {
+        ai_treaty_evaluate(plr0, plr1, ptreaty);
+      }
+      if (plr1->ai.control) {
+        ai_treaty_evaluate(plr1, plr0, ptreaty);
+      }
     }
   }
 }
@@ -536,15 +552,18 @@
   plr0=&game.players[packet->plrno0];
   plr1=&game.players[packet->plrno1];
 
+  assert(plr0 != plr1);
+
   if (!find_treaty(plr0, plr1)) {
-    if (plr0->ai.control || plr1->ai.control) {
-      notify_player(plr0, _("AI controlled players cannot participate in "
-                           "diplomatic meetings."));
+    if (is_barbarian(plr0) || is_barbarian(plr1)) {
+      notify_player(plr0, _("Your diplomatic envoy was decapitated!"));
       return;
     }
-
-    if (player_has_embassy(plr0, plr1) && plr0->is_connected && 
-       plr0->is_alive && plr1->is_connected && plr1->is_alive) {
+    if (player_has_embassy(plr0, plr1) 
+        && (plr0->is_connected || plr0->ai.control)
+       && plr0->is_alive 
+        && (plr1->is_connected || plr1->ai.control) 
+       && plr1->is_alive) {
       struct Treaty *ptreaty;
 
       ptreaty=fc_malloc(sizeof(struct Treaty));
@@ -582,6 +601,7 @@
     return;
   }
   players_iterate(other_player) {
+
     if ( (ptreaty=find_treaty(pplayer, other_player))) {
       struct packet_diplomacy_info packet;
       
Index: server/plrhand.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/plrhand.c,v
retrieving revision 1.256
diff -u -r1.256 plrhand.c
--- server/plrhand.c    2003/01/13 17:37:42     1.256
+++ server/plrhand.c    2003/01/13 17:38:10
@@ -1311,6 +1311,7 @@
     player_map_allocate(pplayer);
   }
   player_init(pplayer);
+  ai_data_init(pplayer);
 }
 
 /********************************************************************** 
@@ -1554,10 +1555,10 @@
    * but for now AI players are always at war.
    */
   players_iterate(other_player) {
-    cplayer->diplstates[other_player->player_no].type = DS_WAR;
+    cplayer->diplstates[other_player->player_no].type = DS_NEUTRAL;
     cplayer->diplstates[other_player->player_no].has_reason_to_cancel = 0;
     cplayer->diplstates[other_player->player_no].turns_left = 0;
-    other_player->diplstates[cplayer->player_no].type = DS_WAR;
+    other_player->diplstates[cplayer->player_no].type = DS_NEUTRAL;
     other_player->diplstates[cplayer->player_no].has_reason_to_cancel = 0;
     other_player->diplstates[cplayer->player_no].turns_left = 0;
     
@@ -1583,7 +1584,7 @@
   for(i = 0; i<game.num_tech_types ; i++)
     cplayer->research.inventions[i] = pplayer->research.inventions[i];
   cplayer->turn_done = TRUE; /* Have other things to think about - paralysis*/
-  cplayer->embassy = 0;   /* all embassys destroyed */
+  cplayer->embassy = 0;   /* all embassies destroyed */
 
   /* Do the ai */
 
Index: server/srv_main.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/srv_main.c,v
retrieving revision 1.112
diff -u -r1.112 srv_main.c
--- server/srv_main.c   2003/01/05 23:24:52     1.112
+++ server/srv_main.c   2003/01/13 17:38:10
@@ -90,6 +90,7 @@
 #include "unithand.h"
 #include "unittools.h"
 
+#include "advdiplomacy.h"
 #include "advmilitary.h"
 #include "aidata.h"
 #include "aihand.h"
@@ -465,6 +466,9 @@
   } players_iterate_end;
 
   players_iterate(pplayer) {
+    if (pplayer->ai.control && !is_barbarian(pplayer)) {
+      ai_diplomacy_actions(pplayer);
+    }
     send_player_cities(pplayer);
   } players_iterate_end;
 
Index: server/unithand.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/unithand.c,v
retrieving revision 1.246
diff -u -r1.246 unithand.c
--- server/unithand.c   2003/01/09 02:36:38     1.246
+++ server/unithand.c   2003/01/13 17:38:10
@@ -980,6 +980,17 @@
   /*** Try to attack if there is an enemy unit on the target tile ***/
   if (pdefender
       && pplayers_at_war(unit_owner(punit), unit_owner(pdefender))) {
+    if (pcity && !pplayers_at_war(city_owner(pcity), unit_owner(punit))) {
+      notify_player_ex(pplayer, punit->x, punit->y, E_NOEVENT,
+                      _("Game: Can't attack %s's unit in the city of %s "
+                        "because you are not at war with %s."),
+                      unit_owner(pdefender)->name,
+                      pcity->name,
+                      city_owner(pcity)->name);
+      how_to_declare_war(pplayer);
+      return FALSE;
+    }
+
     if (!can_unit_attack_tile(punit, dest_x , dest_y)) {
       notify_player_ex(pplayer, punit->x, punit->y, E_NOEVENT,
                       _("Game: You can't attack there."));
@@ -991,18 +1002,10 @@
                       _("Game: This unit has no moves left."));
       return FALSE;
     }
-
-    if (pcity && !pplayers_at_war(city_owner(pcity), unit_owner(punit))) {
-      notify_player_ex(pplayer, punit->x, punit->y, E_NOEVENT,
-                      _("Game: Can't attack %s's unit in the city of %s "
-                        "because you are not at war with %s."),
-                      unit_owner(pdefender)->name,
-                      pcity->name,
-                      city_owner(pcity)->name);
-      how_to_declare_war(pplayer);
-      return FALSE;
-    }
 
+    /* FIXME: This code will never activate for AI players, and for
+     * human players the server-side goto implementation should be
+     * obsoleted for client usage. So in time, remove the code below. */
     if (punit->activity == ACTIVITY_GOTO && 
         !same_pos(punit->goto_dest_x, punit->goto_dest_y, dest_x, dest_y)) {
       notify_player_ex(pplayer, punit->x, punit->y, E_NOEVENT,
Index: server/unittools.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/unittools.c,v
retrieving revision 1.205
diff -u -r1.205 unittools.c
--- server/unittools.c  2003/01/12 22:36:23     1.205
+++ server/unittools.c  2003/01/13 17:38:10
@@ -104,18 +104,23 @@
 }
 
 /**************************************************************************
-  Unit can't attack if:
+ Unit can't attack if:
  1) it doesn't have any attack power.
  2) it's not a fighter and the defender is a flying unit (except city/airbase).
  3) if it's not a marine (and ground unit) and it attacks from ocean.
  4) a ground unit can't attack a ship on an ocean square (except marines).
  5) the players are not at war.
+ 6) a city there is owned by non-attack player
+
+ Does NOT check:
+ 1) Moves left
 **************************************************************************/
 bool can_unit_attack_unit_at_tile(struct unit *punit, struct unit *pdefender,
-                                int dest_x, int dest_y)
+                                  int dest_x, int dest_y)
 {
   enum tile_terrain_type fromtile;
   enum tile_terrain_type totile;
+  struct city *pcity = map_get_city(dest_x, dest_y);
 
   fromtile = map_get_terrain(punit->x, punit->y);
   totile   = map_get_terrain(dest_x, dest_y);
@@ -150,6 +155,10 @@
   /* Shore bombardement */
   if (is_ocean(fromtile) && is_sailing_unit(punit) && !is_ocean(totile)) {
     return (get_attack_power(punit)>0);
+  }
+
+  if (pcity && !pplayers_at_war(city_owner(pcity), unit_owner(punit))) {
+    return FALSE;
   }
 
   return TRUE;
/********************************************************************** 
 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.
***********************************************************************/

#include "city.h"
#include "diptreaty.h"
#include "events.h"
#include "game.h"
#include "packets.h"
#include "player.h"
#include "rand.h"
#include "log.h"
#include "mem.h"
#include "nation.h"
#include "shared.h"
#include "support.h"
#include "tech.h"

#include "citytools.h"
#include "diplhand.h"
#include "plrhand.h"
#include "maphand.h"
#include "settlers.h"  /* amortize */
#include "spaceship.h"

#include "aidata.h"
#include "aitools.h"
#include "advmilitary.h"

#include "advdiplomacy.h"

#define LOG_DIPL LOG_DEBUG
#define LOG_DIPL2 LOG_DEBUG

/* Prototypes */
static int ai_goldequiv_city(struct city *pcity);
static int ai_goldequiv_tech(struct player *pplayer, Tech_Type_id tech);
static int ai_war_desire(struct player *pplayer, struct player *aplayer,
                         struct ai_data *ai);
static int ai_war_fear(struct player *pplayer, struct player *aplayer,
                         struct ai_data *ai);
static int ai_goldequiv_clause(struct player *pplayer, struct player *aplayer, 
                               struct Clause *pclause, struct ai_data *ai);

/* PATCH TODO LIST:
        -> make "away" AI mode where the AI doesn't do diplomacy
        -> make E_DIPLOMACY event type for diplo_notify
*/

/********************************************************************** 
  Send a diplomatic message. Use this instead of notify because we
  may want to highligh/present these messages differently in the
  future.
***********************************************************************/
#define diplo_notify(pplayer, text, ...) \
  notify_player_ex(pplayer, -1, -1, E_DIPLOMATIC_INCIDENT, text, __VA_ARGS__);

#define TALK(x) "*" #x "(AI)* "

/********************************************************************** 
  Evaluate gold worth of a single clause in a treaty. Note that it
  sometimes matter a great deal who is giving what to whom, and
  sometimes (such as with treaties) it does not matter at all.
***********************************************************************/
static int ai_goldequiv_clause(struct player *pplayer, 
                               struct player *aplayer,
                               struct Clause *pclause,
                               struct ai_data *ai)
{
  int worth[MAX_NUM_PLAYERS]; /* worth of what X gives */
  int i;
  bool give = (pplayer == pclause->from);
  int receiver, giver;
  int cost; /* shortcut temp variable */

  for (i = 0; i < MAX_NUM_PLAYERS; i++) {
    worth[i] = 0;
  }

  giver = pclause->from->player_no;
  if (give) {
    receiver = aplayer->player_no;
  } else {
    receiver = pplayer->player_no;
  }

  switch (pclause->type) {
  case CLAUSE_ADVANCE:
    if (give) {
      cost = ai_goldequiv_tech(aplayer, pclause->value);
    } else {
      cost = ai_goldequiv_tech(pplayer, pclause->value);
    }

    /* Share and expect being shared brotherly between allies */
    if (pplayers_allied(pplayer, aplayer)) {
      return 0;
    }

    /* Calculate in tech leak to our enemies, guess 50% chance */
    if (give) { 
      players_iterate(eplayer) {
        if (pplayers_allied(aplayer, eplayer) && aplayer != eplayer
            && eplayer->is_alive
            && get_invention(eplayer, pclause->value) != TECH_KNOWN) {
          worth[eplayer->player_no] -= 
              ai_goldequiv_tech(eplayer, pclause->value) / 2;
        }
      } players_iterate_end;
    }
    worth[receiver] += cost;
    /* FIXME: We should calculate use of tech for allies too, but we
       need to look just how the tech exchange code turns out 
       first. - Per */
  break;

  case CLAUSE_ALLIANCE:
  case CLAUSE_PEACE:
  case CLAUSE_CEASEFIRE:
    cost = ai->diplomacy.other[aplayer->player_no].war_desire;

    /* Breaking treaties give us penalties on future diplomacy, so
     * avoid flip-flopping treaty/war with our enemy. */
    if (aplayer == ai->diplomacy.target) {
      cost *= 3;
    }

    /* We don't want treaties that obstruct our aims, but if cost
       is negative then we want 'em this much. */
    worth[pplayer->player_no] += -(cost * TRADE_WEIGHTING
                                   * MIN(city_list_size(&aplayer->cities), 20));
    freelog(LOG_DIPL, "(%s ai diplo) Calculating pact with %s as (%d*%d*%d=)%d",
            pplayer->name, aplayer->name, cost, TRADE_WEIGHTING, 
            MIN(20, city_list_size(&aplayer->cities)),
            worth[pplayer->player_no]);

    /* Modify for peace */
    if (pclause->type == CLAUSE_PEACE) { 
      if (!pplayers_non_attack(pplayer, aplayer)) {
        diplo_notify(aplayer, TALK(%s) "Let us first cease hostilies, %s",
                     pplayer->name, aplayer->name);
        return -FC_INFINITY;
      } else {
        worth[pplayer->player_no] *= 1.5;
      }
    }

    /* Modify for alliance */
    if (pclause->type == CLAUSE_ALLIANCE) {
      bool player_is_allied_with_ally = FALSE;
      players_iterate(check_pl) {
        /* We want to ally our ally's allies to prevent
         * ugly wars between allies */
        if (check_pl != pplayer && check_pl != aplayer
            && check_pl->is_alive
            && pplayers_allied(pplayer, check_pl)) {
          if (pplayers_allied(aplayer, check_pl)) {
            player_is_allied_with_ally = TRUE;
          } else if (pplayers_at_war(aplayer, check_pl)) {
            diplo_notify(aplayer, TALK(%s) "First make peace with %s, %s",
                         pplayer->name, check_pl->name, aplayer->name);
            return -FC_INFINITY;
          }
        }
      } players_iterate_end;
      if (!pplayers_in_peace(pplayer, aplayer)) {
        diplo_notify(aplayer, TALK(%s) "Let us first make peace, %s",
                     pplayer->name, aplayer->name);
        return -FC_INFINITY;
      } else if (!player_is_allied_with_ally) {
        worth[pplayer->player_no] *= 3;
      } else {
        /* An ally's ally: Let's all join hands! */
        worth[pplayer->player_no] = 1;
      }
    }

    /* Modify for reputation */
    i = (GAME_DEFAULT_REPUTATION - aplayer->reputation) 
        * city_list_size(&aplayer->cities) / 4;
    if (i != 0) {
      worth[pplayer->player_no] -= i;
      freelog(LOG_DIPL, "(%s ai diplo) %s has reputation %d, penalised with 
%d", 
              pplayer->name, aplayer->name, aplayer->reputation, i);
    }
  break;

  case CLAUSE_GOLD:
    worth[receiver] += pclause->value;
    break;

  case CLAUSE_SEAMAP:
    /* Very silly algorithm 1: Sea map more worth if enemy has more
       cities. Reasoning is he has more use of seamap for settling
       new areas the more cities he has already. */
    if (!give || pplayers_allied(pplayer, aplayer)) {
      /* Useless to us - we're omniscient! And allies get it for free! */
      worth[receiver] = 0;
      break;
    }
    worth[receiver] += 25 * city_list_size(&aplayer->cities);
    break;

  case CLAUSE_MAP:
    /* Very silly algorithm 2: Land map more worth the more cities
       we have, since we expose all of these to the enemy. */
    if (!give || pplayers_allied(pplayer, aplayer)) {
      /* Useless to us - we're omniscient! And allies get it for free! */
      worth[receiver] = 0;
      break;
    }
    worth[receiver] += 75 * city_list_size(&pplayer->cities);
    /* Inflate numbers if not peace */
    if (!pplayers_in_peace(pplayer, aplayer)) { 
      worth[receiver] *= 3;
    }
    break;

  case CLAUSE_CITY: {
    struct city *offer = city_list_find_id(&(pclause->from)->cities, 
                                           pclause->value);

    if (!offer || offer->owner != giver) {
      /* City destroyed or taken during negotiations */
      diplo_notify(aplayer, TALK(%s) "You don't have the offered city!",
                   pplayer->name);
      break;
    }
    worth[receiver] += ai_goldequiv_city(offer);
    if (give) {
      /* AI must be crazy to trade away its cities */
      worth[receiver] *= 15;
      if (city_got_building(offer, B_PALACE)) {
        return -FC_INFINITY; /* never! */
      }
    }
    break;
  }

  case CLAUSE_VISION:
    if (give) {
      if (pplayers_allied(pplayer, aplayer)) {
        worth[receiver] = 0;
      } else {
        /* so out of the question */
        return -FC_INFINITY;
      }
    } else {
      worth[receiver] = 1; /* We are omniscient, so... */
    }
    break;
  } /* end of switch */

  i = 0; /* balance */

  /* Check externalities */
  players_iterate(eplayer) {
    /* We are supposed to give away something, check how this benefits
       our enemies */
    if (!eplayer->is_alive) {
      continue;
    }
    if (give && eplayer != aplayer && pplayers_at_war(eplayer, pplayer)) {
      i += worth[eplayer->player_no]; 
    }
    /* We receive something, check how this benefits our friends */
    if (!give && eplayer != pplayer && eplayer != aplayer 
        && pplayers_allied(eplayer, pplayer)) {
      i -= worth[eplayer->player_no];
    }
  } players_iterate_end;

  /* wrt pplayer */
  i += worth[pplayer->player_no]; 
  i -= worth[aplayer->player_no];

  /* Returns balance of what they offer vs what we offer */
  if (give) {
    freelog(LOG_DIPL, "(%s ai diplo) evaluating clause from %s to %s as worth 
%d",
            pplayer->name, pplayer->name, aplayer->name, i);
  } else {
    freelog(LOG_DIPL, "(%s ai diplo) evaluating clause from %s to %s as worth 
%d",
           pplayer->name, aplayer->name, pplayer->name, i);
  }
  return i;
}

/********************************************************************** 
  pplayer is AI player, aplayer is the other player involved, treaty
  is the treaty being considered. It is all a question about money :-)
***********************************************************************/
void ai_treaty_evaluate(struct player *pplayer, struct player *aplayer,
                        struct Treaty *ptreaty)
{
  struct packet_diplomacy_info packet;
  int balance = 0;
  bool has_treaty = FALSE;
  struct ai_data *ai = ai_data_get(pplayer);

  assert(!is_barbarian(pplayer));

  packet.plrno0 = pplayer->player_no;
  packet.plrno1 = aplayer->player_no;
  packet.plrno_from = pplayer->player_no;

  /* Evaluate clauses */
  clause_list_iterate(ptreaty->clauses, pclause) {
    balance += ai_goldequiv_clause(pplayer, aplayer, pclause, ai);
    if (is_pact_clause(pclause->type)) {
      has_treaty = TRUE;
    }
  } clause_list_iterate_end;

  /* If we are at war, and no peace is offered, then no deal */
  if (pplayers_at_war(pplayer, aplayer) && !has_treaty) {
    return;
  }

  /* Accept if balance is good */
  if (balance >= 0) {
    handle_diplomacy_accept_treaty(pplayer, &packet);
  }
}

/********************************************************************** 
  Comments to player from AI on clauses being agreed on. Does not
  alter any state.
***********************************************************************/
void ai_treaty_react(struct player *pplayer,
                     struct player *aplayer,
                     struct Clause *pclause)
{
  switch (pclause->type) {
    case CLAUSE_ALLIANCE:
      diplo_notify(aplayer, TALK(%s) "Yes, may we forever stand united, %s",
                   pplayer->name, aplayer->name);
      freelog(LOG_DIPL2, "(%s ai diplo) %s allies with %s!",
              pplayer->name, pplayer->name, aplayer->name);
      break;
    case CLAUSE_PEACE:
      diplo_notify(aplayer, TALK(%s) "Yes, peace in our time!",
                   pplayer->name);
      freelog(LOG_DIPL2, "(%s ai diplo) %s makes peace with %s",
              pplayer->name, pplayer->name, aplayer->name);
      break;
    case CLAUSE_CEASEFIRE:
      diplo_notify(aplayer, TALK(%s) "Agreed. No more hostilities, %s",
                   pplayer->name, aplayer->name);
      freelog(LOG_DIPL2, "(%s ai diplo) %s agrees to ceasefire with %s",
              pplayer->name, pplayer->name, aplayer->name);
      break;
    default:
      break;
  }
}

/********************************************************************** 
  Check if we can win by space race victory. We can do this if we have
  a spacerace lead.
***********************************************************************/
static bool ai_space_victory(struct player *pplayer)
{
  struct player_spaceship *our_ship = &pplayer->spaceship;
  int our_arrival = MAX((int) our_ship->travel_time -
                        - (game.year - our_ship->launch_year), 1);

  if (game.spacerace == FALSE || our_ship->state == SSHIP_NONE) {
    return FALSE;
  }

  players_iterate(aplayer) {
    struct player_spaceship *enemy_ship = &aplayer->spaceship;
    int enemy_arrival = MAX((int) enemy_ship->travel_time -
                            - (game.year - enemy_ship->launch_year), 1);

    if (!aplayer->is_alive) {
      continue;
    }

    if (enemy_ship->state > our_ship->state) {
      return FALSE;
    }
    if (enemy_ship->state == SSHIP_LAUNCHED
        && our_ship->state == SSHIP_LAUNCHED
        && enemy_arrival >= our_arrival) {
      return FALSE;
    }
  } players_iterate_end;

  return TRUE;
}

/********************************************************************** 
  Calculate our desire to go to war against aplayer (or our hatred of
  aplayer).
***********************************************************************/
static int ai_war_desire(struct player *pplayer, struct player *aplayer,
                         struct ai_data *ai)
{
  int kill_desire, arrival, our_arrival;
  struct player_spaceship *ship = &aplayer->spaceship;
  struct player_spaceship *our_ship = &pplayer->spaceship;
  enum diplstate_type ds = pplayer_get_diplstate(pplayer, aplayer)->type;
  bool cancel_excuse = 
            pplayer->diplstates[aplayer->player_no].has_reason_to_cancel;

  /* Number of cities is a player's base potential. */
  kill_desire = city_list_size(&aplayer->cities);

  /* Count settlers in production for us, indicating our expansionism,
   * while counting all enemy settlers as (worst case) indicators of
   * enemy expansionism */
  city_list_iterate(pplayer->cities, pcity) {
    if (pcity->is_building_unit 
        && unit_type_flag(pcity->currently_building, F_CITIES)) {
      kill_desire -= 1;
    }
  } city_list_iterate_end;
  unit_list_iterate(aplayer->units, punit) { 
    if (unit_flag(punit, F_CITIES)) { kill_desire += 1; }
  } unit_list_iterate_end;

  /* Count big cities as twice the threat */
  city_list_iterate(aplayer->cities, pcity) {
    kill_desire += pcity->size > 8 ? 1 : 0;
  } city_list_iterate_end;

  /* Tech lead is worrisome */
  kill_desire += MAX(aplayer->research.techs_researched -
                     pplayer->research.techs_researched, 0);

  /* Spacerace loss we will not allow! */
  if (ship->state >= SSHIP_STARTED) {
    /* add potential */
    kill_desire += city_list_size(&aplayer->cities);
  }
  if (our_ship->state >= SSHIP_LAUNCHED) { 
    our_arrival = MAX((int) our_ship->travel_time - 
        (game.year - our_ship->launch_year), 1);
  } else {
    our_arrival = FC_INFINITY;
  }
  if (ship->state >= SSHIP_LAUNCHED) { 
    arrival = MAX((int) ship->travel_time - 
        (game.year - ship->launch_year), 1);
    if (arrival < our_arrival) {
      /* The first division is for the unlikely case of several 
         simultaneous ship launches, the second to avoid overflows */
      kill_desire = FC_INFINITY / arrival / 2;
      ai->diplomacy.timer = FC_INFINITY;
      ai->diplomacy.strategy = WIN_CAPITAL;
    }
  }

  /* Jealousy */
  /* FIME: add score comparisons here, but add code for cacheing
     score results first, or we will spam the CPU - Per */

  /* Modify by which treaties we would have to break, and what
   * excuses we have to do so. */
  if (!cancel_excuse) {
    if (ds == DS_CEASEFIRE) {
      kill_desire -= kill_desire / 10; /* 10% off */
    } else if (ds == DS_NEUTRAL) {
      kill_desire -= kill_desire / 7; /* 15% off */
    } else if (ds == DS_PEACE) {
      kill_desire -= kill_desire / 5; /* 20% off */
    } else if (ds == DS_ALLIANCE) {
      kill_desire -= kill_desire / 3; /* 33% off here, more later */
    }
  }

  /* Modify by hatred */
  kill_desire += kill_desire / 100
                 * ai->diplomacy.other[aplayer->player_no].hatred * 10;

  /* Amortize by distance */
  return amortize(kill_desire, 
                  ai->diplomacy.other[aplayer->player_no].distance);
}

/********************************************************************** 
  Calculate relative military strength of us vs him. This is not the
  place to calculate the worth of his allies.
***********************************************************************/
static int ai_war_fear(struct player *pplayer, struct player *aplayer,
                       struct ai_data *ai)
{
  int them = 1;

  /* relative military strength */
  unit_list_iterate(aplayer->units, punit) {
    them += ai_unit_attack_desirability(punit->type);
  } unit_list_iterate_end;

  return (them / ai->diplomacy.mil_strength) 
         - (ai->diplomacy.mil_strength / them);
}

/********************************************************************** 
  How much is a tech worth to player measured in gold
***********************************************************************/
static int ai_goldequiv_tech(struct player *pplayer, Tech_Type_id tech)
{
  int worth;

  if (get_invention(pplayer, tech) == TECH_KNOWN) {
    return 0;
  }
  worth = total_bulbs_required_for_goal(pplayer, tech) * TRADE_WEIGHTING;
  worth += pplayer->ai.tech_want[tech] / 8; /* often way too much */
  if (get_invention(pplayer, tech) == TECH_REACHABLE) {
    worth /= 2;
  }
  freelog(LOG_DIPL, "(%s ai diplo) %s goldequiv'ed to %d (had want %d)",
          pplayer->name, get_tech_name(pplayer, tech), worth, 
          pplayer->ai.tech_want[tech]);

  return worth;
}

/********************************************************************** 
  How much is city worth measured in gold
***********************************************************************/
static int ai_goldequiv_city(struct city *pcity)
{
  int worth;
  struct player *pplayer = city_owner(pcity);

  worth = pcity->size * 150; /* reasonable base cost */
  built_impr_iterate(pcity, impr) {
    if (improvement_types[impr].is_wonder && !wonder_obsolete(impr)) {
      worth += improvement_types[impr].build_cost;
    } else {
      worth += (improvement_types[impr].build_cost / 4);
    }
  } built_impr_iterate_end;
  if (city_unhappy(pcity)) {
    worth *= 0.75;
  }
  freelog(LOG_DIPL, "(%s ai diplo) city %s goldequiv'ed to %d", 
          pplayer->name, pcity->name, worth);

  return worth;
}

/********************************************************************** 
  Suggest a treaty from pplayer to aplayer
***********************************************************************/
static void ai_diplomacy_suggest(struct player *pplayer, 
                                 struct player *aplayer,
                                 enum clause_type what,
                                 int value)
{
  struct packet_diplomacy_info packet;

  if (!player_has_embassy(pplayer, aplayer)) {
    return;
  }

  packet.plrno_from = pplayer->player_no;
  packet.plrno0 = pplayer->player_no;
  packet.plrno1 = aplayer->player_no;
  packet.clause_type = what;
  packet.value = value;

  handle_diplomacy_init(pplayer, &packet);
  handle_diplomacy_create_clause(pplayer, &packet);
}

/********************************************************************** 
  Calculate our diplomatic predispositions here. Don't do anything.

  Only ever called for AI players and never for barbarians.
***********************************************************************/
void ai_diplomacy_calculate(struct player *pplayer, struct ai_data *ai)
{
  int war_desire = 0;
  int war_fear = 0;
  struct player *target = NULL;
  struct city *palace = find_palace(pplayer);

  assert(pplayer->ai.control);
  if (!pplayer->is_alive) {
    return; /* doh */
  }

  /* If we have been wronged, crank up hatred. If allied, may reduce. */
  players_iterate(aplayer) {
    ai->diplomacy.other[aplayer->player_no].hatred +=
            pplayer->diplstates[aplayer->player_no].has_reason_to_cancel;
    if (pplayers_allied(pplayer, aplayer)) {
      ai->diplomacy.other[aplayer->player_no].hatred -= MIN(myrand(4), 1);
    }
  } players_iterate_end;

  /* Stop war against a dead player */
  if (ai->diplomacy.target && !ai->diplomacy.target->is_alive) {
    freelog(LOG_DIPL2, "(%s ai diplo) Target player %s is dead. Victory!",
            pplayer->name, ai->diplomacy.target->name);
    ai->diplomacy.timer = 0;
    ai->diplomacy.target = NULL;
    if (ai->diplomacy.strategy == WIN_CAPITAL) {
      ai->diplomacy.strategy = WIN_OPEN;
    }
  }

  /* Calculate our military strength */
  ai->diplomacy.mil_strength = 1;
  unit_list_iterate(pplayer->units, punit) {
     ai->diplomacy.mil_strength += ai_unit_attack_desirability(punit->type);
  } unit_list_iterate_end;

  /* Ensure that we don't prematurely end an ongoing war */
  if (ai->diplomacy.timer-- > 0) {
    return;
  }

  /*
   * Calculate average distances to other players' empires. A
   * possibly unwarranted assumption here is that our palace will
   * be located in the middle of our empire. If we don't have one, punt.
   */
  players_iterate(aplayer) {
    int cities = 0;
    int dists = 0;

    if (pplayer == aplayer || !palace || !aplayer->is_alive) {
      ai->diplomacy.other[aplayer->player_no].distance = 0;
      continue;
    }

    city_list_iterate(aplayer->cities, pcity) {
      cities++;
      dists += real_map_distance(palace->x, palace->y, pcity->x, pcity->y);
    } city_list_iterate_end;

    if (cities > 0) {
      ai->diplomacy.other[aplayer->player_no].distance = dists / cities;
    } else {
      ai->diplomacy.other[aplayer->player_no].distance = 0;
    }
  } players_iterate_end;

  /* Can we win by space race? */
  if (ai_space_victory(pplayer)) {
    freelog(LOG_DIPL2, "%s going for space race victory!", pplayer->name);
    ai->diplomacy.strategy = WIN_SPACE; /* Yes! */
  } else {
    if (ai->diplomacy.strategy == WIN_SPACE) {
       ai->diplomacy.strategy = WIN_OPEN;
    }
  }

  /* Calculate our fears and desires, and find desired war target */
  players_iterate(aplayer) {
    enum diplstate_type ds = pplayer_get_diplstate(pplayer, aplayer)->type;
    bool want_avoid_war = FALSE;

    /* We don't fear/hate ourselves, those we don't know and those we're
     * allied to or team members. */
    if (aplayer == pplayer
        || !aplayer->is_alive
        || ds == DS_NO_CONTACT
        || (pplayer->team != TEAM_NONE && pplayer->team == aplayer->team)
        || ds == DS_ALLIANCE) {
      ai->diplomacy.other[aplayer->player_no].war_desire = 0;
      ai->diplomacy.other[aplayer->player_no].war_fear = 0;
      continue;
    }
    ai->diplomacy.other[aplayer->player_no].war_fear =
      ai_war_fear(pplayer, aplayer, ai);
    ai->diplomacy.other[aplayer->player_no].war_desire =
      ai_war_desire(pplayer, aplayer, ai);

    /* We don't want war if we can win through the space race. */
    if (ai->diplomacy.strategy == WIN_SPACE) {
      ai->diplomacy.other[aplayer->player_no].war_desire /= 3;
      want_avoid_war = TRUE;
      continue;
    }

    /* Loyalty: hate our allies' enemies */
    players_iterate(eplayer) {
      if (pplayers_allied(pplayer, eplayer)
          && pplayers_at_war(aplayer, eplayer)
          && eplayer->is_alive) {
        ai->diplomacy.other[aplayer->player_no].war_desire *= 3;
        want_avoid_war = FALSE;
      }
    } players_iterate_end;

    /* Chicken out */
    if (want_avoid_war) {
      freelog(LOG_DIPL2, "%s chickening out from war_desire with %s",
              pplayer->name, aplayer->name);
      ai->diplomacy.other[aplayer->player_no].war_desire = -1;
      continue;
    }

    /* Strongly prefer players we are at war with already */
    if (pplayers_non_attack(pplayer, aplayer)) {
      ai->diplomacy.other[aplayer->player_no].war_desire /= 2;
    }
    freelog(LOG_DEBUG, "(%s ai diplo) Against %s we have war desire "
            "%d and fear %d", pplayer->name, aplayer->name,
            ai->diplomacy.other[aplayer->player_no].war_desire,
            ai->diplomacy.other[aplayer->player_no].war_fear);

    /* Find best target */
    if (ai->diplomacy.other[aplayer->player_no].war_desire > war_desire) {
      target = aplayer;
      war_desire = ai->diplomacy.other[aplayer->player_no].war_desire;
      war_fear = ai->diplomacy.other[aplayer->player_no].war_fear;
    }
  } players_iterate_end;

  if (!target) {
    freelog(LOG_DEBUG, "(%s ai diplo) Found no target.", pplayer->name);
    ai->diplomacy.target = NULL;
    return;
  }

  if (target != ai->diplomacy.target) {
    if (ai->diplomacy.target) {
      freelog(LOG_DIPL, "(%s ai diplo) Changing target from %s to %s",
              pplayer->name, ai->diplomacy.target->name, target->name);
    } else {
      freelog(LOG_DIPL, "(%s ai diplo) Setting target to %s",
              pplayer->name, target->name);
    }
    ai->diplomacy.target = target;
    players_iterate(aplayer) {
      ai->diplomacy.other[aplayer->player_no].ally_patience = 0;
    } players_iterate_end;
  }
}

/********************************************************************** 
  Balance a suggested treaty. 
  TODO: Suggest other things than gold.
***********************************************************************/
static void ai_balance_treaty(struct player *pplayer, struct ai_data *ai,
                              struct player *aplayer)
{
  int gold = myrand((pplayer->economic.gold - pplayer->ai.est_upkeep) / 2) 
             + (pplayer->economic.gold - pplayer->ai.est_upkeep) / 2;
  struct Treaty *ptreaty = find_treaty(pplayer, aplayer);
  int balance = 0;

  if (!ptreaty) {
    /* No treaty to balance out */
    return;
  }

  clause_list_iterate(ptreaty->clauses, pclause) {
    balance += ai_goldequiv_clause(pplayer, aplayer, pclause, ai);
  } clause_list_iterate_end;

  if (balance > 0) {
    gold = MIN(balance, gold);
    ai_diplomacy_suggest(pplayer, aplayer, CLAUSE_GOLD, gold);
  }
}

/********************************************************************** 
  Offer techs to other player and ask for techs we need.
***********************************************************************/
static void ai_share_techs(struct player *pplayer,
                           struct player *aplayer)
{
  int index;

  for (index = A_FIRST; index < game.num_tech_types; index++) {
    if ((get_invention(pplayer, index) != TECH_KNOWN)
        && (get_invention(aplayer, index) == TECH_KNOWN)) {
      ai_diplomacy_suggest(aplayer, pplayer, CLAUSE_ADVANCE, index);
    } else if ((get_invention(pplayer, index) == TECH_KNOWN)
        && (get_invention(aplayer, index) != TECH_KNOWN)) {
      ai_diplomacy_suggest(pplayer, aplayer, CLAUSE_ADVANCE, index);
    }
  }
  if (!gives_shared_vision(pplayer, aplayer)) {
    ai_diplomacy_suggest(pplayer, aplayer, CLAUSE_VISION, 0);
  }
  if (!gives_shared_vision(aplayer, pplayer)) {
    ai_diplomacy_suggest(aplayer, pplayer, CLAUSE_VISION, 0);
  }
}

/********************************************************************** 
  Go to war.
***********************************************************************/
static void ai_go_to_war(struct player *pplayer, struct ai_data *ai,
                         struct player *target)
{
  if (gives_shared_vision(pplayer, target)) {
    remove_shared_vision(pplayer, target);
  }
  while (!pplayers_at_war(pplayer, target)) {
    handle_player_cancel_pact(pplayer, target->player_no);
  }
  /* continue war at least in this arbitrary number of turns to show 
   * some spine */
  ai->diplomacy.timer = myrand(4) + 3
                        + ai->diplomacy.other[target->player_no].hatred;
}

/********************************************************************** 
  Do diplomatic actions. Must be called only after calculate function
  above has been run for _all_ AI players.

  Only ever called for AI players and never for barbarians.
***********************************************************************/
void ai_diplomacy_actions(struct player *pplayer)
{
  struct ai_data *ai = ai_data_get(pplayer);
  struct player *target = ai->diplomacy.target;

  assert(pplayer->ai.control);
  if (!pplayer->is_alive) {
    return;
  }

  /*** If we are greviously insulted, go to war. ***/

  players_iterate(aplayer) {
    if (aplayer->reputation < GAME_DEFAULT_REPUTATION / 2
        && ai->diplomacy.other[aplayer->player_no].hatred > 2
        && pplayer->diplstates[aplayer->player_no].has_reason_to_cancel == 2) {
      freelog(LOG_DIPL2, "(%s ai diplo) Declaring war on %s in revenge",
              pplayer->name, target->name);
      diplo_notify(target, TALK(%s) "I will NOT accept such behaviour! This "
                   "means WAR!", pplayer->name);
      ai_go_to_war(pplayer, ai, aplayer);
    }
  } players_iterate_end;

  /*** Stop other players from winning by space race ***/

  if (ai->diplomacy.strategy != WIN_SPACE) {
    players_iterate(aplayer) {
      if (!aplayer->is_alive || aplayer == pplayer) {
        continue;
      }
      /* A spaceship victory is always one single player's victory */
      if (ai_space_victory(aplayer)) {
        if (pplayer->spaceship.state == SSHIP_LAUNCHED
            && pplayers_allied(pplayer, aplayer)) {
          diplo_notify(aplayer, TALK(%s) "Your attempt to conquer space for "
                       "yourself alone betray your true intentions, and I "
                       "will have no more of our alliance!", pplayer->name);
          handle_player_cancel_pact(pplayer, aplayer->player_no);
          if (gives_shared_vision(pplayer, aplayer)) {
            remove_shared_vision(pplayer, aplayer);
          }
        } else {
          diplo_notify(aplayer, TALK(%s) "Your attempt to unilaterally "
                       "dominate outer space is highly offensive. If you "
                       "do not stop constructing your spaceship, "
                       "I may be forced to take action!", pplayer->name);
        }
      }
    } players_iterate_end;
  }

  /*** Declare war ***/

  if (target && !pplayers_at_war(pplayer, target)) {
    freelog(LOG_DIPL2, "(%s ai diplo) Declaring war on %s",
            pplayer->name, target->name);
    if (pplayer->diplstates[target->player_no].has_reason_to_cancel > 0) {
      /* We have good reason */
      diplo_notify(target, TALK(%s) "Your despicable actions will not go "
                   "unpunished!", pplayer->name);
    } if (ai->diplomacy.other[target->player_no].hatred > 2) {
      /* We once had good reason */
      diplo_notify(target, TALK(%s) "Finally I get around to taking "
                   "revenge for the things you have done to me!", 
                   pplayer->name);
    } else {
      /* We have no good reason... so what? */
      diplo_notify(target, TALK(%s) "Peace in ... some other time",
                   pplayer->name);
    }
    ai_go_to_war(pplayer, ai, target);
  }

  /*** Opportunism, Inc. Try to make peace with everyone else ***/

  players_iterate(aplayer) {
    enum diplstate_type ds = pplayer_get_diplstate(pplayer, aplayer)->type;
    bool transitive_war = FALSE;

    if (is_barbarian(aplayer)    /* no barbarism */
        || aplayer == pplayer    /* no self-indulgence */
        || aplayer == target     /* no mercy */
        || !aplayer->is_alive) { /* and no necromancy! */
      continue; /* hah! */
    }

    /* Is there a player that we are allied to which is at
     * war with this player? If so, keep war going. */
    players_iterate(eplayer) {
      if (pplayers_allied(pplayer, eplayer)
          && eplayer->is_alive
          && pplayers_at_war(eplayer, aplayer)) {
        transitive_war = TRUE;
      }
    } players_iterate_end;
    if (transitive_war) {
      continue;
    }

    /* Some players we want treaties with badly */
    if (ai->diplomacy.other[aplayer->player_no].hatred == 0
        && ai->diplomacy.other[aplayer->player_no].war_fear > 0
        && aplayer->reputation > GAME_DEFAULT_REPUTATION / 2) {
      ai->diplomacy.other[aplayer->player_no].war_desire = 
        -(ai->diplomacy.other[aplayer->player_no].war_fear
          * aplayer->reputation / GAME_DEFAULT_REPUTATION);
    } else {
      ai->diplomacy.other[aplayer->player_no].war_desire = -1;
    }

    /* Spam control */
    if (ai->diplomacy.other[aplayer->player_no].spam > 0) {
      ai->diplomacy.other[aplayer->player_no].spam--;
    }
    if ((!player_has_embassy(pplayer, aplayer)
         && !player_has_embassy(aplayer, pplayer))
        || ds == DS_NO_CONTACT) {
      continue;
    }
    if (ai->diplomacy.other[aplayer->player_no].spam > 0) {
      /* Don't spam */
      continue;
    }

    /* Canvass support from existing friends for our war, and try to
     * make friends with enemies. Then we wait some turns until next time
     * we spam them with our gibbering chatter. */
    ai->diplomacy.other[aplayer->player_no].spam = myrand(4)+3;
    switch (ds) {
      case DS_ALLIANCE:
      if ((pplayer->team != TEAM_NONE && aplayer->team == pplayer->team)
          || (target && pplayers_at_war(aplayer, target))) {
        ai_share_techs(pplayer, aplayer);
        break;
      } else if (!target) {
        break;
      }
      freelog(LOG_DIPL, "(%s ai diplo) demanding support from %s to crush %s",
              pplayer->name, aplayer->name, target->name);
      switch (ai->diplomacy.other[aplayer->player_no].ally_patience--) {
        case 0:
        diplo_notify(aplayer, TALK(%s) "Greetings our most trustworthy ally, "
                     "we call upon you to destroy our enemy, %s", pplayer->name,
                     target->name);
        break;
        case -1:
        diplo_notify(aplayer, TALK(%s) "Greetings ally, I see you have not yet "
                     "made war with our enemy, %s. Why do I need to remind "
                     "you of your promises?", pplayer->name, target->name);
        break;
        case -2:
        diplo_notify(aplayer, TALK(%s) "Dishonoured one, we made a pact of "
                     "alliance, and yet you remain at peace with our mortal "
                     "enemy, %s! This is unacceptable, our alliance is no "
                     "more!", pplayer->name, target->name);
        freelog(LOG_DIPL2, "(%s ai diplo) breaking useless alliance with %s",
                pplayer->name, aplayer->name);
        handle_player_cancel_pact(pplayer, aplayer->player_no); /* peace */
        ai->diplomacy.other[aplayer->player_no].hatred++;
        if (pplayer->diplstates[aplayer->player_no].has_reason_to_cancel > 0) {
          handle_player_cancel_pact(pplayer, aplayer->player_no); /* neutral */
        }
        if (gives_shared_vision(pplayer, aplayer)) {
          remove_shared_vision(pplayer, aplayer);
        }
        break;
      }
      break;

      case DS_PEACE:
      if (aplayer->reputation < GAME_DEFAULT_REPUTATION / 2
          || ai->diplomacy.other[aplayer->player_no].hatred > 2) {
        break; /* never */
      }
      ai_diplomacy_suggest(pplayer, aplayer, CLAUSE_ALLIANCE, 0);
      ai_balance_treaty(pplayer, ai, aplayer);
      if (target) {
        diplo_notify(aplayer, TALK(%s) "Greetings friend, may we suggest "
                     "a joint campaign against %s?", pplayer->name, 
target->name);
        freelog(LOG_DIPL, "(%s ai diplo) requesting support from %s to crush 
%s",
                pplayer->name, aplayer->name, target->name);
      } else {
        diplo_notify(aplayer, TALK(%s) "You have proven a good friend, %s. How "
                     "about going into an alliance?", pplayer->name, 
                     aplayer->name);
        freelog(LOG_DIPL, "(%s ai diplo) requesting alliance from %s",
                pplayer->name, aplayer->name);
      }
      break;

      case DS_CEASEFIRE:
      case DS_NEUTRAL:
      if (aplayer->reputation < GAME_DEFAULT_REPUTATION / 4
          || ai->diplomacy.other[aplayer->player_no].hatred > 4) {
        break;
      }
      ai_diplomacy_suggest(pplayer, aplayer, CLAUSE_PEACE, 0);
      ai_balance_treaty(pplayer, ai, aplayer);
      if (target) {
        diplo_notify(aplayer, TALK(%s) "Greetings friend, may we suggest "
                     "a joint campaign against %s?", pplayer->name, 
target->name);
        freelog(LOG_DIPL, "(%s ai diplo) requesting peace from %s to crush %s",
                pplayer->name, aplayer->name, target->name);
      } else {
        diplo_notify(aplayer, TALK(%s) "I have seen little aggression from you, 
"
                     "%s. Maybe you can be trusted. Shall we sign a peace 
treaty?", 
                     pplayer->name, aplayer->name);
        freelog(LOG_DIPL, "(%s ai diplo) requesting peace from %s",
                pplayer->name, aplayer->name);
      }
      break;

      case DS_WAR:
      if (!target && ai->diplomacy.strategy != WIN_SPACE) {
        /* Don't end wars if we don't have other targets, or we can win
         * through other means. */
        break;
      }
      ai_diplomacy_suggest(pplayer, aplayer, CLAUSE_CEASEFIRE, 0);
      ai_balance_treaty(pplayer, ai, aplayer);
      if (target) {
        diplo_notify(aplayer, TALK(%s) "%s is threatening us both, may we 
suggest"
                     " a cessation of hostilities?", pplayer->name, 
target->name);
        freelog(LOG_DIPL, "(%s ai diplo) requesting ceasefire from %s to crush 
%s",
                pplayer->name, aplayer->name, target->name);
      } else {
        diplo_notify(aplayer, TALK(%s) "War is such a terrible waste. Perhaps 
we "
                     "can find a more civilized way of dealing with each 
other?",
                     pplayer->name);
        freelog(LOG_DIPL, "(%s ai diplo) requesting ceasefire from %s",
                pplayer->name, aplayer->name);
      }
      break;

      default:
      assert(FALSE);
      break;
    }
  } players_iterate_end;
}
/********************************************************************** 
 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__ADVATTITUDE_H
#define FC__ADVATTITUDE_H

struct player;
struct ai_choice;
struct Treaty;
struct Clause;
struct ai_data;

void ai_diplomacy_calculate(struct player *pplayer, struct ai_data *ai);
void ai_diplomacy_actions(struct player *pplayer);

void ai_treaty_react(struct player *pplayer, struct player *aplayer, 
                     struct Clause *pclause);
void ai_treaty_evaluate(struct player *pplayer, struct player *aplayer,
                        struct Treaty *ptreaty);

#endif  /* FC__ADVATTITUDE_H */

[Prev in Thread] Current Thread [Next in Thread]
  • [freeciv-ai] AI diplomacy v6 (PR#2413), Per I. Mathisen via RT <=