diff -ru freeciv/ai/advmilitary.c freeciv+pasky/ai/advmilitary.c --- freeciv/ai/advmilitary.c Fri Nov 23 23:00:25 2001 +++ freeciv+pasky/ai/advmilitary.c Fri Nov 23 23:49:16 2001 @@ -155,11 +155,11 @@ } /************************************************************************** -... +How dangerous an unit is for a city? **************************************************************************/ static int assess_danger_unit(struct city *pcity, struct unit *punit) { - int v, sailing; + int danger, sailing; struct unit_type *ptype; if (unit_flag(punit, F_NO_LAND_ATTACK)) return(0); @@ -168,12 +168,12 @@ ptype = unit_type(punit); /* get_attack_power will be wrong if moves_left == 1 || == 2 */ - v = ptype->attack_strength * 10 * punit->hp * ptype->firepower; - if (punit->veteran) v += v/2; - if (sailing && city_got_building(pcity, B_COASTAL)) v /= 2; - if (is_air_unit(punit) && city_got_building(pcity, B_SAM)) v /= 2; - v /= 30; /* rescaling factor to stop the overflow nonsense */ - return(v); + danger = ptype->attack_strength * 10 * punit->hp * ptype->firepower; + if (punit->veteran) danger += danger/2; + if (sailing && city_got_building(pcity, B_COASTAL)) danger /= 2; + if (is_air_unit(punit) && city_got_building(pcity, B_SAM)) danger /= 2; + danger /= 30; /* rescaling factor to stop the overflow nonsense */ + return(danger); } /************************************************************************** @@ -224,11 +224,11 @@ } /********************************************************************** -... +In how big danger given city is. ***********************************************************************/ int assess_danger(struct city *pcity) { - int i, danger = 0, v, dist, m; + int i, danger = 0; Unit_Type_id utype; int danger2 = 0; /* linear for walls */ int danger3 = 0; /* linear for coastal */ @@ -237,15 +237,13 @@ struct player *aplayer, *pplayer; int pikemen = 0; int diplomat = 0; /* > 0 mean that this town can defend - * against diplomats or spies */ + against diplomats or spies */ int urgency = 0; - int igwall; int badmojo = 0; int boatspeed; int boatid, boatdist; int x = pcity->x, y = pcity->y; /* dummy variables */ struct unit virtualunit; - struct unit *funit = &virtualunit; /* saves me a lot of typing. -- Syela */ memset(&virtualunit, 0, sizeof(struct unit)); pplayer = city_owner(pcity); @@ -260,20 +258,22 @@ if (unit_flag(punit, F_DIPLOMAT)) diplomat++; unit_list_iterate_end; - for(i=0; iowner) { aplayer = &game.players[i]; + boatspeed = (get_invention(aplayer, game.rtech.nav) == TECH_KNOWN ? 4*SINGLE_MOVE : 2*SINGLE_MOVE); boatid = find_boat(aplayer, &x, &y, 0); if (boatid) boatdist = warmap.seacost[x][y]; else boatdist = -1; + /* should I treat empty enemy cities as danger? */ /* After much contemplation, I've decided the answer is sometimes -- Syela */ city_list_iterate(aplayer->cities, acity) if (acity->is_building_unit && build_points_left(acity) <= acity->shield_surplus && - ai_fuzzy(pplayer,1)) { + ai_fuzzy(pplayer, 1)) { virtualunit.owner = i; virtualunit.x = acity->x; virtualunit.y = acity->y; @@ -281,16 +281,24 @@ virtualunit.type = utype; virtualunit.veteran = do_make_unit_veteran(acity, utype); virtualunit.hp = unit_types[utype].hp; + /* yes, I know cloning all this code is bad form. I don't really want to write a funct that takes nine ints by reference. -- Syela */ - m = unit_type(funit)->move_rate; - v = assess_danger_unit(pcity, funit); - dist = assess_distance(pcity, funit, m, boatid, boatdist, boatspeed); - igwall = unit_really_ignores_citywalls(funit); - if ((is_ground_unit(funit) && v) || - (is_ground_units_transport(funit))) { - if (dist <= m * 3) urgency++; - if (dist <= m) pcity->ai.grave_danger++; +/* TODO maybe make some little struct for dangers and then move this +to the separate function at last --pasky */ + { + int move, udanger, dist, igwall; + struct unit *funit = &virtualunit; /* saves me a lot of typing. -- Syela */ + + move = unit_type(funit)->move_rate; + udanger = assess_danger_unit(pcity, funit); + dist = assess_distance(pcity, funit, move, boatid, boatdist, boatspeed); + igwall = unit_really_ignores_citywalls(funit); + + if ((is_ground_unit(funit) && udanger) || + (is_ground_units_transport(funit))) { + if (dist <= move * 3) urgency++; + if (dist <= move) pcity->ai.grave_danger++; /* NOTE: This should actually implement grave_danger, which is supposed to be a feedback-sensitive formula for immediate danger. I'm having second thoughts about the best implementation, and therefore this @@ -298,41 +306,51 @@ not totally sure about and can't thoroughly test in the hours before the release. The AI is in fact vulnerable to gang-attacks, but I'm content to let it remain that way for now. -- Syela 980805 */ - } + } - if (unit_flag(funit, F_HORSE)) { - if (pikemen) v /= 2; - else ai_wants_role_unit(pplayer, pcity, F_PIKEMEN, - (v * m / (dist*2))); - } - - if (unit_flag(funit, F_DIPLOMAT) && (dist <= 2 * m)) - pcity->ai.diplomat_threat = !diplomat; - - v *= v; - - if (!igwall) danger2 += v * m / dist; - else if (is_sailing_unit(funit)) danger3 += v * m / dist; - else if (is_air_unit(funit) && !unit_flag(funit, F_NUCLEAR)) danger4 += v * m / dist; - if (unit_flag(funit, F_MISSILE)) danger5 += v * m / MAX(m, dist); - if (!unit_flag(funit, F_NUCLEAR)) { /* only SDI helps against NUCLEAR */ - v = dangerfunct(v, m, dist); - danger += v; - if (igwall) badmojo += v; - } + if (unit_flag(funit, F_HORSE)) { + if (pikemen) udanger /= 2; + else ai_wants_role_unit(pplayer, pcity, F_PIKEMEN, + (udanger * move / (dist*2))); + } + + if (unit_flag(funit, F_DIPLOMAT) && (dist <= 2 * move)) + pcity->ai.diplomat_threat = !diplomat; + + udanger *= udanger; + + if (!igwall) + danger2 += udanger * move / dist; + else if (is_sailing_unit(funit)) + danger3 += udanger * move / dist; + else if (is_air_unit(funit) && !unit_flag(funit, F_NUCLEAR)) + danger4 += udanger * move / dist; + + if (unit_flag(funit, F_MISSILE)) + danger5 += udanger * move / MAX(move, dist); + + if (!unit_flag(funit, F_NUCLEAR)) { /* only SDI helps against NUCLEAR */ + udanger = dangerfunct(udanger, move, dist); + danger += udanger; + if (igwall) badmojo += udanger; + } + } } city_list_iterate_end; - unit_list_iterate(aplayer->units, punit) - m = unit_type(punit)->move_rate; - v = assess_danger_unit(pcity, punit); - dist = assess_distance(pcity, punit, m, boatid, boatdist, boatspeed); - igwall = unit_really_ignores_citywalls(punit); - - if ((is_ground_unit(punit) && v) || - (is_ground_units_transport(punit))) { - if (dist <= m * 3) urgency++; - if (dist <= m) pcity->ai.grave_danger++; + unit_list_iterate(aplayer->units, punit) { + int move, udanger, dist, igwall; + struct unit *funit = punit; /* in order to keep the code common --pasky */ + + move = unit_type(funit)->move_rate; + udanger = assess_danger_unit(pcity, funit); + dist = assess_distance(pcity, funit, move, boatid, boatdist, boatspeed); + igwall = unit_really_ignores_citywalls(funit); + + if ((is_ground_unit(funit) && udanger) || + (is_ground_units_transport(funit))) { + if (dist <= move * 3) urgency++; + if (dist <= move) pcity->ai.grave_danger++; /* NOTE: This should actually implement grave_danger, which is supposed to be a feedback-sensitive formula for immediate danger. I'm having second thoughts about the best implementation, and therefore this @@ -342,34 +360,40 @@ content to let it remain that way for now. -- Syela 980805 */ } - if (unit_flag(punit, F_HORSE)) { - if (pikemen) v /= 2; + if (unit_flag(funit, F_HORSE)) { + if (pikemen) udanger /= 2; else ai_wants_role_unit(pplayer, pcity, F_PIKEMEN, - (v * m / (dist*2))); - } + (udanger * move / (dist*2))); + } - if (unit_flag(punit, F_DIPLOMAT) && (dist <= 2 * m)) - pcity->ai.diplomat_threat = !diplomat; + if (unit_flag(funit, F_DIPLOMAT) && (dist <= 2 * move)) + pcity->ai.diplomat_threat = !diplomat; - v *= v; + udanger *= udanger; - if (!igwall) danger2 += v * m / dist; - else if (is_sailing_unit(punit)) danger3 += v * m / dist; - else if (is_air_unit(punit) && !unit_flag(punit, F_NUCLEAR)) danger4 += v * m / dist; - if (unit_flag(punit, F_MISSILE)) danger5 += v * m / MAX(m, dist); - if (!unit_flag(punit, F_NUCLEAR)) { /* only SDI helps against NUCLEAR */ - v = dangerfunct(v, m, dist); - danger += v; - if (igwall) badmojo += v; - } - unit_list_iterate_end; + if (!igwall) + danger2 += udanger * move / dist; + else if (is_sailing_unit(funit)) + danger3 += udanger * move / dist; + else if (is_air_unit(funit) && !unit_flag(funit, F_NUCLEAR)) + danger4 += udanger * move / dist; + + if (unit_flag(funit, F_MISSILE)) + danger5 += udanger * move / MAX(move, dist); + + if (!unit_flag(funit, F_NUCLEAR)) { /* only SDI helps against NUCLEAR */ + udanger = dangerfunct(udanger, move, dist); + danger += udanger; + if (igwall) badmojo += udanger; + } + } unit_list_iterate_end; } } /* end for */ if (!badmojo) pcity->ai.wallvalue = 90; else pcity->ai.wallvalue = (danger * 9 - badmojo * 8) * 10 / (danger); -/* afraid *100 would create integer overflow, but *10 surely won't */ - + /* afraid *100 would create integer overflow, but *10 surely won't */ + if (danger < 0 || danger > 1<<24) /* I hope never to see this! */ freelog(LOG_ERROR, "Dangerous danger (%d) in %s. Beware of overflow.", danger, pcity->name); @@ -385,11 +409,21 @@ if (danger5 < 0 || danger5 > 1<<24) /* I hope never to see this! */ freelog(LOG_ERROR, "Dangerous danger5 (%d) in %s. Beware of overflow.", danger5, pcity->name); + if (pcity->ai.grave_danger) urgency += 10; /* really, REALLY urgent to defend */ + pcity->ai.danger = danger; + +/* My first attempt to allow ng_wa >= 200 led to stupidity in cities with +no defenders and danger = 0 but danger > 0. Capping ng_wa at 100 + urgency +led to a failure to buy walls as required. Allowing want > 100 with !urgency +led to the AI spending too much gold and falling behind on science. I'm +trying again, but this will require yet more tedious observation -- Syela */ + if (pcity->ai.building_want[B_CITY] > 0 && danger2) { i = assess_defense(pcity); + if (!i) pcity->ai.building_want[B_CITY] = 100 + urgency; else if (urgency) { if (danger2 > i * 2) pcity->ai.building_want[B_CITY] = 200 + urgency; @@ -399,13 +433,13 @@ else pcity->ai.building_want[B_CITY] = danger2 * 100 / i; } } -/* My first attempt to allow ng_wa >= 200 led to stupidity in cities with -no defenders and danger = 0 but danger > 0. Capping ng_wa at 100 + urgency -led to a failure to buy walls as required. Allowing want > 100 with !urgency -led to the AI spending too much gold and falling behind on science. I'm -trying again, but this will require yet more tedious observation -- Syela */ + +/* COASTAL and SAM are TOTALLY UNTESTED and could be VERY WRONG -- Syela */ +/* update 980803; COASTAL seems to work; still don't know about SAM. -- Syela */ + if (pcity->ai.building_want[B_COASTAL] > 0 && danger3) { i = assess_defense_igwall(pcity); + if (!i) pcity->ai.building_want[B_COASTAL] = 100 + urgency; else if (urgency) { if (danger3 > i * 2) pcity->ai.building_want[B_COASTAL] = 200 + urgency; @@ -415,20 +449,23 @@ else pcity->ai.building_want[B_COASTAL] = danger3 * 100 / i; } } -/* COASTAL and SAM are TOTALLY UNTESTED and could be VERY WRONG -- Syela */ -/* update 980803; COASTAL seems to work; still don't know about SAM. -- Syela */ + if (pcity->ai.building_want[B_SAM] > 0 && danger4) { i = assess_defense_igwall(pcity); + if (!i) pcity->ai.building_want[B_SAM] = 100 + urgency; else if (danger4 > i * 2) pcity->ai.building_want[B_SAM] = 200 + urgency; else pcity->ai.building_want[B_SAM] = danger4 * 100 / i; } + if (pcity->ai.building_want[B_SDI] > 0 && danger5) { i = assess_defense_igwall(pcity); + if (!i) pcity->ai.building_want[B_SDI] = 100 + urgency; else if (danger5 > i * 2) pcity->ai.building_want[B_SDI] = 200 + urgency; else pcity->ai.building_want[B_SDI] = danger5 * 100 / i; } + pcity->ai.urgency = urgency; /* need to cache this for bodyguards now -- Syela */ return(urgency); } @@ -436,25 +473,30 @@ /************************************************************************** ... **************************************************************************/ -int unit_desirability(Unit_Type_id i, int def) +int unit_desirability(Unit_Type_id i, int for_defense) { - int cur, a, d; - cur = get_unit_type(i)->hp; - if (unit_types[i].move_type != SEA_MOVING || !def) - cur *= get_unit_type(i)->firepower; - if (def) cur *= 3; - else cur *= get_unit_type(i)->move_rate; - a = get_unit_type(i)->attack_strength; - d = get_unit_type(i)->defense_strength; - if (def) cur *= d; - else if (d > a) return(0); -/* else if (d < 2) cur = (cur * (a + d))/2; Don't believe in this anymore */ - else cur *= a; /* wanted to rank Legion > Catapult > Archer */ -/* which we will do by munging f in the attacker want equations */ - if (unit_type_flag(i, F_IGTER) && !def) cur *= 3; - if (unit_type_flag(i, F_PIKEMEN) && def) cur *= 1.5; - if (unit_types[i].move_type == LAND_MOVING && def) cur *= 1.5; - return(cur); + int desire, attack, defense; + + desire = get_unit_type(i)->hp; + + if (unit_types[i].move_type != SEA_MOVING || !for_defense) + desire *= get_unit_type(i)->firepower; + + if (for_defense) desire *= 3; + else desire *= get_unit_type(i)->move_rate; + + attack = get_unit_type(i)->attack_strength; + defense = get_unit_type(i)->defense_strength; + if (for_defense) desire *= defense; + else if (defense > attack) return(0); + else desire *= attack; + /* wanted to rank Legion > Catapult > Archer */ + /* which we will do by munging f in the attacker want equations */ + + if (unit_type_flag(i, F_IGTER) && !for_defense) desire *= 3; + if (unit_type_flag(i, F_PIKEMEN) && for_defense) desire *= 1.5; + if (unit_types[i].move_type == LAND_MOVING && for_defense) desire *= 1.5; + return(desire); } /************************************************************************** @@ -465,68 +507,95 @@ return(unit_desirability(i, 0)); } +/************************************************************************** +What would be the best defender of that city +**************************************************************************/ static void process_defender_want(struct player *pplayer, struct city *pcity, int danger, struct ai_choice *choice) { Unit_Type_id i; Unit_Type_id bestid = 0; /* ??? Zero is legal value! (Settlers by default) */ - Tech_Type_id tech_req; - int j, k, l, move_type, n; - int best= 0; + int best = 0; int walls = city_got_citywalls(pcity); - int desire[U_LAST]; /* what you get is what you seek */ + int desire[U_LAST]; /* technologies which we want */ int shore = is_terrain_near_tile(pcity->x, pcity->y, T_OCEAN); - int isdef = has_a_normal_defender(pcity); + int defended = has_a_normal_defender(pcity); memset(desire, 0, sizeof(desire)); + for (i = 0; i < game.num_unit_types; i++) { + int move_type; + if (!is_ai_simple_military(i)) continue; + move_type = unit_types[i].move_type; if ((move_type == LAND_MOVING || move_type == SEA_MOVING)) { - k = pplayer->ai.tech_turns[unit_types[i].tech_requirement]; - j = unit_desirability(i, 1); - if (!isdef && unit_type_flag(i, F_FIELDUNIT)) j = 0; - j /= 15; /* good enough, no rounding errors */ - j *= j; + int udesire; /* how much we want the unit */ + int techdist; /* how much technologies away it is */ + int techcost; /* how much the technology would cost us */ + + techdist = pplayer->ai.tech_turns[unit_types[i].tech_requirement]; + + udesire = unit_desirability(i, 1); + if (!defended && unit_type_flag(i, F_FIELDUNIT)) udesire = 0; + udesire /= 15; /* good enough, no rounding errors */ + udesire *= udesire; + if (can_build_unit(pcity, i)) { - if (walls && move_type == LAND_MOVING) { j *= pcity->ai.wallvalue; j /= 10; } - if ((j > best || (j == best && get_unit_type(i)->build_cost <= - get_unit_type(bestid)->build_cost)) && + if (walls && move_type == LAND_MOVING) { + udesire *= pcity->ai.wallvalue; udesire /= 10; + } + + if ((udesire > best || + (udesire == best && + get_unit_type(i)->build_cost <= get_unit_type(bestid)->build_cost)) && unit_types[i].build_cost <= pcity->shield_stock + 40) { - best = j; + best = udesire; bestid = i; } - } else if (k > 0 && (shore || move_type == LAND_MOVING) && - unit_types[i].tech_requirement != A_LAST) { - if (move_type == LAND_MOVING) { j *= pcity->ai.wallvalue; j /= 10; } - l = k * (k + pplayer->research.researchpoints) * game.researchcost / - (game.year > 0 ? 2 : 4); /* cost (shield equiv) of gaining these techs */ - l /= city_list_size(&pplayer->cities); -/* Katvrr advises that with danger high, l should be weighted more heavily */ - desire[i] = j * danger / (unit_types[i].build_cost + l); + + /* first develop required tech */ + } else if (techdist > 0 && (shore || move_type == LAND_MOVING) && + unit_types[i].tech_requirement != A_LAST) { + if (move_type == LAND_MOVING) { + udesire *= pcity->ai.wallvalue; udesire /= 10; + } + + /* cost (shield equiv) of gaining these techs */ + techcost = techdist * (techdist + pplayer->research.researchpoints) * + game.researchcost / (game.year > 0 ? 2 : 4); + techcost /= city_list_size(&pplayer->cities); + /* Katvrr advises that with danger high, techcost should be weighted more heavily */ + + desire[i] = udesire * danger / (unit_types[i].build_cost + techcost); + /* Phalanx would be 16 * danger / 20. Pikemen would be 36 * danger / (20 + l) */ } } } + if (!walls && unit_types[bestid].move_type == LAND_MOVING) { best *= pcity->ai.wallvalue; best /= 10; } /* was getting four-figure desire for battleships otherwise. -- Syela */ -/* Phalanx would be 16 * danger / 20. Pikemen would be 36 * danger / (20 + l) */ if (best == 0) best = 1; /* avoid divide-by-zero below */ -/* multiply by unit_types[bestid].build_cost / best */ + /* develop appropriate techs for units we want to build */ for (i = 0; i < game.num_unit_types; i++) { if (desire[i] && is_ai_simple_military(i)) { + int tdesire; /* desire ... */ + Tech_Type_id tech_req; /* ... for this tech */ + tech_req = unit_types[i].tech_requirement; - n = desire[i] * unit_types[bestid].build_cost / best; - pplayer->ai.tech_want[tech_req] += n; /* not the totally idiotic - pplayer->ai.tech_want[tech_req] += n * pplayer->ai.tech_turns[tech_req]; - I amaze myself. -- Syela */ + tdesire = desire[i] * unit_types[bestid].build_cost / best; + pplayer->ai.tech_want[tech_req] += tdesire; /* not the totally idiotic + pplayer->ai.tech_want[tech_req] += tdesire * pplayer->ai.tech_turns[tech_req]; + I amaze myself. -- Syela */ freelog(LOG_DEBUG, "%s wants %s for defense with desire %d <%d>", - pcity->name, advances[tech_req].name, n, desire[i]); + pcity->name, advances[tech_req].name, tdesire, desire[i]); } } + choice->choice = bestid; choice->want = danger; choice->type = CT_DEFENDER; diff -ru freeciv/ai/aiunit.c freeciv+pasky/ai/aiunit.c --- freeciv/ai/aiunit.c Fri Nov 23 23:00:31 2001 +++ freeciv+pasky/ai/aiunit.c Fri Nov 23 23:01:49 2001 @@ -62,6 +62,8 @@ /************************************************************************** + For curious - ZOC is Zone Of Control + Similar to is_my_zoc(), but with some changes: - destination (x0,y0) need not be adjacent? - don't care about some directions? @@ -76,6 +78,7 @@ if (same_pos(x0, y0, myunit->x, myunit->y)) return 0; /* can't be my zoc */ + if (is_tiles_adjacent(x0, y0, myunit->x, myunit->y) && !is_non_allied_unit_tile(map_get_tile(x0, y0), unit_owner(myunit))) @@ -84,7 +87,8 @@ adjc_iterate(x0, y0, ax, ay) { if (map_get_terrain(ax, ay) != T_OCEAN && is_non_allied_unit_tile(map_get_tile(ax, ay), - unit_owner(myunit))) return 0; + unit_owner(myunit))) + return 0; } adjc_iterate_end; return 1; @@ -112,6 +116,7 @@ if (could_be_my_zoc(punit, src_x, src_y)) return -1; } + return 0; } @@ -138,50 +143,46 @@ all other units it can only produce an estimation. Things like railroad, roads will make the actual move cost lower than the given estimation. Mountains, swamps will increase actual move cost. - ***********************************************************************/ - - static int unit_move_turns(struct unit *punit, int x, int y) - { - int result, move_rate = unit_type(punit)->move_rate; +***********************************************************************/ +static int unit_move_turns(struct unit *punit, int x, int y) +{ + int result, move_rate = unit_type(punit)->move_rate; - switch (unit_type(punit)->move_type) { - case LAND_MOVING: - if (unit_flag(punit, F_IGTER)) { - move_rate *= SINGLE_MOVE; - } - result = warmap.cost[x][y] / move_rate; - break; - - case SEA_MOVING: - if (player_owns_active_wonder(unit_owner(punit), B_LIGHTHOUSE)) { - move_rate += SINGLE_MOVE; - } - if (player_owns_active_wonder(unit_owner(punit), B_MAGELLAN)) { - move_rate += (improvement_variant(B_MAGELLAN) == 1) ? - SINGLE_MOVE : 2 * SINGLE_MOVE; - } - if (player_knows_techs_with_flag(unit_owner(punit), TF_BOAT_FAST)) { - move_rate += SINGLE_MOVE; - } - result = warmap.seacost[x][y] / move_rate; - break; - - case HELI_MOVING: - case AIR_MOVING: - result = real_map_distance(punit->x, punit->y, x,y) * SINGLE_MOVE / move_rate; - break; - - default: - assert(0); - exit(1); - result = -1; - } - return result; - } + switch (unit_type(punit)->move_type) { + case LAND_MOVING: + if (unit_flag(punit, F_IGTER)) { + move_rate *= SINGLE_MOVE; + } + result = warmap.cost[x][y] / move_rate; + break; - + case SEA_MOVING: + if (player_owns_active_wonder(unit_owner(punit), B_LIGHTHOUSE)) { + move_rate += SINGLE_MOVE; + } + if (player_owns_active_wonder(unit_owner(punit), B_MAGELLAN)) { + move_rate += (improvement_variant(B_MAGELLAN) == 1) ? + SINGLE_MOVE : 2 * SINGLE_MOVE; + } + if (player_knows_techs_with_flag(unit_owner(punit), TF_BOAT_FAST)) { + move_rate += SINGLE_MOVE; + } + result = warmap.seacost[x][y] / move_rate; + break; + + case HELI_MOVING: + case AIR_MOVING: + result = real_map_distance(punit->x, punit->y, x,y) * SINGLE_MOVE / move_rate; + break; + default: + assert(0); + exit(1); + result = -1; + } + return result; +} /************************************************************************** is there any hope of reaching this tile without violating ZOC? @@ -627,32 +628,35 @@ int *dest_x, int *dest_y) { int x, y; - int best = 0, a, b, c, d, e, f; - struct unit *pdef; - struct unit *patt; - struct city *pcity; + int best = 0, belligerence; + x = punit->x; y = punit->y; *dest_y = y; *dest_x = x; if (punit->unhappiness) best = 0 - 2 * MORT * TRADE_WEIGHTING; /* desperation */ - f = unit_type(punit)->build_cost; - c = 0; /* only dealing with adjacent victims here */ + + belligerence = unit_belligerence_primitive(punit); + /* ferryboats do not attack. no. -- Syela */ if (get_transporter_capacity(punit)) { unit_list_iterate(map_get_tile(x, y)->units, aunit) if (!is_sailing_unit(aunit)) return(0); unit_list_iterate_end; - } /* ferryboats do not attack. no. -- Syela */ + } adjc_iterate(x, y, x1, y1) { - pdef = get_defender(punit, x1, y1); + struct unit *pdef = get_defender(punit, x1, y1); + if (pdef) { - patt = get_attacker(punit, x1, y1); -/* horsemen in city refused to attack phalanx just outside that was -bodyguarding catapult - patt will resolve this bug nicely -- Syela */ + + /* horsemen in city refused to attack phalanx just outside that was + bodyguarding catapult - patt will resolve this bug nicely -- Syela */ + struct unit *patt = get_attacker(punit, x1, y1); + if (can_unit_attack_tile(punit, x1, y1)) { /* thanks, Roar */ - d = unit_vulnerability(punit, pdef); + int vuln = unit_vulnerability(punit, pdef); + if (map_get_city(x, y) && /* pikemen defend Knights, attack Catapults */ get_total_defense_power(pdef, punit) * get_total_defense_power(punit, pdef) >= /* didn't like > -- Syela */ @@ -664,51 +668,74 @@ unit_type(punit)->name, map_get_city(x, y)->name, unit_owner(pdef)->name, unit_type(pdef)->name); - } else { - a = reinforcements_value(punit, pdef->x, pdef->y); - a += unit_belligerence_primitive(punit); - a *= a; - b = unit_type(pdef)->build_cost; + + } else { + /* The ability of destroying the enemy, the morale of soldiers there ;-). */ + int agress; + /* Something like 'appetency' of the victim, how nice it would be to + * destroy it. Larger price, enemy will have worse loss. If it is in + * the city, loss will be even worse. We correct it with our health + * because there may be more units in the city stacked, and we won't + * destroy them all, so in the next turn they may attack us. So we + * shouldn't do this with already injured units. */ + int bonus; + int coef; + + agress = reinforcements_value(punit, pdef->x, pdef->y); + agress += belligerence; + agress *= agress; + + bonus = unit_type(pdef)->build_cost; if (map_get_city(x1, y1)) /* bonus restored 980804 -- Syela */ - b = (b + 40) * punit->hp / unit_type(punit)->hp; -/* c is always equal to zero in this routine, and f is already known */ -/* arguable that I should use reinforcement_cost here?? -- Syela */ - if (a && is_my_turn(punit, pdef)) { - e = ((b * a - f * d) * SHIELD_WEIGHTING / (a + d) - c * SHIELD_WEIGHTING); -/* no need to amortize! */ - if (e > best && ai_fuzzy(pplayer,1)) { + bonus = (bonus + 40) * punit->hp / unit_type(punit)->hp; + + /* arguable that I should use reinforcement_cost here?? -- Syela */ + if (agress && is_my_turn(punit, pdef)) { + + /* appetency danger */ + coef = ((bonus * agress - unit_type(punit)->build_cost * vuln) / (agress + vuln) + * SHIELD_WEIGHTING * SHIELD_WEIGHTING); + + /* no need to amortize! */ + if (coef > best && ai_fuzzy(pplayer, 1)) { freelog(LOG_DEBUG, "Better than %d is %d (%s)", - best, e, unit_type(pdef)->name); - best = e; *dest_y = y1; *dest_x = x1; + best, coef, unit_type(pdef)->name); + best = coef; *dest_y = y1; *dest_x = x1; } else { freelog(LOG_DEBUG, "NOT better than %d is %d (%s)", - best, e, unit_type(pdef)->name); + best, coef, unit_type(pdef)->name); } } /* end if we have non-zero belligerence */ } } + } else { /* no pdef */ - pcity = map_get_city(x1, y1); + struct city *pcity = map_get_city(x1, y1); + + /* free city waiting for us */ if (pcity && is_ground_unit(punit) && - map_get_terrain(punit->x, punit->y) != T_OCEAN) { - if (pcity->owner != pplayer->player_no) { /* free goodies */ - best = 99999; *dest_y = y1; *dest_x = x1; - } + map_get_terrain(punit->x, punit->y) != T_OCEAN && + pcity->owner != pplayer->player_no) { + best = 99999; *dest_y = y1; *dest_x = x1; } + if (map_get_tile(x1, y1)->special & S_HUT && best < 99999 && could_unit_move_to_tile(punit, punit->x, punit->y, x1, y1) && - !is_barbarian(unit_owner(punit)) && -/* zoc_ok_move(punit, x1, y1) && !is_sailing_unit(punit) &&*/ + !is_barbarian(unit_owner(punit)) && /* barbarians don't care about huts */ +/* zoc_ok_move(punit, x1, y1) && + !is_sailing_unit(punit) && */ punit->ai.ai_role != AIUNIT_ESCORT && /* makes life easier */ !punit->ai.charge && /* above line seems not to work. :( */ punit->ai.ai_role != AIUNIT_DEFEND_HOME) { /* Oops! -- Syela */ best = 99998; *dest_y = y1; *dest_x = x1; } - if( is_land_barbarian(pplayer) && best == 0 && + + /* next to nothing is better than nothing */ + if (is_land_barbarian(pplayer) && best == 0 && get_tile_infrastructure_set(map_get_tile(x1, y1)) && - could_unit_move_to_tile(punit, punit->x, punit->y, x1, y1) ) { + could_unit_move_to_tile(punit, punit->x, punit->y, x1, y1)) { best = 1; *dest_y = y1; *dest_x = x1; - } /* next to nothing is better than nothing */ + } } } adjc_iterate_end;