[freeciv-ai] AI Diplomacy v10 (PR#2413)
[Top] [All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
To: |
undisclosed-recipients: ; |
Subject: |
[freeciv-ai] AI Diplomacy v10 (PR#2413) |
From: |
"Per I. Mathisen" <per@xxxxxxxxxxx> |
Date: |
Sun, 1 Jun 2003 08:15:54 -0700 |
Reply-to: |
rt@xxxxxxxxxxxxxx |
CHANGES:
- moved some functions into new file common/aicore/aisupport.c
- lots of bugfixes and some cleanup of code
- spacerace code now works correctly
- we now do a countdown towards war, which gives AI time to prepare
military (add danger to cities etc to make it build up army)
- added i18n to strings
That's basically it.
- Per
/**********************************************************************
Freeciv - Copyright (C) 2003 - The Freeciv Project
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
***********************************************************************/
#ifndef FC__AISUPPORT_H
#define FC__AISUPPORT_H
struct player;
struct city;
struct player *aisupport_who_leads_spacerace(void);
int aisupport_distance_to_player(struct player *pplayer, struct player *target);
int aisupport_cityworth(struct city *pcity);
#endif
/**********************************************************************
Freeciv - Copyright (C) 2003 - The Freeciv Project
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
***********************************************************************/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "city.h"
#include "game.h"
#include "map.h"
#include "player.h"
#include "shared.h"
#include "spaceship.h"
#include "support.h"
#include "tech.h"
#include "aisupport.h"
/**********************************************************************
Find who is leading the space race. Returns NULL if nobody is.
***********************************************************************/
struct player *aisupport_who_leads_spacerace(void)
{
struct player *best = NULL;
int best_arrival = FC_INFINITY;
int best_state = SSHIP_NONE;
if (game.spacerace == FALSE) {
return NULL;
}
players_iterate(pplayer) {
struct player_spaceship *ship = &pplayer->spaceship;
int arrival = (int) ship->travel_time + ship->launch_year;
if (!pplayer->is_alive || is_barbarian(pplayer)
|| ship->state == SSHIP_NONE) {
continue;
}
if (ship->state != SSHIP_LAUNCHED
&& ship->state > best_state) {
best_state = ship->state;
best = pplayer;
} else if (ship->state == SSHIP_LAUNCHED
&& arrival < best_arrival) {
best_state = ship->state;
best_arrival = arrival;
best = pplayer;
}
} players_iterate_end;
return best;
}
/**********************************************************************
Calculate average distances to other players. We calculate the
average distance from all of our cities to the closest enemy city
(except we do it in reverse).
***********************************************************************/
int aisupport_distance_to_player(struct player *pplayer, struct player *target)
{
int cities = 0;
int dists = 0;
if (pplayer == target
|| !target->is_alive
|| !pplayer->is_alive
|| city_list_size(&pplayer->cities) == 0
|| city_list_size(&target->cities) == 0) {
return 1;
}
/* For all enemy cities, find the closest distance from
* one of our cities to it. */
city_list_iterate(target->cities, pcity) {
int min_dist = FC_INFINITY;
city_list_iterate(pplayer->cities, p2) {
int dist = real_map_distance(p2->x, p2->y, pcity->x, pcity->y);
if (min_dist > dist) {
min_dist = dist;
}
} city_list_iterate_end;
dists += min_dist;
cities++;
} city_list_iterate_end;
return MAX(dists / cities, 1);
}
/**********************************************************************
Rough calculation of the worth of pcity in gold.
***********************************************************************/
int aisupport_cityworth(struct city *pcity)
{
int worth;
worth = pcity->size * 150; /* reasonable base cost */
unit_list_iterate(pcity->units_supported, punit) {
if (same_pos(punit->x, punit->y, pcity->x, pcity->y)) {
if (can_build_unit_direct(pcity, unit_type(punit)->obsoleted_by)) {
worth += unit_type(punit)->build_cost / 4; /* obsolete */
} else {
worth += unit_type(punit)->build_cost / 2; /* good stuff */
}
}
} unit_list_iterate_end;
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;
}
return worth;
}
/**********************************************************************
Freeciv - Copyright (C) 2003 - 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)
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__ADVDIPLOMACY_H
#define FC__ADVDIPLOMACY_H
struct player;
struct ai_choice;
struct Treaty;
struct Clause;
struct ai_data;
void ai_diplomacy_calculate(struct player *pplayer, struct ai_data *ai);
void ai_diplomacy_actions(struct player *pplayer);
void ai_treaty_evaluate(struct player *pplayer, struct player *aplayer,
struct Treaty *ptreaty);
void ai_treaty_accepted(struct player *pplayer, struct player *aplayer,
struct Treaty *ptreaty);
#endif
/**********************************************************************
Freeciv - Copyright (C) 2003 - 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)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
***********************************************************************/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "aisupport.h"
#include "city.h"
#include "diptreaty.h"
#include "events.h"
#include "fcintl.h"
#include "game.h"
#include "log.h"
#include "mem.h"
#include "packets.h"
#include "player.h"
#include "rand.h"
#include "nation.h"
#include "shared.h"
#include "spaceship.h"
#include "support.h"
#include "tech.h"
#include "citytools.h"
#include "diplhand.h"
#include "plrhand.h"
#include "maphand.h"
#include "settlers.h" /* amortize */
#include "aidata.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_NORMAL
/* one hundred thousand */
#define BIG_NUMBER 100000
/* turn this off when we don't want functions to message players */
static bool diplomacy_verbose = TRUE;
/**********************************************************************
Send a diplomatic message. Use this instead of notify directly
because we may want to highligh/present these messages differently
in the future.
***********************************************************************/
#define notify(pplayer, text, ...) \
if (diplomacy_verbose) { \
notify_player_ex(pplayer, -1, -1, E_DIPLOMACY, text, __VA_ARGS__); \
}
#define TALK(x) "*" #x "(AI)* "
/**********************************************************************
This is your typical human reaction. Convert lack of love into
lust for gold.
***********************************************************************/
static int greed(int missing_love)
{
if (missing_love >= 0) {
return 0;
} else if (missing_love < 0 && missing_love > -5) {
return missing_love * 10;
} else if (missing_love <= -5 && missing_love > -10) {
return missing_love * 25;
} else if (missing_love <= -10 && missing_love > -30) {
return missing_love * 50;
} else if (missing_love <= -30 && missing_love > -50) {
return missing_love * 100;
} else if (missing_love <= -50 && missing_love > -70) {
return missing_love * 250;
} else {
return missing_love * 500;
}
}
/**********************************************************************
How much is a tech worth to player measured in gold
***********************************************************************/
static int ai_goldequiv_tech(struct player *pplayer, Tech_Type_id tech)
{
int worth;
if (get_invention(pplayer, tech) == TECH_KNOWN) {
return 0;
}
worth = total_bulbs_required_for_goal(pplayer, tech) * 3;
worth += pplayer->ai.tech_want[tech] / 3;
if (get_invention(pplayer, tech) == TECH_REACHABLE) {
worth /= 2;
}
return worth;
}
/**********************************************************************
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,
bool verbose)
{
int worth = 0; /* worth for pplayer of what aplayer gives */
bool give = (pplayer == pclause->from);
int receiver, giver;
struct ai_dip_intel *adip = &ai->diplomacy.player_intel[aplayer->player_no];
diplomacy_verbose = verbose;
giver = pclause->from->player_no;
if (give) {
receiver = aplayer->player_no;
} else {
receiver = pplayer->player_no;
}
switch (pclause->type) {
case CLAUSE_ADVANCE:
if (give) {
worth -= ai_goldequiv_tech(aplayer, pclause->value);
} else if (get_invention(pplayer, pclause->value) != TECH_KNOWN) {
worth += ai_goldequiv_tech(pplayer, pclause->value);
}
/* Share and expect being shared brotherly between allies */
if (pplayers_allied(pplayer, aplayer)) {
worth = 0;
break;
}
/* Calculate in tech leak to our opponents, guess 50% chance */
players_iterate(eplayer) {
if (eplayer == aplayer
|| eplayer == pplayer
|| !eplayer->is_alive
|| get_invention(eplayer, pclause->value) == TECH_KNOWN) {
continue;
}
if (give && pplayers_allied(aplayer, eplayer)) {
if (is_player_dangerous(pplayer, eplayer)) {
/* Don't risk it falling into enemy hands */
worth = -BIG_NUMBER;
break;
}
worth -= ai_goldequiv_tech(eplayer, pclause->value) / 2;
} else if (!give && pplayers_allied(pplayer, eplayer)) {
/* We can enrichen our side with this tech */
worth += ai_goldequiv_tech(eplayer, pclause->value) / 4;
}
} players_iterate_end;
break;
case CLAUSE_ALLIANCE:
case CLAUSE_PEACE:
case CLAUSE_CEASEFIRE:
{
/* 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, _(TALK(%s) "%s leads our alliance. 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
* ceasefire. */
if (adip->is_allied_with_enemy
&& pclause->type != CLAUSE_CEASEFIRE) {
notify(aplayer, _(TALK(%s) "First break alliance with %s, %s"),
pplayer->name, adip->is_allied_with_enemy->name,
aplayer->name);
worth = -BIG_NUMBER;
break;
}
}
/* Check if we can trust this guy. If we want to blast off to space,
* we don't care, though. */
if (ai->diplomacy.acceptable_reputation > aplayer->reputation
&& ai->diplomacy.strategy != WIN_SPACE) {
notify(aplayer, _(TALK(%s) "Begone scroundel, we all know that"
" you cannot be trusted!"), pplayer->name);
worth = -BIG_NUMBER;
break;
}
/* Reduce treaty level?? */
{
enum diplstate_type ds = pplayer_get_diplstate(pplayer, aplayer)->type;
if ((pclause->type == CLAUSE_PEACE && ds > DS_PEACE)
|| (pclause->type == CLAUSE_CEASEFIRE && ds > DS_CEASEFIRE)) {
notify(aplayer, _(TALK(%s) "I will not let you go that easy, %s."),
pplayer->name, aplayer->name);
worth = -BIG_NUMBER;
break;
}
}
/* Let's all hold hands in one happy family! */
if (adip->is_allied_with_ally) {
worth = 0;
break;
}
/* If this lucky fella got a ceasefire with da boss, then
* let him live. */
if (pplayer_get_diplstate(aplayer, ai->diplomacy.alliance_leader)->type
== DS_CEASEFIRE && pclause->type == CLAUSE_CEASEFIRE) {
notify(aplayer, _(TALK(%s) "%s recommended that I give you a ceasefire."
" This is your lucky day."), pplayer->name,
ai->diplomacy.alliance_leader->name);
if (ai->diplomacy.target == aplayer) {
/* Damn, we lost our target, too! Stupid boss! */
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 */
if (pclause->type == CLAUSE_PEACE) {
if (!pplayers_non_attack(pplayer, aplayer)) {
notify(aplayer, _(TALK(%s) "Let us first cease hostilies, %s"),
pplayer->name, aplayer->name);
worth = -BIG_NUMBER;
} else {
worth = greed(adip->love - (ai->diplomacy.love_incr
+ ai->diplomacy.love_coeff));
}
} else if (pclause->type == CLAUSE_ALLIANCE) {
if (!pplayers_in_peace(pplayer, aplayer)) {
notify(aplayer, _(TALK(%s) "Let us first make peace, %s"),
pplayer->name, aplayer->name);
worth = -BIG_NUMBER;
} else {
worth = greed(adip->love - (ai->diplomacy.love_incr
* ai->diplomacy.love_coeff));
}
/* Ceasefire. Always grant ceasefire when he isn't on our hitlist. */
} else {
worth = 0;
}
break;
case CLAUSE_GOLD:
if (give) {
worth -= pclause->value;
} else {
worth += pclause->value;
}
break;
case CLAUSE_SEAMAP:
if (!give || pplayers_allied(pplayer, aplayer)) {
/* Useless to us - we're omniscient! And allies get it for free! */
worth = 0;
} else {
/* 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. */
worth -= 25 * city_list_size(&aplayer->cities);
}
break;
case CLAUSE_MAP:
if (!give || pplayers_allied(pplayer, aplayer)) {
/* Useless to us - we're omniscient! And allies get it for free! */
worth = 0;
} else {
/* Very silly algorithm 2: Land map more worth the more cities
we have, since we expose all of these to the enemy. */
worth -= 75 * city_list_size(&pplayer->cities);
/* Inflate numbers if not peace */
if (!pplayers_in_peace(pplayer, aplayer)) {
worth *= 3;
}
}
break;
case CLAUSE_CITY: {
struct city *offer = city_list_find_id(&(pclause->from)->cities,
pclause->value);
if (!offer || offer->owner != giver) {
/* City destroyed or taken during negotiations */
notify(aplayer, _(TALK(%s) "You don't have the offered city!"),
pplayer->name);
worth = 0;
} else if (give) {
/* AI must be crazy to trade away its cities */
worth -= aisupport_cityworth(offer);
if (city_got_building(offer, B_PALACE)) {
worth = -BIG_NUMBER; /* Never! Ever! */
} else {
worth *= 15;
}
if (aplayer->player_no == offer->original) {
/* Let them buy back their own city cheaper. */
worth /= 2;
}
} else {
worth = aisupport_cityworth(offer);
}
break;
}
case CLAUSE_VISION:
if (give) {
if (pplayers_allied(pplayer, aplayer)) {
worth = 0;
} else {
/* so out of the question */
worth = -BIG_NUMBER;
}
} else {
worth = 0; /* We are omniscient, so... */
}
break;
default:
die("Unknown clause type in advdiplomacy.c");
break;
} /* end of switch */
diplomacy_verbose = TRUE;
return worth;
}
/**********************************************************************
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 total_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) {
total_balance += ai_goldequiv_clause(pplayer, aplayer, pclause, ai, TRUE);
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 (total_balance >= 0) {
handle_diplomacy_accept_treaty(pplayer, &packet);
}
}
/**********************************************************************
Comments to player from AI on clauses being agreed on. Does not
alter any state.
***********************************************************************/
static void ai_treaty_react(struct player *pplayer,
struct player *aplayer,
struct Clause *pclause)
{
struct ai_data *ai = ai_data_get(pplayer);
struct ai_dip_intel *adip = &ai->diplomacy.player_intel[aplayer->player_no];
switch (pclause->type) {
case CLAUSE_ALLIANCE:
if (adip->is_allied_with_ally) {
notify(aplayer, _(TALK(%s) "Welcome into our alliance %s!"),
pplayer->name, aplayer->name);
} else {
notify(aplayer, _(TALK(%s) "Yes, may we forever stand united, %s"),
pplayer->name, aplayer->name);
}
break;
case CLAUSE_PEACE:
notify(aplayer, _(TALK(%s) "Yes, peace in our time!"),
pplayer->name);
break;
case CLAUSE_CEASEFIRE:
notify(aplayer, _(TALK(%s) "Agreed. No more hostilities, %s"),
pplayer->name, aplayer->name);
break;
default:
break;
}
}
/**********************************************************************
pplayer is AI player, aplayer is the other player involved, ptreaty
is the treaty accepted.
***********************************************************************/
void ai_treaty_accepted(struct player *pplayer, struct player *aplayer,
struct Treaty *ptreaty)
{
int total_balance = 0;
bool gift = TRUE;
struct ai_data *ai = ai_data_get(pplayer);
struct ai_dip_intel *adip = &ai->diplomacy.player_intel[aplayer->player_no];
/* Evaluate clauses */
clause_list_iterate(ptreaty->clauses, pclause) {
int balance = ai_goldequiv_clause(pplayer, aplayer, pclause, ai, TRUE);
total_balance += balance;
gift = (gift && (balance >= 0));
ai_treaty_react(pplayer, aplayer, pclause);
} clause_list_iterate_end;
/* Rather arbitrary algorithm */
if (total_balance > 0 && gift) {
int i = total_balance / (city_list_size(&pplayer->cities) * 50) + 1;
adip->love += i;
freelog(LOG_DIPL2, "%s's gift to %s increased love by %d",
aplayer->name, pplayer->name, i);
}
}
/**********************************************************************
Calculate our desire to go to war against aplayer.
***********************************************************************/
static int ai_war_desire(struct player *pplayer, struct player *aplayer,
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);
/* 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 (ai->diplomacy.spacerace_leader == aplayer) {
ai->diplomacy.strategy = WIN_CAPITAL;
return BIG_NUMBER; /* do NOT amortize this number! */
}
/* Modify by which treaties we would have to break, and what
* excuses we have to do so. FIXME: We only consider immediate
* allies, but we might trigger a wider chain reaction. */
players_iterate(eplayer) {
bool cancel_excuse =
pplayer->diplstates[eplayer->player_no].has_reason_to_cancel;
enum diplstate_type ds = pplayer_get_diplstate(pplayer, eplayer)->type;
if (eplayer == pplayer || !eplayer->is_alive) {
continue;
}
/* Remember: pplayers_allied() returns true when aplayer == eplayer */
if (!cancel_excuse && pplayers_allied(aplayer, eplayer)) {
if (ds == DS_CEASEFIRE) {
kill_desire -= kill_desire / 10; /* 10% off */
} else if (ds == DS_NEUTRAL) {
kill_desire -= kill_desire / 7; /* 15% off */
} else if (ds == DS_PEACE) {
kill_desire -= kill_desire / 5; /* 20% off */
} else if (ds == DS_ALLIANCE) {
kill_desire -= kill_desire / 3; /* 33% off here, more later */
}
}
} players_iterate_end;
/* Modify by hatred */
if (adip->love < 0) {
kill_desire += kill_desire / 100 * adip->love;
}
/* Amortize by distance */
return amortize(kill_desire, adip->distance);
}
/**********************************************************************
Suggest a treaty from pplayer to aplayer
***********************************************************************/
static void ai_diplomacy_suggest(struct player *pplayer,
struct player *aplayer,
enum clause_type what,
int value)
{
struct packet_diplomacy_info packet;
if (!could_meet_with_player(pplayer, aplayer)) {
freelog(LOG_DIPL2, "%s tries to do diplomacy to %s without contact",
pplayer->name, aplayer->name);
return;
}
packet.plrno_from = pplayer->player_no;
packet.plrno0 = pplayer->player_no;
packet.plrno1 = aplayer->player_no;
packet.clause_type = what;
packet.value = value;
handle_diplomacy_init(pplayer, &packet);
handle_diplomacy_create_clause(pplayer, &packet);
}
/**********************************************************************
Calculate our diplomatic predispositions here. Don't do anything.
Only ever called for AI players and never for barbarians.
***********************************************************************/
void ai_diplomacy_calculate(struct player *pplayer, struct ai_data *ai)
{
int war_desire[MAX_NUM_PLAYERS];
int best_desire = 0;
struct player *target = NULL;
memset(war_desire, 0, sizeof(war_desire));
assert(pplayer->ai.control);
if (!pplayer->is_alive) {
return; /* duh */
}
/* 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];
if (pplayer == aplayer || !aplayer->is_alive) {
continue;
}
adip->love -= pplayer->diplstates[a].has_reason_to_cancel;
if (pplayers_non_attack(pplayer, aplayer)
&& !pplayer->diplstates[a].has_reason_to_cancel
&& !adip->is_allied_with_enemy
&& !adip->at_war_with_ally
&& adip->ally_patience >= 0) {
adip->love += ai->diplomacy.love_incr;
freelog(LOG_DEBUG, "(%s ai diplo) Increased love for %s (now %d)",
pplayer->name, aplayer->name, adip->love);
} else if (pplayer->diplstates[aplayer->player_no].type == DS_WAR) {
adip->love -= ai->diplomacy.love_incr;
freelog(LOG_DEBUG, "(%s ai diplo) Reduced love for %s (now %d) ",
pplayer->name, aplayer->name, adip->love);
} else if (pplayer->diplstates[a].has_reason_to_cancel) {
/* Provoked in time of peace */
if (adip->love > 0) {
freelog(LOG_DIPL2, "(%s ai diplo) Provoked by %s! Love halved (was %d)",
pplayer->name, aplayer->name, adip->love);
adip->love /= 2;
}
adip->love -= ai->diplomacy.love_incr;
}
/* Massage our numbers to keep love and its opposite on the ground.
* Gravitate towards zero. */
adip->love -= (adip->love * ai->diplomacy.love_coeff / 100);
if (adip->love > 0) {
adip->love--;
} else if (adip->love < 0) {
adip->love++;
}
} players_iterate_end;
/* Stop war against a dead player */
if (ai->diplomacy.target && !ai->diplomacy.target->is_alive) {
freelog(LOG_DIPL2, "(%s ai diplo) Target player %s is dead! Victory!",
pplayer->name, ai->diplomacy.target->name);
ai->diplomacy.timer = 0;
ai->diplomacy.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);
ai->diplomacy.strategy = WIN_SPACE; /* Yes! */
} else {
if (ai->diplomacy.strategy == WIN_SPACE) {
ai->diplomacy.strategy = WIN_OPEN;
}
}
/* 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 =
aisupport_distance_to_player(pplayer, aplayer);
} players_iterate_end;
/* 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 and those we're
* allied to 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
|| (pplayer->team != TEAM_NONE && pplayer->team == aplayer->team)
|| ds == DS_ALLIANCE
|| (pplayer != 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);
/* 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_non_attack(pplayer, aplayer)) {
war_desire[aplayer->player_no] /= 2;
}
freelog(LOG_DEBUG, "(%s ai diplo) Against %s we have war desire "
"%d ", pplayer->name, aplayer->name,
war_desire[aplayer->player_no]);
/* Find best target */
if (war_desire[aplayer->player_no] > best_desire) {
target = aplayer;
best_desire = war_desire[aplayer->player_no];
}
} players_iterate_end;
if (!target) {
freelog(LOG_DEBUG, "(%s ai diplo) Found no target.", pplayer->name);
ai->diplomacy.target = NULL;
return;
}
/* Switch to target */
if (target != ai->diplomacy.target) {
freelog(LOG_DIPL, "(%s ai diplo) Setting target to %s",
pplayer->name, target->name);
ai->diplomacy.target = target;
ai->diplomacy.timer = myrand(6) + 6; /* Don't reevaluate too often. */
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 */
}
players_iterate(aplayer) {
ai->diplomacy.player_intel[aplayer->player_no].ally_patience = 0;
} players_iterate_end;
}
}
/**********************************************************************
Offer techs to other player and ask for techs we need.
***********************************************************************/
static void ai_share_techs(struct player *pplayer,
struct player *aplayer)
{
int index;
for (index = A_FIRST; index < game.num_tech_types; index++) {
if ((get_invention(pplayer, index) != TECH_KNOWN)
&& (get_invention(aplayer, index) == TECH_KNOWN)) {
ai_diplomacy_suggest(aplayer, pplayer, CLAUSE_ADVANCE, index);
} else if ((get_invention(pplayer, index) == TECH_KNOWN)
&& (get_invention(aplayer, index) != TECH_KNOWN)) {
ai_diplomacy_suggest(pplayer, aplayer, CLAUSE_ADVANCE, index);
}
}
if (!gives_shared_vision(pplayer, aplayer)) {
ai_diplomacy_suggest(pplayer, aplayer, CLAUSE_VISION, 0);
}
if (!gives_shared_vision(aplayer, pplayer)) {
ai_diplomacy_suggest(aplayer, pplayer, CLAUSE_VISION, 0);
}
}
/**********************************************************************
Go to war.
***********************************************************************/
static void ai_go_to_war(struct player *pplayer, struct ai_data *ai,
struct player *target)
{
struct ai_dip_intel *adip = &ai->diplomacy.player_intel[target->player_no];
struct packet_generic_values packet;
packet.id = target->player_no;
packet.value1 = CLAUSE_LAST; /* will take us straight to war */
if (gives_shared_vision(pplayer, target)) {
remove_shared_vision(pplayer, target);
}
handle_player_cancel_pact(pplayer, &packet);
/* Continue war at least in this arbitrary number of turns to show
* some spine */
ai->diplomacy.timer = myrand(4) + 3;
if (adip->love < 0) {
ai->diplomacy.timer += adip->love / 10;
} else {
adip->love = -1; /* We DON'T love our enemies! AIs are heatens! */
}
}
/**********************************************************************
Do diplomatic actions. Must be called only after calculate function
above has been run for _all_ AI players.
Only ever called for AI players and never for barbarians.
***********************************************************************/
void ai_diplomacy_actions(struct player *pplayer)
{
struct ai_data *ai = ai_data_get(pplayer);
struct player *target = ai->diplomacy.target;
assert(pplayer->ai.control);
if (!pplayer->is_alive) {
return;
}
/*** If we are greviously insulted, go to war immediately. ***/
players_iterate(aplayer) {
if (ai->diplomacy.acceptable_reputation > aplayer->reputation
&& ai->diplomacy.player_intel[aplayer->player_no].love < 0
&& pplayer->diplstates[aplayer->player_no].has_reason_to_cancel >= 2) {
freelog(LOG_DIPL2, "(%s ai diplo) Declaring war on %s in revenge",
pplayer->name, target->name);
notify(target, _(TALK(%s) "I will NOT accept such behaviour! This "
"means WAR!"), pplayer->name);
ai_go_to_war(pplayer, ai, aplayer);
}
} players_iterate_end;
/*** Stop other players from winning by space race ***/
if (ai->diplomacy.strategy != WIN_SPACE) {
players_iterate(aplayer) {
struct ai_dip_intel *adip =
&ai->diplomacy.player_intel[aplayer->player_no];
struct player_spaceship *ship = &aplayer->spaceship;
if (!aplayer->is_alive || aplayer == pplayer
|| (pplayer->team != TEAM_NONE && aplayer->team == pplayer->team)
|| ship->state == SSHIP_NONE) {
continue;
}
/* A spaceship victory is always one single player's or team's victory */
if (aplayer->spaceship.state == SSHIP_LAUNCHED
&& ai->diplomacy.spacerace_leader == aplayer
&& pplayers_allied(pplayer, aplayer)) {
struct packet_generic_values packet;
packet.id = aplayer->player_no;
packet.value1 = CLAUSE_ALLIANCE;
notify(aplayer, _(TALK(%s) "Your attempt to conquer space for "
"yourself alone betray your true intentions, and I "
"will have no more of our alliance!"), pplayer->name);
handle_player_cancel_pact(pplayer, &packet);
if (gives_shared_vision(pplayer, aplayer)) {
remove_shared_vision(pplayer, aplayer);
}
adip->love = -(BIG_NUMBER); /* Never forgive this */
} else if (ship->state == SSHIP_STARTED && !adip->warned_about_space) {
adip->warned_about_space = 10 + myrand(6);
notify(aplayer, _(TALK(%s) "Your attempt to unilaterally "
"dominate outer space is highly offensive."), pplayer->name);
notify(aplayer, _(TALK(%s) "If you do not stop constructing your "
"spaceship, I may be forced to take action!"), pplayer->name);
}
if (aplayer->spaceship.state == SSHIP_LAUNCHED
&& aplayer == ai->diplomacy.spacerace_leader) {
/* This means war!!! */
ai->diplomacy.timer = 0; /* Force reevaluation next turn */
}
} players_iterate_end;
}
/*** Declare war - against target ***/
if (target && !pplayers_at_war(pplayer, target)
&& ai->diplomacy.countdown-- <= 0) {
assert(!pplayers_allied(target, pplayer));
if (pplayer->diplstates[target->player_no].has_reason_to_cancel > 0) {
/* We have good reason */
notify(target, _(TALK(%s) "Your despicable actions will not go "
"unpunished!"), pplayer->name);
} if (ai->diplomacy.player_intel[target->player_no].love < 0) {
/* We have a reason of sorts from way back. */
notify(target, _(TALK(%s) "Finally I get around to you! Did "
"you really think you could get away with your crimes?"),
pplayer->name);
} else {
/* We have no legimitate reason... So what? */
notify(target, _(TALK(%s) "Peace in ... some other time"),
pplayer->name);
}
ai_go_to_war(pplayer, ai, target);
}
/*** Declare war - against enemies of allies ***/
players_iterate(aplayer) {
struct ai_dip_intel *adip = &ai->diplomacy.player_intel[aplayer->player_no];
if (aplayer->is_alive
&& adip->at_war_with_ally
&& !adip->is_allied_with_ally
&& !pplayers_at_war(pplayer, aplayer)) {
notify(aplayer, _(TALK(%s) "Your aggression against my allies was your "
"your last mistake!"), pplayer->name);
ai_go_to_war(pplayer, ai, aplayer);
}
} players_iterate_end;
/*** Opportunism, Inc. Try to make peace with everyone else ***/
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];
struct Clause clause;
struct packet_generic_values packet;
/* Meaningless values, but rather not have them unset. */
clause.from = pplayer;
clause.value = 0;
/* No peace to enemies of our allies... or pointless peace. */
if (is_barbarian(aplayer)
|| aplayer == pplayer
|| aplayer == target /* no mercy */
|| !aplayer->is_alive
|| !could_meet_with_player(pplayer, aplayer)
|| adip->at_war_with_ally) {
continue;
}
/* Spam control */
adip->spam = MAX(adip->spam - 1, 0);
adip->asked_about_peace = MAX(adip->asked_about_peace - 1, 0);
adip->asked_about_alliance = MAX(adip->asked_about_alliance - 1, 0);
adip->asked_about_ceasefire = MAX(adip->asked_about_ceasefire - 1, 0);
adip->warned_about_space = MAX(adip->warned_about_space - 1, 0);
if (adip->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. */
if (!aplayer->ai.control) {
if (!pplayers_allied(pplayer, aplayer)) {
adip->spam = myrand(4) + 3; /* Bugger allies often. */
} else {
adip->spam = myrand(8) + 6; /* Others are less important. */
}
}
switch (ds) {
case DS_ALLIANCE:
if ((pplayer->team != TEAM_NONE && aplayer->team == pplayer->team)
|| (target && pplayers_at_war(aplayer, target))) {
/* Share techs only with team mates and _reliable_ allies */
ai_share_techs(pplayer, aplayer);
adip->ally_patience = 0;
break;
} else if (!target) {
adip->ally_patience = 0;
break;
}
switch (adip->ally_patience--) {
case 0:
notify(aplayer, _(TALK(%s) "Greetings our most trustworthy "
"ally, we call upon you to destroy our enemy, %s"),
pplayer->name, target->name);
break;
case -1:
notify(aplayer, _(TALK(%s) "Greetings ally, I see you have not yet "
"made war with our enemy, %s. Why do I need to remind "
"you of your promises?"), pplayer->name, target->name);
break;
case -2:
notify(aplayer, _(TALK(%s) "Dishonoured one, we made a pact of "
"alliance, and yet you remain at peace with our mortal "
"enemy, %s! This is unacceptable, our alliance is no "
"more!"), pplayer->name, target->name);
freelog(LOG_DIPL2, "(%s ai diplo) breaking useless alliance with %s",
pplayer->name, aplayer->name);
packet.id = aplayer->player_no;
packet.value1 = CLAUSE_ALLIANCE;
handle_player_cancel_pact(pplayer, &packet); /* to peace */
if (adip->love > 0) {
adip->love /= 4;
}
if (gives_shared_vision(pplayer, aplayer)) {
remove_shared_vision(pplayer, aplayer);
}
break;
}
break;
case DS_PEACE:
clause.type = CLAUSE_ALLIANCE;
if (ai_goldequiv_clause(pplayer, aplayer, &clause, ai, FALSE) < 0
|| (adip->asked_about_alliance > 0 && !aplayer->ai.control)
|| !target) {
/* Note that we don't ever ask for alliance unless we have a target */
break;
}
ai_diplomacy_suggest(pplayer, aplayer, CLAUSE_ALLIANCE, 0);
adip->asked_about_alliance = !aplayer->ai.control ? 13 : 0;
notify(aplayer, _(TALK(%s) "Greetings friend, may we suggest "
"a joint campaign against %s?"), pplayer->name, target->name);
break;
case DS_CEASEFIRE:
case DS_NEUTRAL:
clause.type = CLAUSE_PEACE;
if (ai_goldequiv_clause(pplayer, aplayer, &clause, ai, FALSE) < 0
|| (adip->asked_about_peace > 0 && !aplayer->ai.control)
|| !target) {
/* Note that we don't ever ask for peace unless we have a target */
break; /* never */
}
ai_diplomacy_suggest(pplayer, aplayer, CLAUSE_PEACE, 0);
adip->asked_about_peace = !aplayer->ai.control ? 12 : 0;
notify(aplayer, _(TALK(%s) "Greetings friend, may we suggest "
"a joint campaign against %s?"), pplayer->name, target->name);
break;
case DS_NO_CONTACT: /* but we do have embassy! weird. */
case DS_WAR:
clause.type = CLAUSE_CEASEFIRE;
if (ai_goldequiv_clause(pplayer, aplayer, &clause, ai, FALSE) < 0
|| (adip->asked_about_ceasefire > 0 && !aplayer->ai.control)
|| !target) {
break; /* Fight until the end! */
}
ai_diplomacy_suggest(pplayer, aplayer, CLAUSE_CEASEFIRE, 0);
adip->asked_about_ceasefire = !aplayer->ai.control ? 9 : 0;
notify(aplayer, _(TALK(%s) "%s is threatening us both, may we "
" suggest a cessation of hostilities?"), pplayer->name,
target->name);
break;
default:
die("Unknown pact type");
break;
}
} players_iterate_end;
}
? ai/advdiplomacy.c
? ai/advdiplomacy.h
? common/aicore/aisupport.c
? common/aicore/aisupport.h
Index: ai/Makefile.am
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/Makefile.am,v
retrieving revision 1.13
diff -u -r1.13 Makefile.am
--- ai/Makefile.am 2003/03/11 17:59:26 1.13
+++ ai/Makefile.am 2003/06/01 14:52:18
@@ -21,6 +21,8 @@
advmilitary.h \
advscience.c \
advscience.h \
+ advdiplomacy.c \
+ advdiplomacy.h \
advspace.c \
advspace.h \
advtrade.c \
Index: ai/advmilitary.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/advmilitary.c,v
retrieving revision 1.145
diff -u -r1.145 advmilitary.c
--- ai/advmilitary.c 2003/05/10 18:11:24 1.145
+++ ai/advmilitary.c 2003/06/01 14:52:19
@@ -31,6 +31,7 @@
#include "aiair.h"
#include "aicity.h"
+#include "aidata.h"
#include "aidiplomat.h"
#include "aihand.h"
#include "ailog.h"
@@ -909,6 +910,7 @@
static void kill_something_with(struct player *pplayer, struct city *pcity,
struct unit *myunit, struct ai_choice *choice)
{
+ struct ai_data *ai = ai_data_get(pplayer);
/* Our attack rating (with reinforcements) */
int attack;
/* Benefit from fighting the target */
@@ -995,7 +997,7 @@
move_rate *= 3;
}
- if (!pplayers_at_war(pplayer, city_owner(acity))) {
+ if (!HOSTILE_PLAYER(pplayer, ai, city_owner(acity))) {
/* Not a valid target */
return;
}
Index: ai/aidata.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aidata.c,v
retrieving revision 1.16
diff -u -r1.16 aidata.c
--- ai/aidata.c 2003/05/29 13:44:38 1.16
+++ ai/aidata.c 2003/06/01 14:52:19
@@ -18,18 +18,22 @@
#include <stdio.h>
#include <string.h>
+#include "aisupport.h"
#include "city.h"
#include "game.h"
#include "government.h"
#include "map.h"
#include "mem.h"
+#include "rand.h"
#include "unit.h"
#include "citytools.h"
+#include "diplhand.h"
#include "maphand.h"
#include "settlers.h"
#include "unittools.h"
+#include "advdiplomacy.h"
#include "advmilitary.h"
#include "aicity.h"
#include "aihand.h"
@@ -60,6 +64,8 @@
bool can_build_antiair = can_player_build_improvement(pplayer, B_SAM);
bool can_build_antinuke = can_player_build_improvement(pplayer, B_SDI);
bool can_build_antimissile = can_player_build_improvement(pplayer, B_SDI);
+ int ally_strength = -1;
+ struct player *ally_strongest = NULL;
/*** Threats ***/
@@ -207,6 +213,10 @@
/*** Diplomacy ***/
+ if (pplayer->ai.control && !is_barbarian(pplayer)) {
+ ai_diplomacy_calculate(pplayer, ai);
+ }
+
/* Question: What can we accept as the reputation of a player before
* we start taking action to prevent us from being suckered?
* Answer: Very little. */
@@ -220,9 +230,17 @@
for (i = 0; i < MAX_NUM_PLAYERS; i++) {
struct player *aplayer = get_player(i);
- ai->diplomacy.player_intel[i].is_allied_with_enemy = FALSE;
- ai->diplomacy.player_intel[i].at_war_with_ally = FALSE;
- ai->diplomacy.player_intel[i].is_allied_with_ally = FALSE;
+ ai->diplomacy.player_intel[i].is_allied_with_enemy = NULL;
+ ai->diplomacy.player_intel[i].at_war_with_ally = NULL;
+ ai->diplomacy.player_intel[i].is_allied_with_ally = NULL;
+
+ /* Determine who is the leader of our alliance. That is,
+ * whoever has the more cities. */
+ if (pplayers_allied(pplayer, aplayer)
+ && city_list_size(&aplayer->cities) > ally_strength) {
+ ally_strength = city_list_size(&aplayer->cities);
+ ally_strongest = aplayer;
+ }
players_iterate(check_pl) {
if (check_pl == pplayer || check_pl == aplayer
@@ -231,18 +249,22 @@
}
if (pplayers_allied(aplayer, check_pl)
&& pplayers_at_war(pplayer, check_pl)) {
- ai->diplomacy.player_intel[i].is_allied_with_enemy = TRUE;
+ ai->diplomacy.player_intel[i].is_allied_with_enemy = check_pl;
}
if (pplayers_allied(pplayer, check_pl)
&& pplayers_at_war(aplayer, check_pl)) {
- ai->diplomacy.player_intel[i].at_war_with_ally = TRUE;
+ ai->diplomacy.player_intel[i].at_war_with_ally = check_pl;
}
if (pplayers_allied(aplayer, check_pl)
&& pplayers_allied(pplayer, check_pl)) {
- ai->diplomacy.player_intel[i].is_allied_with_ally = TRUE;
+ ai->diplomacy.player_intel[i].is_allied_with_ally = check_pl;
}
} players_iterate_end;
}
+ if (ally_strongest != ai->diplomacy.alliance_leader) {
+ ai->diplomacy.alliance_leader = ally_strongest;
+ }
+ ai->diplomacy.spacerace_leader = aisupport_who_leads_spacerace();
/*** Priorities ***/
@@ -278,29 +300,49 @@
}
/**************************************************************************
- Return a pointer to our data
+ Initialize with sane values.
**************************************************************************/
-struct ai_data *ai_data_get(struct player *pplayer)
+void ai_data_init(struct player *pplayer)
{
struct ai_data *ai = &aidata[pplayer->player_no];
+ int i;
- if (ai->num_continents != map.num_continents) {
- /* we discovered more continents, recalculate! */
- ai_data_turn_done(pplayer);
- ai_data_turn_init(pplayer);
+ ai->govt_reeval = 0;
+ ai->government_want = fc_calloc(game.government_count + 1, sizeof(int));
+
+ ai->diplomacy.target = NULL;
+ ai->diplomacy.strategy = WIN_OPEN;
+ ai->diplomacy.timer = 0;
+ ai->diplomacy.countdown = 0;
+ ai->diplomacy.love_coeff = 5; /* 5% */
+ ai->diplomacy.love_incr = 4;
+ ai->diplomacy.alliance_leader = pplayer;
+
+ for (i = 0; i < MAX_NUM_PLAYERS; i++) {
+ ai->diplomacy.player_intel[i].spam = i; /* pseudorandom */
+ ai->diplomacy.player_intel[i].distance = 1;
+ ai->diplomacy.player_intel[i].ally_patience = 0;
+ ai->diplomacy.player_intel[i].love = 1;
+ ai->diplomacy.player_intel[i].asked_about_peace = 0;
+ ai->diplomacy.player_intel[i].asked_about_alliance = 0;
+ ai->diplomacy.player_intel[i].asked_about_ceasefire = 0;
+ ai->diplomacy.player_intel[i].warned_about_space = 0;
}
- return ai;
}
/**************************************************************************
- Initialize with sane values.
+ Return a pointer to our data
**************************************************************************/
-void ai_data_init(struct player *pplayer)
+struct ai_data *ai_data_get(struct player *pplayer)
{
struct ai_data *ai = &aidata[pplayer->player_no];
- ai->govt_reeval = 0;
- ai->government_want = fc_calloc(game.government_count + 1, sizeof(int));
+ if (ai->num_continents != map.num_continents) {
+ /* we discovered more continents, recalculate! */
+ ai_data_turn_done(pplayer);
+ ai_data_turn_init(pplayer);
+ }
+ return ai;
}
/**************************************************************************
Index: ai/aidata.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aidata.h,v
retrieving revision 1.9
diff -u -r1.9 aidata.h
--- ai/aidata.h 2003/05/15 16:02:00 1.9
+++ ai/aidata.h 2003/06/01 14:52:19
@@ -27,10 +27,27 @@
* start of every turn.
*/
+enum winning_strategy {
+ WIN_OPEN, /* still undetermined */
+ WIN_WAR, /* we have no other choice than to crush all opposition */
+ WIN_SPACE, /* we will race for space, peace very important */
+ WIN_CAPITAL /* we cannot win unless we take war_target's capital */
+};
+
struct ai_dip_intel {
- bool is_allied_with_enemy;
- bool at_war_with_ally;
- bool is_allied_with_ally;
+ /* Remember one example of each for text spam purposes. */
+ struct player *is_allied_with_enemy;
+ struct player *at_war_with_ally;
+ struct player *is_allied_with_ally;
+
+ char spam; /* timer to avoid spamming a player with chat */
+ int distance; /* average distance to that player's cities */
+ char ally_patience; /* we EXPECT our allies to help us! */
+ int love; /* basic player <-> player relation */
+ char asked_about_peace; /* don't ask again */
+ char asked_about_alliance; /* don't nag! */
+ char asked_about_ceasefire; /* don't ... you get the point */
+ char warned_about_space;
};
BV_DEFINE(bv_id, MAX_NUM_ID);
@@ -39,6 +56,14 @@
struct {
int acceptable_reputation;
struct ai_dip_intel player_intel[MAX_NUM_PLAYERS];
+ 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 */
+ struct player *alliance_leader; /* Who is leading our alliance */
+ struct player *spacerace_leader; /* who is leading the space pack */
} diplomacy;
/* Long-term threats, not to be confused with short-term danger */
@@ -95,6 +120,7 @@
} goal;
};
+void ai_data_init(struct player *pplayer);
void ai_data_turn_init(struct player *pplayer);
void ai_data_turn_done(struct player *pplayer);
Index: ai/aidiplomat.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aidiplomat.c,v
retrieving revision 1.21
diff -u -r1.21 aidiplomat.c
--- ai/aidiplomat.c 2003/05/06 22:08:25 1.21
+++ ai/aidiplomat.c 2003/06/01 14:52:19
@@ -167,7 +167,7 @@
return;
}
incite_cost = city_incite_cost(pplayer, acity);
- if (pplayers_at_war(pplayer, city_owner(acity))
+ if (HOSTILE_PLAYER(pplayer, ai, city_owner(acity))
&& !city_got_building(acity, B_PALACE)
&& !government_has_flag(get_gov_pplayer(city_owner(acity)),
G_UNBRIBABLE)
@@ -436,6 +436,7 @@
{
struct packet_diplomat_action packet;
int gold_avail = pplayer->economic.gold - pplayer->ai.est_upkeep;
+ struct ai_data *ai = ai_data_get(pplayer);
simple_unit_overlap_path_iterator(punit, pos) {
struct tile *ptile = map_get_tile(pos.x, pos.y);
@@ -450,7 +451,7 @@
}
if (!pvictim
- || !pplayers_at_war(pplayer, unit_owner(pvictim))
+ || !HOSTILE_PLAYER(pplayer, ai, unit_owner(pvictim))
|| unit_list_size(&ptile->units) > 1
|| map_get_city(pos.x, pos.y)
|| government_has_flag(get_gov_pplayer(unit_owner(pvictim)),
Index: ai/aitools.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aitools.c,v
retrieving revision 1.85
diff -u -r1.85 aitools.c
--- ai/aitools.c 2003/05/06 08:13:21 1.85
+++ ai/aitools.c 2003/06/01 14:52:19
@@ -74,8 +74,8 @@
/**********************************************************************
There are some signs that a player might be dangerous: We are at
war with him, he has lousy reputation, he has done lots of ignoble
- things to us, or he is an ally of one of our enemies (a ticking
- bomb to be sure).
+ things to us, he is an ally of one of our enemies (a ticking bomb
+ to be sure), or he is our war target.
***********************************************************************/
bool is_player_dangerous(struct player *pplayer, struct player *aplayer)
{
@@ -84,6 +84,7 @@
= &ai->diplomacy.player_intel[aplayer->player_no];
return (pplayers_at_war(pplayer, aplayer)
+ || ai->diplomacy.target == aplayer
|| pplayer->diplstates[aplayer->player_no].has_reason_to_cancel
|| ai->diplomacy.acceptable_reputation > aplayer->reputation
|| adip->is_allied_with_enemy);
Index: ai/aiunit.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aiunit.c,v
retrieving revision 1.278
diff -u -r1.278 aiunit.c
--- ai/aiunit.c 2003/05/30 18:26:11 1.278
+++ ai/aiunit.c 2003/06/01 14:52:19
@@ -1876,6 +1876,8 @@
int which)
{
int x, y;
+ struct player *pplayer = unit_owner(punit);
+ struct ai_data *ai = ai_data_get(pplayer);
CHECK_UNIT(punit);
@@ -1890,7 +1892,8 @@
square_iterate(x, y, radius, x1, y1) {
struct city *pcity = map_get_city(x1, y1);
- if (pcity && pplayers_at_war(city_owner(pcity), unit_owner(punit))
+ if (pcity
+ && HOSTILE_PLAYER(pplayer, ai, city_owner(pcity))
&& (pcity->ai.invasion & which) != which
&& (dest || !has_defense(pcity))) {
pcity->ai.invasion |= which;
@@ -1909,6 +1912,7 @@
int find_something_to_kill(struct player *pplayer, struct unit *punit,
int *x, int *y)
{
+ struct ai_data *ai = ai_data_get(pplayer);
/* basic attack */
int attack_value = unit_att_rating(punit);
/* Enemy defence rating */
@@ -1966,7 +1970,7 @@
/* First calculate in nearby units */
players_iterate(aplayer) {
- if (!pplayers_at_war(pplayer, aplayer)) {
+ if (!HOSTILE_PLAYER(pplayer, ai, aplayer)) {
continue;
}
city_list_iterate(aplayer->cities, acity) {
@@ -2055,7 +2059,7 @@
}
players_iterate(aplayer) {
- if (!pplayers_at_war(pplayer, aplayer)) {
+ if (!HOSTILE_PLAYER(pplayer, ai, aplayer)) {
/* Not an enemy */
continue;
}
@@ -2226,6 +2230,10 @@
continue;
}
+ if (!can_unit_attack_unit_at_tile(punit, aunit, aunit->x, aunit->y)) {
+ continue;
+ }
+
if (!can_unit_attack_unit_at_tile(punit, aunit, aunit->x, aunit->y)
|| !(aunit == get_defender(punit, aunit->x, aunit->y))) {
/* We cannot attack it, or it is not the main defender. */
@@ -2448,6 +2456,7 @@
struct city *pcity;
struct packet_unit_request req;
int tradeval, best_city = -1, best=0;
+ struct ai_data *ai = ai_data_get(pplayer);
CHECK_UNIT(punit);
@@ -2475,7 +2484,9 @@
/* A caravan without a home? Kinda strange, but it might happen. */
pcity=player_find_city_by_id(pplayer, punit->homecity);
players_iterate(aplayer) {
- if (pplayers_at_war(pplayer, aplayer)) continue;
+ if (HOSTILE_PLAYER(pplayer, ai, aplayer)) {
+ continue;
+ }
city_list_iterate(pplayer->cities,pdest) {
if (pcity && can_establish_trade_route(pcity, pdest)
&& map_get_continent(pcity->x, pcity->y)
Index: ai/aiunit.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aiunit.h,v
retrieving revision 1.45
diff -u -r1.45 aiunit.h
--- ai/aiunit.h 2003/05/10 18:11:25 1.45
+++ ai/aiunit.h 2003/06/01 14:52:19
@@ -36,6 +36,9 @@
> unit_type(punit)->transport_capacity)
#define COULD_OCCUPY(punit) \
(is_ground_unit(punit) || is_heli_unit(punit))
+#define HOSTILE_PLAYER(pplayer, ai, aplayer) \
+ (pplayers_at_war(pplayer, aplayer) \
+ || ai->diplomacy.target == aplayer)
struct player;
struct city;
Index: client/options.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/options.c,v
retrieving revision 1.81
diff -u -r1.81 options.c
--- client/options.c 2003/05/18 16:19:29 1.81
+++ client/options.c 2003/06/01 14:52:19
@@ -283,6 +283,7 @@
GEN_EV(N_("Wonder: Started"), E_WONDER_STARTED),
GEN_EV(N_("Wonder: Stopped"), E_WONDER_STOPPED),
GEN_EV(N_("Wonder: Will Finish Next Turn"), E_WONDER_WILL_BE_BUILT),
+ GEN_EV(N_("Diplomatic Message"), E_DIPLOMACY),
GEN_EV_TERMINATOR
};
Index: common/diptreaty.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/diptreaty.c,v
retrieving revision 1.16
diff -u -r1.16 diptreaty.c
--- common/diptreaty.c 2003/05/15 15:16:07 1.16
+++ common/diptreaty.c 2003/06/01 14:52:19
@@ -40,8 +40,8 @@
|| player_has_embassy(pplayer, aplayer)
|| pplayer->diplstates[aplayer->player_no].contact_turns_left > 0
|| aplayer->diplstates[pplayer->player_no].contact_turns_left >
0)
- && aplayer->is_connected
- && pplayer->is_connected);
+ && (aplayer->is_connected || aplayer->ai.control)
+ && (pplayer->is_connected || pplayer->ai.control));
}
/**************************************************************************
Index: common/events.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/events.h,v
retrieving revision 1.22
diff -u -r1.22 events.h
--- common/events.h 2002/05/07 07:40:53 1.22
+++ common/events.h 2003/06/01 14:52:19
@@ -102,6 +102,7 @@
E_WONDER_STARTED,
E_WONDER_STOPPED,
E_WONDER_WILL_BE_BUILT,
+ E_DIPLOMACY,
/*
* Note: If you add a new event, make sure you make a similar change
* to the events array in client/options.c using GEN_EV and to
Index: common/player.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/player.c,v
retrieving revision 1.118
diff -u -r1.118 player.c
--- common/player.c 2003/05/31 16:22:15 1.118
+++ common/player.c 2003/06/01 14:52:20
@@ -576,6 +576,23 @@
}
/***************************************************************
+ Returns true iff players are allied or at peace.
+***************************************************************/
+bool pplayers_in_peace(const struct player *pplayer,
+ const struct player *pplayer2)
+{
+ enum diplstate_type ds = pplayer_get_diplstate(pplayer, pplayer2)->type;
+
+ if (pplayer == pplayer2) {
+ return TRUE;
+ }
+ if (is_barbarian(pplayer) || is_barbarian(pplayer2)) {
+ return FALSE;
+ }
+ return (ds == DS_PEACE || ds == DS_ALLIANCE);
+}
+
+/***************************************************************
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.100
diff -u -r1.100 player.h
--- common/player.h 2003/05/31 16:22:15 1.100
+++ common/player.h 2003/06/01 14:52:20
@@ -263,6 +263,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: common/aicore/Makefile.am
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/aicore/Makefile.am,v
retrieving revision 1.4
diff -u -r1.4 Makefile.am
--- common/aicore/Makefile.am 2003/03/11 17:59:26 1.4
+++ common/aicore/Makefile.am 2003/06/01 14:52:20
@@ -5,6 +5,8 @@
INCLUDES = -I.. -I$(top_srcdir)/common -I../../intl
libaicore_a_SOURCES = \
+ aisupport.c \
+ aisupport.h \
path_finding.c \
path_finding.h \
pf_tools.c \
Index: server/diplhand.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/diplhand.c,v
retrieving revision 1.70
diff -u -r1.70 diplhand.c
--- server/diplhand.c 2003/05/31 16:22:15 1.70
+++ server/diplhand.c 2003/06/01 14:52:20
@@ -37,6 +37,8 @@
#include "settlers.h"
#include "unittools.h"
+#include "advdiplomacy.h"
+
#include "diplhand.h"
#define SPECLIST_TAG treaty
@@ -313,6 +315,13 @@
}
} clause_list_iterate_end;
+ if (plr0->ai.control) {
+ ai_treaty_accepted(plr0, plr1, ptreaty);
+ }
+ if (plr1->ai.control) {
+ ai_treaty_accepted(plr1, plr0, ptreaty);
+ }
+
clause_list_iterate(ptreaty->clauses, pclause) {
pgiver = pclause->from;
pdest = (plr0==pgiver) ? plr1 : plr0;
@@ -323,6 +332,11 @@
* and try to give us the same tech at the same time. This
* should be handled discreetly instead of giving a core dump. */
if (get_invention(pdest, pclause->value) == TECH_KNOWN) {
+ freelog(LOG_VERBOSE,
+ "The %s already know tech %s, that %s want to give them.",
+ get_nation_name_plural(pdest->nation),
+ advances[pclause->value].name,
+ get_nation_name_plural(pgiver->nation));
break;
}
notify_player_ex(pdest, -1, -1, E_TECH_GAIN,
@@ -434,7 +448,7 @@
}
} clause_list_iterate_end;
- cleanup:
+ cleanup:
treaty_list_unlink(&treaties, ptreaty);
free(ptreaty);
send_player_info(plr0, NULL);
@@ -476,9 +490,14 @@
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);
+ }
}
}
-
}
/**************************************************************************
@@ -519,6 +538,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);
+ }
}
}
}
@@ -587,10 +612,11 @@
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;
}
@@ -632,6 +658,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.275
diff -u -r1.275 plrhand.c
--- server/plrhand.c 2003/05/31 16:22:15 1.275
+++ server/plrhand.c 2003/06/01 14:52:21
@@ -1317,6 +1317,7 @@
player_map_allocate(pplayer);
}
player_init(pplayer);
+ ai_data_init(pplayer);
}
/**********************************************************************
@@ -1372,12 +1373,10 @@
pplayer1->diplstates[player2].contact_turns_left = game.contactturns;
pplayer2->diplstates[player1].contact_turns_left = game.contactturns;
- /* FIXME: Always declaring war for the AI is a kludge until AI
- diplomacy is implemented. */
if (pplayer_get_diplstate(pplayer1, pplayer2)->type == DS_NO_CONTACT) {
pplayer1->diplstates[player2].type
= pplayer2->diplstates[player1].type
- = pplayer1->ai.control || pplayer2->ai.control ? DS_WAR : DS_NEUTRAL;
+ = DS_NEUTRAL;
notify_player_ex(pplayer1, x, y,
E_FIRST_CONTACT,
_("Game: You have made contact with the %s, ruled by %s."),
@@ -1388,6 +1387,9 @@
get_nation_name_plural(pplayer1->nation), pplayer1->name);
send_player_info(pplayer1, pplayer2);
send_player_info(pplayer2, pplayer1);
+ send_player_info(pplayer1, pplayer1);
+ send_player_info(pplayer2, pplayer2);
+ return;
}
if (player_has_embassy(pplayer1, pplayer2)
|| player_has_embassy(pplayer2, pplayer1)) {
@@ -1532,10 +1534,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;
@@ -1561,7 +1563,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/savegame.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/savegame.c,v
retrieving revision 1.118
diff -u -r1.118 savegame.c
--- server/savegame.c 2003/05/31 16:22:15 1.118
+++ server/savegame.c 2003/06/01 14:52:21
@@ -49,6 +49,7 @@
#include "unittools.h"
#include "aicity.h"
+#include "aidata.h"
#include "aiunit.h"
#include "savegame.h"
@@ -691,7 +692,13 @@
plr->reputation=secfile_lookup_int_default(file, GAME_DEFAULT_REPUTATION,
"player%d.reputation", plrno);
- for(i=0; i<game.nplayers; i++) {
+ for (i=0; i < game.nplayers; i++) {
+ struct ai_data *ai = ai_data_get(plr);
+
+ ai->diplomacy.player_intel[i].love =
+ secfile_lookup_int_default(file, 0,
+ "player%d.ai_diplomacy%d.love", plrno, i);
+
plr->diplstates[i].type =
secfile_lookup_int_default(file, DS_WAR,
"player%d.diplstate%d.type", plrno, i);
@@ -1347,6 +1354,10 @@
secfile_insert_int(file, plr->reputation, "player%d.reputation", plrno);
for (i = 0; i < MAX_NUM_PLAYERS+MAX_NUM_BARBARIANS; i++) {
+ struct ai_data *ai = ai_data_get(plr);
+
+ secfile_insert_int(file, ai->diplomacy.player_intel[i].love,
+ "player%d.ai_diplomacy%d.love", plrno, i);
secfile_insert_int(file, plr->diplstates[i].type,
"player%d.diplstate%d.type", plrno, i);
secfile_insert_int(file, plr->diplstates[i].turns_left,
Index: server/srv_main.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/srv_main.c,v
retrieving revision 1.127
diff -u -r1.127 srv_main.c
--- server/srv_main.c 2003/05/15 16:02:00 1.127
+++ server/srv_main.c 2003/06/01 14:52:21
@@ -91,6 +91,7 @@
#include "unithand.h"
#include "unittools.h"
+#include "advdiplomacy.h"
#include "advmilitary.h"
#include "aidata.h"
#include "aihand.h"
@@ -467,9 +468,15 @@
send_player_cities(pplayer);
} players_iterate_end;
- flush_packets(); /* to curb major city spam */
-
+ flush_packets(); /* to curb major city spam */
conn_list_do_unbuffer(&game.game_connections);
+
+ /* Try to avoid hiding events under a diplomacy dialog */
+ players_iterate(pplayer) {
+ if (pplayer->ai.control && !is_barbarian(pplayer)) {
+ ai_diplomacy_actions(pplayer);
+ }
+ } players_iterate_end;
}
/**************************************************************************
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [freeciv-ai] AI Diplomacy v10 (PR#2413),
Per I. Mathisen <=
|
|