-
Notifications
You must be signed in to change notification settings - Fork 268
/
Copy pathhistory.py
148 lines (122 loc) · 4.75 KB
/
history.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
from collections import Counter
from axelrod.action import Action, actions_to_str
C, D = Action.C, Action.D
class History(object):
"""
History class to track the history of play and metadata including
the number of cooperations and defections, and if available, the
opponents plays and the state distribution of the history of play.
"""
def __init__(self, plays=None, coplays=None):
"""
Parameters
----------
plays:
An ordered iterable of the actions of the player.
coplays:
An ordered iterable of the actions of the coplayer (aka opponent).
"""
self._plays = []
# Coplays is tracked mainly for computation of the state distribution
# when cloning or dualing.
self._coplays = []
self._actions = Counter()
self._state_distribution = Counter()
if plays:
self.extend(plays, coplays)
def append(self, play, coplay):
"""Appends a new (play, coplay) pair an updates metadata for
number of cooperations and defections, and the state distribution."""
self._plays.append(play)
self._actions[play] += 1
self._coplays.append(coplay)
self._state_distribution[(play, coplay)] += 1
def copy(self):
"""Returns a new object with the same data."""
return self.__class__(plays=self._plays, coplays=self._coplays)
def flip_plays(self):
"""Creates a flipped plays history for use with DualTransformer."""
flipped_plays = [action.flip() for action in self._plays]
return self.__class__(plays=flipped_plays, coplays=self._coplays)
def extend(self, plays, coplays):
"""A function that emulates list.extend."""
# We could repeatedly call self.append but this is more efficient.
self._plays.extend(plays)
self._actions.update(plays)
self._coplays.extend(coplays)
self._state_distribution.update(zip(plays, coplays))
def reset(self):
"""Clears all data in the History object."""
self._plays.clear()
self._coplays.clear()
self._actions.clear()
self._state_distribution.clear()
@property
def coplays(self):
return self._coplays
@property
def cooperations(self):
return self._actions[C]
@property
def defections(self):
return self._actions[D]
@property
def state_distribution(self):
return self._state_distribution
def __eq__(self, other):
if isinstance(other, list):
return self._plays == other
elif isinstance(other, History):
return (
self._plays == other._plays and self._coplays == other._coplays
)
raise TypeError("Cannot compare types.")
def __getitem__(self, key):
# Passthrough keys and slice objects
return self._plays[key]
def __str__(self):
return actions_to_str(self._plays)
def __list__(self):
return self._plays
def __len__(self):
return len(self._plays)
def __repr__(self):
return repr(self.__list__())
class LimitedHistory(History):
"""
History class that only tracks the last N rounds. Used for testing memory
depth.
"""
def __init__(self, memory_depth, plays=None, coplays=None):
"""
Parameters
----------
memory_depth, int:
length of history to retain
"""
self.memory_depth = memory_depth
super().__init__(plays=plays, coplays=coplays)
def flip_plays(self):
"""Creates a flipped plays history for use with DualTransformer."""
flipped_plays = [action.flip() for action in self._plays]
return self.__class__(
self.memory_depth, plays=flipped_plays, coplays=self._coplays
)
def append(self, play, coplay):
"""Appends a new (play, coplay) pair an updates metadata for
number of cooperations and defections, and the state distribution."""
self._plays.append(play)
self._actions[play] += 1
self._coplays.append(coplay)
self._state_distribution[(play, coplay)] += 1
if len(self._plays) > self.memory_depth:
first_play, first_coplay = self._plays.pop(0), self._coplays.pop(0)
self._actions[first_play] -= 1
self._state_distribution[(first_play, first_coplay)] -= 1
def extend(self, new_plays, new_coplays):
"""A function that emulates list.extend, respecting the stated memory depth."""
self._plays.extend(new_plays)
self._coplays.extend(new_coplays)
if len(self._plays) > self.memory_depth:
self._plays = self._plays[-self.memory_depth :]
self._coplays = self._coplays[-self.memory_depth :]