Skip to content

Commit 2681380

Browse files
committed
Merge pull request #473 from Axelrod-Python/404
404 - Cleaning up documentation for strategies
2 parents 8542807 + 3cf2ee2 commit 2681380

File tree

16 files changed

+308
-278
lines changed

16 files changed

+308
-278
lines changed

axelrod/_strategy_utils.py

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
"""Utilities used by various strategies"""
2+
import functools
3+
import collections
4+
import itertools
5+
6+
from axelrod import RoundRobin, update_history
7+
from axelrod import Actions
8+
9+
from axelrod.strategies.cycler import Cycler
10+
11+
C, D = Actions.C, Actions.D
12+
13+
def detect_cycle(history, min_size=1, offset=0):
14+
"""Detects cycles in the sequence history.
15+
16+
Mainly used by hunter strategies.
17+
18+
Parameters
19+
20+
history: sequence of C and D
21+
The sequence to look for cycles within
22+
min_size: int, 1
23+
The minimum length of the cycle
24+
offset: int, 0
25+
The amount of history to skip initially
26+
"""
27+
history_tail = history[-offset:]
28+
for i in range(min_size, len(history_tail) // 2):
29+
cycle = tuple(history_tail[:i])
30+
for j, elem in enumerate(history_tail):
31+
if elem != cycle[j % len(cycle)]:
32+
break
33+
if j == len(history_tail) - 1:
34+
# We made it to the end, is the cycle itself a cycle?
35+
# I.E. CCC is not ok as cycle if min_size is really 2
36+
# Since this is the same as C
37+
return cycle
38+
return None
39+
40+
41+
def limited_simulate_play(player_1, player_2, h1):
42+
"""Here we want to replay player_1's history to player_2, allowing
43+
player_2's strategy method to set any internal variables as needed. If you
44+
need a more complete simulation, see `simulate_play` in player.py. This
45+
function is specifically designed for the needs of MindReader."""
46+
h2 = player_2.strategy(player_1)
47+
update_history(player_1, h1)
48+
update_history(player_2, h2)
49+
50+
def simulate_match(player_1, player_2, strategy, rounds=10):
51+
"""Simulates a number of matches."""
52+
for match in range(rounds):
53+
limited_simulate_play(player_1, player_2, strategy)
54+
55+
def look_ahead(player_1, player_2, game, rounds=10):
56+
"""Looks ahead for `rounds` and selects the next strategy appropriately."""
57+
results = []
58+
59+
# Simulate plays for `rounds` rounds
60+
strategies = [C, D]
61+
for strategy in strategies:
62+
# Instead of a deepcopy, create a new opponent and play out the history
63+
opponent_ = player_2.clone()
64+
player_ = Cycler(strategy) # Either cooperator or defector
65+
for h1 in player_1.history:
66+
limited_simulate_play(player_, opponent_, h1)
67+
68+
round_robin = RoundRobin(players=[player_, opponent_], game=game,
69+
turns=rounds)
70+
simulate_match(player_, opponent_, strategy, rounds)
71+
results.append(round_robin._calculate_scores(player_, opponent_)[0])
72+
73+
return strategies[results.index(max(results))]
74+
75+
76+
class Memoized(object):
77+
"""Decorator. Caches a function's return value each time it is called.
78+
If called later with the same arguments, the cached value is returned
79+
(not reevaluated). From:
80+
https://wiki.python.org/moin/PythonDecoratorLibrary#Memoize
81+
"""
82+
def __init__(self, func):
83+
self.func = func
84+
self.cache = {}
85+
86+
def __call__(self, *args):
87+
if not isinstance(args, collections.Hashable):
88+
# uncacheable. a list, for instance.
89+
# better to not cache than blow up.
90+
return self.func(*args)
91+
if args in self.cache:
92+
return self.cache[args]
93+
else:
94+
value = self.func(*args)
95+
self.cache[args] = value
96+
return value
97+
98+
def __repr__(self):
99+
"""Return the function's docstring."""
100+
return self.func.__doc__
101+
102+
def __get__(self, obj, objtype):
103+
"""Support instance methods."""
104+
return functools.partial(self.__call__, obj)
105+
106+
107+
@Memoized
108+
def recursive_thue_morse(n):
109+
"""The recursive definition of the Thue-Morse sequence. The first few terms
110+
of the Thue-Morse sequence are:
111+
0 1 1 0 1 0 0 1 1 0 0 1 0 1 1 0 . . ."""
112+
113+
if n == 0:
114+
return 0
115+
if n % 2 == 0:
116+
return recursive_thue_morse(n / 2)
117+
if n % 2 == 1:
118+
return 1 - recursive_thue_morse((n - 1) / 2)
119+
120+
121+
def thue_morse_generator(start=0):
122+
"""A generator for the Thue-Morse sequence."""
123+
124+
for n in itertools.count(start):
125+
yield recursive_thue_morse(n)

axelrod/strategies/calculator.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from axelrod import Actions, Player
44
from .axelrod_first import Joss
5-
from .hunter import detect_cycle
5+
from axelrod._strategy_utils import detect_cycle
66

77
class Calculator(Player):
88
"""

axelrod/strategies/hunter.py

Lines changed: 1 addition & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,8 @@
11
from axelrod import Actions, Player
2+
from axelrod._strategy_utils import detect_cycle
23

34
C, D = Actions.C, Actions.D
45

5-
def detect_cycle(history, min_size=1, offset=0):
6-
"""Detects cycles in the sequence history.
7-
8-
Parameters
9-
----------
10-
history: sequence of C and D
11-
The sequence to look for cycles within
12-
min_size: int, 1
13-
The minimum length of the cycle
14-
offset: int, 0
15-
The amount of history to skip initially
16-
"""
17-
history_tail = history[-offset:]
18-
for i in range(min_size, len(history_tail) // 2):
19-
cycle = tuple(history_tail[:i])
20-
for j, elem in enumerate(history_tail):
21-
if elem != cycle[j % len(cycle)]:
22-
break
23-
if j == len(history_tail) - 1:
24-
# We made it to the end, is the cycle itself a cycle?
25-
# I.E. CCC is not ok as cycle if min_size is really 2
26-
# Since this is the same as C
27-
return cycle
28-
return None
29-
306

317
class DefectorHunter(Player):
328
"""A player who hunts for defectors."""

axelrod/strategies/lookerup.py

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,33 +19,39 @@ class LookerUp(Player):
1919
- my last action was a C the opponents
2020
- last action was a D
2121
then the corresponding key would be
22+
2223
('C', 'C', 'D')
24+
2325
and the value would contain the action to play on this turn.
2426
2527
Some well-known strategies can be expressed as special cases; for example
2628
Cooperator is given by the dict:
29+
2730
{('', '', '') : C}
31+
2832
where m and n are both zero. Tit-For-Tat is given by:
2933
30-
{
34+
{
3135
('', 'C', 'D') : D,
3236
('', 'D', 'D') : D,
3337
('', 'C', 'C') : C,
3438
('', 'D', 'C') : C,
35-
}
39+
}
3640
3741
where m=1 and n=0.
3842
3943
Lookup tables where the action depends on the opponent's first actions (as
4044
opposed to most recent actions) will have a non-empty first string in the
4145
tuple. For example, this fragment of a dict:
4246
43-
{
47+
{
48+
4449
...
4550
('C', 'C', 'C') : C.
4651
('D', 'C', 'C') : D,
4752
...
48-
}
53+
54+
}
4955
5056
states that if self and opponent both cooperated on the previous turn, we
5157
should cooperate this turn unless the opponent started by defecting, in
@@ -55,12 +61,14 @@ class LookerUp(Player):
5561
(so m or n are greater than 1), simply concatenate the strings together.
5662
Below is an incomplete example where m=3 and n=2.
5763
58-
{
64+
{
65+
5966
...
6067
('CC', 'CDD', 'CCC') : C.
6168
('CD', 'CCD', 'CCC') : D,
6269
...
63-
}
70+
71+
}
6472
"""
6573

6674
name = 'LookerUp'

axelrod/strategies/memoryone.py

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,15 @@ class MemoryOnePlayer(Player):
2525
def __init__(self, four_vector=None, initial=C):
2626
"""
2727
Parameters
28-
----------
28+
2929
fourvector, list or tuple of floats of length 4
3030
The response probabilities to the preceeding round of play
3131
( P(C|CC), P(C|CD), P(C|DC), P(C|DD) )
3232
initial, C or D
3333
The initial move
3434
3535
Special Cases
36-
-------------
36+
3737
Alternator is equivalent to MemoryOnePlayer((0, 0, 1, 1), C)
3838
Cooperator is equivalent to MemoryOnePlayer((1, 1, 1, 1), C)
3939
Defector is equivalent to MemoryOnePlayer((0, 0, 0, 0), C)
@@ -96,12 +96,12 @@ class GTFT(MemoryOnePlayer):
9696
def __init__(self, p=None):
9797
"""
9898
Parameters
99-
----------
99+
100100
p, float
101101
A parameter used to compute the four-vector
102102
103103
Special Cases
104-
-------------
104+
105105
TitForTat is equivalent to GTFT(0)
106106
"""
107107
self.p = p
@@ -153,13 +153,13 @@ class StochasticWSLS(MemoryOnePlayer):
153153
def __init__(self, ep=0.05):
154154
"""
155155
Parameters
156-
----------
156+
157157
ep, float
158158
A parameter used to compute the four-vector -- the probability of
159159
cooperating when the previous round was CD or DC
160160
161161
Special Cases
162-
-------------
162+
163163
WinStayLoseShift is equivalent to StochasticWSLS(0)
164164
"""
165165

@@ -195,7 +195,7 @@ class LRPlayer(MemoryOnePlayer):
195195
def receive_tournament_attributes(self, phi=0, s=None, l=None):
196196
"""
197197
Parameters
198-
----------
198+
199199
phi, s, l: floats
200200
Parameter used to compute the four-vector according to the
201201
parameterization of the strategies below.
@@ -230,7 +230,7 @@ class ZDExtort2(LRPlayer):
230230
def __init__(self, phi=1./9, s=0.5):
231231
"""
232232
Parameters
233-
----------
233+
234234
phi, s: floats
235235
Parameters passed through to LRPlayer to determine
236236
the four vector.
@@ -255,7 +255,7 @@ class ZDExtort2v2(LRPlayer):
255255
def __init__(self, phi=1./8, s=0.5, l=1):
256256
"""
257257
Parameters
258-
----------
258+
259259
phi, s: floats
260260
Parameters passed through to LRPlayer to determine
261261
the four vector.
@@ -280,7 +280,7 @@ class ZDExtort4(LRPlayer):
280280
def __init__(self, phi=4./17, s=0.25, l=1):
281281
"""
282282
Parameters
283-
----------
283+
284284
phi, s: floats
285285
Parameters passed through to LRPlayer to determine
286286
the four vector.
@@ -304,7 +304,7 @@ class ZDGen2(LRPlayer):
304304
def __init__(self, phi=1./8, s=0.5, l=3):
305305
"""
306306
Parameters
307-
----------
307+
308308
phi, s: floats
309309
Parameters passed through to LRPlayer to determine
310310
the four vector.
@@ -327,7 +327,7 @@ class ZDGTFT2(LRPlayer):
327327
def __init__(self, phi=0.25, s=0.5):
328328
"""
329329
Parameters
330-
----------
330+
331331
phi, s: floats
332332
Parameters passed through to LRPlayer to determine
333333
the four vector.
@@ -353,7 +353,7 @@ class ZDSet2(LRPlayer):
353353
def __init__(self, phi=1./4, s=0., l=2):
354354
"""
355355
Parameters
356-
----------
356+
357357
phi, s: floats
358358
Parameters passed through to LRPlayer to determine
359359
the four vector.
@@ -383,12 +383,12 @@ class SoftJoss(MemoryOnePlayer):
383383
def __init__(self, q=0.9):
384384
"""
385385
Parameters
386-
----------
386+
387387
q, float
388388
A parameter used to compute the four-vector
389389
390390
Special Cases
391-
-------------
391+
392392
Cooperator is equivalent to SoftJoss(0)
393393
TitForTat is equivalent to SoftJoss(1)
394394
"""

axelrod/strategies/meta.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,7 @@ class MetaMixer(MetaPlayer):
279279
In essence this is creating a Mixed strategy.
280280
281281
Parameters
282-
----------
282+
283283
team : list of strategy classes, optional
284284
Team of strategies that are to be randomly played
285285
If none is passed will select the ordinary strategies.

0 commit comments

Comments
 (0)