Skip to content

Commit 1a7688b

Browse files
authored
Merge pull request #1215 from Axelrod-Python/adaptor
New Strategy: Implementation of Adaptor
2 parents 8dba4c0 + ae51ba4 commit 1a7688b

File tree

10 files changed

+213
-8
lines changed

10 files changed

+213
-8
lines changed

axelrod/game.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ def RPST(self) -> Tuple[Score, Score, Score, Score]:
3838
P = self.scores[(D, D)][0]
3939
S = self.scores[(C, D)][0]
4040
T = self.scores[(D, C)][0]
41-
return (R, P, S, T)
41+
return R, P, S, T
4242

4343
def score(self, pair: Tuple[Action, Action]) -> Tuple[Score, Score]:
4444
"""Returns the appropriate score for a decision pair.

axelrod/strategies/_strategies.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from .adaptive import Adaptive
2+
from .adaptor import AdaptorBrief, AdaptorLong
23
from .alternator import Alternator
34
from .ann import EvolvedANN, EvolvedANN5, EvolvedANNNoise05
45
from .apavlov import APavlov2006, APavlov2011
@@ -230,6 +231,8 @@
230231
all_strategies = [
231232
Adaptive,
232233
AdaptiveTitForTat,
234+
AdaptorBrief,
235+
AdaptorLong,
233236
Aggravater,
234237
Alexei,
235238
ALLCorALLD,

axelrod/strategies/adaptor.py

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
from typing import Dict, Tuple
2+
3+
from axelrod.action import Action
4+
from axelrod.player import Player
5+
from axelrod.random_ import random_choice
6+
7+
from numpy import heaviside
8+
9+
C, D = Action.C, Action.D
10+
11+
12+
class AbstractAdaptor(Player):
13+
"""
14+
An adaptive strategy that updates an internal state based on the last
15+
round of play. Using this state the player Cooperates with a probability
16+
derived from the state.
17+
18+
s, float:
19+
the internal state, initially 0
20+
perr, float:
21+
an error threshold for misinterpreted moves
22+
delta, a dictionary of floats:
23+
additive update values for s depending on the last round's outcome
24+
25+
Names:
26+
27+
- Adaptor: [Hauert2002]_
28+
29+
"""
30+
31+
name = "AbstractAdaptor"
32+
classifier = {
33+
"memory_depth": float("inf"), # Long memory
34+
"stochastic": True,
35+
"makes_use_of": set(),
36+
"long_run_time": False,
37+
"inspects_source": False,
38+
"manipulates_source": False,
39+
"manipulates_state": False,
40+
}
41+
42+
def __init__(self, delta: Dict[Tuple[Action, Action], float],
43+
perr: float = 0.01) -> None:
44+
super().__init__()
45+
self.perr = perr
46+
self.delta = delta
47+
self.s = 0.
48+
49+
def strategy(self, opponent: Player) -> Action:
50+
if self.history:
51+
# Update internal state from the last play
52+
last_round = (self.history[-1], opponent.history[-1])
53+
self.s += self.delta[last_round]
54+
55+
# Compute probability of Cooperation
56+
p = self.perr + (1.0 - 2 * self.perr) * (
57+
heaviside(self.s + 1, 1) - heaviside(self.s - 1, 1))
58+
# Draw action
59+
action = random_choice(p)
60+
return action
61+
62+
63+
class AdaptorBrief(AbstractAdaptor):
64+
"""
65+
An Adaptor trained on short interactions.
66+
67+
Names:
68+
69+
- AdaptorBrief: [Hauert2002]_
70+
71+
"""
72+
73+
name = "AdaptorBrief"
74+
75+
def __init__(self) -> None:
76+
delta = {
77+
(C, C): 0., # R
78+
(C, D): -1.001505, # S
79+
(D, C): 0.992107, # T
80+
(D, D): -0.638734 # P
81+
}
82+
super().__init__(delta=delta)
83+
84+
85+
class AdaptorLong(AbstractAdaptor):
86+
"""
87+
An Adaptor trained on long interactions.
88+
89+
Names:
90+
91+
- AdaptorLong: [Hauert2002]_
92+
93+
"""
94+
95+
name = "AdaptorLong"
96+
97+
def __init__(self) -> None:
98+
delta = {
99+
(C, C): 0., # R
100+
(C, D): 1.888159, # S
101+
(D, C): 1.858883, # T
102+
(D, D): -0.995703 # P
103+
}
104+
super().__init__(delta=delta)

axelrod/strategies/bush_mosteller.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,15 +51,15 @@ def __init__(
5151
aspiration_level_divider: float, 3.0
5252
Value that regulates the aspiration level,
5353
isn't modified during match
54-
learning rate [0 , 1]
55-
Percentage of learning speed
54+
learning rate [0 , 1]
55+
Percentage of learning speed
5656
Variables / Constants
57-
_stimulus (Var: [-1 , 1]): float
57+
stimulus (Var: [-1 , 1]): float
5858
Value that impacts the changes of action probability
5959
_aspiration_level: float
6060
Value that impacts the stimulus changes, isn't modified during match
6161
_init_c_prob , _init_d_prob : float
62-
Values used to properly set up reset(),
62+
Values used to properly set up reset(),
6363
set to original probabilities
6464
"""
6565
super().__init__()
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
"""Tests for the adaptor"""
2+
3+
import unittest
4+
5+
import axelrod
6+
from axelrod import Game
7+
8+
from .test_player import TestPlayer, test_four_vector
9+
10+
C, D = axelrod.Action.C, axelrod.Action.D
11+
12+
13+
class TestAdaptorBrief(TestPlayer):
14+
15+
name = "AdaptorBrief"
16+
player = axelrod.AdaptorBrief
17+
expected_classifier = {
18+
"memory_depth": float("inf"),
19+
"stochastic": True,
20+
"makes_use_of": set(),
21+
"inspects_source": False,
22+
"manipulates_source": False,
23+
"manipulates_state": False,
24+
}
25+
26+
def test_strategy(self):
27+
# No error.
28+
actions = [(C, C), (C, C), (C, C), (C, C)]
29+
self.versus_test(
30+
opponent=axelrod.AdaptorBrief(), expected_actions=actions, seed=0
31+
)
32+
33+
# Error corrected.
34+
actions = [(C, C), (C, D), (D, C), (C, C)]
35+
self.versus_test(
36+
opponent=axelrod.AdaptorBrief(), expected_actions=actions, seed=22
37+
)
38+
39+
# Error corrected, example 2
40+
actions = [(D, C), (C, D), (D, C), (C, D), (C, C)]
41+
self.versus_test(
42+
opponent=axelrod.AdaptorBrief(), expected_actions=actions, seed=925
43+
)
44+
45+
# Versus Cooperator
46+
actions = [(C, C)] * 8
47+
self.versus_test(
48+
opponent=axelrod.Cooperator(), expected_actions=actions, seed=0
49+
)
50+
51+
# Versus Defector
52+
actions = [(C, D), (D, D), (D, D), (D, D), (D, D), (D, D), (D, D)]
53+
self.versus_test(
54+
opponent=axelrod.Defector(), expected_actions=actions, seed=0
55+
)
56+
57+
58+
class TestAdaptorLong(TestPlayer):
59+
60+
name = "AdaptorLong"
61+
player = axelrod.AdaptorLong
62+
expected_classifier = {
63+
"memory_depth": float("inf"),
64+
"stochastic": True,
65+
"makes_use_of": set(),
66+
"inspects_source": False,
67+
"manipulates_source": False,
68+
"manipulates_state": False,
69+
}
70+
71+
def test_strategy(self):
72+
# No error.
73+
actions = [(C, C), (C, C), (C, C), (C, C)]
74+
self.versus_test(
75+
opponent=axelrod.AdaptorLong(), expected_actions=actions, seed=0
76+
)
77+
78+
# Error corrected.
79+
actions = [(C, C), (C, D), (D, D), (C, C), (C, C)]
80+
self.versus_test(
81+
opponent=axelrod.AdaptorLong(), expected_actions=actions, seed=22
82+
)
83+
84+
# Versus Cooperator
85+
actions = [(C, C)] * 8
86+
self.versus_test(
87+
opponent=axelrod.Cooperator(), expected_actions=actions, seed=0
88+
)
89+
90+
# Versus Defector
91+
actions = [(C, D), (D, D), (C, D), (D, D), (D, D), (C, D), (D, D)]
92+
self.versus_test(
93+
opponent=axelrod.Defector(), expected_actions=actions, seed=0
94+
)

axelrod/tests/strategies/test_memoryone.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,8 @@ class TestWinShiftLoseStayTestPlayer(TestPlayer):
7171
def test_strategy(self):
7272
# Check that switches if does not get best payoff.
7373
actions = [(D, C), (C, D), (C, C), (D, D), (D, C)]
74-
self.versus_test(opponent=axelrod.Alternator(), expected_actions=actions)
74+
self.versus_test(opponent=axelrod.Alternator(),
75+
expected_actions=actions)
7576

7677

7778
class TestGTFT(TestPlayer):

axelrod/tests/strategies/test_meta.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -548,7 +548,7 @@ class TestNMWEStochastic(TestMetaPlayer):
548548
}
549549

550550
def test_strategy(self):
551-
actions = [(C, C), (C, D), (D, C), (D, D), (D, C)]
551+
actions = [(C, C), (C, D), (C, C), (D, D), (D, C)]
552552
self.versus_test(opponent=axelrod.Alternator(), expected_actions=actions)
553553

554554

docs/reference/all_strategies.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ Here are the docstrings of all the strategies in the library.
88

99
.. automodule:: axelrod.strategies.adaptive
1010
:members:
11+
.. automodule:: axelrod.strategies.adaptor
12+
:members:
1113
.. automodule:: axelrod.strategies.alternator
1214
:members:
1315
.. automodule:: axelrod.strategies.ann

docs/reference/bibliography.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ documentation.
2727
.. [Berg2015] Berg, P. Van Den, & Weissing, F. J. (2015). The importance of mechanisms for the evolution of cooperation. Proceedings of the Royal Society B-Biological Sciences, 282.
2828
.. [Eckhart2015] Eckhart Arnold (2016) CoopSim v0.9.9 beta 6. https://github.com/jecki/CoopSim/
2929
.. [Frean1994] Frean, Marcus R. "The Prisoner's Dilemma without Synchrony." Proceedings: Biological Sciences, vol. 257, no. 1348, 1994, pp. 75–79. www.jstor.org/stable/50253.
30+
.. [Hauert2002] Hauert, Christoph, and Olaf Stenull. "Simple adaptive strategy wins the prisoner's dilemma." Journal of Theoretical Biology 218.3 (2002): 261-272.
3031
.. [Hilbe2013] Hilbe, C., Nowak, M.A. and Traulsen, A. (2013). Adaptive dynamics of extortion and compliance, PLoS ONE, 8(11), p. e77886. doi: 10.1371/journal.pone.0077886.
3132
.. [Hilbe2017] Hilbe, C., Martinez-Vaquero, L. A., Chatterjee K., Nowak M. A. (2017). Memory-n strategies of direct reciprocity, Proceedings of the National Academy of Sciences May 2017, 114 (18) 4715-4720; doi: 10.1073/pnas.1621239114.
3233
.. [Kuhn2017] Kuhn, Steven, "Prisoner's Dilemma", The Stanford Encyclopedia of Philosophy (Spring 2017 Edition), Edward N. Zalta (ed.), https://plato.stanford.edu/archives/spr2017/entries/prisoner-dilemma/

docs/tutorials/advanced/classification_of_strategies.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ strategies::
4747
... }
4848
>>> strategies = axl.filtered_strategies(filterset)
4949
>>> len(strategies)
50-
82
50+
84
5151

5252
Or, to find out how many strategies only use 1 turn worth of memory to
5353
make a decision::

0 commit comments

Comments
 (0)