Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 56 additions & 74 deletions axelrod/data/all_classifiers.yml
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
$\\phi$:
$\phi$:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you help me understand what is happening with this file @hodgesmr ? Was this done by running https://github.com/Axelrod-Python/Axelrod/blob/dev/axelrod/classifier.py#L91 ?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This diff is from the commit: c2619eb

I was following the documentation for adding a new strategy: "To classify the new strategy, run rebuild_classifier_table: python rebuild_classifier_table.py"

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup. Cool.

inspects_source: false
long_run_time: false
makes_use_of: !!set {}
manipulates_source: false
manipulates_state: false
memory_depth: .inf
stochastic: false
$\\pi$:
$\pi$:
inspects_source: false
long_run_time: false
makes_use_of: !!set {}
Expand Down Expand Up @@ -170,6 +170,14 @@ BackStabber:
manipulates_state: false
memory_depth: .inf
stochastic: false
Bayesian Forgiver:
inspects_source: false
long_run_time: false
makes_use_of: !!set {}
manipulates_source: false
manipulates_state: false
memory_depth: .inf
stochastic: false
Better and Better:
inspects_source: false
long_run_time: false
Expand All @@ -186,6 +194,14 @@ Bully:
manipulates_state: false
memory_depth: 1
stochastic: false
Burn Both Ends:
inspects_source: false
long_run_time: false
makes_use_of: !!set {}
manipulates_source: false
manipulates_state: false
memory_depth: 1
stochastic: true
Bush Mosteller:
inspects_source: false
long_run_time: false
Expand All @@ -195,6 +211,14 @@ Bush Mosteller:
manipulates_state: false
memory_depth: .inf
stochastic: true
CAPRI:
inspects_source: false
long_run_time: false
makes_use_of: !!set {}
manipulates_source: false
manipulates_state: false
memory_depth: 3
stochastic: false
Calculator:
inspects_source: false
long_run_time: false
Expand Down Expand Up @@ -439,31 +463,31 @@ Evolved ANN 5 Noise 05:
manipulates_state: false
memory_depth: .inf
stochastic: false
EvolvedAttention:
Evolved FSM 16:
inspects_source: false
long_run_time: True
long_run_time: false
makes_use_of: !!set {}
manipulates_source: false
manipulates_state: false
memory_depth: 200
memory_depth: .inf
stochastic: false
Evolved FSM 16:
Evolved FSM 16 Noise 05:
inspects_source: false
long_run_time: false
makes_use_of: !!set {}
manipulates_source: false
manipulates_state: false
memory_depth: .inf
stochastic: false
Evolved FSM 16 Noise 05:
Evolved FSM 4:
inspects_source: false
long_run_time: false
makes_use_of: !!set {}
manipulates_source: false
manipulates_state: false
memory_depth: .inf
stochastic: false
Evolved FSM 4:
Evolved FSM 6:
inspects_source: false
long_run_time: false
makes_use_of: !!set {}
Expand All @@ -479,6 +503,14 @@ Evolved HMM 5:
manipulates_state: false
memory_depth: 5
stochastic: true
EvolvedAttention:
inspects_source: false
long_run_time: true
makes_use_of: !!set {}
manipulates_source: false
manipulates_state: false
memory_depth: 200
stochastic: false
EvolvedLookerUp1_1_1:
inspects_source: false
long_run_time: false
Expand Down Expand Up @@ -659,39 +691,23 @@ Fortress4:
manipulates_state: false
memory_depth: 3
stochastic: false
GTFT:
FrequencyAnalyzer:
inspects_source: false
long_run_time: false
makes_use_of: !!set
game: null
manipulates_source: false
manipulates_state: false
memory_depth: 1
stochastic: true
Geller:
inspects_source: true
long_run_time: false
makes_use_of: !!set {}
manipulates_source: false
manipulates_state: false
memory_depth: .inf
stochastic: true
Geller Cooperator:
inspects_source: true
long_run_time: false
makes_use_of: !!set {}
manipulates_source: false
manipulates_state: false
memory_depth: .inf
stochastic: false
Geller Defector:
inspects_source: true
GTFT:
inspects_source: false
long_run_time: false
makes_use_of: !!set {}
makes_use_of: !!set
game: null
manipulates_source: false
manipulates_state: false
memory_depth: .inf
stochastic: false
memory_depth: 1
stochastic: true
General Soft Grudger:
inspects_source: false
long_run_time: false
Expand Down Expand Up @@ -1099,47 +1115,14 @@ Michaelos:
manipulates_state: false
memory_depth: .inf
stochastic: true
Mind Bender:
inspects_source: false
long_run_time: false
makes_use_of: !!set {}
manipulates_source: true
manipulates_state: false
memory_depth: -10
stochastic: false
Mind Controller:
Momentum:
inspects_source: false
long_run_time: false
makes_use_of: !!set {}
manipulates_source: true
manipulates_state: false
memory_depth: -10
stochastic: false
Mind Reader:
inspects_source: true
long_run_time: false
makes_use_of: !!set
game: null
manipulates_source: false
manipulates_state: false
memory_depth: .inf
stochastic: false
Mind Warper:
inspects_source: false
long_run_time: false
makes_use_of: !!set {}
manipulates_source: true
manipulates_state: false
memory_depth: -10
stochastic: false
Mirror Mind Reader:
inspects_source: true
long_run_time: false
makes_use_of: !!set {}
manipulates_source: true
manipulates_state: false
memory_depth: .inf
stochastic: false
N Tit(s) For M Tat(s):
inspects_source: false
long_run_time: false
Expand Down Expand Up @@ -1344,15 +1327,6 @@ Prober 4:
manipulates_state: false
memory_depth: .inf
stochastic: false
Protected Mind Reader:
inspects_source: true
long_run_time: false
makes_use_of: !!set
game: null
manipulates_source: true
manipulates_state: false
memory_depth: .inf
stochastic: false
Pun1:
inspects_source: false
long_run_time: false
Expand Down Expand Up @@ -1733,6 +1707,14 @@ Spiteful Tit For Tat:
manipulates_state: false
memory_depth: .inf
stochastic: false
SpitefulCC:
inspects_source: false
long_run_time: false
makes_use_of: !!set {}
manipulates_source: false
manipulates_state: false
memory_depth: .inf
stochastic: false
Stalker:
inspects_source: false
long_run_time: false
Expand Down
2 changes: 2 additions & 0 deletions axelrod/strategies/_strategies.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
SecondByYamachi,
)
from .backstabber import BackStabber, DoubleCrosser
from .bayesian_forgiver import BayesianForgiver
from .better_and_better import BetterAndBetter
from .bush_mosteller import BushMosteller
from .calculator import Calculator
Expand Down Expand Up @@ -307,6 +308,7 @@
ArrogantQLearner,
AverageCopier,
BackStabber,
BayesianForgiver,
BetterAndBetter,
Bully,
BurnBothEnds,
Expand Down
142 changes: 142 additions & 0 deletions axelrod/strategies/bayesian_forgiver.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
"""
Bayesian Forgiver - A strategy using Bayesian inference for adaptive forgiveness.

This strategy maintains a Bayesian belief about the opponent's cooperation probability
using a Beta distribution, and makes forgiveness decisions based on both the estimated
cooperation rate and the uncertainty in that estimate.
"""

from axelrod.action import Action
from axelrod.player import Player

C, D = Action.C, Action.D


class BayesianForgiver(Player):
"""
A strategy that uses Bayesian inference to model opponent behavior and
adaptively adjust forgiveness based on uncertainty.

The strategy maintains a Beta distribution representing beliefs about the
opponent's cooperation probability. It uses both the mean (expected cooperation
rate) and variance (uncertainty) to make decisions:

- When uncertain about the opponent's nature, it is cautious
- When certain the opponent is hostile, it punishes consistently
- When certain the opponent is cooperative, it cooperates consistently

Algorithm:
1. Maintain Beta(alpha, beta) distribution for opponent's cooperation probability
2. Start with Beta(1, 1) - neutral/uniform prior
3. Update after each round: C → alpha += 1, D → beta += 1
4. Calculate mean = alpha / (alpha + beta)
5. Calculate uncertainty (std deviation)
6. Adaptive forgiveness: threshold = base_threshold + uncertainty_factor * uncertainty
7. Forgive a defection only if the estimated cooperation rate clears this threshold

Names:
- Bayesian Forgiver: Original name by Matt Hodges
"""

name = "Bayesian Forgiver"
classifier = {
"memory_depth": float("inf"),
"stochastic": False,
"long_run_time": False,
"inspects_source": False,
"manipulates_source": False,
"manipulates_state": False,
}

def __init__(
self,
prior_alpha: float = 1.0,
prior_beta: float = 1.0,
base_forgiveness_threshold: float = 0.45,
uncertainty_factor: float = 2.5,
) -> None:
"""
Initialize the Bayesian Forgiver strategy.

Parameters
----------
prior_alpha : float
Initial alpha parameter for Beta distribution (default: 1.0)
Represents prior belief in cooperation count + 1
Higher values indicate stronger prior belief in cooperation
prior_beta : float
Initial beta parameter for Beta distribution (default: 1.0)
Represents prior belief in defection count + 1
Higher values indicate stronger prior belief in defection
base_forgiveness_threshold : float
Base threshold for forgiveness decision (default: 0.45)
If estimated cooperation probability > threshold, forgive defections
uncertainty_factor : float
How much uncertainty increases the forgiveness threshold (default: 2.5)

Note: Default parameters have been optimized through grid search
to maximize performance against common IPD strategies.
"""
super().__init__()
self.prior_alpha = prior_alpha
self.prior_beta = prior_beta
self.base_forgiveness_threshold = base_forgiveness_threshold
self.uncertainty_factor = uncertainty_factor

# Initialize Bayesian belief parameters
self.alpha = prior_alpha
self.beta = prior_beta

def reset(self):
"""Reset the strategy to initial state."""
super().reset()
self.alpha = self.prior_alpha
self.beta = self.prior_beta

def strategy(self, opponent: Player) -> Action:
"""
Determine next action using Bayesian opponent model.

Returns
-------
Action
C (cooperate) or D (defect)
"""
# First move: Start with cooperation (optimistic prior)
if not self.history:
return C

# Update Bayesian belief based on opponent's last action
if opponent.history[-1] == C:
self.alpha += 1.0
else:
self.beta += 1.0

# Calculate statistics from Beta distribution
total = self.alpha + self.beta
mean_cooperation = self.alpha / total

# Calculate variance and standard deviation (uncertainty)
# Var(Beta(α,β)) = αβ / ((α+β)²(α+β+1))
variance = (self.alpha * self.beta) / (total * total * (total + 1))
uncertainty = variance**0.5

# Adaptive forgiveness threshold
forgiveness_threshold = (
self.base_forgiveness_threshold
+ self.uncertainty_factor * uncertainty
)

# Decision logic
if opponent.history[-1] == C:
# Opponent cooperated last round - reciprocate cooperation
return C
else:
# Opponent defected last round - decide whether to forgive or punish
if mean_cooperation >= forgiveness_threshold:
# Forgive only when the estimated cooperation rate is high enough.
return C
else:
# Opponent appears to be hostile with sufficient confidence
# Punish the defection
return D
Loading