Skip to content

Commit cf65f33

Browse files
Nikoletamarcharper
authored andcommitted
add implementation of a generic memory two + 2 strategies (#1171)
* add implementation of a generic memory two + 2 strategies Close #1063 A generic class for memory two players, similar to the MemoryOnePlayer class. If transition rates are not given the the player is set to default which is Cooperator. I have added the equivilent tests and a reference from issue #1063. Moreover, this implements two strategies of interest from #1063: - AON2 and Delayed AON1 Each strategy has 16 states. I have added tests for most of the states. * add AON2 and Delayed AON1 to all strategies * add to the documentation of the strategies Add information about the vectors which are reported as equivilent. I am not sure I can say that they are actually different in the documentation without saying why and without adding the fingerprints. * Fix type hint. (#8) * remove initial action option * minor tweak to documentation * add to tests initialisation of stochastic mem two * fix doc test * test stochastic behaviour of memory two player Added tests against two opponents. For different seed the behaviour of the memory two strategy changes. * address comments on pr by Marc
1 parent 5e09643 commit cf65f33

5 files changed

Lines changed: 374 additions & 3 deletions

File tree

axelrod/strategies/_strategies.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@
5757
MemoryOnePlayer, ALLCorALLD, FirmButFair, GTFT, SoftJoss,
5858
StochasticCooperator, StochasticWSLS, WinStayLoseShift, WinShiftLoseStay,
5959
ReactivePlayer)
60-
from .memorytwo import MEM2
60+
from .memorytwo import MemoryTwoPlayer, AON2, DelayedAON1, MEM2
6161
from .mindcontrol import MindController, MindWarper, MindBender
6262
from .mindreader import MindReader, ProtectedMindReader, MirrorMindReader
6363
from .mutual import Desperate, Hopeless, Willing
@@ -103,6 +103,7 @@
103103
AlternatorHunter,
104104
AntiCycler,
105105
AntiTitForTat,
106+
AON2,
106107
APavlov2006,
107108
APavlov2011,
108109
Appeaser,
@@ -136,6 +137,7 @@
136137
Defector,
137138
DefectorHunter,
138139
Desperate,
140+
DelayedAON1,
139141
DoubleCrosser,
140142
Doubler,
141143
DoubleResurrection,

axelrod/strategies/memorytwo.py

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,204 @@
1+
"""Memory Two strategies."""
2+
3+
import itertools
4+
import warnings
5+
from typing import Tuple, Dict
6+
17
from axelrod.action import Action
28
from axelrod.player import Player
9+
from axelrod.random_ import random_choice
310
from .titfortat import TitForTat, TitFor2Tats
411
from .defector import Defector
512

13+
614
C, D = Action.C, Action.D
715

816

17+
class MemoryTwoPlayer(Player):
18+
"""
19+
Uses a sixteen-vector for strategies based on the 16 conditional probabilities
20+
P(X | I,J,K,L) where X, I, J, K, L in [C, D] and I, J are the players last
21+
two moves and K, L are the opponents last two moves. These conditional
22+
probabilities are the following:
23+
1. P(C|CC, CC)
24+
2. P(C|CC, CD)
25+
3. P(C|CC, DC)
26+
4. P(C|CC, DD)
27+
5. P(C|CD, CC)
28+
6. P(C|CD, CD)
29+
7. P(C|CD, DC)
30+
8. P(C|CD, DD)
31+
9. P(C|DC, CC)
32+
10. P(C|DC, CD)
33+
11. P(C|DC, DC)
34+
12. P(C|DC, DD)
35+
13. P(C|DD, CC)
36+
14. P(C|DD, CD)
37+
15. P(C|DD, DC)
38+
16. P(C|DD, DD))
39+
Cooperator is set as the default player if sixteen_vector is not given.
40+
41+
Names
42+
43+
- Memory Two: [Hilbe2017]_
44+
"""
45+
46+
name = 'Generic Memory Two Player'
47+
classifier = {
48+
'memory_depth': 2,
49+
'stochastic': False,
50+
'makes_use_of': set(),
51+
'long_run_time': False,
52+
'inspects_source': False,
53+
'manipulates_source': False,
54+
'manipulates_state': False
55+
}
56+
57+
def __init__(self, sixteen_vector: Tuple[float, ...] = None,
58+
initial: Action = C) -> None:
59+
"""
60+
Parameters
61+
----------
62+
63+
sixteen_vector: list or tuple of floats of length 16
64+
The response probabilities to the preceding round of play
65+
initial: C or D
66+
The initial 2 moves
67+
"""
68+
super().__init__()
69+
self._initial = initial
70+
self.set_initial_sixteen_vector(sixteen_vector)
71+
72+
def set_initial_sixteen_vector(self, sixteen_vector):
73+
if sixteen_vector is None:
74+
sixteen_vector = tuple([1] * 16)
75+
warnings.warn("Memory two player is set to default, Cooperator.")
76+
77+
self.set_sixteen_vector(sixteen_vector)
78+
if self.name == 'Generic Memory Two Player':
79+
self.name = "%s: %s" % (self.name, sixteen_vector)
80+
81+
def set_sixteen_vector(self, sixteen_vector: Tuple):
82+
if not all(0 <= p <= 1 for p in sixteen_vector):
83+
raise ValueError("An element in the probability vector, {}, is not "
84+
"between 0 and 1.".format(str(sixteen_vector)))
85+
86+
states = [(hist[:2], hist[2:])
87+
for hist in list(itertools.product((C, D), repeat=4))]
88+
89+
self._sixteen_vector = dict(zip(states, sixteen_vector)) # type: Dict[tuple, float]
90+
self.classifier['stochastic'] = any(0 < x < 1 for x in set(sixteen_vector))
91+
92+
def strategy(self, opponent: Player) -> Action:
93+
if len(opponent.history) <= 1:
94+
return self._initial
95+
# Determine which probability to use
96+
p = self._sixteen_vector[(tuple(self.history[-2:]),
97+
tuple(opponent.history[-2:]))]
98+
# Draw a random number in [0, 1] to decide
99+
return random_choice(p)
100+
101+
102+
class AON2(MemoryTwoPlayer):
103+
"""
104+
AON2 a memory two strategy introduced in [Hilbe2017]_. It belongs to the
105+
AONk (all-or-none) family of strategies. These strategies were designed to
106+
satisfy the three following properties:
107+
108+
1. Mutually Cooperative. A strategy is mutually cooperative if there are
109+
histories for which the strategy prescribes to cooperate, and if it continues
110+
to cooperate after rounds with mutual cooperation (provided the last k actions
111+
of the focal player were actually consistent).
112+
113+
2. Error correcting. A strategy is error correcting after at most k rounds if,
114+
after any history, it generally takes a group of players at most k + 1 rounds
115+
to re-establish mutual cooperation.
116+
117+
3. Retaliating. A strategy is retaliating for at least k rounds if, after
118+
rounds in which the focal player cooperated while the coplayer defected,
119+
the strategy responds by defecting the following k rounds.
120+
121+
In [Hilbe2017]_ the following vectors are reported as "equivalent" to AON2
122+
with their respective self-cooperation rate (note that these are not the same):
123+
124+
1. [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], self-cooperation
125+
rate: 0.952
126+
2. [1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], self-cooperation
127+
rate: 0.951
128+
3. [1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], self-cooperation
129+
rate: 0.951
130+
4. [1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1], self-cooperation
131+
rate: 0.952
132+
133+
AON2 is implemented using vector 1 due its self-cooperation rate.
134+
135+
In essence it is a strategy that starts off by cooperating and will cooperate
136+
again only after the states (CC, CC), (CD, CD), (DC, DC), (DD, DD).
137+
138+
Names:
139+
140+
- AON2: [Hilbe2017]_
141+
"""
142+
143+
name = 'AON2'
144+
classifier = {
145+
'memory_depth': 2,
146+
'stochastic': False,
147+
'makes_use_of': set(),
148+
'long_run_time': False,
149+
'inspects_source': False,
150+
'manipulates_source': False,
151+
'manipulates_state': False
152+
}
153+
154+
def __init__(self) -> None:
155+
sixteen_vector = (1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)
156+
super().__init__(sixteen_vector)
157+
158+
159+
class DelayedAON1(MemoryTwoPlayer):
160+
"""
161+
Delayed AON1 a memory two strategy also introduced in [Hilbe2017]_ and belongs
162+
to the AONk family. Note that AON1 is equivalent to Win Stay Lose Shift.
163+
164+
In [Hilbe2017]_ the following vectors are reported as "equivalent" to Delayed
165+
AON1 with their respective self-cooperation rate (note that these are not the
166+
same):
167+
168+
1. [1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1], self-cooperation
169+
rate: 0.952
170+
2. [1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1], self-cooperation
171+
rate: 0.970
172+
3. [1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1], self-cooperation
173+
rate: 0.971
174+
175+
Delayed AON1 is implemented using vector 3 due its self-cooperation rate.
176+
177+
In essence it is a strategy that starts off by cooperating and will cooperate
178+
again only after the states (CC, CC), (CD, CD), (CD, DD), (DD, CD),
179+
(DC, DC) and (DD, DD).
180+
181+
Names:
182+
183+
- Delayed AON1: [Hilbe2017]_
184+
"""
185+
186+
name = 'Delayed AON1'
187+
classifier = {
188+
'memory_depth': 2,
189+
'stochastic': False,
190+
'makes_use_of': set(),
191+
'long_run_time': False,
192+
'inspects_source': False,
193+
'manipulates_source': False,
194+
'manipulates_state': False
195+
}
196+
197+
def __init__(self) -> None:
198+
sixteen_vector = (1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1)
199+
super().__init__(sixteen_vector)
200+
201+
9202
class MEM2(Player):
10203
"""A memory-two player that switches between TFT, TFTT, and ALLD.
11204

0 commit comments

Comments
 (0)