Project

General

Profile

Feature #616 » 0004-Add-MaxDistanceSq-requirement-maximum-squared-distan.patch

main v2 - Alina Lenk, 05/22/2024 01:42 AM

View differences:

ai/default/daieffects.c
case VUT_ACTION:
case VUT_GOOD:
case VUT_MINCALFRAG:
case VUT_MAX_DISTANCE_SQ:
case VUT_COUNT:
/* No sensible implementation possible with data available. */
break;
ai/default/daimilitary.c
case VUT_TERRAINCLASS:
case VUT_TERRFLAG:
case VUT_TERRAINALTER:
case VUT_MAX_DISTANCE_SQ:
case VUT_NONE:
return tri_req_active(context, other_context, req);
case VUT_COUNT:
common/fc_types.h
int min_techs;
int min_cities;
int latitude;
int distance_sq;
enum topo_flag topo_property;
enum wrap_flag wrap_property;
common/metaknowledge.c
}
}
if (req->source.kind == VUT_MAX_DISTANCE_SQ) {
if (context->tile == nullptr || other_context->tile == nullptr) {
/* The tiles may exist but not be passed when the problem type is
* RPT_POSSIBLE. */
return prob_type == RPT_CERTAIN;
}
/* Tile locations and their distance are fixed */
return TRUE;
}
if (req->source.kind == VUT_ACTION
|| req->source.kind == VUT_OTYPE) {
/* This requirement type is intended to specify the situation. */
common/reqtext.c
}
break;
case VUT_MAX_DISTANCE_SQ:
switch (preq->range) {
case REQ_RANGE_TILE:
fc_strlcat(buf, prefix, bufsz);
/* Test some special cases */
switch (preq->source.value.distance_sq)
{
case 0:
if (preq->present) {
fc_strlcat(buf, _("Must be the same tile."), bufsz);
} else {
fc_strlcat(buf, _("Must not be the same tile."), bufsz);
}
break;
case 1:
if (preq->present) {
fc_strlcat(buf, _("Must be cardinally adjacent."), bufsz);
} else {
fc_strlcat(buf, _("Must not be cardinally adjacent."), bufsz);
}
break;
case 2:
case 3:
if (preq->present) {
fc_strlcat(buf, _("Must be adjacent."), bufsz);
} else {
fc_strlcat(buf, _("Must not be adjacent."), bufsz);
}
break;
default:
if (preq->present) {
cat_snprintf(buf, bufsz,
_("The squared distance between the tiles "
"must be at most %d."),
preq->source.value.distance_sq);
} else {
cat_snprintf(buf, bufsz,
_("The squared distance between the tiles "
"must be at least %d."),
preq->source.value.distance_sq + 1);
}
break;
}
return TRUE;
case REQ_RANGE_CADJACENT:
case REQ_RANGE_ADJACENT:
case REQ_RANGE_CITY:
case REQ_RANGE_TRADE_ROUTE:
case REQ_RANGE_CONTINENT:
case REQ_RANGE_PLAYER:
case REQ_RANGE_TEAM:
case REQ_RANGE_ALLIANCE:
case REQ_RANGE_WORLD:
case REQ_RANGE_LOCAL:
case REQ_RANGE_COUNT:
/* Not supported. */
break;
}
break;
case VUT_COUNT:
break;
}
common/requirements.c
return;
}
break;
case VUT_MAX_DISTANCE_SQ:
source->value.distance_sq = atoi(value);
if (0 <= source->value.distance_sq) {
return;
}
break;
case VUT_COUNT:
break;
}
......
case VUT_MAXLATITUDE:
source.value.latitude = value;
return source;
case VUT_MAX_DISTANCE_SQ:
source.value.distance_sq = value;
return source;
case VUT_COUNT:
break;
}
......
case VUT_MINLATITUDE:
case VUT_MAXLATITUDE:
return source->value.latitude;
case VUT_MAX_DISTANCE_SQ:
return source->value.distance_sq;
case VUT_COUNT:
break;
}
......
case VUT_MAXTILEUNITS:
case VUT_MINLATITUDE:
case VUT_MAXLATITUDE:
case VUT_MAX_DISTANCE_SQ:
req.range = REQ_RANGE_TILE;
break;
case VUT_COUNTER:
......
&& req.range != REQ_RANGE_ADJACENT);
break;
case VUT_TERRAINALTER: /* XXX could in principle support C/ADJACENT */
case VUT_MAX_DISTANCE_SQ:
invalid = (req.range != REQ_RANGE_TILE);
break;
case VUT_CITYTILE:
......
case VUT_MINCITIES:
case VUT_MINLATITUDE:
case VUT_MAXLATITUDE:
case VUT_MAX_DISTANCE_SQ:
/* Most requirements don't support 'survives'. */
invalid = survives;
break;
......
return are_requirements_contradictions(req1, &nreq2);
}
/**********************************************************************//**
Returns TRUE iff two bounds that could each be either an upper or lower
bound are contradicting each other. This function assumes that of the
upper and lower bounds, one will be inclusive, the other exclusive.
**************************************************************************/
static inline bool are_bounds_contradictions(int bound1, bool is_upper1,
int bound2, bool is_upper2)
{
/* If the bounds are on opposite sides, and one is inclusive, the other
* exclusive, the number of values that satisfy both bounds is exactly
* their difference, (upper bound) - (lower bound).
* The bounds contradict each other iff this difference is 0 or less,
* i.e. iff (upper bound) <= (lower bound) */
if (is_upper1 && !is_upper2) {
return bound1 <= bound2;
} else if (!is_upper1 && is_upper2) {
return bound1 >= bound2;
}
/* Both are upper or both are lower ~> no contradiction possible */
return FALSE;
}
/**********************************************************************//**
Returns TRUE if req1 and req2 contradicts each other.
......
/* Finding contradictions across requirement kinds aren't supported
* for MinMoveFrags requirements. */
return FALSE;
} else if (req1->present == req2->present) {
/* No contradiction possible. */
return FALSE;
} else {
/* Number of move fragments left can't be larger than the number
* required to be present and smaller than the number required to not
* be present when the number required to be present is smaller than
* the number required to not be present. */
if (req1->present) {
return req1->source.value.minmoves >= req2->source.value.minmoves;
} else {
return req1->source.value.minmoves <= req2->source.value.minmoves;
}
}
break;
return are_bounds_contradictions(
req1->source.value.minmoves, !req1->present,
req2->source.value.minmoves, !req2->present);
case VUT_MINLATITUDE:
case VUT_MAXLATITUDE:
if (req2->source.kind != VUT_MINLATITUDE
......
}
return FALSE;
case VUT_MAX_DISTANCE_SQ:
if (req2->source.kind != VUT_MAX_DISTANCE_SQ) {
/* Finding contradictions across requirement kinds isn't supported
* for MaxDistanceSq requirements. */
return FALSE;
}
return are_bounds_contradictions(
req1->source.value.distance_sq, req1->present,
req2->source.value.distance_sq, req2->present);
default:
/* No special knowledge exists. The requirements aren't the exact
* opposite of each other per the initial check. */
......
#define IS_REQ_ACTIVE_VARIANT_ASSERT(_kind) \
{ \
fc_assert_ret_val(req != NULL, TRI_MAYBE); \
fc_assert_ret_val(req != nullptr, TRI_MAYBE); \
fc_assert_ret_val(req->source.kind == _kind, TRI_MAYBE); \
fc_assert(context != NULL); \
fc_assert(context != nullptr); \
fc_assert(other_context != nullptr); \
}
/**********************************************************************//**
......
return TRI_MAYBE;
}
/**********************************************************************//**
Determine whether a maximum squared distance requirement is satisfied in
a given context, ignoring parts of the requirement that can be handled
uniformly for all requirement types.
context, other_context and req must not be null,
and req must be a max squared distance requirement
**************************************************************************/
static enum fc_tristate
is_max_distance_sq_req_active(const struct civ_map *nmap,
const struct req_context *context,
const struct req_context *other_context,
const struct requirement *req)
{
IS_REQ_ACTIVE_VARIANT_ASSERT(VUT_MAX_DISTANCE_SQ);
switch (req->range) {
case REQ_RANGE_TILE:
if (context->tile == nullptr || other_context->tile == nullptr) {
return TRI_MAYBE;
}
return BOOL_TO_TRISTATE(
sq_map_distance(context->tile, other_context->tile)
<= req->source.value.distance_sq
);
default:
break;
}
fc_assert_msg(FALSE,
"Illegal range %d for max squared distance requirement.",
req->range);
return TRI_MAYBE;
}
/**********************************************************************//**
Determine whether a minimum year requirement is satisfied in a given
context, ignoring parts of the requirement that can be handled uniformly
......
[VUT_IMPR_FLAG] = {is_buildingflag_req_active, REQUCH_YES},
[VUT_PLAYER_FLAG] = {is_plr_flag_req_active, REQUCH_NO},
[VUT_PLAYER_STATE] = {is_plr_state_req_active, REQUCH_NO},
[VUT_MAX_DISTANCE_SQ] = {is_max_distance_sq_req_active, REQUCH_YES},
[VUT_MAXLATITUDE] = {is_latitude_req_active, REQUCH_YES},
[VUT_MAXTILEUNITS] = {is_maxunitsontile_req_active, REQUCH_NO},
[VUT_MINCALFRAG] = {is_mincalfrag_req_active, REQUCH_NO},
......
case VUT_TERRFLAG:
case VUT_TERRAINALTER:
case VUT_MINYEAR:
case VUT_MAX_DISTANCE_SQ:
case VUT_NONE:
case VUT_COUNT:
/* Not implemented. */
......
case VUT_MINLATITUDE:
case VUT_MAXLATITUDE:
return psource1->value.latitude == psource2->value.latitude;
case VUT_MAX_DISTANCE_SQ:
return psource1->value.distance_sq == psource2->value.distance_sq;
case VUT_COUNT:
break;
}
......
case VUT_MAXLATITUDE:
fc_snprintf(buffer, sizeof(buffer), "%d", psource->value.latitude);
return buffer;
case VUT_MAX_DISTANCE_SQ:
fc_snprintf(buffer, sizeof(buffer), "%d", psource->value.distance_sq);
return buffer;
case VUT_COUNT:
break;
......
cat_snprintf(buf, bufsz, _("Latitude <= %d"),
psource->value.latitude);
return buf;
case VUT_MAX_DISTANCE_SQ:
/* TRANS: here <= means 'less than or equal'. */
cat_snprintf(buf, bufsz, _("Squared distance <= %d"),
psource->value.distance_sq);
return buf;
case VUT_COUNT:
break;
}
doc/README.actions
target requirement vector to the actor requirement vector.
* Asymmetric local DiplRel requirements must test for the same thing in
the opposite direction. Example: "Hosts embassy" -> "Has embassy"
A "MaxDistanceSq" requirement with the range "Tile" should always be put in
the actor requirements, for the same reasons.
Actions and Lua
===============
doc/README.effects
MinMoveFrags: Local
MinVeteran: Local
MinHitPoints: Local
MaxDistanceSq: Tile
MinSize is the minimum size of a city required.
......
is a port, it's a tile of the nearby ocean but not of its continent).
MinLatitude and MaxLatitude are numbers from -1000 (south pole) to 1000
(north pole).
MaxDistanceSq is about the (squared) distance between two tiles; currently
only available for action enablers (see also README.actions) and a select
few effects.
CityStatus is "OwnedByOriginal", "Transferred", "Starved",
"Disorder", or "Celebration"
The difference between "OwnedByOriginal" and "Transferred" is that
gen_headers/enums/fc_types_enums.def
PLAYER_STATE "PlayerState"
FORM_AGE "FormAge"
MINCITIES "MinCities"
MAX_DISTANCE_SQ "MaxDistanceSq"
end
server/cityturn.c
case VUT_ACTION:
case VUT_OTYPE:
case VUT_SPECIALIST:
case VUT_MAX_DISTANCE_SQ:
case VUT_TERRAINALTER: /* XXX could do this in principle */
/* Will only happen with a bogus ruleset. */
log_error("worklist_change_build_target() has bogus preq");
server/ruleset/rssanity.c
case VUT_IMPR_GENUS:
case VUT_ORIGINAL_OWNER: /* City range -> only one original owner */
case VUT_FORM_AGE:
case VUT_MAX_DISTANCE_SQ: /* Breaks nothing, but has no sense either */
/* There can be only one requirement of these types (with current
* range limitations)
* Requirements might be identical, but we consider multiple
......
req_to_fstring(preq, &astr));
astr_free(&astr);
ok = FALSE;
} else if (preq->source.kind == VUT_MAX_DISTANCE_SQ
&& preq->range == REQ_RANGE_TILE) {
struct astring astr;
/* A Tile-ranged MaxDistanceSq requirement can be expressed as a
* requirement in actor_reqs. Demand that it is there. */
ruleset_error(logger, LOG_ERROR,
_("Action enabler for %s has a tile MaxDistanceSq "
"requirement %s in target_reqs! Please read the "
"section \"Requirement vector rules\" in "
"doc/README.actions"),
action_id_rule_name(act),
req_to_fstring(preq, &astr));
astr_free(&astr);
ok = FALSE;
}
} requirement_vector_iterate_end;
tools/ruledit/univ_value.c
case VUT_MAXLATITUDE:
src->value.latitude = 0;
return TRUE;
case VUT_MAX_DISTANCE_SQ:
src->value.distance_sq = 0;
return TRUE;
case VUT_COUNT:
fc_assert(src->kind != VUT_COUNT);
return FALSE;
......
case VUT_MINCITIES:
case VUT_MINLATITUDE:
case VUT_MAXLATITUDE:
case VUT_MAX_DISTANCE_SQ:
/* Requirement types having numerical value */
cb(nullptr, FALSE, data);
break;
(2-2/3)