Spaces:
Running
Running
| """Procedurally generated games for KantBench.""" | |
| from __future__ import annotations | |
| import random as _rand | |
| from common.games import GAMES, GameConfig | |
| from constant_definitions.game_constants import DEFAULT_NUM_ROUNDS | |
| from constant_definitions.auction_nplayer_constants import ( | |
| GENERATED_DEFAULT_ACTIONS, GENERATED_PAYOFF_MIN, GENERATED_PAYOFF_MAX, | |
| GENERATED_SEED_DEFAULT, | |
| ) | |
| _ONE = int(bool(True)) | |
| def _action_label(index: int) -> str: | |
| """Generate action label: a, b, c, ... z, aa, ab, ...""" | |
| alphabet_size = ord("z") - ord("a") + _ONE | |
| if index < alphabet_size: | |
| return chr(ord("a") + index) | |
| first = index // alphabet_size - _ONE | |
| second = index % alphabet_size | |
| return chr(ord("a") + first) + chr(ord("a") + second) | |
| def generate_random_symmetric( | |
| num_actions: int = GENERATED_DEFAULT_ACTIONS, | |
| payoff_min: int = GENERATED_PAYOFF_MIN, | |
| payoff_max: int = GENERATED_PAYOFF_MAX, | |
| seed: int = GENERATED_SEED_DEFAULT, | |
| ) -> GameConfig: | |
| """Generate a random symmetric NxN matrix game. | |
| In a symmetric game, the payoff for the first player choosing (a, b) | |
| equals the payoff for the second player facing (b, a). | |
| """ | |
| rng = _rand.Random(seed) | |
| actions = [_action_label(i) for i in range(num_actions)] | |
| matrix: dict[tuple[str, str], tuple[float, float]] = {} | |
| for i, a in enumerate(actions): | |
| for j, b in enumerate(actions): | |
| if (a, b) not in matrix: | |
| p_first = float(rng.randint(payoff_min, payoff_max)) | |
| p_second = float(rng.randint(payoff_min, payoff_max)) | |
| matrix[(a, b)] = (p_first, p_second) | |
| matrix[(b, a)] = (p_second, p_first) | |
| def _payoff(pa: str, oa: str) -> tuple[float, float]: | |
| return matrix[(pa, oa)] | |
| return GameConfig( | |
| name=f"Random Symmetric {num_actions}x{num_actions} (seed={seed})", | |
| description=( | |
| f"A randomly generated {num_actions}x{num_actions} symmetric " | |
| f"matrix game with payoffs in [{payoff_min}, {payoff_max}]. " | |
| f"Tests generalization to novel strategic structures." | |
| ), | |
| actions=actions, | |
| game_type="matrix", | |
| default_rounds=DEFAULT_NUM_ROUNDS, | |
| payoff_fn=_payoff, | |
| ) | |
| def generate_random_asymmetric( | |
| num_actions: int = GENERATED_DEFAULT_ACTIONS, | |
| payoff_min: int = GENERATED_PAYOFF_MIN, | |
| payoff_max: int = GENERATED_PAYOFF_MAX, | |
| seed: int = GENERATED_SEED_DEFAULT, | |
| ) -> GameConfig: | |
| """Generate a random asymmetric NxN matrix game. | |
| Each cell has independently drawn payoffs for both players. | |
| """ | |
| rng = _rand.Random(seed) | |
| actions = [_action_label(i) for i in range(num_actions)] | |
| matrix: dict[tuple[str, str], tuple[float, float]] = {} | |
| for a in actions: | |
| for b in actions: | |
| p_first = float(rng.randint(payoff_min, payoff_max)) | |
| p_second = float(rng.randint(payoff_min, payoff_max)) | |
| matrix[(a, b)] = (p_first, p_second) | |
| def _payoff(pa: str, oa: str) -> tuple[float, float]: | |
| return matrix[(pa, oa)] | |
| return GameConfig( | |
| name=f"Random Asymmetric {num_actions}x{num_actions} (seed={seed})", | |
| description=( | |
| f"A randomly generated {num_actions}x{num_actions} asymmetric " | |
| f"matrix game with independent payoffs in [{payoff_min}, {payoff_max}]. " | |
| f"Tests reasoning in novel non-symmetric strategic settings." | |
| ), | |
| actions=actions, | |
| game_type="matrix", | |
| default_rounds=DEFAULT_NUM_ROUNDS, | |
| payoff_fn=_payoff, | |
| ) | |
| def generate_parameterized_pd( | |
| temptation: int, | |
| reward: int, | |
| punishment: int, | |
| sucker: int, | |
| seed: int = GENERATED_SEED_DEFAULT, | |
| ) -> GameConfig: | |
| """Create a Prisoner's Dilemma with custom T > R > P > S payoffs.""" | |
| matrix: dict[tuple[str, str], tuple[float, float]] = { | |
| ("cooperate", "cooperate"): (float(reward), float(reward)), | |
| ("cooperate", "defect"): (float(sucker), float(temptation)), | |
| ("defect", "cooperate"): (float(temptation), float(sucker)), | |
| ("defect", "defect"): (float(punishment), float(punishment)), | |
| } | |
| def _payoff(pa: str, oa: str) -> tuple[float, float]: | |
| return matrix[(pa, oa)] | |
| return GameConfig( | |
| name=f"PD(T={temptation},R={reward},P={punishment},S={sucker})", | |
| description=( | |
| f"A parameterized Prisoner's Dilemma with T={temptation}, " | |
| f"R={reward}, P={punishment}, S={sucker}. Tests sensitivity " | |
| f"to varying incentive structures." | |
| ), | |
| actions=["cooperate", "defect"], | |
| game_type="matrix", | |
| default_rounds=DEFAULT_NUM_ROUNDS, | |
| payoff_fn=_payoff, | |
| ) | |
| # -- Register default generated instances -- | |
| _DEFAULT_SYMMETRIC = generate_random_symmetric() | |
| _DEFAULT_ASYMMETRIC = generate_random_asymmetric(seed=GENERATED_SEED_DEFAULT + _ONE) | |
| GENERATED_GAMES: dict[str, GameConfig] = { | |
| "random_symmetric_3x3": _DEFAULT_SYMMETRIC, | |
| "random_asymmetric_3x3": _DEFAULT_ASYMMETRIC, | |
| } | |
| GAMES.update(GENERATED_GAMES) | |