From 6a0a589e7d98e356be510f803e9346d16ba0a7d5 Mon Sep 17 00:00:00 2001 From: Ihnatus Date: Tue, 13 Dec 2022 23:28:55 +0300 Subject: [PATCH] Evaluate requirements in a vector with an arbitrary callback Provides the evaluator tri_reqs_cb_active() that takes the requirement vector, the callback, the context and some side data and tells fc_tristate for the vector, in process it may collect the uncertain requirements in another vector. Also, provides an example callback default_tester_cb() that consults a table of requirements that are considered true disregarding the context, and if neither of those requirements gives a definite value for given one from the vector, evaluates it usual way. All this is a base for upcoming patches. See OSDN#46266. Signed-off-by: Ihnatus --- common/requirements.c | 112 ++++++++++++++++++++++++++++++++++++++++++ common/requirements.h | 27 ++++++++++ 2 files changed, 139 insertions(+) diff --git a/common/requirements.c b/common/requirements.c index 14e195741e..03416631e8 100644 --- a/common/requirements.c +++ b/common/requirements.c @@ -1385,6 +1385,19 @@ static bool present_implies_not_present(const struct requirement *req1, &present->source); } +/**********************************************************************//** + Returns TRUE if req2 is always fulfilled when req1 is (i.e. req1 => req2) +**************************************************************************/ +bool req_implies_req(const struct requirement *req1, + const struct requirement *req2) +{ + struct requirement nreq2; + + req_copy(&nreq2, req2); + nreq2.present = !nreq2.present; + return are_requirements_contradictions(req1, &nreq2); +} + /**********************************************************************//** Returns TRUE if req1 and req2 contradicts each other. @@ -4136,6 +4149,32 @@ enum fc_tristate tri_req_present(const struct req_context *context, return req_definitions[req->source.kind].cb(context, other_player, req); } +/**********************************************************************//** + Evaluates req in context with other_player to fc_tristate. + + context may be NULL. This is equivalent to passing an empty context. + + Fields of context that are NULL are considered unspecified + and will produce TRI_MAYBE if req needs them to evaluate. +**************************************************************************/ +enum fc_tristate tri_req_active(const struct req_context *context, + const struct player *other_player, + const struct requirement *req) +{ + enum fc_tristate eval = tri_req_present(context, other_player, req); + + if (!req->present) { + if (TRI_NO == eval) { + return TRI_YES; + } + if (TRI_YES == eval) { + return TRI_NO; + } + } + + return eval; +} + /**********************************************************************//** Checks the requirement(s) to see if they are active on the given target. @@ -4163,6 +4202,79 @@ bool are_reqs_active(const struct req_context *context, return TRUE; } +/**********************************************************************//** + A tester callback for tri_reqs_cb_active() that uses the default + requirement calculation except for requirements in data[n_data] array + and ones implied by them or contradicting them +**************************************************************************/ +enum fc_tristate default_tester_cb + (const struct req_context *context, + const struct player *other_player, + const struct requirement *req, + void *data, int n_data) +{ + fc_assert_ret_val(data || n_data == 0, TRI_NO); + + for (int i = 0; i < n_data; i++) { + if (are_requirements_contradictions(&((struct requirement *) data)[i], + req)) { + return TRI_NO; + } else if (req_implies_req(&((struct requirement *) data)[i], req)) { + return TRI_YES; + } + } + return tri_req_active(context, other_player, req); +} + +/**********************************************************************//** + Test requirements in reqs with tester according to (data, n_data) + and give the resulting tristate. + If maybe_reqs is not NULL, copies requirements that are evaluated + to TRI_MAYBE into it (stops as soon as one evaluates to TRI_NO). +**************************************************************************/ +enum fc_tristate + tri_reqs_cb_active(const struct req_context *context, + const struct player *other_player, + const struct requirement_vector *reqs, + struct requirement_vector *maybe_reqs, + req_tester_cb tester, + void *data, int n_data) +{ + bool active = TRUE; + bool certain = TRUE; + int sz = requirement_vector_size(reqs); + + fc_assert_ret_val(NULL != tester, TRI_NO); + requirement_vector_iterate(reqs, preq) { + switch(tester(context, other_player, preq, + data, n_data)) { + case TRI_NO: + active = FALSE; + certain = TRUE; + break; + case TRI_YES: + break; + case TRI_MAYBE: + certain = FALSE; + if (maybe_reqs) { + requirement_vector_append(maybe_reqs, *preq); + } + break; + default: + fc_assert(FALSE); + active = FALSE; + } + if (!active) { + break; + } + sz--; + } requirement_vector_iterate_end; + + return certain ? (active ? TRI_YES : TRI_NO) : TRI_MAYBE; +} + + + /**********************************************************************//** Gives a suggestion may req ever evaluate to another value with given context. (The other player is not supplied since it has no value diff --git a/common/requirements.h b/common/requirements.h index cdd834a292..523c3d9df7 100644 --- a/common/requirements.h +++ b/common/requirements.h @@ -149,9 +149,15 @@ bool are_requirements_equal(const struct requirement *req1, bool are_requirements_contradictions(const struct requirement *req1, const struct requirement *req2); +bool req_implies_req(const struct requirement *req1, + const struct requirement *req2); + bool does_req_contradicts_reqs(const struct requirement *req, const struct requirement_vector *vec); +enum fc_tristate tri_req_active(const struct req_context *context, + const struct player *other_player, + const struct requirement *req); bool is_req_active(const struct req_context *context, const struct player *other_player, const struct requirement *req, @@ -161,6 +167,27 @@ bool are_reqs_active(const struct req_context *context, const struct requirement_vector *reqs, const enum req_problem_type prob_type); +/* Type of a callback that tests requirements due to a context + * and something else in some manner different from tri_req_active() */ +typedef enum fc_tristate + (*req_tester_cb)(const struct req_context *context, + const struct player *other_player, + const struct requirement *req, + void *data, int n_data); + +enum fc_tristate +default_tester_cb(const struct req_context *context, + const struct player *other_player, + const struct requirement *req, + void *data, int n_data); +enum fc_tristate + tri_reqs_cb_active(const struct req_context *context, + const struct player *other_player, + const struct requirement_vector *reqs, + struct requirement_vector *maybe_reqs, + req_tester_cb tester, + void *data, int n_data); + enum req_unchanging_status is_req_unchanging(const struct req_context *context, const struct requirement *req); -- 2.37.2