From 0dcf9e4d5e40d78fb58a1ee9e108e22f2be5900e Mon Sep 17 00:00:00 2001 From: Ihnatus Date: Tue, 4 Apr 2023 00:19:45 +0300 Subject: [PATCH] AI: evaluate unit fortifying bonuses in aitech.c Scales considered unit type city defender worth on expected fortify defense bonus. The core requirement-analizing function is recorded in daimilitary.c to be reused in other places. See OSDN#41781. Signed-off-by: Ihnatus --- ai/default/aitech.c | 18 ++ ai/default/daimilitary.c | 72 ++++++ ai/default/daimilitary.h | 4 + ai/default/daiunit.c | 457 +++++++++++++++++++++++++++++++++++++++ ai/default/daiunit.h | 18 ++ common/city.c | 2 +- common/city.h | 2 +- common/unittype.c | 2 +- common/unittype.h | 2 +- 9 files changed, 573 insertions(+), 4 deletions(-) diff --git a/ai/default/aitech.c b/ai/default/aitech.c index 8d548286b1..928914f076 100644 --- a/ai/default/aitech.c +++ b/ai/default/aitech.c @@ -41,6 +41,7 @@ #include "daidata.h" #include "daieffects.h" #include "dailog.h" +#include "daimilitary.c" /* fortify_weighter */ #include "daiplayer.h" #include "aitech.h" @@ -412,6 +413,23 @@ struct unit_type *dai_wants_defender_against(struct ai_type *ait, /* Either the unit uses city defense bonus, or scrambles with its own one */ int def = deftype->defense_strength * (scramble ? scramble : defbonus * mp_pct) / div_bonus_pct; + /* Estimated build turns (to track bonuses soon to expire) */ + int build_turns = 1 + + (utype_build_shield_cost(pcity, pplayer, deftype) - 1) + / MAX(1, pcity->surplus[O_SHIELD]); + + /* FIXME: units that have veteran levels, and especially may be built + * with them, should be favoured (just don't value battleships too low + * only because we have Barracks II and can't build Port Facility) */ + /* Consider if the unit uses some "fortified" bonus. */ + def = def * 0.01 + * get_effect_expected_value(&(const struct req_context){ + .player = pplayer, + .city = pcity, + .tile = city_tile(pcity), + .unittype = deftype, + }, NULL, EFT_FORTIFY_DEFENSE_BONUS, + fortify_weighter, NULL, build_turns); def_values[utype_index(deftype)] = def; diff --git a/ai/default/daimilitary.c b/ai/default/daimilitary.c index 6e910b999e..5f68c28fb5 100644 --- a/ai/default/daimilitary.c +++ b/ai/default/daimilitary.c @@ -950,6 +950,78 @@ static unsigned int assess_danger(struct ai_type *ait, struct city *pcity, return urgency; } +/**********************************************************************//** + A weighter callback for "Fortify_Defense_Bonus" effects, + a wrapper around planned_unit_reqs_prob (context must be passable to it) + n_data is estimated turns to build the unit + Considers if the unit of context unit type will be fortified in the city, + and will it be automatic or not. + If the bonus is blocked by walls, thinks about that present walls + may ever fail and not present walls may be ever erected. + Player must be present in context if there is a multiplier - to get it. +**************************************************************************/ +double fortify_weighter(const struct effect *eft, + const struct req_context *context, + const struct player *other_player, + void *data, int n_data) +{ + int value = eft->value; + const struct multiplier *pmul = eft->multiplier; + const struct unit_type *ut = context->unittype; + double result; + struct unit_req_constraints constr = { + .minhp = 1, .caphp = ut->hp + 1, + .minage = 0, .capage = FC_INFINITY, + .mincal = 0, .capcal = game.info.fragment_count + 1 + }; + + if (0 == value) { + return 0.; + } + if (pmul) { + fc_assert_ret_val(context->player, 0.); + result = value + * player_multiplier_effect_value(context->player, pmul) / 100; + } else { + result = value; + } + result *= + planned_unit_reqs_prob(eft->type, context->city, &eft->reqs, context, + other_player, &constr, n_data); + /* It's supposed that negative intervals are blocked on game load */ + if (constr.minhp > 1 || constr.caphp <= ut->hp) { + /* It's no surprise for a defender to be damaged. + * We even won't look at regeneration speed. + * In rulesets with artificially big firepowers, that gives + * false assumptions, but the reqs are redundant there any way */ + result *= 0.7 * (constr.caphp - constr.minhp) / ut->hp + 0.3; + } + if (constr.minage > 0 || constr.capage != FC_INFINITY) { + /* WAG: attacks in 5 turns, 85% defenders survive or something + * So probability that an attack happens at age A is about + * (1 - 0.2 + 0.2 * 0.85) ^ A * 0.2 == 0.2 * 0.97 ^ A + * that cumulates in 0.2 * (1 - 0.97 ^ (A + 1)) / (1 - 0.97) + * Total expected number of attacks is 0.2 / 0.03, divide on it */ + double att_frac = constr.minage > 0 ? pow(0.97, constr.minage) : 1.; + + if (constr.capage != FC_INFINITY) { + att_frac -= pow(0.97, constr.capage); + } + result *= att_frac; + } + if (game.info.fragment_count > 0 + && get_world_bonus(EFT_TURN_FRAGMENTS) % game.info.fragment_count) { + /* Supposed that turn fragments grow smoothly */ + if (constr.capcal < constr.mincal) { + return 0.; + } else { + result *= (double) (constr.capcal - constr.mincal) + / game.info.fragment_count; + } + } + return result; +} + /**********************************************************************//** How much we would want that unit to defend a city? (Do not use this function to find bodyguards for ships or air units.) diff --git a/ai/default/daimilitary.h b/ai/default/daimilitary.h index 6d17e9df14..35964bb7b0 100644 --- a/ai/default/daimilitary.h +++ b/ai/default/daimilitary.h @@ -49,6 +49,10 @@ int assess_defense_quadratic(struct ai_type *ait, struct city *pcity); int assess_defense_unit(struct ai_type *ait, struct city *pcity, struct unit *punit, bool igwall); int assess_defense(struct ai_type *ait, struct city *pcity); +double fortify_weighter(const struct effect *eft, + const struct req_context *context, + const struct player *other_player, + void *data, int n_data); int dai_unit_defense_desirability(struct ai_type *ait, const struct unit_type *punittype); int dai_unit_attack_desirability(struct ai_type *ait, diff --git a/ai/default/daiunit.c b/ai/default/daiunit.c index 2806b8cc1b..57051a3969 100644 --- a/ai/default/daiunit.c +++ b/ai/default/daiunit.c @@ -35,6 +35,7 @@ #include "map.h" #include "movement.h" #include "packets.h" +#include "research.h" #include "specialist.h" #include "traderoutes.h" #include "unit.h" @@ -348,6 +349,462 @@ int kill_desire(int benefit, int attack, int loss, int vuln, int victim_count) return desire; } +/**********************************************************************//** + Estimate probability of unit that is only planned in pcity to be buil + to fulfill effect reqs when it will be used. + This function currently does not use any stored data about AI plans. + It is mainly designed to help estimating effects + related to defense and trading by the unit; effect_type hints + the context interpretation and prevents endless recursion. + Requirements that are supposed to form interval constraints + (i.e., unit age and hitpoints) are recorded into data and + their assessment is left to the caller; data must be set and + correctly initialized. + context must contain unit type and player. +**************************************************************************/ +double planned_unit_reqs_prob(enum effect_type effect, + const struct city *pcity, + const struct requirement_vector *reqs, + const struct req_context *context, + const struct player *other_player, + struct unit_req_constraints *data, + int turns_to_build) +{ + const struct unit_type *ut; + const struct player *pplayer; + int vet; + double result = 1.; + + fc_assert_ret_val(reqs, 0.); + fc_assert_ret_val(context, 0.); + fc_assert_ret_val(pcity, 0.); + fc_assert_ret_val(data, 0.); + ut = context->unittype; + fc_assert_ret_val(ut, 0.); + pplayer = context->player; + fc_assert_ret_val(pplayer, 0.); + vet = city_production_unit_veteran_level(pcity, ut); + + requirement_vector_iterate(reqs, preq) { + double prob = 0.; /* initialized to silence warnings */ + + switch (preq->source.kind) { + case VUT_ACTIVITY: + /* Avoid recursion from some botched ruleset. + * Actually, EFT_FORTIFY_DEFENSE_BONUS in pcity is presumed. + * FIXME: if the total effect is negative, we better shall not + * fortify at all, but this is unhandlable by AI for now */ + if (EFT_COUNT != effect + && ACTIVITY_FORTIFIED == preq->source.value.activity) { + prob = 0.; + /* we must become fortifying to get fortified */ + if (!utype_can_do_action_result(ut, ACTRES_FORTIFY)) { + break; + } else if (preq->present) { + data->minage = MAX(data->minage, 1); /* need a turn to fortify */ + } + action_by_result_iterate(act, act_id, ACTRES_FORTIFY) { + action_enabler_list_iterate(action_enablers_for_action(act_id), + ae) { + prob += + planned_unit_reqs_prob(EFT_COUNT, pcity, &ae->actor_reqs, context, + other_player, data, turns_to_build); + } action_enabler_list_iterate_end; + } action_by_result_iterate_end; + prob *= CITYDEF_FORTIFY_PROB; + break; + } else if (ACTIVITY_IDLE != preq->source.value.activity) { + /* Unlikely for a city defender, not for long at least */ + prob = 0.; + } else { + /* FIXME: it is always doable but AI is not adapted to it */ + continue; /* requirement_vector_iterate */ + } + break; + case VUT_IMPROVEMENT: + /* Here, we mostly mean City Walls or Great Wall in rulesets + * where their bonus is incompatible with the fortifying one + * Presuming Walls are available early, and GW obsoletes soon */ + { + const struct impr_type *b = preq->source.value.building; + + /* A normal building may be sold or sabotaged, + * or built if it has any positive effect + * FIXME: cache doesn't tell positive from negative effects */ + /* TODO: Here we could test if utype_needs_improvement() == b + * but that function does not look at range and tests too hard */ + if (is_improvement(b)) { + /* Some actions are performed from other cites, + * but we suggest that there it is obsolete as well */ + if (improvement_obsolete(pplayer, b, pcity)) { + prob = 0.; + break; + } + prob = is_req_active(context, other_player, preq, RPT_CERTAIN) + ? (preq->present ? 0.85 : 0.5) : 0.5; + if (b->sabotage > 0 && building_has_effect(b, EFT_DEFEND_BONUS)) { + /* We could test if there is an action doing it + * but we consider the parameter not redundant */ + prob *= 1. - b->sabotage / 200.; + } + } else { + bool wonder_here = + is_req_active(context, other_player, preq, RPT_CERTAIN) + ? preq->present : !preq->present; + + if (requirement_vector_size(&b->obsolete_by) > 0) { + if (!wonder_here + && improvement_obsolete(pplayer, b, + preq->range <= REQ_RANGE_CONTINENT + ? pcity : NULL)) { + prob = 0.; + break; + } else { + prob = 0.75; + } + } else { + prob = 0.9; + } + if (!wonder_here) { + /* A wonder is less likely to appear right here but maybe... */ + if (preq->range <= REQ_RANGE_CONTINENT) { + /* We don't rely on connections more than on city itself */ + if (can_city_build_improvement_direct(pcity, b)) { + prob /= city_list_size(pplayer->cities); + } else { + prob = 0.; + break; + } + } else if (!can_player_build_improvement_direct(pplayer, b)) { + prob = 0.; + break; + } + if (preq->range < REQ_RANGE_WORLD && is_great_wonder(b)) { + int plrs_alive = 0, plrs_range = 0; + + players_iterate_alive(aplr) { + plrs_alive++; + if (pplayer == aplr + || (REQ_RANGE_ALLIANCE == preq->range + && pplayers_allied(pplayer, aplr)) + || (REQ_RANGE_TEAM == preq->range + && players_on_same_team(pplayer, aplr))) { + plrs_range++; + } + } players_iterate_alive_end; + fc_assert_action(plrs_alive > 0, continue); + prob *= (double) plrs_range / plrs_alive; + } + } + } + } + break; + /* TODO: case VUT_IMPR_FLAG? */ + case VUT_ADVANCE: + /* Suggest the player is a usual one, so don't test range + * FIXME: that may fail in rulesets with nation specific root reqs + * Once is known, hardly gets unknown; if is up to be researched, + * probably will be known when the unit will defend */ + { + const struct research *pres = research_get(context->player); + const struct advance *tech = preq->source.value.advance; + + fc_assert_ret_val(tech, 0.); + if (is_tech_req_for_utype(ut, tech)) { + /* TODO: For long range research planning, advances indirectly + * required by the utype may be considered known */ + prob = 1.; + break; + } + /* Look if we can learn an unknown tech (hope we won't forget one) */ + if (is_req_active(context, other_player, preq, RPT_CERTAIN) + ? !preq->present : preq->present) { + if (TECH_PREREQS_KNOWN == + research_invention_state(pres, advance_number(tech))) { + prob = 0.5; + } else { + /* don't look so far */ + prob = 0.; + } + break; + } + } + continue; + /* TODO: case VUT_TECHFLAG same way, but don't want to iterate */ + case VUT_ACTION: + /* A trade action is suggested. */ + /* FIXME: will there be a possible target, is not speculated. */ + /* TODO: when defensive effects get action specific, look at effect */ + enum gen_action act_id = action_number(preq->source.value.action); + + prob = 0.; + if (utype_can_do_action(ut, act_id)) { + const struct req_context actx = { + .player = pplayer, + .unittype = ut, + }; + + /* We can do really little here but checking for veteranship */ + action_enabler_list_iterate(action_enablers_for_action(act_id), + ae) { + bool baden = FALSE; + + requirement_vector_iterate(&ae->actor_reqs, areq) { + if (VUT_MINVETERAN == areq->source.kind + ? (vet >= areq->source.value.minveteran + ? !areq->present : areq->present) + : TRI_NO + == tri_req_active_turns(turns_to_build, 20 /* WAG */, + &actx, NULL, areq)) { + baden = TRUE; + break; + } + } requirement_vector_iterate_end; + if (baden) { + continue; /* action_enabler_list_iterate */ + } else { + prob = 1.; + break; /* action_enabler_list_iterate */ + } + } action_enabler_list_iterate_end; + } + break; + case VUT_MAXTILEUNITS: + /* TODO: support other defensive effects if needed */ + if (EFT_FORTIFY_DEFENSE_BONUS != effect) { + /* No idea how many units are there trading, hope enough */ + result *= 0.75; + continue; /* requirement_vector_iterate */ + } + fc_assert_ret_val(NULL != context->tile, 0.); + if (REQ_RANGE_TILE == preq->range) { + if (unit_list_size(context->tile->units) + < preq->source.value.max_tile_units) { + prob = 0.8; /* WAG */ + } else { + prob = 0.1; /* WAG */ + } + } else { + /* Don't even try to hypotetize on the area */ + if (is_req_active(context, other_player, preq, RPT_CERTAIN)) { + result *= 0.8; /* WAG */ + } else { + result *= 0.5; /* WAG */ + } + continue; /* requirement_vector_iterate */ + } + break; + case VUT_CITYSTATUS: + switch (preq->source.value.citystatus) { + case CITYS_OWNED_BY_ORIGINAL: + /* Certainly known (or supposed to be, for traderoute) */ + if (!is_req_active(context, other_player, preq, RPT_CERTAIN)) { + return 0.; + } + break; + case CITYS_STARVED: + case CITYS_DISORDER: + case CITYS_CELEBRATION: + if (preq->present) { + result *= + is_req_active(context, other_player, preq, RPT_CERTAIN) + ? (REQ_RANGE_TRADEROUTE == preq->range ? 0.75 : 0.5) + : (REQ_RANGE_TRADEROUTE == preq->range ? 0.25 : 0.1); /* WAG */ + } else { + result *= (REQ_RANGE_TRADEROUTE == preq->range ? 0.75 : 0.9); + } + break; + case CITYS_LAST: + /* error */ + fc_assert(CITYS_LAST != preq->source.value.citystatus); + } + continue; /* requirement_vector_iterate */ + case VUT_UNITSTATE: + /* TODO: trading effects can have better support + * from action requirement analysis */ + switch (preq->source.value.unit_state) { + case USP_LIVABLE_TILE: + /* A city is here */ + prob = 1.; + break; + case USP_NATIVE_TILE: + prob = is_native_tile(ut, context->tile) ? 1. : 0.; + break; + case USP_NATIVE_EXTRA: + prob = tile_has_native_base(context->tile, ut) ? 1. : 0.; + break; + case USP_HAS_HOME_CITY: + /* Sometimes we can un-home the unit but it's too mad to consider */ + prob = utype_has_flag(ut, UTYF_NOHOME) ? 0. : 1.; + break; + case USP_MOVED_THIS_TURN: + prob = 1. - CITYDEF_UNMOVED_PROB; + break; + case USP_TRANSPORTED: + case USP_TRANSPORTING: + /* FIXME: a frenzied ruleset with such reqs may exist + * but our AI is not smart enough for it yet */ + prob = 0.; + break; + case USP_COUNT: + /* error */ + fc_assert(preq->source.value.unit_state != USP_COUNT); + continue; /* requirement_vector_iterate */ + } + break; + case VUT_MINMOVES: + /* Here we just suppose a single requirement */ + { + int mr + = utype_move_rate(ut, context->tile, pplayer, vet, ut->hp); + + prob = mr >= preq->source.value.minmoves + ? (EFT_FORTIFY_DEFENSE_BONUS == effect + ? CITYDEF_UNMOVED_PROB + : /* AI can't fiddle here now */ + preq->source.value.minmoves / MAX(mr, 1.)) + : 0.; + } + break; + case VUT_MINVETERAN: + /* Don't look if it becomes veteran later */ + prob = vet >= preq->source.value.minveteran ? 1. : 0.; + break; + case VUT_MINHP: + if (preq->source.value.min_hit_points > ut->hp) { + prob = 0.; + } else { + /* That may be an interval, we should look for another req */ + if (preq->present) { + data->minhp = MAX(data->minhp, + preq->source.value.min_hit_points); + } else { + data->caphp = MIN(data->caphp, + preq->source.value.min_hit_points); + } + continue; /* requirement_vector_iterate */ + } + break; + case VUT_MINCALFRAG: + /* That may be an interval, we should look for another req */ + if (preq->present) { + data->mincal = MAX(data->mincal, preq->source.value.mincalfrag); + } else { + data->capcal = MIN(data->capcal, preq->source.value.mincalfrag); + }; + continue; /* requirement_vector_iterate */ + case VUT_MINSIZE: + /* We hope the city grows after the unit is built, + * but alas it may shrink as well... Suppose a single req */ + if (!context->city) { + /* Unlikely we trade from another city? */ + prob = 0; + } else { + prob = city_size_get(context->city) + - utype_pop_value(ut, context->city) + <= preq->source.value.minsize ? 0.9 : 0.1; + } + break; + case VUT_AGE: + /* Special handling only for unit */ + if (REQ_RANGE_LOCAL == preq->range) { + if (preq->present) { + data->minage = MAX(data->minage, preq->source.value.age); + } else { + data->capage = MIN(data->capage, preq->source.value.age); + } + continue; + } + fc__fallthrough; + case VUT_COUNTER: + /* TODO: Turns_Owned is likely to grow ahead... */ + case VUT_CITYTILE: + /* TODO: Trading effects might ever need something here */ + case VUT_NONE: + case VUT_IMPR_GENUS: + case VUT_IMPR_FLAG: + /* TODO: see above */ + case VUT_UTYPE: + case VUT_UTFLAG: + case VUT_UCLASS: + case VUT_UCFLAG: + case VUT_OTYPE: + case VUT_SPECIALIST: + case VUT_DIPLREL_TILE_O: + case VUT_DIPLREL_UNITANY_O: + /* TODO: for trading, might calculate DiplRel from action reqs */ + case VUT_EXTRA: + case VUT_ROADFLAG: + case VUT_EXTRAFLAG: + case VUT_TERRAIN: + case VUT_TERRFLAG: + case VUT_TERRAINCLASS: + case VUT_TERRAINALTER: + /* NOTE: some trading effects might be calculated to other tile */ + case VUT_MINLATITUDE: + case VUT_MAXLATITUDE: + case VUT_MINCULTURE: + /* TODO: may try to extrapolate the growth */ + case VUT_AI_LEVEL: + case VUT_MINFOREIGNPCT: + case VUT_NATIONALITY: + case VUT_ORIGINAL_OWNER: + case VUT_GOOD: + case VUT_GOVERNMENT: + case VUT_ACHIEVEMENT: + case VUT_STYLE: + case VUT_TECHFLAG: + /* TODO: see above */ + case VUT_NATION: + case VUT_NATIONGROUP: + case VUT_DIPLREL: + case VUT_DIPLREL_TILE: + case VUT_DIPLREL_UNITANY: + /* TODO: see above */ + case VUT_MINYEAR: + case VUT_TOPO: + case VUT_MINTECHS: + case VUT_SERVERSETTING: + /* WAG: Look ahead for 20 turns */ + switch (tri_req_active_turns(turns_to_build, 20, + context, other_player, preq)) { + case TRI_NO: + if (is_req_unchanging(context, preq) >= REQUCH_CTRL) { + return 0.; + } else { + result *= 0.1; /* Maybe one day it will get fortified... */ + } + break; + case TRI_YES: + /* OK */ + break; + case TRI_MAYBE: + if (EFT_FORTIFY_DEFENSE_BONUS == effect) { + fc_assert_msg(FALSE, "Requirement %i is suddenly uncertain", + preq->source.kind); + }; + /* Either we meet a living tyrannosaurus in the street tonight, + * or not, 50:50. Just, won't guess. Most reqs are definite */ + result *= 0.5; + } + continue; /* requirement_vector_iterate */ + case VUT_COUNT: + fc_assert_ret_val(preq->source.kind < VUT_COUNT, 0.); + } + + if (!preq->present) { + prob = 1. - prob; + } + if (prob <= 0.) { + return 0.; + } else if (prob < 1.) { + result *= prob; + } + } requirement_vector_iterate_end; + + return result; +} + /**********************************************************************//** Compute how much we want to kill certain victim we've chosen, counted in SHIELDs. See comment to kill_desire. diff --git a/ai/default/daiunit.h b/ai/default/daiunit.h index ce11e91423..471b71e929 100644 --- a/ai/default/daiunit.h +++ b/ai/default/daiunit.h @@ -15,6 +15,7 @@ /* common */ #include "combat.h" +#include "effects.h" #include "fc_types.h" #include "terrain.h" #include "unittype.h" @@ -55,6 +56,15 @@ struct unit_type_ai struct unit_type_list *potential_charges; }; +/* Presumed probabilities that a city defender in time of an attack + * is unmoved or fortified */ +#define CITYDEF_UNMOVED_PROB 0.87 +#define CITYDEF_FORTIFY_PROB (CITYDEF_UNMOVED_PROB * CITYDEF_UNMOVED_PROB) +/* Constraints set by a requirement vector. Need generalization? */ +struct unit_req_constraints { + int minhp, caphp, minage, capage, mincal, capcal; +}; + /* Simple military macros */ /* pplayers_at_war() thinks no contacts equals war, which often is @@ -121,6 +131,14 @@ int unittype_def_rating_squared(const struct unit_type *att_type, struct player *def_player, struct tile *ptile, bool fortified, int veteran); int kill_desire(int benefit, int attack, int loss, int vuln, int attack_count); +double planned_unit_reqs_prob(enum effect_type effect, + const struct city *pcity, + const struct requirement_vector *reqs, + const struct req_context *context, + const struct player *other_player, + struct unit_req_constraints *data, + int turns_to_build) + fc__attribute((nonnull (2, 3, 4, 6))); const struct impr_type *utype_needs_improvement(const struct unit_type *putype, const struct city *pcity); diff --git a/common/city.c b/common/city.c index 0de4bd8476..3580573008 100644 --- a/common/city.c +++ b/common/city.c @@ -793,7 +793,7 @@ bool city_production_build_units(const struct city *pcity, /************************************************************************//** How many veteran levels will created unit of this type get? ****************************************************************************/ -int city_production_unit_veteran_level(struct city *pcity, +int city_production_unit_veteran_level(const struct city *pcity, const struct unit_type *punittype) { int levels = get_unittype_bonus(city_owner(pcity), pcity->tile, punittype, diff --git a/common/city.h b/common/city.h index 3be4ce4eca..44808d105f 100644 --- a/common/city.h +++ b/common/city.h @@ -615,7 +615,7 @@ bool city_got_defense_effect(const struct city *pcity, int city_production_build_shield_cost(const struct city *pcity); bool city_production_build_units(const struct city *pcity, bool add_production, int *num_units); -int city_production_unit_veteran_level(struct city *pcity, +int city_production_unit_veteran_level(const struct city *pcity, const struct unit_type *punittype); bool city_production_is_genus(const struct city *pcity, diff --git a/common/unittype.c b/common/unittype.c index bad8d580aa..a89c6b0fa4 100644 --- a/common/unittype.c +++ b/common/unittype.c @@ -2764,7 +2764,7 @@ struct advance *utype_primary_tech_req(const struct unit_type *ptype) Tell if the given tech is one of unit's tech requirements **************************************************************************/ bool is_tech_req_for_utype(const struct unit_type *ptype, - struct advance *padv) + const struct advance *padv) { unit_tech_reqs_iterate(ptype, preq) { if (padv == preq) { diff --git a/common/unittype.h b/common/unittype.h index 30cc4a08c1..887ce30b70 100644 --- a/common/unittype.h +++ b/common/unittype.h @@ -862,7 +862,7 @@ do { \ /* Used on client to show just one req */ struct advance *utype_primary_tech_req(const struct unit_type *ptype); bool is_tech_req_for_utype(const struct unit_type *ptype, - struct advance *padv); + const struct advance *padv); void *utype_ai_data(const struct unit_type *ptype, const struct ai_type *ai); void utype_set_ai_data(struct unit_type *ptype, const struct ai_type *ai, -- 2.37.2