Skip to content

Commit d0c1636

Browse files
Merge pull request #953 from Chadys/802
Automatically set strategy classes (and transformers) __repr__ to include parameters. #802
2 parents 1d0e359 + 1151f2c commit d0c1636

31 files changed

+197
-202
lines changed

.gitignore

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,9 +102,12 @@ ENV/
102102
# Rope project settings
103103
.ropeproject
104104

105-
# Sublime Text Settings
105+
# Sublime Text settings
106106
*.sublime-*
107107

108+
# Jetbrain editor settings
109+
.idea/
110+
108111
# Docker files
109112
Dockerfile
110-
docker-compose.yml
113+
docker-compose.yml

axelrod/player.py

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from functools import wraps
33
import random
44
import copy
5+
import inspect
56

67
from axelrod.actions import Actions, flip_action, Action
78
from .game import DefaultGame
@@ -73,7 +74,7 @@ class Player(object):
7374
"""
7475

7576
name = "Player"
76-
classifier = {} # type: Dict[str, Any]
77+
classifier = {} # type: Dict[str, Any]
7778
default_classifier = {
7879
'stochastic': False,
7980
'memory_depth': float('inf'),
@@ -87,10 +88,26 @@ class Player(object):
8788
def __new__(cls, *args, **kwargs):
8889
"""Caches arguments for Player cloning."""
8990
obj = super().__new__(cls)
90-
obj.init_args = args
91-
obj.init_kwargs = kwargs
91+
obj.init_kwargs = cls.init_params(*args, **kwargs)
9292
return obj
9393

94+
@classmethod
95+
def init_params(cls, *args, **kwargs):
96+
"""
97+
Return a dictionary containing the init parameters of a strategy (without 'self').
98+
Use *args and *kwargs as value if specified
99+
and complete the rest with the default values.
100+
"""
101+
sig = inspect.signature(cls.__init__)
102+
# the 'self' parameter needs to be removed or the first *args will be assigned to it
103+
self_param = sig.parameters.get('self')
104+
new_params = list(sig.parameters.values())
105+
new_params.remove(self_param)
106+
sig = sig.replace(parameters=new_params)
107+
boundargs = sig.bind_partial(*args, **kwargs)
108+
boundargs.apply_defaults()
109+
return boundargs.arguments
110+
94111
def __init__(self):
95112
"""Initiates an empty history and 0 score for a player."""
96113
self.history = []
@@ -122,8 +139,15 @@ def set_match_attributes(self, length=-1, game=None, noise=0):
122139
self.receive_match_attributes()
123140

124141
def __repr__(self):
125-
"""The string method for the strategy."""
126-
return self.name
142+
"""The string method for the strategy.
143+
Appends the `__init__` parameters to the strategy's name."""
144+
name = self.name
145+
prefix = ': '
146+
gen = (value for value in self.init_kwargs.values() if value is not None)
147+
for value in gen:
148+
name = ''.join([name, prefix, str(value)])
149+
prefix = ', '
150+
return name
127151

128152
@staticmethod
129153
def _add_noise(noise, s1, s2):
@@ -158,7 +182,7 @@ def clone(self):
158182
# be significant changes required throughout the library.
159183
# Override in special cases only if absolutely necessary
160184
cls = self.__class__
161-
new_player = cls(*self.init_args, **self.init_kwargs)
185+
new_player = cls(**self.init_kwargs)
162186
new_player.match_attributes = copy.copy(self.match_attributes)
163187
return new_player
164188

axelrod/strategies/axelrod_first.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -259,9 +259,6 @@ def __init__(self, p: float = 0.9) -> None:
259259
self.p = p
260260
super().__init__(four_vector)
261261

262-
def __repr__(self) -> str:
263-
return "%s: %s" % (self.name, round(self.p, 2))
264-
265262

266263
class Nydegger(Player):
267264
"""

axelrod/strategies/cycler.py

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,6 @@ def __init__(self, cycle: str = "CCD") -> None:
7979
super().__init__()
8080
self.cycle_str = cycle
8181
self.cycle = self.get_new_itertools_cycle()
82-
self.name = "Cycler {}".format(cycle)
8382
self.classifier['memory_depth'] = len(cycle) - 1
8483

8584
def get_new_itertools_cycle(self):
@@ -99,8 +98,8 @@ class CyclerDC(Cycler):
9998
classifier = copy.copy(Cycler.classifier)
10099
classifier['memory_depth'] = 1
101100

102-
def __init__(self, cycle="DC") -> None:
103-
super().__init__(cycle=cycle)
101+
def __init__(self) -> None:
102+
super().__init__(cycle="DC")
104103

105104

106105
class CyclerCCD(Cycler):
@@ -109,8 +108,8 @@ class CyclerCCD(Cycler):
109108
classifier = copy.copy(Cycler.classifier)
110109
classifier['memory_depth'] = 2
111110

112-
def __init__(self, cycle="CCD") -> None:
113-
super().__init__(cycle=cycle)
111+
def __init__(self) -> None:
112+
super().__init__(cycle="CCD")
114113

115114

116115
class CyclerDDC(Cycler):
@@ -119,8 +118,8 @@ class CyclerDDC(Cycler):
119118
classifier = copy.copy(Cycler.classifier)
120119
classifier['memory_depth'] = 2
121120

122-
def __init__(self, cycle="DDC") -> None:
123-
super().__init__(cycle=cycle)
121+
def __init__(self) -> None:
122+
super().__init__(cycle="DDC")
124123

125124

126125
class CyclerCCCD(Cycler):
@@ -129,8 +128,8 @@ class CyclerCCCD(Cycler):
129128
classifier = copy.copy(Cycler.classifier)
130129
classifier['memory_depth'] = 3
131130

132-
def __init__(self, cycle="CCCD") -> None:
133-
super().__init__(cycle=cycle)
131+
def __init__(self) -> None:
132+
super().__init__(cycle="CCCD")
134133

135134

136135
class CyclerCCCCCD(Cycler):
@@ -139,8 +138,8 @@ class CyclerCCCCCD(Cycler):
139138
classifier = copy.copy(Cycler.classifier)
140139
classifier['memory_depth'] = 5
141140

142-
def __init__(self, cycle="CCCCCD") -> None:
143-
super().__init__(cycle=cycle)
141+
def __init__(self) -> None:
142+
super().__init__(cycle="CCCCCD")
144143

145144

146145
class CyclerCCCDCD(Cycler):
@@ -149,5 +148,5 @@ class CyclerCCCDCD(Cycler):
149148
classifier = copy.copy(Cycler.classifier)
150149
classifier['memory_depth'] = 5
151150

152-
def __init__(self, cycle="CCCDCD") -> None:
153-
super().__init__(cycle=cycle)
151+
def __init__(self) -> None:
152+
super().__init__(cycle="CCCDCD")

axelrod/strategies/gobymajority.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,14 +51,16 @@ def __init__(self, memory_depth: int = float('inf'), soft: bool = True) -> None:
5151
self.memory = self.classifier['memory_depth']
5252
else:
5353
self.memory = 0
54-
5554
self.name = (
5655
'Go By Majority' + (self.memory > 0) * (": %i" % self.memory))
5756
if self.soft:
5857
self.name = "Soft " + self.name
5958
else:
6059
self.name = "Hard " + self.name
6160

61+
def __repr__(self):
62+
return self.name
63+
6264
def strategy(self, opponent: Player) -> Action:
6365
"""This is affected by the history of the opponent.
6466

axelrod/strategies/human.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ class Human(Player):
5050
'manipulates_state': False
5151
}
5252

53-
def __init__(self, name='Human', c_symbol='C', d_symbol='D'):
53+
def __init__(self, name='human', c_symbol='C', d_symbol='D'):
5454
"""
5555
Parameters
5656
----------
@@ -64,7 +64,7 @@ def __init__(self, name='Human', c_symbol='C', d_symbol='D'):
6464
and prompt
6565
"""
6666
super().__init__()
67-
self.name = name
67+
self.human_name = name
6868
self.symbols = {
6969
C: c_symbol,
7070
D: d_symbol
@@ -81,7 +81,7 @@ def _history_toolbar(self, cli):
8181
self.symbols[action] for action in self.opponent_history]
8282
history = list(zip(my_history, opponent_history))
8383
if self.history:
84-
content = 'History ({}, opponent): {}'.format(self.name, history)
84+
content = 'History ({}, opponent): {}'.format(self.human_name, history)
8585
else:
8686
content = ''
8787
return [(Token.Toolbar, content)]
@@ -105,7 +105,7 @@ def _status_messages(self):
105105
toolbar = self._history_toolbar
106106
print_statement = (
107107
'{}Turn {}: {} played {}, opponent played {}'.format(
108-
linesep, len(self.history), self.name,
108+
linesep, len(self.history), self.human_name,
109109
self.symbols[self.history[-1]],
110110
self.symbols[self.opponent_history[-1]])
111111
)
@@ -130,7 +130,7 @@ def _get_human_input(self) -> Action: # pragma: no cover
130130
"""
131131
action = prompt(
132132
'Turn {} action [C or D] for {}: '.format(
133-
len(self.history) + 1, self.name),
133+
len(self.history) + 1, self.human_name),
134134
validator=ActionValidator(),
135135
get_bottom_toolbar_tokens=self.status_messages['toolbar'],
136136
style=toolbar_style)

axelrod/strategies/meta.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ def __init__(self, team=None):
5656
for t in self.team:
5757
self.classifier['makes_use_of'].update(t.classifier['makes_use_of'])
5858

59+
def __repr__(self):
60+
team_size = len(self.team)
61+
return '{}: {} player{}'.format(self.name, team_size, 's' if team_size > 1 else '')
62+
5963
def strategy(self, opponent):
6064
# Get the results of all our players.
6165
results = []
@@ -148,7 +152,6 @@ def reset(self):
148152

149153

150154
NiceMetaWinner = NiceTransformer()(MetaWinner)
151-
NiceMetaWinner.name = "Nice Meta Winner"
152155

153156

154157
class MetaWinnerEnsemble(MetaWinner):
@@ -175,7 +178,6 @@ def meta_strategy(self, results, opponent):
175178

176179

177180
NiceMetaWinnerEnsemble = NiceTransformer()(MetaWinnerEnsemble)
178-
NiceMetaWinnerEnsemble.name = "Nice Meta Winner Ensemble"
179181

180182

181183
class MetaHunter(MetaPlayer):

axelrod/strategies/negation.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
class Negation(Player):
99
"""
1010
A player starts by cooperating or defecting randomly if it's their first move,
11-
then simply doing the opposite of the opponents last move thereafter.
11+
then simply doing the opposite of the opponents last move thereafter.
1212
1313
Names:
1414

axelrod/strategies/prober.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -283,9 +283,6 @@ def strategy(self, opponent: Player) -> Action:
283283
choice = random_choice(1 - self.p)
284284
return choice
285285

286-
def __repr__(self) -> str:
287-
return "%s: %s" % (self.name, round(self.p, 2))
288-
289286

290287
class RemorsefulProber(NaiveProber):
291288
"""

axelrod/strategies/rand.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,3 @@ def __init__(self, p: float=0.5) -> None:
4242

4343
def strategy(self, opponent: Player) -> Action:
4444
return random_choice(self.p)
45-
46-
def __repr__(self) -> str:
47-
return "%s: %s" % (self.name, round(self.p, 2))

0 commit comments

Comments
 (0)