Skip to content

Axelrod Python Library: Simulating the Evolution of Cooperation with Iterated Prisoner's Dilemma Tournaments

By Jeff 6 views
Axelrod Tournament Score Comparison across strategies
Axelrod Tournament Score Comparison across strategies

The question of how cooperation emerges among self-interested agents is one of the most enduring puzzles in social science. Robert Axelrod's landmark 1980 computer tournaments demonstrated that simple reciprocal strategies—most famously Tit-for-Tat—could outperform more aggressive alternatives in repeated interactions. The Axelrod Python library brings this experimental framework into the modern era, providing a rigorous, reproducible platform for running iterated Prisoner's Dilemma (IPD) tournaments, studying evolutionary dynamics, and testing novel strategies at scale.

What Is the Axelrod Library?

The Axelrod library is an open-source Python package (MIT license) that implements a comprehensive framework for IPD research. It ships with over 230 built-in strategies—ranging from classics like Cooperator, Defector, and Tit-for-Tat to sophisticated memory-based and stochastic strategies—and provides a clean API for defining custom strategies. The library supports:

  • Round-robin tournaments (every strategy plays every other)
  • Probabilistic ending (each match ends with a fixed probability per turn, preventing end-game defection artifacts)
  • Evolutionary and ecological simulations (Moran processes, population dynamics)
  • Fingerprinting (characterizing strategy behavior against a probe set)
  • Reproducible seeding for stochastic strategies

Core Concepts and Architecture

Strategies and Matches

A strategy in Axelrod is a Python class that inherits from axelrod.Player. Each player maintains a history of its own moves and its opponent's moves, and the strategy() method returns axelrod.Action.C (cooperate) or axelrod.Action.D (defect) based on that history.

import axelrod

players = [
    axelrod.TitForTat(),
    axelrod.Defector(),
    axelrod.Cooperator(),
    axelrod.GradualKiller(),
    axelrod.WinStayLoseShift(),
    axelrod.Random(p=0.5),
]

tournament = axelrod.Tournament(players, turns=200, repetitions=20, seed=42)
results = tournament.play()
print(results.ranking)

The Tournament class handles all pairings, score accumulation, and statistical aggregation. The results object exposes ranked scores, cooperation rates, normalised scores, and payoff matrices—all ready for downstream analysis.

Payoff Matrix and Scoring

The default payoff matrix follows the canonical IPD values (R=3, P=1, T=5, S=0), satisfying T > R > P > S and 2R > T + S. Researchers can override these to model different social dilemmas—stag hunt, snowdrift, or asymmetric games—by passing a custom game object:

game = axelrod.Game(r=3, p=1, s=0, t=5)
tournament = axelrod.Tournament(players, game=game, turns=200, repetitions=50)

Evolutionary Dynamics: The Moran Process

Beyond static tournaments, the library implements the Moran process, a stochastic evolutionary model where strategies reproduce proportionally to fitness and less-fit strategies are gradually eliminated. This is particularly valuable for studying which strategies are evolutionarily stable:

initial_population = [
    axelrod.TitForTat(),
    axelrod.Defector(),
    axelrod.Cooperator(),
    axelrod.Random(),
]

mp = axelrod.MoranProcess(initial_population, turns=100, seed=0)
populations = mp.play()
print(mp.winning_strategy_name)

The Moran process reveals that cooperation can persist even in mixed populations when reciprocal strategies are present—a result that static tournaments alone cannot demonstrate.

Moran Process evolutionary population dynamics

Strategy Fingerprinting

One of the library's most powerful analytical tools is strategy fingerprinting, which characterizes a strategy's behavior by playing it against a parameterized probe strategy (typically axelrod.TitForTat with varying initial cooperation probability). The resulting fingerprint is a 2D heatmap showing cooperation rate as a function of probe parameters, enabling visual comparison of strategy families:

strategy = axelrod.Grudger()
fingerprint = axelrod.Fingerprint(strategy, axelrod.TitForTat)
fingerprint.fingerprint(turns=50, repetitions=10, seed=1)
fingerprint.plot()

Fingerprints are especially useful for detecting behavioral equivalence between strategies that appear syntactically different but produce identical outcomes.

Strategy fingerprints showing cooperation rate heatmaps

Defining Custom Strategies

Practitioners modeling real-world social dynamics—negotiation protocols, norm enforcement, institutional cooperation—can implement domain-specific strategies:

class PunishThenForgive(axelrod.Player):
    """Defects for N turns after betrayal, then resets."""
    name = "PunishThenForgive"
    classifier = {
        "memory_depth": 5,
        "stochastic": False,
        "makes_use_of": [],
        "long_run_time": False,
        "inspects_source": False,
        "manipulates_source": False,
        "manipulates_state": False,
    }

    def __init__(self, punishment_length: int = 3):
        super().__init__()
        self.punishment_length = punishment_length
        self._punish_count = 0

    def strategy(self, opponent: axelrod.Player) -> axelrod.Action:
        if opponent.history and opponent.history[-1] == axelrod.Action.D:
            self._punish_count = self.punishment_length
        if self._punish_count > 0:
            self._punish_count -= 1
            return axelrod.Action.D
        return axelrod.Action.C

This pattern is directly applicable to modeling graduated sanctions in common-pool resource governance, escalation ladders in international relations, or credit scoring in peer-to-peer lending networks.

Ecological Simulations

The axelrod.Ecosystem class models population dynamics over time without the replacement mechanism of the Moran process. Strategies grow or shrink in proportion to their tournament scores each generation, providing a deterministic view of which behavioral phenotypes dominate:

tournament = axelrod.Tournament(players, turns=100, repetitions=5, seed=7)
results = tournament.play()

ecosystem = axelrod.Ecosystem(results)
ecosystem.reproduce(100)
ecosystem.plot()

Ecological simulations are well-suited to studying market entry dynamics, the spread of social norms, or the long-run viability of altruistic institutions.

Axelrod Ecosystem simulation population dynamics over generations

Practical Considerations

Reproducibility: Always set seed on both the Tournament and any stochastic players. The library uses Python's random module internally, so a fixed seed guarantees identical results across runs.

Performance: With 230+ strategies, a full round-robin tournament involves ~26,000 pairings. Use with_morality=False and reduce repetitions for exploratory runs; reserve high-repetition runs for publication-quality results. Parallel execution is supported via processes parameter.

Strategy classification: Each built-in strategy carries a classifier dict indicating memory depth, stochasticity, and whether it inspects or manipulates opponent source code. Filter strategies by classifier to construct ecologically meaningful subsets:

short_memory = [s for s in axelrod.all_strategies
                if s.classifier["memory_depth"] <= 3
                and not s.classifier["stochastic"]]

Applications in Social Simulation Research

The Axelrod library has been applied across a wide range of social science domains:

  • Institutional economics: Testing whether conditional cooperation strategies sustain public goods provision under varying punishment regimes
  • International relations: Modeling arms-race dynamics and treaty compliance as repeated games between state actors
  • Organizational behavior: Studying how reciprocity norms emerge in repeated supplier-buyer relationships
  • Network epidemiology: Coupling IPD dynamics with network topology to study how social structure shapes cooperative behavior

Further Resources

The Axelrod Python library transforms a classic thought experiment into a rigorous computational laboratory. For social simulation researchers, it offers an unmatched combination of breadth—hundreds of strategies, multiple simulation modes—and methodological transparency, making it an essential tool for anyone studying the emergence and stability of cooperative behavior in social systems.

Tags: iterated-prisoners-dilemma evolutionary-game-theory agent-based-modeling cooperation python