Skip to content

Commit 3ec848a

Browse files
committed
bayesian forgiver tests
1 parent b341f8c commit 3ec848a

File tree

1 file changed

+175
-0
lines changed

1 file changed

+175
-0
lines changed
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
"""Tests for the Bayesian Forgiver strategy."""
2+
3+
import axelrod as axl
4+
from axelrod.tests.property import strategy_lists
5+
6+
from .test_player import TestPlayer
7+
8+
C, D = axl.Action.C, axl.Action.D
9+
10+
11+
class TestBayesianForgiver(TestPlayer):
12+
"""Test suite for BayesianForgiver strategy."""
13+
14+
name = "Bayesian Forgiver: 1.0, 1.0, 0.45, 2.5"
15+
player = axl.BayesianForgiver
16+
expected_classifier = {
17+
"memory_depth": float("inf"),
18+
"stochastic": False,
19+
"makes_use_of": set(),
20+
"inspects_source": False,
21+
"manipulates_source": False,
22+
"manipulates_state": False,
23+
}
24+
25+
def test_initial_strategy(self):
26+
"""Test that the strategy starts by cooperating."""
27+
actions = [(C, C)]
28+
self.versus_test(axl.Cooperator(), expected_actions=actions)
29+
30+
def test_vs_cooperator(self):
31+
"""Test behavior against always cooperate - should cooperate."""
32+
actions = [(C, C), (C, C), (C, C), (C, C), (C, C)]
33+
self.versus_test(axl.Cooperator(), expected_actions=actions)
34+
35+
def test_vs_defector(self):
36+
"""Test behavior against always defect - should eventually defect back."""
37+
# First move: cooperate
38+
# After seeing D, Bayesian model updates: alpha=1, beta=2, mean=0.33
39+
# Even with high uncertainty, mean is below threshold, so defect
40+
actions = [(C, D), (D, D), (D, D), (D, D), (D, D)]
41+
self.versus_test(axl.Defector(), expected_actions=actions)
42+
43+
def test_vs_alternator(self):
44+
"""Test behavior against alternating strategy."""
45+
# With parameters (1.0, 1.0, 0.45, 2.5):
46+
# Early uncertainty is high, so threshold is high, but mean cooperation ~0.5
47+
# When opponent defects, we evaluate: mean < threshold → defect
48+
# This creates an alternating pattern
49+
actions = [(C, C), (C, D), (D, C), (C, D), (D, C), (C, D)]
50+
self.versus_test(axl.Alternator(), expected_actions=actions)
51+
52+
def test_vs_tit_for_tat(self):
53+
"""Test behavior against Tit For Tat - should achieve mutual cooperation."""
54+
actions = [(C, C), (C, C), (C, C), (C, C), (C, C)]
55+
self.versus_test(axl.TitForTat(), expected_actions=actions)
56+
57+
def test_vs_suspicious_tit_for_tat(self):
58+
"""Test against Suspicious Tit For Tat (starts with D)."""
59+
# STF starts with D, then mirrors
60+
# BF starts with C, sees D → defects back
61+
# This creates an alternating pattern
62+
actions = [(C, D), (D, C), (C, D), (D, C), (C, D)]
63+
self.versus_test(axl.SuspiciousTitForTat(), expected_actions=actions)
64+
65+
def test_vs_grudger(self):
66+
"""Test behavior against Grudger."""
67+
# Grudger cooperates until any defection, then defects forever
68+
# BayesianForgiver should cooperate, so mutual cooperation
69+
actions = [(C, C), (C, C), (C, C), (C, C), (C, C)]
70+
self.versus_test(axl.Grudger(), expected_actions=actions)
71+
72+
def test_vs_random(self):
73+
"""Test behavior against Random strategy with specific seed."""
74+
# Random behavior - BayesianForgiver should adapt
75+
# With seed=3, Random plays: C, C, C, C, D
76+
actions = [(C, C), (C, C), (C, C), (C, C), (C, D)]
77+
self.versus_test(axl.Random(), expected_actions=actions, seed=3)
78+
79+
def test_vs_mock_single_defection(self):
80+
"""Test response to a single defection."""
81+
# Opponent cooperates then defects once then cooperates
82+
# With new parameters: after seeing one D among many Cs, still punishes
83+
# but quickly returns to cooperation
84+
opponent = axl.MockPlayer(actions=[C, C, C, D, C, C, C])
85+
actions = [
86+
(C, C), # Both cooperate
87+
(C, C), # Both cooperate
88+
(C, C), # Both cooperate
89+
(C, D), # Opponent defects - alpha=4, beta=2
90+
(D, C), # Punish the defection
91+
(C, C), # Resume cooperation
92+
(C, C), # Continue cooperation
93+
]
94+
self.versus_test(opponent, expected_actions=actions)
95+
96+
def test_vs_mock_consistent_defector(self):
97+
"""Test punishment of consistent defector."""
98+
# Opponent defects consistently
99+
opponent = axl.MockPlayer(actions=[D, D, D, D, D, D])
100+
actions = [
101+
(C, D), # Start optimistic, see D - alpha=1, beta=2
102+
(D, D), # Mean = 0.33, below threshold even with uncertainty
103+
(D, D), # Continue defecting
104+
(D, D), # Continue defecting
105+
(D, D), # Continue defecting
106+
(D, D), # Continue defecting
107+
]
108+
self.versus_test(opponent, expected_actions=actions)
109+
110+
def test_vs_mock_mixed_behavior(self):
111+
"""Test adaptation to mixed behavior."""
112+
# Opponent with mixed C and D: [C, C, D, C, D, C, C, D]
113+
# With default parameters, responds more reactively to defections
114+
opponent = axl.MockPlayer(actions=[C, C, D, C, D, C, C, D])
115+
actions = [
116+
(C, C), # Start cooperating
117+
(C, C), # Continue cooperating
118+
(C, D), # Opponent defects
119+
(D, C), # Punish defection
120+
(C, D), # Opponent cooperated, we cooperate, they defect
121+
(D, C), # Punish again
122+
(C, C), # Resume cooperation
123+
(C, D), # Cooperate, opponent defects
124+
]
125+
self.versus_test(opponent, expected_actions=actions)
126+
127+
def test_parameter_changes(self):
128+
"""Test that different parameters affect behavior."""
129+
# Test with different prior - more pessimistic
130+
player = self.player(prior_alpha=1.0, prior_beta=2.0)
131+
opponent = axl.Defector()
132+
match = axl.Match([player, opponent], turns=3)
133+
result = match.play()
134+
# With pessimistic prior and defector opponent, should defect quickly
135+
self.assertEqual(result[0], (C, D)) # First move still C
136+
self.assertEqual(result[1][0], D) # Should defect after seeing D
137+
138+
def test_reset(self):
139+
"""Test that reset properly reinitializes the strategy."""
140+
player = self.player()
141+
opponent = axl.Cooperator()
142+
143+
# Play some rounds - opponent cooperates so alpha should increase
144+
for _ in range(5):
145+
player.strategy(opponent)
146+
opponent.strategy(player)
147+
player.update_history(C, C)
148+
opponent.update_history(C, C)
149+
150+
# Alpha should have changed (beta stays the same since opponent always cooperates)
151+
self.assertNotEqual(player.alpha, player.prior_alpha)
152+
153+
# Reset
154+
player.reset()
155+
156+
# Should be back to initial values
157+
self.assertEqual(player.alpha, player.prior_alpha)
158+
self.assertEqual(player.beta, player.prior_beta)
159+
160+
def test_clone(self):
161+
"""Test that cloning preserves parameters."""
162+
player = self.player(
163+
prior_alpha=3.0,
164+
prior_beta=2.0,
165+
base_forgiveness_threshold=0.4,
166+
uncertainty_factor=2.0,
167+
)
168+
clone = player.clone()
169+
170+
self.assertEqual(clone.prior_alpha, 3.0)
171+
self.assertEqual(clone.prior_beta, 2.0)
172+
self.assertEqual(clone.base_forgiveness_threshold, 0.4)
173+
self.assertEqual(clone.uncertainty_factor, 2.0)
174+
self.assertEqual(clone.alpha, 3.0)
175+
self.assertEqual(clone.beta, 2.0)

0 commit comments

Comments
 (0)