Index: ai/aidiplomat.c =================================================================== RCS file: /home/freeciv/CVS/freeciv/ai/aidiplomat.c,v retrieving revision 1.13 diff -u -r1.13 aidiplomat.c --- ai/aidiplomat.c 2003/02/17 22:49:27 1.13 +++ ai/aidiplomat.c 2003/02/19 19:22:31 @@ -34,6 +34,8 @@ #include "timing.h" #include "unit.h" +#include "pf_tools.h" + #include "barbarian.h" #include "citytools.h" #include "cityturn.h" @@ -55,8 +57,8 @@ #define LOG_DIPLOMAT LOG_DEBUG -static struct city *find_city_to_diplomat(struct player *pplayer, int x, - int y, bool foul); +static void find_city_to_diplomat(struct player *pplayer, struct unit *punit, + struct city **ctarget, int *move_dist); /****************************************************************************** Number of improvements that can be sabotaged in pcity. @@ -129,8 +131,6 @@ /********************************************************************** Calculates our need for diplomats as offensive units. May replace values in choice. - - Requires an initialized warmap! ***********************************************************************/ void ai_choose_diplomat_offensive(struct player *pplayer, struct city *pcity, @@ -151,11 +151,13 @@ /* Do we have a good reason for building diplomats? */ { struct unit_type *ut = get_unit_type(u); - struct city *acity = find_city_to_diplomat(pplayer, pcity->x, - pcity->y, FALSE); + struct city *acity; int want, loss, p_success, p_failure, time_to_dest; int gain_incite = 0, gain_theft = 0, gain = 1; int incite_cost; + struct unit *punit = create_unit_virtual(pplayer, pcity, u, FALSE); + + find_city_to_diplomat(pplayer, punit, &acity, &time_to_dest); if (acity == NULL || acity->ai.already_considered_for_diplomat) { /* Found no target or city already considered */ @@ -192,7 +194,7 @@ /* Probability to lose our unit */ p_failure = (unit_type_flag(u, F_SPY) ? 100 - p_success : 100); - time_to_dest = WARMAP_COST(acity->x, acity->y) / ut->move_rate; + time_to_dest /= ut->move_rate; time_to_dest *= (time_to_dest/2); /* No long treks, please */ /* Almost kill_desire */ @@ -222,6 +224,7 @@ choice->choice = u; acity->ai.already_considered_for_diplomat = TRUE; } + destroy_unit_virtual(punit); } } @@ -300,96 +303,95 @@ /************************************************************************** Find a city to send diplomats against, or NULL if none available on - this continent. x,y are coordinates of diplomat or city which wishes - to build diplomats, and foul is TRUE if diplomat has done something bad - before. - - Requires an initialized warmap! + this continent. punit can be virtual. **************************************************************************/ -static struct city *find_city_to_diplomat(struct player *pplayer, int x, - int y, bool foul) +static void find_city_to_diplomat(struct player *pplayer, struct unit *punit, + struct city **ctarget, int *move_dist) { bool has_embassy; int incite_cost = 0; /* incite cost */ - int move_cost = 0; /* move cost */ int best_dist = MAX(map.xsize, map.ysize); - int continent = map_get_continent(x, y); bool dipldef; /* whether target is protected by diplomats */ - bool handicap = ai_handicap(pplayer, H_TARGETS); - struct city *ctarget = NULL; + + assert(punit != NULL); + *ctarget = NULL; - players_iterate(aplayer) { + unit_path_iterator(punit, x, y, move_cost) { + struct city *acity = map_get_city(x, y); + struct player *aplayer; + bool can_incite; + + if (!acity) { + continue; + } + aplayer = city_owner(acity); + /* sneaky way of avoiding foul diplomat capture -AJS */ - has_embassy = player_has_embassy(pplayer, aplayer) || foul; + has_embassy = player_has_embassy(pplayer, aplayer) || punit->foul; if (aplayer == pplayer || is_barbarian(aplayer) || (pplayers_allied(pplayer, aplayer) && has_embassy)) { continue; } - city_list_iterate(aplayer->cities, acity) { - bool can_incite - = !(city_got_building(acity, B_PALACE) || - government_has_flag(get_gov_pplayer(aplayer), G_UNBRIBABLE)); - - if (handicap && !map_get_known(acity->x, acity->y, pplayer)) { - /* Target is not visible */ - continue; - } - if (continent != map_get_continent(acity->x, acity->y)) { - /* Diplomats don't do sea travel yet */ - continue; - } - - incite_cost = city_incite_cost(pplayer, acity); - move_cost = WARMAP_COST(acity->x, acity->y); - dipldef = (count_diplomats_on_tile(acity->x, acity->y) > 0); - /* Three actions to consider: - * 1. establishing embassy OR - * 2. stealing techs OR - * 3. inciting revolt */ - if ((!ctarget || best_dist > move_cost) - && (!has_embassy - || (acity->steal == 0 && pplayer->research.techs_researched < - city_owner(acity)->research.techs_researched && !dipldef) - || (incite_cost < (pplayer->economic.gold - pplayer->ai.est_upkeep) - && can_incite && !dipldef))) { - /* We have the closest enemy city so far on the same continent */ - ctarget = acity; - best_dist = move_cost; - } - } city_list_iterate_end; - } players_iterate_end; + can_incite = !(city_got_building(acity, B_PALACE) + || government_has_flag(get_gov_pplayer(aplayer), + G_UNBRIBABLE)); - return ctarget; + incite_cost = city_incite_cost(pplayer, acity); + dipldef = (count_diplomats_on_tile(acity->x, acity->y) > 0); + /* Three actions to consider: + * 1. establishing embassy OR + * 2. stealing techs OR + * 3. inciting revolt */ + if ((!*ctarget || best_dist > move_cost) + && (!has_embassy + || (acity->steal == 0 && pplayer->research.techs_researched < + city_owner(acity)->research.techs_researched && !dipldef) + || (incite_cost < (pplayer->economic.gold - pplayer->ai.est_upkeep) + && can_incite && !dipldef))) { + /* We have the closest enemy city so far on the same continent */ + *ctarget = acity; + best_dist = move_cost; + } + } unit_path_iterator_end; } /************************************************************************** Go to nearest/most threatened city (can be the current city too). - - Requires an initialized warmap! **************************************************************************/ -static struct city *ai_diplomat_defend(struct player *pplayer, int x, int y, +static struct city *ai_diplomat_defend(struct player *pplayer, + struct unit *punit, Unit_Type_id utype) { - int dist, urgency; int best_dist = 30; /* any city closer than this is better than none */ int best_urgency = 0; struct city *ctarget = NULL; - int continent = map_get_continent(x, y); + struct city *pcity = map_get_city(punit->x, punit->y); - city_list_iterate(pplayer->cities, acity) { - int dipls; + if (pcity + && count_diplomats_on_tile(pcity->x, pcity->y) == 1 + && pcity->ai.urgency > 0) { + /* Danger and we are only diplomat present - stay. */ + return pcity; + } - if (continent != map_get_continent(acity->x, acity->y)) { + unit_path_iterator(punit, x, y, move_cost) { + struct city *acity = map_get_city(x, y); + struct player *aplayer; + int dipls, urgency; + + if (!acity) { + continue; + } + aplayer = city_owner(acity); + if (aplayer != pplayer) { continue; } - /* No urgency -- no bother */ urgency = acity->ai.urgency; - /* How many diplomats in acity (excluding ourselves) */ - dipls = (count_diplomats_on_tile(acity->x, acity->y) - - (same_pos(x, y, acity->x, acity->y) ? 1 : 0)); + dipls = (count_diplomats_on_tile(x, y) + - (same_pos(x, y, punit->x, punit->y) ? 1 : 0)); if (dipls == 0 && acity->ai.diplomat_threat) { /* We are _really_ needed there */ urgency = (urgency + 1) * 5; @@ -398,11 +400,10 @@ urgency /= 3; } - dist = WARMAP_COST(acity->x, acity->y); /* This formula may not be optimal, but it works. */ - if (dist > best_dist) { + if (move_cost > best_dist) { /* punish city for being so far away */ - urgency /= (float)(dist/best_dist); + urgency /= (float)(move_cost / best_dist); } if (urgency > best_urgency) { @@ -410,91 +411,64 @@ ctarget = acity; best_urgency = urgency; /* squelch divide-by-zero */ - best_dist = MAX(dist,1); + best_dist = MAX(move_cost, 1); } - } city_list_iterate_end; + } unit_path_iterator_end; + return ctarget; } /************************************************************************** Find units that we can reach, and bribe them. Returns TRUE if survived the ordeal, FALSE if not or we expended all our movement. - - Requires an initialized warmap! Might move the unit! + Will try to bribe a ship on the coast as well as land stuff. **************************************************************************/ static bool ai_diplomat_bribe_nearby(struct player *pplayer, struct unit *punit) { struct packet_diplomat_action packet; - int move_rate = punit->moves_left; - struct unit *pvictim; - struct tile *ptile; int gold_avail = pplayer->economic.gold - pplayer->ai.est_upkeep; - int cost, destx, desty; - bool threat = FALSE; - int sanity = punit->id; - - /* Check ALL possible targets */ - whole_map_iterate(x, y) { - ptile = map_get_tile(x, y); - if (WARMAP_COST(x, y) > move_rate && !is_ocean(ptile->terrain)) { - /* Can't get there */ - continue; - } - destx = x; - desty = y; - if (is_ocean(ptile->terrain)) { - /* Try to bribe a ship on the coast */ - int best = 9999; - adjc_iterate(x, y, x2, y2) { - if (best > WARMAP_COST(x2, y2)) { - best = WARMAP_COST(x2, y2); - destx = x2; - desty = y2; - } - } adjc_iterate_end; - if (best >= move_rate) { - /* Can't get there, either */ - continue; - } - } - pvictim = unit_list_get(&ptile->units, 0); - if (!pvictim || !pplayers_at_war(pplayer, unit_owner(pvictim))) { - continue; - } - if (unit_list_size(&ptile->units) > 1) { - /* Can't do a stack */ - continue; - } - if (map_get_city(x, y)) { - /* Can't do city */ - continue; - } - if (government_has_flag(get_gov_pplayer(unit_owner(pvictim)), - G_UNBRIBABLE)) { - /* Can't bribe */ + + unit_overlap_path_iterator(punit, x, y, px, py, move_cost) { + struct tile *ptile = map_get_tile(x, y); + bool threat = FALSE; + int newval, bestval = 0, cost; + struct unit *pvictim = unit_list_get(&ptile->units, 0); + int sanity = punit->id; + + if (move_cost > punit->moves_left && !is_ocean(ptile->terrain)) { + /* Didn't find anything within range. */ + break; + } + + if (!pvictim + || !pplayers_at_war(pplayer, unit_owner(pvictim)) + || unit_list_size(&ptile->units) > 1 + || map_get_city(x, y) + || government_has_flag(get_gov_pplayer(unit_owner(pvictim)), + G_UNBRIBABLE)) { continue; } + +freelog(LOG_NORMAL, "checking (%d, %d) cost %d", x, y, move_cost); + /* Calculate if enemy is a threat */ - { - /* First find best defender on our tile */ - int newval, bestval = 0; - unit_list_iterate(ptile->units, aunit) { - newval = DEFENCE_POWER(aunit); - if (bestval < newval) { - bestval = newval; - } - } unit_list_iterate_end; - /* Compare with victim's attack power */ - newval = ATTACK_POWER(pvictim); - if (newval > bestval - && unit_type(pvictim)->move_rate > WARMAP_COST(x, y)) { - /* Enemy can probably kill us */ - threat = TRUE; - } else { - /* Enemy cannot reach us or probably not kill us */ - threat = FALSE; + /* First find best defender on our tile */ + unit_list_iterate(ptile->units, aunit) { + newval = DEFENCE_POWER(aunit); + if (bestval < newval) { + bestval = newval; } + } unit_list_iterate_end; + /* Compare with victim's attack power */ + newval = ATTACK_POWER(pvictim); + if (newval > bestval + && unit_type(pvictim)->move_rate > move_cost) { + /* Enemy can probably kill us */ + threat = TRUE; + } else { + /* Enemy cannot reach us or probably not kill us */ + threat = FALSE; } /* Don't bribe settlers! */ if (unit_flag(pvictim, F_SETTLERS) @@ -512,12 +486,15 @@ continue; } /* Found someone! */ - if (!ai_unit_goto(punit, destx, desty) || punit->moves_left <= 0) { +freelog(LOG_NORMAL, "going (%d, %d) path is %d", px, py, UPI_path.positions_used); + if (!ai_unit_goto(punit, px, py) || punit->moves_left <= 0) { return FALSE; } +pf_print_path(&UPI_path); if (diplomat_can_do_action(punit, DIPLOMAT_BRIBE, x, y)) { +freelog(LOG_NORMAL, "trying (%d, %d) cost %d", x, y, move_cost); packet.diplomat_id = punit->id; - packet.target_id = pvictim->id; + packet.target_id = unit_list_get(&ptile->units, 0)->id; packet.action_type = DIPLOMAT_BRIBE; handle_diplomat_action(pplayer, &packet); /* autoattack might kill us as we move in */ @@ -528,10 +505,12 @@ } } else { /* usually because we ended move early due to another unit */ +freelog(LOG_NORMAL, "failed (%d, %d) cost %d", x, y, move_cost); UNIT_LOG(LOG_DIPLOMAT, punit, "could not bribe target " " (%d, %d), %d moves left", x, y, punit->moves_left); + return FALSE; } - } whole_map_iterate_end; + } unit_overlap_path_iterator_end; return (punit->moves_left > 0); } @@ -553,15 +532,12 @@ CHECK_UNIT(punit); pcity = map_get_city(punit->x, punit->y); - generate_warmap(pcity, punit); /* Look for someone to bribe */ if (!ai_diplomat_bribe_nearby(pplayer, punit)) { /* Died or ran out of moves */ return; } - /* Note that the warmap may now be slightly wrong, but we - * don't care, its accuracy is good enough for our purposes */ /* If we are the only diplomat in a threatened city, then stay to defend */ pcity = map_get_city(punit->x, punit->y); /* we may have moved */ @@ -608,13 +584,15 @@ /* If we are not busy, acquire a target. */ if (punit->ai.ai_role == AIUNIT_NONE) { enum ai_unit_task task; + int move_dist; /* dummy */ - if ((ctarget = find_city_to_diplomat(pplayer, punit->x, punit->y, - punit->foul)) != NULL) { + find_city_to_diplomat(pplayer, punit, &ctarget, &move_dist); + + if (ctarget) { task = AIUNIT_ATTACK; punit->ai.bodyguard = -1; /* want one */ UNIT_LOG(LOG_DIPLOMAT, punit, "going on attack"); - } else if ((ctarget = ai_diplomat_defend(pplayer, punit->x, punit->y, + } else if ((ctarget = ai_diplomat_defend(pplayer, punit, punit->type)) != NULL) { task = AIUNIT_DEFEND_HOME; UNIT_LOG(LOG_DIPLOMAT, punit, "going defensive"); @@ -656,10 +634,9 @@ int dist = real_map_distance(punit->x, punit->y, punit->goto_dest_x, punit->goto_dest_y); - UNIT_LOG(LOG_DIPLOMAT, punit, "attack, dist %d to %s (%s goto)" - "[%d mc]", dist, ctarget ? ctarget->name : "(none)", - punit->activity == ACTIVITY_GOTO ? "has" : "no", - WARMAP_COST(punit->goto_dest_x, punit->goto_dest_y)); + UNIT_LOG(LOG_DIPLOMAT, punit, "attack, dist %d to %s (%s goto)", + dist, ctarget ? ctarget->name : "(none)", + punit->activity == ACTIVITY_GOTO ? "has" : "no"); if (dist == 1) { /* Do our stuff */ ai_unit_new_role(punit, AIUNIT_NONE, -1, -1);