"""Rule catalog and payoff transforms for the meta-gaming variant system. Each rule is a payoff transform: given base payoffs and actions, return modified payoffs. The ``apply_rule`` dispatcher looks up a rule by name and delegates to the corresponding transform function. """ from __future__ import annotations from typing import Callable from constant_definitions.var.meta.meta_rule_constants import ( RULE_NONE, RULE_EQUAL_SPLIT, RULE_COOP_BONUS, RULE_DEFECT_PENALTY, RULE_MIN_GUARANTEE, RULE_BAN_DEFECT, COOP_BONUS_NUMERATOR, COOP_BONUS_DENOMINATOR, DEFECT_PENALTY_NUMERATOR, DEFECT_PENALTY_DENOMINATOR, MIN_GUARANTEE_NUMERATOR, MIN_GUARANTEE_DENOMINATOR, BAN_DEFECT_PENALTY_NUMERATOR, BAN_DEFECT_PENALTY_DENOMINATOR, EQUAL_SPLIT_DENOMINATOR, META_SEPARATOR, META_SPLIT_LIMIT, ) RuleTransform = Callable[ [float, float, str, str], tuple[float, float] ] _COOPERATIVE_ACTIONS = frozenset({"cooperate", "stag", "dove"}) _COOP_BONUS = COOP_BONUS_NUMERATOR / COOP_BONUS_DENOMINATOR _DEFECT_PENALTY = DEFECT_PENALTY_NUMERATOR / DEFECT_PENALTY_DENOMINATOR _MIN_GUARANTEE = MIN_GUARANTEE_NUMERATOR / MIN_GUARANTEE_DENOMINATOR _BAN_PENALTY = BAN_DEFECT_PENALTY_NUMERATOR / BAN_DEFECT_PENALTY_DENOMINATOR def _is_cooperative(action: str) -> bool: """Return True if *action* is a cooperative action.""" return action in _COOPERATIVE_ACTIONS def _rule_none( base_p: float, base_o: float, p_action: str, o_action: str, ) -> tuple[float, float]: return (base_p, base_o) def _rule_equal_split( base_p: float, base_o: float, p_action: str, o_action: str, ) -> tuple[float, float]: total = base_p + base_o share = total / EQUAL_SPLIT_DENOMINATOR return (share, share) def _rule_coop_bonus( base_p: float, base_o: float, p_action: str, o_action: str, ) -> tuple[float, float]: p_pay = base_p + (_COOP_BONUS if _is_cooperative(p_action) else float()) o_pay = base_o + (_COOP_BONUS if _is_cooperative(o_action) else float()) return (p_pay, o_pay) def _rule_defect_penalty( base_p: float, base_o: float, p_action: str, o_action: str, ) -> tuple[float, float]: p_pay = base_p - ( _DEFECT_PENALTY if not _is_cooperative(p_action) else float() ) o_pay = base_o - ( _DEFECT_PENALTY if not _is_cooperative(o_action) else float() ) return (p_pay, o_pay) def _rule_min_guarantee( base_p: float, base_o: float, p_action: str, o_action: str, ) -> tuple[float, float]: p_pay = max(base_p, _MIN_GUARANTEE) o_pay = max(base_o, _MIN_GUARANTEE) return (p_pay, o_pay) def _rule_ban_defect( base_p: float, base_o: float, p_action: str, o_action: str, ) -> tuple[float, float]: p_pay = base_p - ( _BAN_PENALTY if not _is_cooperative(p_action) else float() ) o_pay = base_o - ( _BAN_PENALTY if not _is_cooperative(o_action) else float() ) return (p_pay, o_pay) RULE_CATALOG: dict[str, RuleTransform] = { RULE_NONE: _rule_none, RULE_EQUAL_SPLIT: _rule_equal_split, RULE_COOP_BONUS: _rule_coop_bonus, RULE_DEFECT_PENALTY: _rule_defect_penalty, RULE_MIN_GUARANTEE: _rule_min_guarantee, RULE_BAN_DEFECT: _rule_ban_defect, } def apply_rule( rule_name: str, base_p: float, base_o: float, p_action: str, o_action: str, ) -> tuple[float, float]: """Look up *rule_name* in the catalog and apply its transform.""" return RULE_CATALOG[rule_name](base_p, base_o, p_action, o_action) _ZERO = int() _ONE = int(bool(True)) _TWO = _ONE + _ONE def parse_meta_action( action: str, split_limit: int = META_SPLIT_LIMIT, ) -> tuple[str, str, str]: """Parse an encoded meta-action into (prefix, rule, base_action). The action format is ``prefix_rule_baseaction`` where *rule* is a single token (no underscores). Using ``split`` with the configured split limit yields exactly three parts. """ parts = action.split(META_SEPARATOR, split_limit) return (parts[_ZERO], parts[_ONE], parts[_TWO])