Spaces:
Running
Running
File size: 4,047 Bytes
688c130 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 | """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])
|