Complete.Org: Mailing Lists: Archives: freeciv-dev: November 2002:
[Freeciv-Dev] (PR#2413) ai diplomacy patch
Home

[Freeciv-Dev] (PR#2413) ai diplomacy patch

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
To: undisclosed-recipients:;
Subject: [Freeciv-Dev] (PR#2413) ai diplomacy patch
From: "Per I. Mathisen via RT" <rt@xxxxxxxxxxxxxx>
Date: Mon, 25 Nov 2002 14:54:00 -0800
Reply-to: rt@xxxxxxxxxxxxxx

This is a new version of the AI diplomacy patch. Thanks a lot to Jordi for
finding bugs and thus encouraging me to work on this patch again :-)

There are lots and lots of changes. Most importantly, the AI will now
suggest treaties to you through the diplomacy dialog, and AIs will make
treaties with each other.

I am not sure if this patch will work with gtk2 anymore. There were some
big changes there that screwed my diff and I didn't see immediately how to
fix it, but it might work somehow still...

Even if you don't have time to review it properly, or even test it, please
take time to skim over it and comment on the overall design, please.

  - 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      2002/11/25 22:49:50
@@ -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.3
diff -u -r1.3 aidata.c
--- ai/aidata.c 2002/11/14 09:14:50     1.3
+++ ai/aidata.c 2002/11/25 22:49:50
@@ -26,10 +26,12 @@
 #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"
@@ -188,6 +190,12 @@
       ai->stats.workers[ptile->continent]++;
     }
   } unit_list_iterate_end;
+
+  /* Diplomacy */
+
+  if (pplayer->ai.control && game.diplomacy && !is_barbarian(pplayer)) {
+    ai_diplomacy_calculate(pplayer, ai);
+  }
 }
 
 /**************************************************************************
@@ -199,6 +207,31 @@
   free(ai->threats.continent);
   free(ai->stats.workers);
   free(ai->stats.cities);
+
+  /* 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. */
+  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.personality = 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;
+  }
 }
 
 /**************************************************************************
Index: ai/aidata.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aidata.h,v
retrieving revision 1.2
diff -u -r1.2 aidata.h
--- ai/aidata.h 2002/11/15 22:15:01     1.2
+++ ai/aidata.h 2002/11/25 22:49:50
@@ -24,7 +24,42 @@
  * 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_HUNT,     /* enemy has F_GAMELOSS unit which we will hunt down */
+  WIN_CAPITAL   /* we cannot win unless we take war_target's capital */
+};
+
+/* FIXME: these should be set by difficulty and civworld */
+enum diplomacy_type {
+  DIP_NONE=0,           /* No attitude */
+  DIP_PACIFIST=1,       /* War desire -50% if positive */
+  DIP_MILITARIST=2,     /* War desire +50% if positive */
+  DIP_TRADER=4,         /* Worth of our offer -25%, trade cities */
+  DIP_GREEDY=8,         /* Worth of our offer +50% */
+  DIP_XENOPHOBE=16,     /* Only ever agrees to ceasefire and tech */
+  DIP_TRUSTWORTHY=32,   /* Does not break treaties unless violated */
+  DIP_BACKSTABBER=64,   /* Happily agrees to anything and then... */
+  DIP_MEGALOMANIAC=128, /* Does not share victory */
+};
+
 struct ai_data {
+  /* AI diplomacy */
+  struct {
+    enum winning_strategy strategy;
+    int timer; /* pursue our goals with some stubbornness */
+    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 */
+    } other[MAX_NUM_PLAYERS];
+    struct player *target;    /* concentrate on this player */
+    int mil_strength;         /* Estimate of our military strength */
+    int personality;          /* Personality traits */
+  } diplomacy;
+
   /* Long-term threats, not to be confused with short-term danger */
   struct {
     bool invasions;   /* check if we need to consider invasions */
@@ -51,6 +86,8 @@
 
   int num_continents; /* last time we updated our continent data */
 };
+
+void ai_data_init(struct player *pplayer);
 
 void ai_data_turn_init(struct player *pplayer);
 void ai_data_turn_done(struct player *pplayer);
Index: client/gui-gtk/plrdlg.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/gui-gtk/plrdlg.c,v
retrieving revision 1.41
diff -u -r1.41 plrdlg.c
--- client/gui-gtk/plrdlg.c     2002/11/14 09:14:54     1.41
+++ client/gui-gtk/plrdlg.c     2002/11/25 22:49:50
@@ -488,10 +488,11 @@
   if (pplayer->is_alive 
       && pplayer != game.player_ptr
       && player_has_embassy(game.player_ptr, pplayer)) {
-    if (pplayer->is_connected)
+    if (pplayer->is_connected || pplayer->ai.control) {
       gtk_widget_set_sensitive(players_meet_command, TRUE);
-    else
+    } else {
       gtk_widget_set_sensitive(players_meet_command, FALSE);
+    }
     gtk_widget_set_sensitive(players_int_command, TRUE);
     return;
   }
Index: client/gui-win32/plrdlg.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/gui-win32/plrdlg.c,v
retrieving revision 1.10
diff -u -r1.10 plrdlg.c
--- client/gui-win32/plrdlg.c   2002/11/14 09:14:59     1.10
+++ client/gui-win32/plrdlg.c   2002/11/25 22:49:51
@@ -240,10 +240,11 @@
   if (pplayer->is_alive 
       && pplayer != game.player_ptr
       && player_has_embassy(game.player_ptr, pplayer)) {
-    if (pplayer->is_connected)
+    if (pplayer->is_connected || pplayer->ai.control) {
       EnableWindow(GetDlgItem(players_dialog,ID_PLAYERS_MEET), TRUE);
-    else
+    } else {
       EnableWindow(GetDlgItem(players_dialog,ID_PLAYERS_MEET), FALSE);
+    }
     EnableWindow(GetDlgItem(players_dialog,ID_PLAYERS_INT), TRUE);
     return;
   }
Index: client/gui-xaw/plrdlg.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/gui-xaw/plrdlg.c,v
retrieving revision 1.33
diff -u -r1.33 plrdlg.c
--- client/gui-xaw/plrdlg.c     2002/11/14 09:15:01     1.33
+++ client/gui-xaw/plrdlg.c     2002/11/25 22:49:51
@@ -303,10 +303,11 @@
     if (pplayer->is_alive
         && pplayer != game.player_ptr
         && player_has_embassy(game.player_ptr, pplayer)) {
-      if(pplayer->is_connected)
+      if (pplayer->is_connected || pplayer->ai.control) {
        XtSetSensitive(players_meet_command, TRUE);
-      else
+      } else {
        XtSetSensitive(players_meet_command, FALSE);
+      }
       XtSetSensitive(players_int_command, TRUE);
       return;
     }
Index: common/game.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/game.c,v
retrieving revision 1.152
diff -u -r1.152 game.c
--- common/game.c       2002/11/14 09:15:01     1.152
+++ common/game.c       2002/11/25 22:49:51
@@ -667,6 +667,7 @@
   game.researchcost = GAME_DEFAULT_RESEARCHCOST;
   game.diplcost    = GAME_DEFAULT_DIPLCOST;
   game.diplchance  = GAME_DEFAULT_DIPLCHANCE;
+  game.diplomacy   = GAME_DEFAULT_DIPLOMACY;
   game.freecost    = GAME_DEFAULT_FREECOST;
   game.conquercost = GAME_DEFAULT_CONQUERCOST;
   game.settlers    = GAME_DEFAULT_SETTLERS;
Index: common/game.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/game.h,v
retrieving revision 1.116
diff -u -r1.116 game.h
--- common/game.h       2002/11/11 10:00:47     1.116
+++ common/game.h       2002/11/25 22:49:51
@@ -80,6 +80,7 @@
   int researchcost; /* Multiplier on cost of new research */
   int diplcost, freecost, conquercost;
   int diplchance;
+  bool diplomacy;
   int cityfactor;
   int citymindist;
   int civilwarsize;
@@ -314,6 +315,7 @@
 #define GAME_DEFAULT_DIPLCOST        0
 #define GAME_MIN_DIPLCOST            0
 #define GAME_MAX_DIPLCOST            100
+#define GAME_DEFAULT_DIPLOMACY       TRUE
 
 #define GAME_DEFAULT_FOGOFWAR        TRUE
 
Index: common/player.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/player.c,v
retrieving revision 1.103
diff -u -r1.103 player.c
--- common/player.c     2002/11/18 12:19:51     1.103
+++ common/player.c     2002/11/25 22:49:51
@@ -591,6 +591,20 @@
 }
 
 /***************************************************************
+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.88
diff -u -r1.88 player.h
--- common/player.h     2002/11/25 19:18:09     1.88
+++ common/player.h     2002/11/25 22:49:51
@@ -256,6 +256,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   2002/11/25 22:49:51
@@ -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,23 @@
   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 (!game.diplomacy && (plr0->ai.control || plr1->ai.control)) {
+      notify_player(plr0, _("AI controlled players cannot participate in " 
+                            "diplomatic meetings."));
+      return;
+    }
+    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 +606,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.250
diff -u -r1.250 plrhand.c
--- server/plrhand.c    2002/11/14 09:15:04     1.250
+++ server/plrhand.c    2002/11/25 22:49:51
@@ -1569,10 +1569,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;
     
@@ -1598,7 +1598,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.106
diff -u -r1.106 srv_main.c
--- server/srv_main.c   2002/11/14 09:15:05     1.106
+++ server/srv_main.c   2002/11/25 22:49:51
@@ -90,6 +90,7 @@
 #include "unithand.h"
 #include "unittools.h"
 
+#include "advdiplomacy.h"
 #include "advmilitary.h"
 #include "aidata.h"
 #include "aihand.h"
@@ -1954,6 +1955,11 @@
   }
 
   (void) send_server_info_to_metaserver(TRUE, FALSE);
+
+  /* Initialize AI data structures */
+  players_iterate(pplayer) {
+    ai_data_init(pplayer);
+  } players_iterate_end;
 
   if (game.is_new_game) {
     load_rulesets();
/********************************************************************** 
 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_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 */
/********************************************************************** 
 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 "tech.h"
#include "diptreaty.h"
#include "player.h"
#include "game.h"
#include "support.h"
#include "shared.h"
#include "packets.h"
#include "rand.h"
#include "log.h"
#include "events.h"
#include "mem.h"

#include "diplhand.h"
#include "spaceship.h"
#include "plrhand.h"
#include "citytools.h"

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

#include "advdiplomacy.h"

#define LOG_DIPL LOG_NORMAL

/* 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 bool ai_attitude(struct ai_data *at, enum diplomacy_type dtype);
static int ai_goldequiv_clause(struct player *pplayer, struct player *aplayer, 
                               struct Clause *pclause, struct ai_data *ai);

/* PATCH TODO LIST:
        -> stdinhand.c, add game.diplomacy set option
        -> advmilitary.c, fix diplomat tech want excess
        -> ai_suggest_clauses(...), if balance is not ok?
        -> allow AIs to cheat an embassy on meet in beginning?
*/

/********************************************************************** 
NOTES ON THIS AI DIPLOMACY FILE:
 -> Be very careful with infinities - they can lead to overflows.
 -> Everything is measured in gold, even treaties. However, treaties
    are meant to be broken, if that is measured profitable.
 -> Some players can be give attitudes (see aidata.h). These can
    severely impair an ai's ability to pursue a diplomacy to its
    own gain, but it is very good for modpacks and easy difficulties.
 -> We rely on omniscience here. Really.
***********************************************************************/

/********************************************************************** 
  Send a diplomatic message. Use this instead of notify because we
  may want to highligh/present these messages differently in the
  future.

  TODO: Replace E_DIPLOMATIC_INCIDENT with a new type.
  TODO: Rewrite ...
***********************************************************************/
#define diplo_notify(pplayer, text, ...) \
  notify_player_ex(pplayer, -1, -1, E_DIPLOMATIC_INCIDENT, text, __VA_ARGS__);

/********************************************************************** 
  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;

  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: {
    int tech_cost;

    if (give) {
      tech_cost = ai_goldequiv_tech(aplayer, pclause->value);
    } else {
      tech_cost = ai_goldequiv_tech(pplayer, pclause->value);
    }

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

    /* Calculate in tech leak to our enemies, guess 50% chance */
    if (give) { 
      players_iterate(eplayer) {
        if (pplayers_allied(aplayer, eplayer) && aplayer != eplayer
            && get_invention(eplayer, pclause->value) != TECH_KNOWN) {
          worth[eplayer->player_no] -= 
              ai_goldequiv_tech(eplayer, pclause->value) / 2;
        }
      } players_iterate_end;
    }
    worth[receiver] += tech_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: {
    /* TODO: Note that right now most of this code is dead code because
     * we set war_desire to zero for everyone but our no 1 target at
     * another place in the code. - Per */
    int war_desire = ai->diplomacy.other[aplayer->player_no].war_desire;

    /* Attitude adjustment: Don't be too eager to get into a relationship
     * that you can't stomach getting out of. */
    if (ai_attitude(ai, DIP_BACKSTABBER)) {
      war_desire /= 1.5;
    }
    if (ai_attitude(ai, DIP_TRUSTWORTHY)) {
      war_desire *= 1.5;
    }

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

    /* No peace to our chosen victim if we are militarist */
    if (aplayer == ai->diplomacy.target 
        && ai_attitude(ai, DIP_MILITARIST)) {
      worth[pplayer->player_no] = -FC_INFINITY;
    }

    /* We don't want treaties that obstruct our aims, but if war_desire
       is negative then we want 'em this much. */
    worth[pplayer->player_no] += -(war_desire * TRADE_WEIGHTING
                                   * city_list_size(&aplayer->cities));
    freelog(LOG_DIPL, "(%s ai diplo) Calculating pact with %s as (%d*%d*%d=)%d",
            pplayer->name, aplayer->name,
            war_desire, TRADE_WEIGHTING, 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, "%s says: 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
            && pplayers_allied(pplayer, check_pl)
            && pplayers_allied(aplayer, check_pl)) {
          player_is_allied_with_ally = TRUE;
        }
      } players_iterate_end;
      if (!pplayers_in_peace(pplayer, aplayer)) {
        diplo_notify(aplayer, "%s says: Let us first cease hostilies, %s",
                     pplayer->name, aplayer->name);
        return -FC_INFINITY;
      } else if (!player_is_allied_with_ally) {
        worth[pplayer->player_no] *= 3;
      } else {
        /* 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);
    if (give && ai_attitude(ai, DIP_XENOPHOBE)) {
      worth[receiver] *= 3; /* "And what exactly do you want with this...?" */
    }
    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)) { 
      if (!give && ai_attitude(ai, DIP_TRADER)) {
        worth[receiver] *= 2;
      } else if (give && ai_attitude(ai, DIP_MILITARIST)) {
        worth[receiver] *= 10;
      } else {
        worth[receiver] *= 3;
      }
    }
    if (give && ai_attitude(ai, DIP_XENOPHOBE)) { 
      worth[giver] -= FC_INFINITY; /* NEVER!!! */
    }
    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 */
      break;
    }
    worth[receiver] += ai_goldequiv_city(offer);
    if (give) {
      /* AI must be crazy to trade away its cities, but traders do/are */
      if (ai_attitude(ai, DIP_TRADER) 
          && pplayers_in_peace(pplayer, aplayer)) {
        worth[receiver] *= 2;
      } else {
        worth[receiver] *= 15;
      }
    }
    break;
  }

  case CLAUSE_VISION:
    if (give) {
      if (pplayers_allied(pplayer, aplayer)) {
        worth[receiver] = 0;
      } else {
        /* so out of the question */
        worth[receiver] += 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 (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);
  }
}

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

/********************************************************************** 
  Return TRUE iff given player has given attitude.
***********************************************************************/
static bool ai_attitude(struct ai_data *ai, enum diplomacy_type dtype)
{
  return BOOL_VAL(ai->diplomacy.personality & dtype);
}

/********************************************************************** 
  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;

  /* Show some weakness */
  if (!pplayers_at_war(pplayer, aplayer)
      && ai_attitude(ai, DIP_TRUSTWORTHY)) {
    return 0;
  }

  /* We can aim for an allied victory */
  if (!ai_attitude(ai, DIP_MEGALOMANIAC)
      && pplayers_allied(pplayer, aplayer)) {
    return 0;
  }

  /* 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 */

  return kill_desire;
}

/********************************************************************** 
  Calculate relative military strength of us vs him
  TODO: special checks for nukes (they inflate our numbers), and 
  add in 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 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)
{
  struct packet_diplomacy_info packet;

  packet.plrno_from = pplayer->player_no;
  packet.plrno0 = pplayer->player_no;
  packet.plrno1 = aplayer->player_no;
  packet.clause_type = what;
  packet.value = 0; /* dummy */

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

/********************************************************************** 
  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;

  /* Stop war against a dead player */
  if (ai->diplomacy.target && !ai->diplomacy.target->is_alive) {
    freelog(LOG_DIPL, "(%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 our fears and desires, and find desired war target */
  players_iterate(aplayer) {
    enum diplstate_type ds = pplayer_get_diplstate(pplayer, aplayer)->type;

    /* We don't fear/hate ourselves, those we don't know and those we're
     * allied to (unless we're the backstabbing type) */
    if (aplayer == pplayer
        || ds == DS_NO_CONTACT
        || (ds == DS_ALLIANCE && !ai_attitude(ai, DIP_BACKSTABBER))) {
      continue;
    }
    ai->diplomacy.other[aplayer->player_no].war_desire =
      ai_war_desire(pplayer, aplayer, ai);
    ai->diplomacy.other[aplayer->player_no].war_fear =
      ai_war_fear(pplayer, aplayer, ai);

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

    /* 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);
    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;
  }
  if (!pplayers_at_war(pplayer, target)) {
    freelog(LOG_DIPL, "(%s ai diplo) Declaring war on %s",
            pplayer->name, target->name);
    diplo_notify(target, "%s says: Peace in ... some other time", 
pplayer->name);
    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 = 6;
  }

  /* Opportunism, Inc */
  players_iterate(aplayer) {
    enum diplstate_type ds = pplayer_get_diplstate(pplayer, aplayer)->type;

    /* Talk is cheap, and so is peace, so fake friendliness */
    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 (is_barbarian(aplayer)    /* no barbarism */
        || aplayer == pplayer    /* no self-indulgence */
        || aplayer == target     /* no mercy */
        || !aplayer->is_alive    /* and no necromancy! */
        || !pplayer->is_alive
        || !player_has_embassy(pplayer, aplayer)
        || 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:
      diplo_notify(aplayer, "%s says: Greetings our most trustworthy ally, "
                   "we call upon you to destroy our enemy, %s", pplayer->name,
                   target->name);
      freelog(LOG_DIPL, "(%s ai diplo) demanding support from %s to crush %s",
              pplayer->name, aplayer->name, target->name);
      break;

      case DS_PEACE:
      /* TODO: add reputation check here!! */
      ai_diplomacy_suggest(pplayer, aplayer, CLAUSE_ALLIANCE);
      diplo_notify(aplayer, "%s says: 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);
      break;

      case DS_CEASEFIRE:
      case DS_NEUTRAL:
      ai_diplomacy_suggest(pplayer, aplayer, CLAUSE_PEACE);
      diplo_notify(aplayer, "%s says: 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);
      break;

      case DS_WAR:
      diplo_notify(aplayer, "%s says: %s is threatening us both, may we suggest"
                   " a cessation of hostilities?", pplayer->name, target->name);
      freelog(LOG_DIPL, "(%s ai diplo) requesting peace from %s to crush %s",
              pplayer->name, aplayer->name, target->name);
      ai_diplomacy_suggest(pplayer, aplayer, CLAUSE_CEASEFIRE);
      break;

      default:
      assert(FALSE);
      break;
    }
  } players_iterate_end;
}

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