[Freeciv-Dev] (PR#13524) AI Diplomacy revisited
[Top] [All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
Subject: |
[Freeciv-Dev] (PR#13524) AI Diplomacy revisited |
From: |
"Per I. Mathisen" <per@xxxxxxxxxxx> |
Date: |
Sat, 23 Jul 2005 17:28:45 -0700 |
Reply-to: |
bugs@xxxxxxxxxxx |
<URL: http://bugs.freeciv.org/Ticket/Display.html?id=13524 >
I am rewriting larger portions of the AI diplomacy code, in order to get
rid of the 'lobster logic' (always attack the stronger player). Instead,
the AI becomes opportunistic and gets greedy on players with lots of
cities and little defense.
The previous concept of countdown torwards a single war target has been
replaced by a more general 'war footing' concept, which allows countdowns
to war against multiple players at once. When on war footing (preparing
for an attack), the AI will give priority to taxes (gold) and military
stuff. This needs to be adjusted carefully, though.
It should be slower to get treaties with the AI now, but you can get peace
if you really want, as opposed to the 'if you are the lobster, nothing
helps' of the current code.
The AI will now inform allies when attacked or prearing for war.
Logging has been imporoved considerably.
This patch needs much, much more testing, but I am putting it out here for
comments anyway.
- Per
Index: ai/advdiplomacy.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/advdiplomacy.c,v
retrieving revision 1.85
diff -u -r1.85 advdiplomacy.c
--- ai/advdiplomacy.c 22 Jul 2005 16:18:04 -0000 1.85
+++ ai/advdiplomacy.c 23 Jul 2005 23:35:21 -0000
@@ -1,5 +1,5 @@
/**********************************************************************
- Freeciv - Copyright (C) 2003 - The Freeciv Team
+ Freeciv - Copyright (C) 2005 - The Freeciv Team
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)
@@ -43,41 +43,31 @@
#include "aidata.h"
#include "ailog.h"
+#include "aiunit.h"
#include "aitools.h"
#include "advmilitary.h"
#include "advdiplomacy.h"
-/*
-
- "When a lobsterman leaves a trap out in the sea and can't get
- to it for a while, he will find the remains of many lobsters,
- but only one survivor. You might think that the survivor of the
- lobster-battles would be the biggest lobster, but actually it
- will always be the SECOND-SMALLEST. That's because when there
- are a bunch of lobsters in the tank, they always gang up on the
- biggest one first, until there are only two left, and then the
- bigger one wins."
- (Anecdote by banjo@xxxxxxxxxx)
-
-Although the AIs are not this flawlessly deterministic in choosing their
-enemies (they respect alliances and try to preserve treaties among other
-things), this is the basic premise. Gang up on the biggest threat and
-bring it down. Don't be too afraid to ally up with the others and don't
-worry about our own safety. If everyone think the same way, there'll be
-safety in numbers :-) If not, well, at least it makes a good show.
-
-*/
-
#define LOG_DIPL LOG_DEBUG
#define LOG_DIPL2 LOG_DEBUG
-/* one hundred thousand */
+/* One hundred thousand. Basically a number of gold that no player is
+ * ever likely to have, but not so big that we get integer overflows. */
#define BIG_NUMBER 100000
+/* This is how much negative AI love we need before we decide to embark
+ * on opportunistic war for spoils. */
+#define WAR_THRESHOLD -(MAX_AI_LOVE / 8)
+
/* turn this off when we don't want functions to message players */
static bool diplomacy_verbose = TRUE;
+/* pplayers_at_war() thinks no contacts equals war, which often is
+ * very annoying. */
+#define WAR(plr1, plr2) \
+ (plr1->diplstates[plr2->player_no].type == DS_WAR)
+
/**********************************************************************
Send a diplomatic message. Use this instead of notify directly
because we may want to highligh/present these messages differently
@@ -107,8 +97,7 @@
/* Don't change the operation order here.
* We do not want integer overflows */
return -((missing_love * MAX_AI_LOVE) / 1000) *
- ((missing_love * MAX_AI_LOVE) / 1000) /
- 50;
+ ((missing_love * MAX_AI_LOVE) / 1000) / 10;
}
}
@@ -136,7 +125,6 @@
static int ai_goldequiv_tech(struct player *pplayer, Tech_type_id tech)
{
int bulbs, tech_want, worth;
- struct ai_data *ai = ai_data_get(pplayer);
if (get_invention(pplayer, tech) == TECH_KNOWN) {
return 0;
@@ -147,9 +135,6 @@
if (get_invention(pplayer, tech) == TECH_REACHABLE) {
worth /= 2;
}
- DIPLO_LOG(LOG_DEBUG, pplayer, ai, "eval tech %s to %d (bulbs=%d, "
- "tech_want=%d)", get_tech_name(pplayer, tech), worth, bulbs,
- tech_want);
return worth;
}
@@ -185,14 +170,10 @@
static bool ai_players_can_agree_on_ceasefire(struct player* player1,
struct player* player2)
{
- struct ai_data *ai1 = ai_data_get(player1);
+ struct ai_data *ai = ai_data_get(player1);
- return (ai1->diplomacy.target != player2 &&
- (player1 == ai1->diplomacy.alliance_leader ||
- !pplayers_at_war(player2, ai1->diplomacy.alliance_leader)) &&
- player1->ai.love[player2->player_no] > - (MAX_AI_LOVE * 4 / 10) &&
- (ai1->diplomacy.target == NULL ||
- !pplayers_allied(ai1->diplomacy.target, player2)));
+ return (player1->ai.love[player2->player_no] > - (MAX_AI_LOVE * 4 / 10)
+ && ai->diplomacy.player_intel[player2->player_no].countdown < 0);
}
/**********************************************************************
@@ -276,7 +257,6 @@
switch (pclause->type) {
case CLAUSE_ADVANCE:
-
if (give) {
worth -= compute_tech_sell_price(pplayer, aplayer, pclause->value,
&is_dangerous);
@@ -287,8 +267,7 @@
worth += compute_tech_sell_price(aplayer, pplayer, pclause->value,
&is_dangerous);
}
-
- break;
+ break;
case CLAUSE_ALLIANCE:
case CLAUSE_PEACE:
@@ -301,18 +280,7 @@
break;
}
- /* This guy is at war with our alliance and we're not alliance
- * leader. */
- if (pplayer != ai->diplomacy.alliance_leader
- && pplayers_at_war(aplayer, ai->diplomacy.alliance_leader)) {
- notify(aplayer, _("*%s (AI)* %s leads our alliance. You must contact "
- "and make peace with him first."), pplayer->name,
- ai->diplomacy.alliance_leader->name);
- worth = -BIG_NUMBER;
- break;
- }
-
- /* And this guy is allied to one of our enemies. Only accept
+ /* This guy is allied to one of our enemies. Only accept
* ceasefire. */
if (adip->is_allied_with_enemy
&& pclause->type != CLAUSE_CEASEFIRE) {
@@ -342,51 +310,31 @@
break;
}
- /* If this lucky fella got a ceasefire or peace with da boss, then
- * let him live. */
- if ((pplayer_get_diplstate(aplayer, ai->diplomacy.alliance_leader)->type
- == DS_CEASEFIRE
- || pplayer_get_diplstate(aplayer, ai->diplomacy.alliance_leader)->type
- == DS_PEACE)
- && pclause->type == CLAUSE_CEASEFIRE) {
- if (ai->diplomacy.target == aplayer) {
- /* Damn, we lost our target, too! Stupid boss! */
- /* FIXME: We shouldn't do this here - the other player may not
- * accept the whole treaty! */
- ai->diplomacy.target = NULL;
- ai->diplomacy.timer = 0;
- ai->diplomacy.countdown = 0;
- }
- worth = 0;
- break;
- }
-
- /* Breaking treaties give us penalties on future diplomacy, so
- * avoid flip-flopping treaty/war with our chosen enemy. */
- if (aplayer == ai->diplomacy.target) {
- worth = -BIG_NUMBER;
- break;
- }
-
- /* Steps of the ladder */
+ /* Steps of the treaty ladder */
if (pclause->type == CLAUSE_PEACE) {
+ struct player_diplstate *ds = &pplayer->diplstates[aplayer->player_no];
+
if (!pplayers_non_attack(pplayer, aplayer)) {
notify(aplayer, _("*%s (AI)* Let us first cease hostilies, %s"),
pplayer->name, aplayer->name);
worth = -BIG_NUMBER;
+ } else if (ds->type == DS_CEASEFIRE && ds->turns_left > 2) {
+ notify(aplayer, _("*%s (AI)* I wish to see you keep the current "
+ "ceasefire first, %s"), pplayer->name, aplayer->name);
+ worth = -BIG_NUMBER;
} else {
worth = greed(pplayer->ai.love[aplayer->player_no]
- ai->diplomacy.req_love_for_peace);
}
+ DIPLO_LOG(LOG_DIPL, pplayer, aplayer, "peace clause worth %d", worth);
} else if (pclause->type == CLAUSE_ALLIANCE) {
if (!pplayers_in_peace(pplayer, aplayer)) {
- notify(aplayer, _("*%s (AI)* Let us first make peace, %s"),
- pplayer->name, aplayer->name);
- worth = -BIG_NUMBER;
- } else {
worth = greed(pplayer->ai.love[aplayer->player_no]
- - ai->diplomacy.req_love_for_alliance);
+ - ai->diplomacy.req_love_for_peace);
}
+ worth += greed(pplayer->ai.love[aplayer->player_no]
+ - ai->diplomacy.req_love_for_alliance);
+ DIPLO_LOG(LOG_DIPL, pplayer, aplayer, "ally clause worth %d", worth);
} else {
if (pplayer->ai.control && aplayer->ai.control &&
ai_players_can_agree_on_ceasefire(pplayer, aplayer)) {
@@ -466,7 +414,8 @@
} else {
worth = city_gold_worth(offer);
}
- DIPLO_LOG(LOG_DEBUG, pplayer, ai, "worth of %s is %d", offer->name, worth);
+ DIPLO_LOG(LOG_DEBUG, pplayer, aplayer, "worth of %s is %d",
+ offer->name, worth);
break;
}
@@ -489,6 +438,7 @@
worth = 0; /* We are omniscient, so... */
}
break;
+
case CLAUSE_EMBASSY:
if (give) {
if (ds_after == DS_PEACE || ds_after == DS_ALLIANCE) {
@@ -500,6 +450,7 @@
worth = 0; /* We don't need no stinkin' embassy, do we? */
}
break;
+
case CLAUSE_LAST:
break;
} /* end of switch */
@@ -592,14 +543,17 @@
notify(aplayer, _("*%s (AI)* Yes, may we forever stand united, %s"),
pplayer->name, aplayer->name);
}
+ DIPLO_LOG(LOG_DIPL, pplayer, aplayer, "become allies");
break;
case CLAUSE_PEACE:
notify(aplayer, _("*%s (AI)* Yes, peace in our time!"),
pplayer->name);
+ DIPLO_LOG(LOG_DIPL, pplayer, aplayer, "sign peace treaty");
break;
case CLAUSE_CEASEFIRE:
notify(aplayer, _("*%s (AI)* Agreed. No more hostilities, %s"),
pplayer->name, aplayer->name);
+ DIPLO_LOG(LOG_DIPL, pplayer, aplayer, "sign ceasefire");
break;
default:
break;
@@ -638,8 +592,9 @@
total_balance += balance;
gift = (gift && (balance >= 0));
ai_treaty_react(pplayer, aplayer, pclause);
- if (pclause->type == CLAUSE_ALLIANCE && ai->diplomacy.target == aplayer) {
- ai->diplomacy.target = NULL; /* Oooops... */
+ if (pclause->type == CLAUSE_PEACE || pclause->type == CLAUSE_ALLIANCE) {
+ /* Cancel a countdown towards war if we just agreed to peace... */
+ ai->diplomacy.player_intel[aplayer->player_no].countdown = -1;
}
} clause_list_iterate_end;
@@ -651,55 +606,83 @@
i = MIN(i, ai->diplomacy.love_incr * 150) * 10;
pplayer->ai.love[aplayer->player_no] += i;
- DIPLO_LOG(LOG_DIPL2, pplayer, ai, "%s's gift to %s increased love by %d",
- aplayer->name, pplayer->name, i);
+ DIPLO_LOG(LOG_DIPL2, pplayer, aplayer, "gift increased love by %d", i);
}
}
/**********************************************************************
- Calculate our desire to go to war against aplayer.
+ Calculate our desire to go to war against aplayer. We want to
+ attack a player that is easy to beat and will yield a nice profit.
+
+ This function is full of hardcoded constants by necessity. They are
+ not #defines since they are not used anywhere else.
***********************************************************************/
-static int ai_war_desire(struct player *pplayer, struct player *aplayer,
+static int ai_war_desire(struct player *pplayer, struct player *target,
struct ai_data *ai)
{
- int kill_desire;
- struct player_spaceship *ship = &aplayer->spaceship;
- struct ai_dip_intel *adip = &ai->diplomacy.player_intel[aplayer->player_no];
-
- /* Number of cities is a player's base potential. */
- kill_desire = city_list_size(aplayer->cities);
+ int want = 0, fear = 0, distance = 0, settlers = 0, cities = 0;
+ struct player_spaceship *ship = &target->spaceship;
- /* 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(get_unit_type(pcity->currently_building),
- F_CITIES)) {
- kill_desire -= 1;
- }
+ city_list_iterate(target->cities, pcity) {
+ want += 100; /* base city want */
+ want += pcity->size * 20;
+ want += pcity->surplus[O_SHIELD] * 8;
+ want += pcity->surplus[O_TRADE] * 6;
+ fear += get_city_bonus(pcity, EFT_LAND_DEFEND);
+ fear += get_city_bonus(pcity, EFT_SEA_DEFEND);
+ built_impr_iterate(pcity, id) {
+ want += impr_build_shield_cost(id);
+ if (is_great_wonder(id)) {
+ want += impr_build_shield_cost(id) * 2;
+ } else if (is_small_wonder(id)) {
+ want += impr_build_shield_cost(id);
+ }
+ } built_impr_iterate_end;
} city_list_iterate_end;
- unit_list_iterate(aplayer->units, punit) {
+ unit_list_iterate(target->units, punit) {
+ fear += ATTACK_POWER(punit);
+
+ /* Fear enemy expansionism */
if (unit_flag(punit, F_CITIES)) {
- kill_desire += 1;
+ want += 100;
}
} unit_list_iterate_end;
+ unit_list_iterate(pplayer->units, punit) {
+ fear -= ATTACK_POWER(punit) / 2;
- /* Count big cities as twice the threat */
- city_list_iterate(aplayer->cities, pcity) {
- kill_desire += pcity->size > 8 ? 1 : 0;
+ /* Our own expansionism reduces want for war */
+ if (unit_flag(punit, F_CITIES)) {
+ want -= 200;
+ settlers++;
+ }
+ } unit_list_iterate_end;
+ city_list_iterate(pplayer->cities, pcity) {
+ if (pcity->is_building_unit
+ && unit_type_flag(get_unit_type(pcity->currently_building),
+ F_CITIES)) {
+ want -= 150;
+ settlers++;
+ }
+ cities++;
} city_list_iterate_end;
- /* Tech lead is worrisome */
- kill_desire += MAX(get_player_research(aplayer)->techs_researched
- - get_player_research(pplayer)->techs_researched, 0);
+ /* Modify by settler/cities ratio to prevent early wars when
+ * we should be expanding. */
+ want -= want / MAX(cities - settlers, 1);
+
+ /* Calculate average distances to other player's empire. */
+ distance = player_distance_to_player(pplayer, target);
+ ai->diplomacy.player_intel[target->player_no].distance = distance;
+
+ /* Tech lead is worrisome. FIXME: Only consider 'military' techs. */
+ fear += MAX(get_player_research(target)->techs_researched
+ - get_player_research(target)->techs_researched, 0) * 100;
/* Spacerace loss we will not allow! */
if (ship->state >= SSHIP_STARTED) {
- /* add potential */
- kill_desire += city_list_size(aplayer->cities);
+ want *= 2;
}
- if (ai->diplomacy.spacerace_leader == aplayer) {
+ if (ai->diplomacy.spacerace_leader == target) {
ai->diplomacy.strategy = WIN_CAPITAL;
return BIG_NUMBER; /* do NOT amortize this number! */
}
@@ -716,32 +699,39 @@
continue;
}
- /* Remember: pplayers_allied() returns true when aplayer == eplayer */
- if (!cancel_excuse && pplayers_allied(aplayer, eplayer)) {
+ /* Remember: pplayers_allied() returns true when target == eplayer */
+ if (!cancel_excuse && pplayers_allied(target, eplayer)) {
if (ds == DS_NEUTRAL) {
- kill_desire -= kill_desire / 10; /* 10% off */
+ want -= want / 10; /* 10% off */
} else if (ds == DS_CEASEFIRE) {
- kill_desire -= kill_desire / 7; /* 15% off */
+ want -= want / 7; /* 15% off */
} else if (ds == DS_PEACE) {
- kill_desire -= kill_desire / 5; /* 20% off */
+ want -= want / 5; /* 20% off */
} else if (ds == DS_ALLIANCE) {
- kill_desire -= kill_desire / 3; /* 33% off here, more later */
+ want -= want / 3; /* 33% off here, more later */
}
}
} players_iterate_end;
/* Modify by love. Increase the divisor to make ai go to war earlier */
- kill_desire -= MAX(0, kill_desire
- * pplayer->ai.love[aplayer->player_no]
- / (2 * MAX_AI_LOVE));
+ want -= MAX(0, want * pplayer->ai.love[target->player_no]
+ / (2 * MAX_AI_LOVE));
/* Make novice AI more peaceful with human players */
- if (ai_handicap(pplayer, H_DIPLOMACY) && !aplayer->ai.control) {
- kill_desire = kill_desire / 2 - 5;
+ if (ai_handicap(pplayer, H_DIPLOMACY) && !target->ai.control) {
+ want /= 2;
}
/* Amortize by distance */
- return amortize(kill_desire, adip->distance);
+ want = amortize(want, distance);
+
+ if (pplayers_allied(pplayer, target)) {
+ want /= 4;
+ }
+
+ DIPLO_LOG(LOG_DEBUG, pplayer, target, "War want %d, war fear %d",
+ want, fear);
+ return (want - fear);
}
/**********************************************************************
@@ -777,8 +767,7 @@
{
int war_desire[MAX_NUM_PLAYERS + MAX_NUM_BARBARIANS];
int best_desire = 0;
- struct player *target = NULL;
- int mult;
+ struct player *best_target = NULL;
memset(war_desire, 0, sizeof(war_desire));
@@ -787,11 +776,39 @@
return; /* duh */
}
+ /* Calculate our desires, and find desired war target */
+ players_iterate(aplayer) {
+ enum diplstate_type ds = pplayer_get_diplstate(pplayer, aplayer)->type;
+ struct ai_dip_intel *adip =
&ai->diplomacy.player_intel[aplayer->player_no];
+
+ /* We don't hate ourselves, those we don't know or team members
+ * Defer judgement on alliance members we're not (yet) allied to
+ * to the alliance leader. Always respect ceasefires the boss has signed.
+ */
+ if (aplayer == pplayer
+ || !aplayer->is_alive
+ || ds == DS_NO_CONTACT
+ || players_on_same_team(pplayer, aplayer)
+ || (pplayer != ai->diplomacy.alliance_leader &&
+ aplayer != ai->diplomacy.alliance_leader &&
+ adip->is_allied_with_ally)
+ || (pplayer_get_diplstate(aplayer, ai->diplomacy.alliance_leader)->type
+ == DS_CEASEFIRE)) {
+ continue;
+ }
+ war_desire[aplayer->player_no] = ai_war_desire(pplayer, aplayer, ai);
+ if (war_desire[aplayer->player_no] > best_desire) {
+ best_desire = war_desire[aplayer->player_no];
+ best_target = aplayer;
+ }
+ } players_iterate_end;
+
/* Time to make love. If we've been wronged, hold off that love
* for a while. Also, cool our head each turn with love_coeff. */
players_iterate(aplayer) {
int a = aplayer->player_no;
struct ai_dip_intel *adip = &ai->diplomacy.player_intel[a];
+ int amount = 0;
if (pplayer == aplayer || !aplayer->is_alive) {
continue;
@@ -803,70 +820,54 @@
&& pplayer->diplstates[a].has_reason_to_cancel == 0
&& !adip->is_allied_with_enemy
&& !adip->at_war_with_ally
- && aplayer != ai->diplomacy.target
+ && aplayer != best_target
&& adip->ally_patience >= 0) {
- pplayer->ai.love[aplayer->player_no] += ai->diplomacy.love_incr;
- DIPLO_LOG(LOG_DEBUG, pplayer, ai, "Increased love for %s (now %d)",
- aplayer->name, pplayer->ai.love[aplayer->player_no]);
- } else if (pplayer->diplstates[aplayer->player_no].type == DS_WAR) {
- pplayer->ai.love[aplayer->player_no] -= ai->diplomacy.love_incr;
- if (ai->diplomacy.target != aplayer &&
- pplayer->ai.love[aplayer->player_no] < 0) {
+ amount += ai->diplomacy.love_incr;
+ /* Increase love by each enemy he is at war with */
+ players_iterate(eplayer) {
+ if (WAR(eplayer, aplayer) && WAR(pplayer, eplayer)) {
+ amount += ai->diplomacy.love_incr / 2;
+ }
+ } players_iterate_end;
+ pplayer->ai.love[aplayer->player_no] += amount;
+ DIPLO_LOG(LOG_DEBUG, pplayer, aplayer, "Increased love by %d", amount);
+ } else if (WAR(pplayer, aplayer)) {
+ amount -= ai->diplomacy.love_incr;
+ if (pplayer->ai.love[aplayer->player_no] < 0) {
/* Give him a better chance for a cease fire */
- pplayer->ai.love[aplayer->player_no] += (MAX_AI_LOVE) * 3 / 100;
+ amount += (MAX_AI_LOVE) * 3 / 100;
}
- DIPLO_LOG(LOG_DEBUG, pplayer, ai, "Reduced love for %s (now %d) ",
- aplayer->name, pplayer->ai.love[aplayer->player_no]);
+ pplayer->ai.love[aplayer->player_no] += amount;
+ DIPLO_LOG(LOG_DEBUG, pplayer, aplayer, "%d love lost to war", amount);
} else if (pplayer->diplstates[a].has_reason_to_cancel != 0) {
/* Provoked in time of peace */
if (pplayer->ai.love[aplayer->player_no] > 0) {
- DIPLO_LOG(LOG_DEBUG, pplayer, ai, "Provoked by %s! Love halved "
- "(was %d)", aplayer->name,
- pplayer->ai.love[aplayer->player_no]);
- pplayer->ai.love[aplayer->player_no] /= 2;
+ amount -= pplayer->ai.love[aplayer->player_no] / 2;
}
- pplayer->ai.love[aplayer->player_no] -= ai->diplomacy.love_incr;
+ amount -= ai->diplomacy.love_incr * 2;
+ pplayer->ai.love[aplayer->player_no] += amount;
+ DIPLO_LOG(LOG_DEBUG, pplayer, aplayer, "Provoked! %d love lost!",
+ amount);
}
-
- /* Reduce love by number of units in our territory.
+ amount = 0;
+
+ /* Reduce love due to units in our territory.
* AI is so naive, that we have to count it even if players are allied */
- mult =
- pplayers_at_war(pplayer, aplayer) || adip->is_allied_with_enemy ?
- 2 : 1;
-
- pplayer->ai.love[aplayer->player_no] -=
- MIN(player_in_territory(pplayer, aplayer) * (MAX_AI_LOVE / 100),
- pplayers_allied(aplayer, pplayer) ?
- ai->diplomacy.love_incr - 1 : (MAX_AI_LOVE / 2)) * mult;
-
+ amount -= MIN(player_in_territory(pplayer, aplayer) * (MAX_AI_LOVE / 200),
+ ai->diplomacy.love_incr
+ * ((adip->is_allied_with_enemy != NULL) + 1));
+ pplayer->ai.love[aplayer->player_no] += amount;
+ if (amount != 0) {
+ DIPLO_LOG(LOG_DEBUG, pplayer, aplayer, "%d love lost due to units inside
"
+ "our borders", amount);
+ }
+
/* Increase the love if aplayer has got a building that makes
* us love him more. Typically it's Eiffel Tower */
pplayer->ai.love[aplayer->player_no] +=
get_player_bonus(aplayer, EFT_GAIN_AI_LOVE) * MAX_AI_LOVE / 1000;
-
- /* Massage our numbers to keep love and its opposite on the ground.
- * Gravitate towards zero. */
- pplayer->ai.love[aplayer->player_no] -=
- (pplayer->ai.love[aplayer->player_no] * ai->diplomacy.love_coeff / 100);
-
- /* ai love should always be in range [-MAX_AI_LOVE..MAX_AI_LOVE] */
- pplayer->ai.love[aplayer->player_no] =
- MAX(-MAX_AI_LOVE,
- MIN(MAX_AI_LOVE, pplayer->ai.love[aplayer->player_no]));
} players_iterate_end;
- /* Stop war against a dead player */
- if (ai->diplomacy.target && !ai->diplomacy.target->is_alive) {
- DIPLO_LOG(LOG_DIPL2, pplayer, ai, "Target player %s is dead! Victory!",
- ai->diplomacy.target->name);
- ai->diplomacy.timer = 0;
- ai->diplomacy.countdown = 0;
- ai->diplomacy.target = NULL;
- if (ai->diplomacy.strategy == WIN_CAPITAL) {
- ai->diplomacy.strategy = WIN_OPEN;
- }
- }
-
/* Can we win by space race? */
if (ai->diplomacy.spacerace_leader == pplayer) {
freelog(LOG_DIPL2, "%s going for space race victory!", pplayer->name);
@@ -877,90 +878,26 @@
}
}
- if (ai->diplomacy.countdown > 0) {
- ai->diplomacy.countdown--;
- }
-
- /* Ensure that we don't prematurely end an ongoing war */
- if (ai->diplomacy.timer-- > 0) {
- return;
- }
-
- /* Calculate average distances to other players' empires. */
players_iterate(aplayer) {
- ai->diplomacy.player_intel[aplayer->player_no].distance =
- player_distance_to_player(pplayer, aplayer);
- } players_iterate_end;
+ int *love = &pplayer->ai.love[aplayer->player_no];
- /* Calculate our desires, and find desired war target */
- players_iterate(aplayer) {
- enum diplstate_type ds = pplayer_get_diplstate(pplayer, aplayer)->type;
- struct ai_dip_intel *adip =
&ai->diplomacy.player_intel[aplayer->player_no];
+ if (aplayer == best_target) {
+ int reduction = MIN(best_desire, MAX_AI_LOVE / 20);
- /* We don't hate ourselves, those we don't know or team members
- * Defer judgement on alliance members we're not (yet) allied to
- * to the alliance leader. Always respect ceasefires the boss has signed.
- */
- if (aplayer == pplayer
- || !aplayer->is_alive
- || ds == DS_NO_CONTACT
- || players_on_same_team(pplayer, aplayer)
- || (pplayer != ai->diplomacy.alliance_leader &&
- aplayer != ai->diplomacy.alliance_leader &&
- adip->is_allied_with_ally)
- || (pplayer_get_diplstate(aplayer, ai->diplomacy.alliance_leader)->type
- == DS_CEASEFIRE)) {
- continue;
+ *love -= reduction;
+ DIPLO_LOG(LOG_DEBUG, pplayer, aplayer, "Wants war, reducing "
+ "love by %d ", reduction);
}
- war_desire[aplayer->player_no] = 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 && !adip->at_war_with_ally) {
- continue;
- }
-
- /* Strongly prefer players we are at war with already. */
- if (!pplayers_at_war(pplayer, aplayer)) {
- war_desire[aplayer->player_no] /= 2;
- }
-
- DIPLO_LOG(LOG_DEBUG, pplayer, ai, "Against %s we have war desire "
- "%d ", aplayer->name, war_desire[aplayer->player_no]);
+ /* Massage our numbers to keep love and its opposite on the ground.
+ * Gravitate towards zero. */
+ *love -= *love * (ai->diplomacy.love_coeff / 100);
- /* Find best target */
- if (war_desire[aplayer->player_no] > best_desire) {
- target = aplayer;
- best_desire = war_desire[aplayer->player_no];
- }
+ /* ai love should always be in range [-MAX_AI_LOVE..MAX_AI_LOVE] */
+ *love = MAX(-MAX_AI_LOVE, MIN(MAX_AI_LOVE, *love));
} players_iterate_end;
-
- if (!target) {
- DIPLO_LOG(LOG_DEBUG, pplayer, ai, "Found no target.");
- ai->diplomacy.target = NULL;
- return;
- }
-
- /* Switch to target */
- if (target != ai->diplomacy.target) {
- DIPLO_LOG(LOG_DIPL, pplayer, ai, "Setting target to %s", target->name);
- ai->diplomacy.target = target;
- if (ai->diplomacy.strategy == WIN_CAPITAL) {
- ai->diplomacy.countdown = 1; /* Quickly!! */
- } else if (pplayer->diplstates[target->player_no].has_reason_to_cancel >
1) {
- /* Turns until we lose our casus bellum, exploit that. */
- ai->diplomacy.countdown =
- pplayer->diplstates[target->player_no].has_reason_to_cancel
- - 1;
- } else {
- ai->diplomacy.countdown = 6; /* Take the time we need - WAG */
- }
- /* Don't reevaluate too often. */
- ai->diplomacy.timer = myrand(6) + 6 + ai->diplomacy.countdown;
- players_iterate(aplayer) {
- ai->diplomacy.player_intel[aplayer->player_no].ally_patience = 0;
- } players_iterate_end;
- }
}
+
/**********************************************************************
Find two techs that can be exchanged and suggest that
***********************************************************************/
@@ -1000,7 +937,6 @@
}
} tech_type_iterate_end;
-
tech_type_iterate(tech) {
if (worth[tech] <= 0) {
continue;
@@ -1028,6 +964,7 @@
} tech_type_iterate_end;
} tech_type_iterate_end;
}
+
/**********************************************************************
Offer techs and stuff to other player and ask for techs we need.
***********************************************************************/
@@ -1071,10 +1008,49 @@
Go to war.
***********************************************************************/
static void ai_go_to_war(struct player *pplayer, struct ai_data *ai,
- struct player *target)
+ struct player *target, enum war_reason reason)
{
+ struct ai_dip_intel *adip = &ai->diplomacy.player_intel[target->player_no];
+
assert(pplayer != target);
+ switch (reason) {
+ case WAR_REASON_SPACE:
+ notify(target, _("*%s (AI)* Space will never be yours. "), pplayer->name);
+ break;
+ case WAR_REASON_BEHAVIOUR:
+ notify(target, _("*%s (AI)* I have tolerated your vicious antics "
+ "long enough! To war!"), pplayer->name);
+ break;
+ case WAR_REASON_NONE:
+ notify(target, _("*%s (AI)* Peace in ... some other time"),
+ pplayer->name);
+ break;
+ case WAR_REASON_HATRED:
+ notify(target, _("*%s (AI)* Finally I get around to you! Did "
+ "you really think you could get away with your crimes?"),
+ pplayer->name);
+ break;
+ case WAR_REASON_EXCUSE:
+ notify(target, _("*%s (AI)* Your covert hostilities brought "
+ "this war upon you!"), pplayer->name);
+ break;
+ case WAR_REASON_ALLIANCE:
+ if (adip->at_war_with_ally) {
+ notify(target, _("*%s (AI)* Your aggression against %s was "
+ "your last mistake!"),
+ pplayer->name,
+ adip->at_war_with_ally->name);
+ } else {
+ /* Ooops! */
+ DIPLO_LOG(LOG_DIPL, pplayer, target, "Wanted to declare war "
+ "for his war against an ally, but can no longer find "
+ "this ally! War declaration aborted.");
+ return;
+ }
+ break;
+ }
+
if (gives_shared_vision(pplayer, target)) {
remove_shared_vision(pplayer, target);
}
@@ -1088,15 +1064,12 @@
/* This will take us straight to war. */
handle_diplomacy_cancel_pact(pplayer, target->player_no, CLAUSE_LAST);
- /* Continue war at least in this arbitrary number of turns to show
- * some spine */
- ai->diplomacy.timer = myrand(4) + 3;
- if (pplayer->ai.love[target->player_no] < 0) {
- ai->diplomacy.timer -= pplayer->ai.love[target->player_no] / 10;
- } else {
- /* We DO NOT love our enemies! AIs are heatens! */
- pplayer->ai.love[target->player_no] = -1;
+ /* Throw a tantrum */
+ if (pplayer->ai.love[target->player_no] > 0) {
+ pplayer->ai.love[target->player_no] = -1;
}
+ pplayer->ai.love[target->player_no] -= MAX_AI_LOVE / 8;
+
assert(!gives_shared_vision(pplayer, target));
}
@@ -1106,10 +1079,85 @@
Only ever called for AI players and never for barbarians.
***********************************************************************/
+void static war_countdown(struct player *pplayer, struct player *target,
+ int countdown, enum war_reason reason)
+{
+ struct ai_data *ai = ai_data_get(pplayer);
+ struct ai_dip_intel *adip = &ai->diplomacy.player_intel[target->player_no];
+
+ DIPLO_LOG(LOG_DIPL, pplayer, target, "countdown to war in %d", countdown);
+
+ /* Otherwise we're resetting an existing countdown, which is very bad */
+ assert(adip->countdown == -1);
+
+ adip->countdown = countdown;
+ adip->war_reason = reason;
+
+ players_iterate(ally) {
+ if (!pplayers_allied(pplayer, ally) || !ally->is_alive) {
+ continue;
+ }
+
+ switch (reason) {
+ case WAR_REASON_SPACE:
+ notify(ally, _("*%s (AI)* We will be launching an all-out war "
+ "against %s in %d turns to stop the spaceship "
+ "launch."), pplayer->name, target->name, countdown);
+ notify(ally, _("*%s (AI)* Your aid in this matter will be expected. "
+ "Long live our glorious alliance!"), pplayer->name);
+ break;
+ case WAR_REASON_BEHAVIOUR:
+ case WAR_REASON_EXCUSE:
+ notify(ally, _("*%s (AI)* %s has grossly violated his treaties with us "
+ "for own gain. We will answer in force in %d turns "
+ "and expect you to honour your alliance with us and do "
+ "likewise!"), pplayer->name, target->name, countdown);
+ break;
+ case WAR_REASON_NONE:
+ notify(ally, _("*%s (AI)* We intend to pillage and plunder the rich "
+ "civilization of %s. We declare war in %d turns."),
+ pplayer->name, target->name, countdown);
+ notify(ally, _("*%s (AI)* If you want a piece of the loot, feel "
+ "free to join in the action!"), pplayer->name);
+ break;
+ case WAR_REASON_HATRED:
+ notify(ally, _("*%s (AI)* We have had it with %s. Let us tear this "
+ "pathetic civilization apart. We declare war in %d "
+ "turns."), pplayer->name, target->name, countdown);
+ notify(ally, _("*%s (AI)* As our glorious allies, we expect your "
+ "help in this war."), pplayer->name);
+ break;
+ case WAR_REASON_ALLIANCE:
+ if (WAR(ally, target)) {
+ notify(ally, _("*%s (AI)* We will honour our alliance and declare "
+ "war on %s in %d turns. Hold on - we are coming!"),
+ pplayer->name, target->name, countdown);
+ } else if (adip->at_war_with_ally) {
+ notify(ally, _("*%s (AI)* We will honour our alliance with %s and "
+ "declare war on %s in %d turns. We expect you to "
+ "do likewise."), pplayer->name,
+ adip->at_war_with_ally->name, target->name,
+ countdown);
+ } else {
+ assert(FALSE); /* Huh? */
+ }
+ break;
+ }
+ } players_iterate_end;
+}
+
+/**********************************************************************
+ 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;
+ bool need_targets = TRUE;
+ struct player *target = NULL;
+ int most_hatred = MAX_AI_LOVE;
assert(pplayer->ai.control);
if (!pplayer->is_alive) {
@@ -1120,12 +1168,10 @@
players_iterate(aplayer) {
if (pplayer->ai.love[aplayer->player_no] < 0
- && pplayer->diplstates[aplayer->player_no].has_reason_to_cancel >= 2) {
- DIPLO_LOG(LOG_DIPL2, pplayer, ai, "Declaring war on %s in revenge",
- target->name);
- notify(target, _("*%s (AI)* I will NOT accept such behaviour! This "
- "means WAR!"), pplayer->name);
- ai_go_to_war(pplayer, ai, aplayer);
+ && pplayer->diplstates[aplayer->player_no].has_reason_to_cancel >= 2
+ && ai->diplomacy.player_intel[aplayer->player_no].countdown < 0) {
+ DIPLO_LOG(LOG_DIPL2, pplayer, aplayer, "Plans war in revenge");
+ war_countdown(pplayer, aplayer, map.size, WAR_REASON_BEHAVIOUR);
}
} players_iterate_end;
@@ -1139,6 +1185,7 @@
if (!aplayer->is_alive || aplayer == pplayer
|| players_on_same_team(pplayer, aplayer)
+ || ai->diplomacy.player_intel[aplayer->player_no].countdown < 0
|| ship->state == SSHIP_NONE) {
continue;
}
@@ -1158,6 +1205,7 @@
pplayer->ai.love[aplayer->player_no] = -(BIG_NUMBER);
} else if (ship->state == SSHIP_STARTED
&& adip->warned_about_space == 0) {
+ pplayer->ai.love[aplayer->player_no] -= MAX_AI_LOVE / 10;
adip->warned_about_space = 10 + myrand(6);
notify(aplayer, _("*%s (AI)* Your attempt to unilaterally "
"dominate outer space is highly offensive."), pplayer->name);
@@ -1167,38 +1215,44 @@
if (aplayer->spaceship.state == SSHIP_LAUNCHED
&& aplayer == ai->diplomacy.spacerace_leader) {
/* This means war!!! */
- ai->diplomacy.timer = 0; /* Force reevaluation next turn */
+ pplayer->ai.love[aplayer->player_no] -= MAX_AI_LOVE / 2;
+ DIPLO_LOG(LOG_DIPL, pplayer, aplayer, "plans war due to spaceship");
+ war_countdown(pplayer, aplayer, 4 + map.size, WAR_REASON_SPACE);
}
} players_iterate_end;
}
- /*** Declare war - against target ***/
+ /*** Declare war against somebody if we are out of targets ***/
+
+ players_iterate(aplayer) {
+ if (aplayer->is_alive
+ && WAR(pplayer, aplayer)) {
+ need_targets = FALSE;
+ } else if (aplayer->is_alive
+ && pplayer->ai.love[aplayer->player_no] < most_hatred) {
+ most_hatred = pplayer->ai.love[aplayer->player_no];
+ target = aplayer;
+ }
+ } players_iterate_end;
+ if (need_targets && target && most_hatred < WAR_THRESHOLD
+ && ai->diplomacy.player_intel[target->player_no].countdown < 0) {
+ enum war_reason war_reason;
- if (target && pplayers_at_war(pplayer, target)) {
- ai->diplomacy.countdown = 0; /* cosmetic */
- }
- if (target && !pplayers_at_war(pplayer, target)
- && ai->diplomacy.countdown <= 0
- && !ai_handicap(pplayer, H_AWAY)) {
if (pplayers_allied(pplayer, target)) {
- DIPLO_LOG(LOG_DEBUG, pplayer, ai, "Went to war against %s, who is "
- "an ally!", target->name); /* Oh, my. */
+ DIPLO_LOG(LOG_DEBUG, pplayer, target, "Went to war against an ally!");
}
if (pplayer->diplstates[target->player_no].has_reason_to_cancel > 0) {
/* We have good reason */
- notify(target, _("*%s (AI)* Your despicable actions will not go "
- "unpunished!"), pplayer->name);
+ war_reason = WAR_REASON_EXCUSE;
} if (pplayer->ai.love[target->player_no] < 0) {
- /* We have a reason of sorts from way back. */
- notify(target, _("*%s (AI)* Finally I get around to you! Did "
- "you really think you could get away with your crimes?"),
- pplayer->name);
+ /* We have a reason of sorts from way back, maybe? */
+ war_reason = WAR_REASON_HATRED;
} else {
/* We have no legimitate reason... So what? */
- notify(target, _("*%s (AI)* Peace in ... some other time"),
- pplayer->name);
+ war_reason = WAR_REASON_NONE;
}
- ai_go_to_war(pplayer, ai, target);
+ DIPLO_LOG(LOG_DEBUG, pplayer, target, "plans war for spoils");
+ war_countdown(pplayer, target, 4 + map.size, war_reason);
}
/*** Declare war - against enemies of allies ***/
@@ -1208,19 +1262,33 @@
if (aplayer->is_alive
&& adip->at_war_with_ally
+ && adip->countdown < 0
&& !adip->is_allied_with_ally
&& !pplayers_at_war(pplayer, aplayer)
&& (pplayer_get_diplstate(pplayer, aplayer)->type != DS_CEASEFIRE ||
myrand(5) < 1)) {
- notify(aplayer, _("*%s (AI)* Your aggression against %s was "
- "your last mistake!"),
- pplayer->name,
- adip->at_war_with_ally->name);
- ai_go_to_war(pplayer, ai, aplayer);
+ DIPLO_LOG(LOG_DEBUG, pplayer, target, "plans war to help ally %s",
+ adip->at_war_with_ally->name);
+ war_countdown(pplayer, aplayer, 2 + map.size, WAR_REASON_ALLIANCE);
+ }
+ } players_iterate_end;
+
+ /*** Actually declare war (when we have moved units into position) ***/
+
+ players_iterate(aplayer) {
+ struct ai_dip_intel *adip =
+ &ai->diplomacy.player_intel[aplayer->player_no];
+
+ if (adip->countdown > 0) {
+ adip->countdown--;
+ } else if (adip->countdown == 0) {
+ DIPLO_LOG(LOG_DIPL2, pplayer, aplayer, "Declaring war!");
+ ai_go_to_war(pplayer, ai, aplayer, adip->war_reason);
+ adip->countdown = -1;
}
} players_iterate_end;
- /*** Opportunism, Inc. Try to make peace with everyone else ***/
+ /*** Try to make peace with everyone we love ***/
players_iterate(aplayer) {
enum diplstate_type ds = pplayer_get_diplstate(pplayer, aplayer)->type;
@@ -1248,6 +1316,7 @@
|| aplayer == pplayer
|| aplayer == target /* no mercy */
|| !aplayer->is_alive
+ || adip->countdown > 0
|| !could_meet_with_player(pplayer, aplayer)
|| adip->at_war_with_ally) {
continue;
@@ -1280,13 +1349,23 @@
ai_share(pplayer, aplayer);
break;
case DS_ALLIANCE:
- if (players_on_same_team(pplayer, aplayer)
- || (target && (!pplayers_at_war(pplayer, target)
- || pplayers_at_war(aplayer, target)))) {
- /* Share techs only with team mates and _reliable_ allies.
- * This means, if we have a target, then unless we are still
- * in countdown mode, we _except_ our allies to be at war
- * with our target too! */
+ /* See if our allies are diligently declaring war on our enemies... */
+ target = NULL;
+ players_iterate(eplayer) {
+ int e = eplayer->player_no;
+
+ if (WAR(pplayer, eplayer)
+ && (ai_data_get(aplayer)->diplomacy.player_intel[e].countdown < 0)
+ && !pplayers_at_war(aplayer, eplayer)) {
+ target = eplayer;
+ break;
+ }
+ } players_iterate_end;
+
+ if ((players_on_same_team(pplayer, aplayer)
+ || pplayer->ai.love[aplayer->player_no] > MAX_AI_LOVE / 2)
+ && !target) {
+ /* Share techs only with team mates and allies we really like. */
ai_share(pplayer, aplayer);
adip->ally_patience = 0;
break;
@@ -1294,14 +1373,7 @@
adip->ally_patience = 0;
break;
}
- if (target && pplayer->ai.control) {
- DIPLO_LOG(LOG_DIPL2, pplayer, ai, "Ally %s not at war with enemy %s "
- "(patience %d, %s %s)", aplayer->name,
- target->name, adip->ally_patience, adip->at_war_with_ally
- ? "war_with_ally" : "", adip->is_allied_with_ally ?
- "allied_with_ally" : "");
- }
switch (adip->ally_patience--) {
case 0:
notify(aplayer, _("*%s (AI)* Greetings our most trustworthy "
@@ -1318,8 +1390,7 @@
"alliance, and yet you remain at peace with our mortal "
"enemy, %s! This is unacceptable, our alliance is no "
"more!"), pplayer->name, target->name);
- DIPLO_LOG(LOG_DIPL2, pplayer, ai, "breaking useless alliance with "
- "%s", aplayer->name);
+ DIPLO_LOG(LOG_DIPL2, pplayer, aplayer, "breaking useless alliance");
/* to peace */
handle_diplomacy_cancel_pact(pplayer, aplayer->player_no,
CLAUSE_ALLIANCE);
@@ -1337,15 +1408,14 @@
clause.type = CLAUSE_ALLIANCE;
if (ai_goldequiv_clause(pplayer, aplayer,
&clause, ai, FALSE, DS_ALLIANCE) < 0
- || (adip->asked_about_alliance > 0 && !aplayer->ai.control)
- || !target) {
- /* Note that we don't ever ask for alliance unless we have a target */
+ || (adip->asked_about_alliance > 0 && !aplayer->ai.control)) {
break;
}
ai_diplomacy_suggest(pplayer, aplayer, CLAUSE_ALLIANCE, 0);
adip->asked_about_alliance = !aplayer->ai.control ? 13 : 0;
notify(aplayer, _("*%s (AI)* Greetings friend, may we suggest "
- "a joint campaign against %s?"), pplayer->name, target->name);
+ "making a common cause and join in an alliance?"),
+ pplayer->name);
break;
case DS_CEASEFIRE:
@@ -1353,15 +1423,13 @@
clause.type = CLAUSE_PEACE;
if (ai_goldequiv_clause(pplayer, aplayer, &clause,
ai, FALSE, CLAUSE_PEACE) < 0
- || (adip->asked_about_peace > 0 && !aplayer->ai.control)
- || !target) {
- /* Note that we don't ever ask for peace unless we have a target */
+ || (adip->asked_about_peace > 0 && !aplayer->ai.control)) {
break; /* never */
}
ai_diplomacy_suggest(pplayer, aplayer, CLAUSE_PEACE, 0);
adip->asked_about_peace = !aplayer->ai.control ? 12 : 0;
notify(aplayer, _("*%s (AI)* Greetings neighbour, may we suggest "
- "a joint campaign against %s?"), pplayer->name, target->name);
+ "more peaceful relations?"), pplayer->name);
break;
case DS_NO_CONTACT: /* but we do have embassy! weird. */
@@ -1369,15 +1437,14 @@
clause.type = CLAUSE_CEASEFIRE;
if (ai_goldequiv_clause(pplayer, aplayer,
&clause, ai, FALSE, CLAUSE_CEASEFIRE) < 0
- || (adip->asked_about_ceasefire > 0 && !aplayer->ai.control)
- || !target) {
+ || (adip->asked_about_ceasefire > 0 && !aplayer->ai.control)) {
break; /* Fight until the end! */
}
ai_diplomacy_suggest(pplayer, aplayer, CLAUSE_CEASEFIRE, 0);
adip->asked_about_ceasefire = !aplayer->ai.control ? 9 : 0;
- notify(aplayer, _("*%s (AI)* %s is threatening us both, may we "
- "suggest a cessation of hostilities?"), pplayer->name,
- target->name);
+ notify(aplayer, _("*%s (AI)* we grow weary of this constant "
+ "bloodshed, may we suggest a cessation of hostilities?"),
+ pplayer->name);
break;
default:
die("Unknown pact type");
@@ -1386,6 +1453,23 @@
} players_iterate_end;
}
+/**********************************************************************
+ Are we going to be declaring war in a few turns time? If so, go
+ on a war footing, and try to buy out as many units as possible.
+***********************************************************************/
+bool ai_on_war_footing(struct player *pplayer)
+{
+ struct ai_data *ai = ai_data_get(pplayer);
+
+ players_iterate(plr) {
+ if (ai->diplomacy.player_intel[plr->player_no].countdown >= 0) {
+ return TRUE;
+ }
+ } players_iterate_end;
+
+ return FALSE;
+}
+
/* AI attitude call-backs */
/**********************************************************************
@@ -1396,6 +1480,10 @@
{
if (violator == victim) {
players_iterate(pplayer) {
+ if (!pplayer->ai.control) {
+ continue;
+ }
+
if (pplayer != violator) {
pplayer->ai.love[violator->player_no] -= MAX_AI_LOVE / 20;
}
@@ -1403,6 +1491,10 @@
return;
} else {
players_iterate(pplayer) {
+ if (!pplayer->ai.control) {
+ continue;
+ }
+
if (pplayer != violator) {
pplayer->ai.love[violator->player_no] -= MAX_AI_LOVE / 10;
if (victim == pplayer) {
@@ -1419,6 +1511,10 @@
void ai_incident_diplomat(struct player *violator, struct player *victim)
{
players_iterate(pplayer) {
+ if (!pplayer->ai.control) {
+ continue;
+ }
+
if (pplayer != violator) {
/* Dislike backstabbing bastards */
pplayer->ai.love[violator->player_no] -= MAX_AI_LOVE / 100;
@@ -1440,6 +1536,10 @@
void ai_incident_war(struct player *violator, struct player *victim)
{
players_iterate(pplayer) {
+ if (!pplayer->ai.control) {
+ continue;
+ }
+
if (pplayer != violator) {
/* Dislike backstabbing bastards */
pplayer->ai.love[violator->player_no] -= MAX_AI_LOVE / 30;
@@ -1454,6 +1554,16 @@
if (victim == pplayer) {
pplayer->ai.love[violator->player_no] -=
MIN(pplayer->ai.love[violator->player_no] - MAX_AI_LOVE / 3, -1);
+ /* Scream for help!! */
+ players_iterate(ally) {
+ if (!pplayers_allied(pplayer, ally) || !ally->is_alive) {
+ continue;
+ }
+ notify(ally, _("*%s (AI)* We have been savagely attacked by "
+ "%s, and we need your help! Honour our glorious "
+ "alliance and your name will never be forgotten!"),
+ victim->name, violator->name);
+ } players_iterate_end;
}
}
} players_iterate_end;
Index: ai/advdiplomacy.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/advdiplomacy.h,v
retrieving revision 1.4
diff -u -r1.4 advdiplomacy.h
--- ai/advdiplomacy.h 20 Jul 2005 18:28:48 -0000 1.4
+++ ai/advdiplomacy.h 23 Jul 2005 23:35:21 -0000
@@ -20,6 +20,11 @@
struct Clause;
struct ai_data;
+enum war_reason {
+ WAR_REASON_BEHAVIOUR, WAR_REASON_SPACE, WAR_REASON_EXCUSE,
+ WAR_REASON_HATRED, WAR_REASON_ALLIANCE, WAR_REASON_NONE
+};
+
void ai_diplomacy_begin_new_phase(struct player *pplayer,
struct ai_data *ai);
void ai_diplomacy_actions(struct player *pplayer);
@@ -33,4 +38,6 @@
void ai_incident_diplomat(struct player *violator, struct player *victim);
void ai_incident_nuclear(struct player *violator, struct player *victim);
+bool ai_on_war_footing(struct player *pplayer);
+
#endif
Index: ai/aicity.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aicity.c,v
retrieving revision 1.231
diff -u -r1.231 aicity.c
--- ai/aicity.c 22 Jul 2005 16:18:04 -0000 1.231
+++ ai/aicity.c 23 Jul 2005 23:35:22 -0000
@@ -927,7 +927,8 @@
ai_barbarian_choose_build(pplayer, &(pcity->ai.choice));
} else {
/* FIXME: 101 is the "overriding military emergency" indicator */
- if (pcity->ai.choice.want <= 100 || pcity->ai.urgency == 0) {
+ if ((pcity->ai.choice.want <= 100 || pcity->ai.urgency == 0)
+ && !(ai_on_war_footing(pplayer) && pcity->ai.choice.want > 0)) {
domestic_advisor_choose_build(pplayer, pcity, &newchoice);
copy_if_better_choice(&newchoice, &(pcity->ai.choice));
}
@@ -1026,11 +1027,10 @@
unit_list_iterate(pcity->tile->units, punit) {
struct unit_type *punittype = can_upgrade_unittype(pplayer, punit->type);
- if (military && (!is_military_unit(punit) || !is_ground_unit(punit))) {
+ if (military && !IS_ATTACKER(punit)) {
/* Only upgrade military units this round */
continue;
- } else if (!military && is_military_unit(punit)
- && unit_type(punit)->transport_capacity == 0) {
+ } else if (!military && IS_ATTACKER(punit)) {
/* Only civilians or tranports this round */
continue;
}
@@ -1061,6 +1061,7 @@
{
struct ai_choice bestchoice;
int cached_limit = ai_gold_reserve(pplayer);
+ bool war_footing = ai_on_war_footing(pplayer);
/* Disband explorers that are at home but don't serve a purpose.
* FIXME: This is a hack, and should be removed once we
@@ -1112,6 +1113,7 @@
} else {
/* If we have urgent want, spend more */
int upgrade_limit = limit;
+
if (pcity->ai.urgency > 1) {
upgrade_limit = pplayer->ai.est_upkeep;
}
@@ -1140,10 +1142,12 @@
/* Don't buy settlers in size 1 cities unless we grow next turn */
continue;
} else if (city_list_size(pplayer->cities) > 6) {
- /* Don't waste precious money buying settlers late game
- * since this raises taxes, and we want science. Adjust this
- * again when our tax algorithm is smarter. */
- continue;
+ /* Don't waste precious money buying settlers late game
+ * since this raises taxes, and we want science. Adjust this
+ * again when our tax algorithm is smarter. */
+ continue;
+ } else if (war_footing) {
+ continue;
}
} else {
/* We are not a settler. Therefore we increase the cash need we
@@ -1157,8 +1161,9 @@
|| (pplayer->economic.gold - buycost < limit);
if (bestchoice.type == CT_ATTACKER
- && buycost
- > unit_build_shield_cost(get_unit_type(bestchoice.choice)) * 2) {
+ && buycost
+ > unit_build_shield_cost(get_unit_type(bestchoice.choice)) * 2
+ && !war_footing) {
/* Too expensive for an offensive unit */
continue;
}
@@ -1194,10 +1199,12 @@
}
} while (TRUE);
- /* Civilian upgrades now */
- city_list_iterate(pplayer->cities, pcity) {
- ai_upgrade_units(pcity, cached_limit, FALSE);
- } city_list_iterate_end;
+ if (!war_footing) {
+ /* Civilian upgrades now */
+ city_list_iterate(pplayer->cities, pcity) {
+ ai_upgrade_units(pcity, cached_limit, FALSE);
+ } city_list_iterate_end;
+ }
freelog(LOG_BUY, "%s wants to keep %d in reserve (tax factor %d)",
pplayer->name, cached_limit, pplayer->ai.maxbuycost);
@@ -1238,6 +1245,9 @@
TIMING_LOG(AIT_CITY_MILITARY, TIMER_START);
military_advisor_choose_build(pplayer, pcity, &pcity->ai.choice);
TIMING_LOG(AIT_CITY_MILITARY, TIMER_STOP);
+ if (ai_on_war_footing(pplayer) && pcity->ai.choice.want > 0) {
+ continue; /* Go, soldiers! */
+ }
/* Will record its findings in pcity->settler_want */
TIMING_LOG(AIT_CITY_TERRAIN, TIMER_START);
contemplate_terrain_improvements(pcity);
Index: ai/aidata.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aidata.c,v
retrieving revision 1.74
diff -u -r1.74 aidata.c
--- ai/aidata.c 22 Jul 2005 16:18:04 -0000 1.74
+++ ai/aidata.c 23 Jul 2005 23:35:23 -0000
@@ -574,10 +574,8 @@
(game.control.government_count + 1) * sizeof(*ai->government_want));
ai->wonder_city = 0;
- ai->diplomacy.target = NULL;
ai->diplomacy.strategy = WIN_OPEN;
ai->diplomacy.timer = 0;
- ai->diplomacy.countdown = 0;
ai->diplomacy.love_coeff = 4; /* 4% */
ai->diplomacy.love_incr = MAX_AI_LOVE * 4 / 100;
ai->diplomacy.req_love_for_peace = MAX_AI_LOVE * 8 / 100;
@@ -587,6 +585,8 @@
for (i = 0; i < MAX_NUM_PLAYERS + MAX_NUM_BARBARIANS; i++) {
ai->diplomacy.player_intel[i].spam = i % 5; /* pseudorandom */
+ ai->diplomacy.player_intel[i].countdown = -1;
+ ai->diplomacy.player_intel[i].war_reason = WAR_REASON_NONE;
ai->diplomacy.player_intel[i].distance = 1;
ai->diplomacy.player_intel[i].ally_patience = 0;
pplayer->ai.love[i] = 1;
Index: ai/aidata.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aidata.h,v
retrieving revision 1.31
diff -u -r1.31 aidata.h
--- ai/aidata.h 21 Jul 2005 08:12:28 -0000 1.31
+++ ai/aidata.h 23 Jul 2005 23:35:23 -0000
@@ -21,6 +21,8 @@
#include "fc_types.h"
#include "improvement.h"
+#include "advdiplomacy.h"
+
/*
* This file and aidata.c contains global data structures for the AI
* and some of the functions that fill them with useful values at the
@@ -48,6 +50,8 @@
char spam; /* timer to avoid spamming a player with chat */
int distance; /* average distance to that player's cities */
+ int countdown; /* we're on a countdown to war declaration */
+ enum war_reason war_reason; /* why we declare war */
char ally_patience; /* we EXPECT our allies to help us! */
char asked_about_peace; /* don't ask again */
char asked_about_alliance; /* don't nag! */
@@ -69,8 +73,6 @@
struct ai_dip_intel player_intel[MAX_NUM_PLAYERS + MAX_NUM_BARBARIANS];
enum winning_strategy strategy;
int timer; /* pursue our goals with some stubbornness, in turns */
- int countdown; /* countdown to we actually declare war */
- struct player *target; /* Concentrate on this player */
char love_coeff; /* Reduce love with this % each turn */
char love_incr; /* Modify love with this fixed amount */
int req_love_for_peace;
Index: ai/aihand.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aihand.c,v
retrieving revision 1.118
diff -u -r1.118 aihand.c
--- ai/aihand.c 29 Jun 2005 08:21:25 -0000 1.118
+++ ai/aihand.c 23 Jul 2005 23:35:23 -0000
@@ -242,7 +242,7 @@
/* Ok, we now have the desired tax and luxury rates. Do we really want
* science? If not, swap it with tax if it is bigger. */
- if (ai_wants_no_science(pplayer)
+ if ((ai_wants_no_science(pplayer) || ai_on_war_footing(pplayer))
&& pplayer->economic.science > pplayer->economic.tax) {
int science = pplayer->economic.science;
/* Swap science and tax */
Index: ai/ailog.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/ailog.c,v
retrieving revision 1.28
diff -u -r1.28 ailog.c
--- ai/ailog.c 7 May 2005 13:35:25 -0000 1.28
+++ ai/ailog.c 23 Jul 2005 23:35:24 -0000
@@ -76,37 +76,32 @@
/**************************************************************************
Log player messages, they will appear like this
- 2: perrin [ti12 co6 lo5 e] Increased love for a (now 9)
+
where ti is timer, co countdown and lo love for target, who is e.
**************************************************************************/
-void DIPLO_LOG(int level, struct player *pplayer, struct ai_data *ai,
+void DIPLO_LOG(int level, struct player *pplayer, struct player *aplayer,
const char *msg, ...)
{
- char targetbuffer[250];
char buffer[500];
char buffer2[500];
va_list ap;
int minlevel = MIN(LOGLEVEL_PLAYER, level);
+ struct ai_data *ai;
+ struct ai_dip_intel *adip;
if (BV_ISSET(pplayer->debug, PLAYER_DEBUG_DIPLOMACY)) {
minlevel = LOG_NORMAL;
} else if (minlevel > fc_log_level) {
return;
}
+ ai = ai_data_get(pplayer);
+ adip = &ai->diplomacy.player_intel[aplayer->player_no];
- if (ai->diplomacy.target) {
- my_snprintf(targetbuffer, sizeof(targetbuffer), "[ti%d co%d lo%d %s] ",
- ai->diplomacy.timer, ai->diplomacy.countdown,
- pplayer->ai.love[ai->diplomacy.target->player_no],
- ai->diplomacy.target->name);
- }
- my_snprintf(buffer, sizeof(buffer), "%s %s%s%s ", pplayer->name,
- ai->diplomacy.target ? targetbuffer : "",
- ai->diplomacy.spacerace_leader &&
- ai->diplomacy.spacerace_leader->player_no == pplayer->player_no
?
- "(spacelead) " : "",
- ai->diplomacy.alliance_leader->player_no == pplayer->player_no ?
- "(*)" : "");
+ my_snprintf(buffer, sizeof(buffer), "%s->%s(l%d,c%d,d%d%s): ",
+ pplayer->name, aplayer->name,
+ pplayer->ai.love[aplayer->player_no], adip->countdown,
+ adip->distance, adip->is_allied_with_enemy ? "?" :
+ (adip->at_war_with_ally ? "!" : ""));
va_start(ap, msg);
my_vsnprintf(buffer2, sizeof(buffer2), msg, ap);
Index: ai/ailog.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/ailog.h,v
retrieving revision 1.14
diff -u -r1.14 ailog.h
--- ai/ailog.h 7 May 2005 13:35:25 -0000 1.14
+++ ai/ailog.h 23 Jul 2005 23:35:24 -0000
@@ -74,7 +74,7 @@
void TECH_LOG(int level, struct player *pplayer, Tech_type_id id,
const char *msg, ...)
fc__attribute((format (printf, 4, 5)));
-void DIPLO_LOG(int level, struct player *pplayer, struct ai_data *ai,
+void DIPLO_LOG(int level, struct player *pplayer, struct player *aplayer,
const char *msg, ...)
fc__attribute((format (printf, 4, 5)));
void CITY_LOG(int level, struct city *pcity, const char *msg, ...)
Index: ai/aitools.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aitools.c,v
retrieving revision 1.157
diff -u -r1.157 aitools.c
--- ai/aitools.c 23 Jul 2005 18:02:43 -0000 1.157
+++ ai/aitools.c 23 Jul 2005 23:35:25 -0000
@@ -123,7 +123,7 @@
return (pplayer != aplayer
&& (pplayers_at_war(pplayer, aplayer)
- || ai->diplomacy.target == aplayer
+ || adip->countdown >= 0
|| reason != 0
|| adip->is_allied_with_enemy));
}
Index: ai/aiunit.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aiunit.h,v
retrieving revision 1.60
diff -u -r1.60 aiunit.h
--- ai/aiunit.h 22 Jul 2005 16:18:04 -0000 1.60
+++ ai/aiunit.h 23 Jul 2005 23:35:25 -0000
@@ -37,7 +37,7 @@
> unit_type(punit)->transport_capacity)
#define HOSTILE_PLAYER(pplayer, ai, aplayer) \
(pplayers_at_war(pplayer, aplayer) \
- || ai->diplomacy.target == aplayer)
+ || ai->diplomacy.player_intel[aplayer->player_no].countdown >= 0)
#define UNITTYPE_COSTS(ut) \
(ut->pop_cost * 3 + ut->happy_cost \
+ ut->upkeep[O_SHIELD] + ut->upkeep[O_FOOD] + ut->upkeep[O_GOLD])
Index: server/savegame.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/savegame.c,v
retrieving revision 1.268
diff -u -r1.268 savegame.c
--- server/savegame.c 22 Jul 2005 16:18:06 -0000 1.268
+++ server/savegame.c 23 Jul 2005 23:35:51 -0000
@@ -1737,7 +1737,6 @@
struct ai_data *ai;
struct government *gov;
int id;
- int target_no;
struct team *pteam;
struct player_research *research;
@@ -1888,6 +1887,10 @@
= secfile_lookup_int_default(file, 1, "player%d.ai%d.love", plrno, i);
ai->diplomacy.player_intel[i].spam
= secfile_lookup_int_default(file, 0, "player%d.ai%d.spam", plrno, i);
+ ai->diplomacy.player_intel[i].countdown
+ = secfile_lookup_int_default(file, -1, "player%d.ai%d.countdown",
plrno, i);
+ ai->diplomacy.player_intel[i].war_reason
+ = secfile_lookup_int_default(file, 0, "player%d.ai%d.war_reason",
plrno, i);
ai->diplomacy.player_intel[i].ally_patience
= secfile_lookup_int_default(file, 0, "player%d.ai%d.patience",
plrno, i);
ai->diplomacy.player_intel[i].warned_about_space
@@ -1899,10 +1902,6 @@
ai->diplomacy.player_intel[i].asked_about_ceasefire
= secfile_lookup_int_default(file, 0, "player%d.ai%d.ask_ceasefire",
plrno, i);
}
- /* Diplomacy target is saved as player number or -1 if none */
- target_no = secfile_lookup_int_default(file, -1,
- "player%d.ai.target", plrno);
- ai->diplomacy.target = target_no == -1 ? NULL : &game.players[target_no];
/* Backwards-compatibility: the tech goal value is still stored in the
* "ai" section even though it was moved into the research struct. */
@@ -2690,6 +2689,10 @@
"player%d.ai%d.love", plrno, i);
secfile_insert_int(file, ai->diplomacy.player_intel[i].spam,
"player%d.ai%d.spam", plrno, i);
+ secfile_insert_int(file, ai->diplomacy.player_intel[i].countdown,
+ "player%d.ai%d.countdown", plrno, i);
+ secfile_insert_int(file, ai->diplomacy.player_intel[i].war_reason,
+ "player%d.ai%d.war_reason", plrno, i);
secfile_insert_int(file, ai->diplomacy.player_intel[i].ally_patience,
"player%d.ai%d.patience", plrno, i);
secfile_insert_int(file, ai->diplomacy.player_intel[i].warned_about_space,
@@ -2701,10 +2704,6 @@
secfile_insert_int(file,
ai->diplomacy.player_intel[i].asked_about_ceasefire,
"player%d.ai%d.ask_ceasefire", plrno, i);
}
- secfile_insert_int(file,
- ai->diplomacy.target == NULL ?
- -1 : ai->diplomacy.target->player_no,
- "player%d.ai.target", plrno);
save_technology(file, "player%d.ai.tech_goal",
plrno, get_player_research(plr)->tech_goal);
secfile_insert_int(file, plr->ai.skill_level,
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [Freeciv-Dev] (PR#13524) AI Diplomacy revisited,
Per I. Mathisen <=
|
|