diff -u3NrX /home/perrin/freeciv/diff_ignore freeciv/README.AI-diplomacy freeciv-ai/README.AI-diplomacy --- freeciv/README.AI-diplomacy Thu Jan 1 00:00:00 1970 +++ freeciv-ai/README.AI-diplomacy Wed Feb 27 16:01:50 2002 @@ -0,0 +1,41 @@ +This document describes the AI's knowledge of diplomacy. + +At the moment, the AI cannot change its diplomatic state. +The AI starts out in NO_CONTACT mode, and proceeds to WAR +on first-contact. + +The AI knows about friendly units and cities, and does not +consider them to be either targets nor dangers. Caravans +are sent to friendly cities, and ships that do not have +targets are sent on a goto to the closest allied port. + +It is currently totally trusting and does not expect +diplomatic states to ever change. So if one is to add +active diplomacy to the AI, this must be changed. + +For people who want to hack at this part of the AI code, +please note + * pplayers_at_war(p1,p2) returns FALSE if p1==p2 + * pplayers_non_attack(p1,p2) returns FALSE if p1==p2 + * pplayers_allied(p1,p2) returns TRUE if p1==p2 +ie we do not ever consider a player to be at war with +himself, we never consider a player to have any kind of +non-attack treaty with himself, and we always consider +a player to have an alliance with himself. + +The introduction of diplomacy is fraught with many +problems. One is that it usually gains only human players, +not AI players, since humans are so much smarter and know +how to exploit diplomacy, while for AIs they mostly only +add constraints on what it can do. Another is that it can +be very difficult to write diplomacy that is useful for +and not in the way of modpacks. Which means diplomacy +either has to be optional, or have finegrained controls on +who can do what diplomatic deals to whom, set from +rulesets. + +But one possibility for diplomacy that it would be easy +to introduce, is an initial PEACE mode for AIs under 'easy' +difficulty. This can be turned to WAR by a simple countdown +timer started after first contact. This way 'easy' will be +more easy - a frequently requested feature. diff -u3NrX /home/perrin/freeciv/diff_ignore freeciv/ai/advmilitary.c freeciv-ai/ai/advmilitary.c --- freeciv/ai/advmilitary.c Mon Feb 25 19:05:13 2002 +++ freeciv-ai/ai/advmilitary.c Wed Feb 27 16:01:50 2002 @@ -223,7 +223,7 @@ assess_danger(pcity); city_list_iterate_end; } - + /********************************************************************** ... ***********************************************************************/ @@ -262,7 +262,7 @@ unit_list_iterate_end; players_iterate(aplayer) { - if (aplayer != city_owner(pcity)) { + if (pplayers_at_war(city_owner(pcity),aplayer)) { boatspeed = (get_invention(aplayer, game.rtech.nav) == TECH_KNOWN ? 12 : 6); boatid = find_boat(aplayer, &x, &y, 0); @@ -688,7 +688,8 @@ } else { aunit = NULL; } - if (acity && acity->owner == pplayer->player_no) { + + if (acity && !pplayers_at_war(pplayer, city_owner(acity))) { acity = NULL; } diff -u3NrX /home/perrin/freeciv/diff_ignore freeciv/ai/aitools.c freeciv-ai/ai/aitools.c --- freeciv/ai/aitools.c Thu Feb 21 09:44:50 2002 +++ freeciv-ai/ai/aitools.c Wed Feb 27 16:01:50 2002 @@ -47,7 +47,6 @@ If (enemy != 0) it looks only for enemy cities If (pplayer != NULL) it looks for cities known to pplayer **************************************************************************/ - struct city *dist_nearest_city(struct player *pplayer, int x, int y, bool everywhere, bool enemy) { @@ -56,7 +55,7 @@ int con = map_get_continent(x, y); players_iterate(pplay) { - if(enemy && pplay == pplayer) continue; + if ((enemy) && (pplayer) && (!pplayers_at_war(pplayer,pplay))) continue; city_list_iterate(pplay->cities, pcity) if (real_map_distance(x, y, pcity->x, pcity->y) < dist && diff -u3NrX /home/perrin/freeciv/diff_ignore freeciv/ai/aiunit.c freeciv-ai/ai/aiunit.c --- freeciv/ai/aiunit.c Tue Feb 26 15:27:06 2002 +++ freeciv-ai/ai/aiunit.c Wed Feb 27 16:04:36 2002 @@ -644,7 +644,7 @@ square_iterate(x, y, radius, x1, y1) { struct city *pcity = map_get_city(x1, y1); - if (pcity && pcity->owner != punit->owner + if (pcity && pplayers_at_war(city_owner(pcity), unit_owner(punit)) && (pcity->ai.invasion & which) != which && (want_attack || !has_defense(pcity))) { pcity->ai.invasion |= which; @@ -673,7 +673,9 @@ } /************************************************************************** - this is still pretty dopey but better than the original -- Syela + Calculates the values of nearby units to see if we can expect any + help in our attack. Note that is includes the units of allied players + in this calculation. **************************************************************************/ static int reinforcements_value(struct unit *punit, int x, int y) { @@ -681,7 +683,8 @@ square_iterate(x, y, 1, i, j) { struct tile *ptile = map_get_tile(i, j); unit_list_iterate(ptile->units, aunit) { - if (aunit == punit || aunit->owner != punit->owner) continue; + if (aunit == punit + || !pplayers_allied(unit_owner(punit), unit_owner(aunit))) continue; val += (unit_belligerence_basic(aunit)); } unit_list_iterate_end; } square_iterate_end; @@ -689,6 +692,10 @@ return val; } +/************************************************************************** + TODO: Should this function take allied units into considerations? + Currently it does not do this. - Per +**************************************************************************/ static int city_reinforcements_cost_and_value(struct city *pcity, struct unit *punit) { int val, val2 = 0, val3 = 0; @@ -738,7 +745,7 @@ #endif /************************************************************************* -... + TODO: We should maybe include allied units in the calculations - Per **************************************************************************/ static bool is_my_turn(struct unit *punit, struct unit *pdef) { @@ -903,10 +910,7 @@ /* No defender... */ /* ...and free foreign city waiting for us. Who would resist! */ - /* TODO: When diplomacy is implemented, don't capture enemy cities so - * enthusiastically. -- mike, pasky */ - if (pcity && pcity->owner != pplayer->player_no - && is_non_allied_city_tile(map_get_tile(x1, y1), pplayer) + if (pcity && pplayers_at_war(pplayer, city_owner(pcity)) && is_ground_unit(punit) && map_get_terrain(punit->x, punit->y) != T_OCEAN) { SET_BEST(99999); @@ -1433,8 +1437,8 @@ /* this is horrible, but I need to do something like this somewhere. -- Syela */ players_iterate(aplayer) { - if (aplayer == pplayer) continue; /* AI will try to conquer only enemy cities. -- Nb */ + if (!pplayers_at_war(pplayer, aplayer)) continue; city_list_iterate(aplayer->cities, acity) city_reinforcements_cost_and_value(acity, punit); acity->ai.invasion = 0; @@ -1500,7 +1504,7 @@ handicap=ai_handicap(pplayer, H_TARGETS); players_iterate(aplayer) { - if (aplayer != pplayer) { /* enemy */ + if (pplayers_at_war(pplayer, aplayer)) { /* enemy */ city_list_iterate(aplayer->cities, acity) if (handicap && !map_get_known(acity->x, acity->y, pplayer)) continue; sanity = (goto_is_sane(punit, acity->x, acity->y, TRUE) && @@ -1692,20 +1696,29 @@ return(best); } +/************************************************************************* + Find safe harbour (preferably with PORT). An allied player's city is + just as good as one of our own, since both replenish our hitpoints and + reduce unhappiness. +**************************************************************************/ static bool find_nearest_friendly_port(struct unit *punit) { struct player *pplayer = unit_owner(punit); int best = 6 * THRESHOLD + 1, cur; generate_warmap(map_get_city(punit->x, punit->y), punit); - city_list_iterate(pplayer->cities, pcity) - cur = warmap.seacost[pcity->x][pcity->y]; - if (city_got_building(pcity, B_PORT)) cur /= 3; - if (cur < best) { - punit->goto_dest_x = pcity->x; - punit->goto_dest_y = pcity->y; - best = cur; + players_iterate(aplayer) { + if (pplayers_allied(pplayer,aplayer)) { + city_list_iterate(aplayer->cities, pcity) { + cur = warmap.seacost[pcity->x][pcity->y]; + if (city_got_building(pcity, B_PORT)) cur /= 3; + if (cur < best) { + punit->goto_dest_x = pcity->x; + punit->goto_dest_y = pcity->y; + best = cur; + } + } city_list_iterate_end; } - city_list_iterate_end; + } players_iterate_end; if (best > 6 * THRESHOLD) return FALSE; freelog(LOG_DEBUG, "Friendly port nearest to (%d,%d) is %s@(%d,%d) [%d]", punit->x, punit->y, @@ -1790,7 +1803,9 @@ } /************************************************************************* -... + Use caravans for building wonders, or send caravans to establish + trade with a city on the same continent, owned by yourself or an + ally. **************************************************************************/ static void ai_manage_caravan(struct player *pplayer, struct unit *punit) { @@ -1821,7 +1836,9 @@ else { /* A caravan without a home? Kinda strange, but it might happen. */ pcity=player_find_city_by_id(pplayer, punit->homecity); - city_list_iterate(pplayer->cities,pdest) + players_iterate(aplayer) { + if (pplayers_at_war(pplayer, aplayer)) continue; + city_list_iterate(pplayer->cities,pdest) { if (pcity && can_establish_trade_route(pcity, pdest) && map_get_continent(pcity->x, pcity->y) == map_get_continent(pdest->x, pdest->y)) { @@ -1833,7 +1850,8 @@ } } } - city_list_iterate_end; + } city_list_iterate_end; + } players_iterate_end; pcity=player_find_city_by_id(pplayer, best_city); if (pcity) { if (!same_pos(pcity->x, pcity->y, punit->x, punit->y)) { @@ -1855,7 +1873,6 @@ When empty, it tries to find some units to carry or goes home or explores. Military units handled by ai_manage_military() **************************************************************************/ - static void ai_manage_ferryboat(struct player *pplayer, struct unit *punit) { /* It's about 12 feet square and has a capacity of almost 1000 pounds. It is well constructed of teak, and looks seaworthy. */ @@ -2333,7 +2350,10 @@ continent=map_get_continent(pdiplomat->x, pdiplomat->y); handicap = ai_handicap(pplayer, H_TARGETS); players_iterate(aplayer) { - if (aplayer == pplayer) continue; + /* don't target ourselves or friendly players that we already + have embassies with */ + if ((!pplayers_at_war(pplayer, aplayer)) + && (player_has_embassy(pplayer, aplayer))) continue; /* sneaky way of avoiding foul diplomat capture -AJS */ has_emb=player_has_embassy(pplayer, aplayer) || pdiplomat->foul; city_list_iterate(aplayer->cities, acity) diff -u3NrX /home/perrin/freeciv/diff_ignore freeciv/common/combat.c freeciv-ai/common/combat.c --- freeciv/common/combat.c Sat Feb 16 17:05:05 2002 +++ freeciv-ai/common/combat.c Wed Feb 27 16:05:09 2002 @@ -228,7 +228,7 @@ { square_iterate(x, y, 2, x1, y1) { struct city *pcity = map_get_city(x1, y1); - if (pcity && (city_owner(pcity) != owner) + if (pcity && (!pplayers_allied(city_owner(pcity), owner)) && city_got_building(pcity, B_SDI)) return pcity; } square_iterate_end; diff -u3NrX /home/perrin/freeciv/diff_ignore freeciv/common/player.c freeciv-ai/common/player.c --- freeciv/common/player.c Tue Feb 26 19:33:24 2002 +++ freeciv-ai/common/player.c Wed Feb 27 16:01:50 2002 @@ -237,19 +237,19 @@ ***************************************************************/ bool player_can_see_unit(struct player *pplayer, struct unit *punit) { - if (punit->owner==pplayer->player_no) + if (pplayers_allied(unit_owner(punit), pplayer)) return TRUE; if (is_hiding_unit(punit)) { /* Search for units/cities that might be able to see the sub/missile */ struct city *pcity; square_iterate(punit->x, punit->y, 1, x, y) { unit_list_iterate(map_get_tile(x, y)->units, punit2) { - if (punit2->owner == pplayer->player_no) + if (pplayers_allied(unit_owner(punit2), pplayer)) return TRUE; } unit_list_iterate_end; pcity = map_get_city(x, y); - if (pcity && pcity->owner == pplayer->player_no) + if (pcity && (pplayers_allied(city_owner(pcity), pplayer))) return TRUE; } square_iterate_end; return FALSE; diff -u3NrX /home/perrin/freeciv/diff_ignore freeciv/server/plrhand.c freeciv-ai/server/plrhand.c --- freeciv/server/plrhand.c Wed Feb 27 15:19:33 2002 +++ freeciv-ai/server/plrhand.c Wed Feb 27 16:01:50 2002 @@ -1378,32 +1378,6 @@ } square_iterate_end; } -/*************************************************************************** -FIXME: This is a kluge to keep the AI working for the moment. -When the AI is taught to handle diplomacy, remove this and the call to it. -***************************************************************************/ -void neutralize_ai_player(struct player *pplayer) -{ - int other_player; - /* To make sure the rulesets are loaded we must do this. - If the game is a new game all players (inclusive ai's) would be - DS_NO_CONTACT, which is just as good as war. - */ - if (game.is_new_game) - return; - - for (other_player = 0; other_player < game.nplayers; other_player++) { - struct player *pother = get_player(other_player); - if (!pother->is_alive || pplayer == pother - || pplayer_get_diplstate(pplayer, pother)->type == DS_NO_CONTACT) - continue; - while (pplayers_non_attack(pplayer, pother) - || pplayers_allied(pplayer, pother)) { - handle_player_cancel_pact(pplayer, pother->player_no); - } - } -} - /************************************************************************** Setup pconn as a client connected to pplayer: Updates pconn->player, pplayer->connections, pplayer->is_connected. diff -u3NrX /home/perrin/freeciv/diff_ignore freeciv/server/plrhand.h freeciv-ai/server/plrhand.h --- freeciv/server/plrhand.h Thu Feb 14 15:17:36 2002 +++ freeciv-ai/server/plrhand.h Wed Feb 27 16:01:50 2002 @@ -37,7 +37,6 @@ void make_contact(struct player *pplayer1, struct player *pplayer2, int x, int y); void maybe_make_first_contact(int x, int y, struct player *pplayer); -void neutralize_ai_player(struct player *pplayer); void send_player_info(struct player *src, struct player *dest); void send_player_info_c(struct player *src, struct conn_list *dest); diff -u3NrX /home/perrin/freeciv/diff_ignore freeciv/server/savegame.c freeciv-ai/server/savegame.c --- freeciv/server/savegame.c Wed Feb 27 15:19:33 2002 +++ freeciv-ai/server/savegame.c Wed Feb 27 16:01:50 2002 @@ -1999,27 +1999,6 @@ player_map_load(&game.players[i], i, file); } - /* - * FIXME: This is a kluge to keep the AI working until it can - * handle diplomacy. - * - * When loading old savegames the diplstate is set to DS_NEUTRAL, - * so we need to set it to DS_WAR for the AIs for now. - */ - players_iterate(pplayer) { - if (pplayer->ai.control) { - players_iterate(pother) { - if (pplayer != pother && - pplayer->diplstates[pother->player_no].type != DS_NO_CONTACT) { - pplayer->diplstates[pother->player_no].type = - pother->diplstates[pplayer->player_no].type = DS_WAR; - pplayer->diplstates[pother->player_no].turns_left = - pother->diplstates[pplayer->player_no].turns_left = 16; - } - } players_iterate_end; - } - } players_iterate_end; - /* We do this here since if the did it in player_load, player 1 would try to unfog (unloaded) player 2's map when player 1's units were loaded */ diff -u3NrX /home/perrin/freeciv/diff_ignore freeciv/server/settlers.c freeciv-ai/server/settlers.c --- freeciv/server/settlers.c Wed Feb 27 15:19:33 2002 +++ freeciv-ai/server/settlers.c Wed Feb 27 16:01:50 2002 @@ -411,8 +411,8 @@ /* I'm still not sure this is exactly right -- Syela */ unit_list_iterate(map_get_tile(x, y)->units, punit) if (myunit==punit) continue; - if (punit->owner!=pplayer->player_no) - return TRUE; + if (!pplayers_allied(unit_owner(punit), pplayer)) + return TRUE; /* oops, tile is occupied! */ if (unit_flag(punit, F_SETTLERS) && unit_flag(myunit, F_SETTLERS)) return TRUE; unit_list_iterate_end; diff -u3NrX /home/perrin/freeciv/diff_ignore freeciv/server/stdinhand.c freeciv-ai/server/stdinhand.c --- freeciv/server/stdinhand.c Wed Feb 27 15:19:33 2002 +++ freeciv-ai/server/stdinhand.c Wed Feb 27 16:01:50 2002 @@ -1723,9 +1723,7 @@ level could have been set as AI, then toggled, then saved, then reloaded. */ set_ai_level(caller, pplayer->name, pplayer->ai.skill_level); - /* The ai can't handle pacts and stacked enemy units, - so do this *before* access_danger. */ - neutralize_ai_player(pplayer); + /* the AI can't do active diplomacy */ cancel_all_meetings(pplayer); /* The following is sometimes necessary to avoid using uninitialized data... */ diff -u3NrX /home/perrin/freeciv/diff_ignore freeciv/server/unittools.c freeciv-ai/server/unittools.c --- freeciv/server/unittools.c Wed Feb 27 15:19:33 2002 +++ freeciv-ai/server/unittools.c Wed Feb 27 16:01:50 2002 @@ -1360,7 +1360,7 @@ struct player *pplayer = unit_owner(punit); struct city *pcity = map_get_tile(x,y)->city; - if (pcity && pcity->owner == punit->owner) + if (pcity && (!pplayers_allied(city_owner(pcity), unit_owner(punit)))) return FALSE; db = get_tile_type(map_get_terrain(x, y))->defense_bonus;