Creating a New Game Directly in Python¶
This guide explains how to create a new game purely in Python using EGTtools, without needing to modify or compile C++ code.
If you want to create a new C++ game, see instead: Creating a New Game in C++ (EGTtools).
Implementing a Game from Scratch: the AbstractGame Class¶
The Game class is central to the EGTtools library: it defines the environment where strategic interactions happen. All games must extend the AbstractGame abstract base class.
A new game must implement the following methods:
🛠️ Required Methods:
play
calculate_payoffs
calculate_fitness
__str__
nb_strategies
type
payoffs
payoff
save_payoffs
Below is a description of the class and the purpose of each method:
class AbstractGame:
def play(self, group_composition: Union[List[int], numpy.ndarray], game_payoffs: numpy.ndarray) -> None:
"""Calculate payoffs for each strategy inside a group."""
pass
def calculate_payoffs(self) -> np.ndarray:
"""Set up the payoff matrix for all possible group compositions."""
pass
def calculate_fitness(self, strategy_index: int, pop_size: int, population_state: numpy.ndarray) -> float:
"""Return the fitness of a strategy given the current population state."""
pass
def __str__(self) -> str:
"""Return a string representation of the game."""
pass
def nb_strategies(self) -> int:
"""Return the number of available strategies."""
pass
def type(self) -> str:
"""Return a string describing the game type."""
pass
def payoffs(self) -> np.ndarray:
"""Return the payoff matrix."""
pass
def payoff(self, strategy: int, group_configuration: List[int]) -> float:
"""Return the payoff of a strategy for a given group composition."""
pass
def save_payoffs(self, file_name: str) -> None:
"""Save the payoff matrix and game parameters to a file."""
pass
Simplifying Game Implementation: AbstractTwoPLayerGame and AbstractNPlayerGame¶
In many cases, the fitness of a strategy is simply its expected payoff at a given state. For this reason, EGTtools provides two simplified abstract classes:
📚 Simplified Base Classes:
egttools.games.AbstractTwoPLayerGame — for two-player games
egttools.games.AbstractNPlayerGame — for N-player games
When using these classes, you only need to implement:
play
calculate_payoffs
Example: The N-Player Stag Hunt Game¶
Here is an example of how to implement the N-player Stag Hunt Game:
from egttools.games import AbstractNPlayerGame
from egttools import sample_simplex
import numpy as np
from typing import Union, List
class NPlayerStagHunt(AbstractNPlayerGame):
def __init__(self, group_size, enhancement_factor, cooperation_threshold, cost):
self.group_size_ = group_size
self.enhancement_factor_ = enhancement_factor
self.cooperation_threshold_ = cooperation_threshold
self.cost_ = cost
self.strategies = ['Defect', 'Cooperate']
self.nb_strategies_ = 2
super().__init__(self.nb_strategies_, self.group_size_)
def play(self, group_composition: Union[List[int], np.ndarray], game_payoffs: np.ndarray) -> None:
if group_composition[0] == 0:
game_payoffs[0] = 0
game_payoffs[1] = self.cost_ * (self.enhancement_factor_ - 1)
elif group_composition[1] == 0:
game_payoffs[0] = 0
game_payoffs[1] = 0
else:
payoff = (group_composition[1] * self.enhancement_factor_) / self.group_size_ \
if group_composition[1] >= self.cooperation_threshold_ else 0
game_payoffs[0] = payoff
game_payoffs[1] = payoff - self.cost_
def calculate_payoffs(self) -> np.ndarray:
payoffs_container = np.zeros(shape=(self.nb_strategies_,), dtype=np.float64)
for i in range(self.nb_group_configurations_):
group_composition = sample_simplex(i, self.group_size_, self.nb_strategies_)
self.play(group_composition, payoffs_container)
for strategy_index, strategy_payoff in enumerate(payoffs_container):
self.payoffs_[strategy_index, i] = strategy_payoff
payoffs_container[:] = 0
return self.payoffs_
Already Have a Precomputed Payoff Matrix?¶
If you have a ready-made payoff matrix, you can avoid writing a full class. Use:
📦 Matrix-based Holders:
Matrix2PlayerGameHolder — for 2-player games (requires a square matrix)
MatrixNPlayerGameHolder — for N-player games (requires a (nb_strategies, nb_group_configurations) matrix)
Note
Calculate the number of group configurations using egttools.calculate_nb_states(group_size, nb_strategies).
Use egttools.sample_simplex(index, group_size, nb_strategies) to retrieve the group configuration for a column.
🚨 Important: When a strategy is not present in a group configuration, its expected payoff must be set to 0.
You can find a full example here: examples/hawk_dove_dynamics
List of Implemented Games¶
NormalFormGame — Iterated matrix games.
PGG — Public Goods Game with group-based cooperation.
OneShotCRD — One-shot Collective Risk Dilemma Santos and Pacheco [1].
CRDGame — Collective Risk Dilemma over multiple rounds Milinski et al. [2].
NPlayerStagHunt — N-player Stag Hunt Game Pacheco et al. [3].
See also: Creating New Strategies to learn how to extend or create new strategies.