From fb452992a9557e9c0c24a5ae4e64ccfca6caac4b Mon Sep 17 00:00:00 2001 From: Frelhelm Date: Fri, 13 Jan 2023 11:00:23 +0100 Subject: [PATCH] New Policy for Map Exchange in Diplomacy Panel: -- DS_ALLIANCE and DS_TEAM relations always render maps for free -- DS_WAR, DS_CEASEFIRE and DS_NO_CONTACT make map trade unavailable -- love-for other-player below -MAX_AI_LOVE/2 or "sharing vision" make map trade unavailable -- recent map trade w/ other-player <= 7 days make map trade unavailable (discriminates giving and receiving maps) otherwise map trade is enabled Map value calculation: Basic Map Value: 10 * map-scale, w/ min 25 and max 120 (map-scale is for now the number of cities of the giving player) -- if AI is the giving party then (asymmetric evaluation): added 1/3 of basic value for each of the following conditions: -- dipl.-state other than DS_PEACE -- love-for below -MAX_AI_LOVE/4 -- other-player is dangerous -- other-player is rich (> 500) -- other-player is super rich (> 1200) Time related deductions: Of the basic map value are subtracted time or event related parts of the value. -- a general deduction for game age (ratio of turn per 600) -- a rebate for map trade events of the past w/ other-player (0..60 %) (discriminates giving and receiving maps) For receiving (purchasing) maps holds further: -- full reduction (value 0) in the first 20 days after having purchased a map from other-player -- linear sinking deduction in the days 21..32 after purchasing a map from other-player -- gifted maps (value 0) from other don't count as purchase Open Requirements: -- 4 int values to be serialised and retrieved into the save-game in the frame of 'struct ai_dip_intel'. --- ai/default/daidata.h | 5 + ai/default/daidiplomacy.c | 201 ++++++++++++++++++++++++++------------ 2 files changed, 146 insertions(+), 60 deletions(-) diff --git a/ai/default/daidata.h b/ai/default/daidata.h index 756d526..2b8b716 100644 --- a/ai/default/daidata.h +++ b/ai/default/daidata.h @@ -65,6 +65,11 @@ struct ai_dip_intel { signed char asked_about_alliance; /* don't nag! */ signed char asked_about_ceasefire; /* don't ... you get the point */ signed char warned_about_space; + + int maps_given; /* number of maps given to (trade) */ + int maps_received; /* number of maps received from (trade) */ + int map_given_turn; /* game turn when last given a map */ + int map_received_turn; /* game turn when last received a map */ }; struct ai_plr diff --git a/ai/default/daidiplomacy.c b/ai/default/daidiplomacy.c index 6ea0158..a718ffe 100644 --- a/ai/default/daidiplomacy.c +++ b/ai/default/daidiplomacy.c @@ -304,20 +304,19 @@ static int dai_goldequiv_clause(struct ai_type *ait, { bool close_here; struct ai_plr *ai; - int worth = 0; /* worth for pplayer of what aplayer gives */ - bool give = (pplayer == pclause->from); - struct player *giver; + bool give = pplayer == pclause->from; + struct player *giver = pclause->from; const struct player *penemy; struct ai_dip_intel *adip = dai_diplomacy_get(ait, pplayer, aplayer); + int worth = 0; /* worth for pplayer of what giver gives */ bool is_dangerous; + int love_for = pplayer->ai_common.love[player_index(aplayer)]; ai = dai_plr_data_get(ait, pplayer, &close_here); - fc_assert_ret_val(pplayer != aplayer, 0); diplomacy_verbose = verbose; ds_after = MAX(ds_after, player_diplstate_get(pplayer, aplayer)->type); - giver = pclause->from; switch (pclause->type) { case CLAUSE_ADVANCE: @@ -412,21 +411,18 @@ static int dai_goldequiv_clause(struct ai_type *ait, } else if (adip->countdown >= 0 || adip->countdown < -1) { worth = -BIG_NUMBER; /* but say nothing */ } else { - worth = greed(pplayer->ai_common.love[player_index(aplayer)] - - ai->diplomacy.req_love_for_peace); + worth = greed(love_for - ai->diplomacy.req_love_for_peace); } } else if (pclause->type == CLAUSE_ALLIANCE) { if (!pplayers_in_peace(pplayer, aplayer)) { - worth = greed(pplayer->ai_common.love[player_index(aplayer)] - - ai->diplomacy.req_love_for_peace); + worth = greed(love_for - ai->diplomacy.req_love_for_peace); } if (adip->countdown >= 0 || adip->countdown < -1) { worth = -BIG_NUMBER; /* but say nothing */ } else { - worth += greed(pplayer->ai_common.love[player_index(aplayer)] - - ai->diplomacy.req_love_for_alliance); + worth += greed(love_for - ai->diplomacy.req_love_for_alliance); } - if (pplayer->ai_common.love[player_index(aplayer)] < MAX_AI_LOVE / 10) { + if (love_for < MAX_AI_LOVE / 10) { dai_diplo_notify(aplayer, _("*%s (AI)* I simply do not trust you with an " "alliance yet, %s."), player_name(pplayer), @@ -446,11 +442,9 @@ static int dai_goldequiv_clause(struct ai_type *ait, worth = 0; /* show some good faith */ break; } else { - worth = greed(pplayer->ai_common.love[player_index(aplayer)]); + worth = greed(love_for); DIPLO_LOG(ait, LOG_DIPL, pplayer, aplayer, "ceasefire worth=%d love=%d " - "turns=%d", worth, - pplayer->ai_common.love[player_index(aplayer)], - turns); + "turns=%d", worth, love_for, turns); } } } @@ -473,46 +467,122 @@ static int dai_goldequiv_clause(struct ai_type *ait, break; case CLAUSE_SEAMAP: - if (!give || ds_after == DS_ALLIANCE) { - /* 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 they have more use of seamap for settling - new areas the more cities they have already. */ - worth -= 15 * city_list_size(aplayer->cities); - /* Don't like them? Don't give it to them! */ - worth = MIN(pplayer->ai_common.love[player_index(aplayer)] * 7, worth); - /* Make maps from novice player cheap */ - if (has_handicap(pplayer, H_DIPLOMACY)) { - worth /= 2; - } + case CLAUSE_MAP: + if (ds_after == DS_ALLIANCE || ds_after == DS_TEAM) { + /* allies get the map for free! */ + worth = 0; + break; } - DIPLO_LOG(ait, LOG_DIPL, pplayer, aplayer, "seamap clause worth %d", - worth); - break; - case CLAUSE_MAP: - if (!give || ds_after == DS_ALLIANCE) { - /* Useless to us - we're omniscient! And allies get it for free! */ - worth = 0; + /* BASE VALUE of a map */ + /* Very silly algorithm 2: Land map more worth the more cities + we have, since we expose all of these to the enemy. */ + int map_scale = city_list_size(giver->cities); + worth = MIN(MAX(10 * map_scale, 25), 120); + int worth_add = worth * 35 / 100; + printf("map of %s (love %d), cities %d, valued %d\n", giver->name, love_for, map_scale, worth); + + if (give) { + /* inflate numbers if not peace */ + if (ds_after != DS_PEACE) { + worth += worth_add; + } + + /* inflate numbers if other is weird */ + if (love_for < -MAX_AI_LOVE / 4) { + worth += worth_add; + } + + /* inflate numbers if other dangerous */ + if (adv_is_player_dangerous(pplayer, aplayer)) { + worth += worth_add; + } + + /* inflate numbers if other is rich */ + if (aplayer->economic.gold >= 500) { + worth += worth_add; + } + + /* inflate numbers if other is super rich */ + if (aplayer->economic.gold >= 1200) { + worth += worth_add; + } + } + printf("map worth 1 = %d\n" , worth); + + /* time deduction (map cheaper with game age) */ + worth -= MIN(worth * MIN(game.info.turn, 600) / 600, worth * 8 /10); + + /* decrease value over map trade events (rebate) */ + int rv[] = {0, 10, 20, 30, 40, 50, 50, 60, 60, 60}; + int index = give ? adip->maps_given : adip->maps_received; + int rebate = worth * rv[MIN(MAX(0, index-1), 9)] / 100; + worth -= rebate; + if (rebate > 0) printf("rebate for trade (index %d) = %d\n" , index, rebate); + + /* Make maps from novice player cheaper */ + if (give && has_handicap(pplayer, H_DIPLOMACY)) { + worth /= 2; + } + printf("map worth 2 = %d, turn %d\n" , worth, game.info.turn); + + /* giving */ + if (give) { + /* exclude reasons for giving the map away */ + /* if at WAR, CEASEFIRE or NO_CONTACT */ + if (ds_after == DS_WAR || ds_after == DS_CEASEFIRE || ds_after == DS_NO_CONTACT || + /* if don't like him at all */ + love_for < -MAX_AI_LOVE / 2 || + /* given shared vision to other */ + gives_shared_vision(pplayer, aplayer) || + /* if other received our map recently (7 days) */ + game.info.turn - adip->map_given_turn <= 7) { + + worth = -BIG_NUMBER; + printf("map giving excluded!\n"); + dai_diplo_notify(aplayer, _("*%s (AI)* We won't give our precious maps this time, %s!"), + player_name(pplayer), + player_name(aplayer)); + break; + } + + /* invert the worth for calculation */ + worth = - worth; + printf("giving map worth = %d\n" , worth); + + /* receiving */ } else { - /* Very silly algorithm 2: Land map more worth the more cities - we have, since we expose all of these to the enemy. */ - worth -= 40 * MAX(city_list_size(pplayer->cities), 1); - /* Inflate numbers if not peace */ - if (!pplayers_in_peace(pplayer, aplayer)) { - worth *= 2; - } - /* Don't like them? Don't give it to them! */ - worth = MIN(pplayer->ai_common.love[player_index(aplayer)] * 10, worth); - /* Make maps from novice player cheap */ - if (has_handicap(pplayer, H_DIPLOMACY)) { - worth /= 6; - } + /* devalue reasons for reveiving map */ + /* if at WAR, CEASEFIRE or NO_CONTACT */ + if (ds_after == DS_WAR || ds_after == DS_CEASEFIRE || ds_after == DS_NO_CONTACT || + /* if don't like him at all */ + love_for < -MAX_AI_LOVE / 2 || + /* have shared vision from other */ + gives_shared_vision(aplayer, pplayer) || + /* if other received our map recently (7 days) */ + game.info.turn - adip->map_received_turn <= 7) { + + worth = 0; + printf("receiving map devaluated!\n"); + break; + + } else { + /* reduction for time passed since last reception of map */ + if (adip->map_received_turn > 0) { + int days = game.info.turn - adip->map_received_turn; + int redux = 12; + if (days > 20) { + redux -= MIN(12, days-20); + } + printf("reduction for time passed since last purchase: %d\n", worth * redux / 12); + worth -= worth * redux / 12; + } + } + printf("receiving map worth = %d\n" , worth); } - DIPLO_LOG(ait, LOG_DIPL, pplayer, aplayer, "landmap clause worth %d", - worth); + + pclause->value = ABS(worth); + DIPLO_LOG(ait, LOG_DIPL, pplayer, aplayer, "world-map clause worth %d", worth); break; case CLAUSE_CITY: { @@ -555,10 +625,10 @@ static int dai_goldequiv_clause(struct ai_type *ait, _("*%s (AI)* Sorry, sharing vision with you " "is not safe."), player_name(pplayer)); - worth = -BIG_NUMBER; - } else { + worth = -BIG_NUMBER; + } else { worth = 0; - } + } } else { /* so out of the question */ worth = -BIG_NUMBER; @@ -575,8 +645,7 @@ static int dai_goldequiv_clause(struct ai_type *ait, } else if (ds_after == DS_PEACE) { worth = -5 * game.info.turn; } else { - worth = MIN(-50 * game.info.turn - + pplayer->ai_common.love[player_index(aplayer)], + worth = MIN(-50 * game.info.turn + love_for, -5 * game.info.turn); } } else if (game.info.tech_leakage == TECH_LEAKAGE_EMBASSIES) { @@ -759,6 +828,7 @@ void dai_treaty_accepted(struct ai_type *ait, struct player *pplayer, } clause_list_iterate_end; /* Evaluate clauses */ + struct ai_dip_intel *adip = dai_diplomacy_get(ait, pplayer, aplayer); clause_list_iterate(ptreaty->clauses, pclause) { int balance = dai_goldequiv_clause(ait, pplayer, aplayer, pclause, TRUE, ds_after); @@ -766,11 +836,22 @@ void dai_treaty_accepted(struct ai_type *ait, struct player *pplayer, total_balance += balance; gift = (gift && (balance >= 0)); dai_treaty_react(ait, pplayer, aplayer, pclause); - if (is_pact_clause(pclause->type) - && dai_diplomacy_get(ait, pplayer, aplayer)->countdown != -1) { + if (is_pact_clause(pclause->type) && adip->countdown != -1) { /* Cancel a countdown towards war if we just agreed to peace... */ DIPLO_LOG(ait, LOG_DIPL, pplayer, aplayer, "countdown nullified"); - dai_diplomacy_get(ait, pplayer, aplayer)->countdown = -1; + adip->countdown = -1; + } + + /* update AI memory for map deals (don't count worthless deals, like gifts) */ + if ((pclause->type == CLAUSE_SEAMAP || pclause->type == CLAUSE_MAP) && pclause->value) { + if (pplayer == pclause->from) { + adip->maps_given++; + adip->map_given_turn = game.info.turn; + } else { + adip->maps_received++; + adip->map_received_turn = game.info.turn; + } + printf("-- updated memory over map deal (giver = %s), worth %d\n", pclause->from->name, pclause->value); } } clause_list_iterate_end; -- 2.17.1