Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement SpitefulTitForTat strategy from PRISON #749

Merged
merged 9 commits into from
Oct 28, 2016
3 changes: 2 additions & 1 deletion axelrod/strategies/_strategies.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
from .titfortat import (
TitForTat, TitFor2Tats, TwoTitsForTat, Bully, SneakyTitForTat,
SuspiciousTitForTat, AntiTitForTat, HardTitForTat, HardTitFor2Tats,
OmegaTFT, Gradual, ContriteTitForTat, SlowTitForTwoTats, AdaptiveTitForTat)
OmegaTFT, Gradual, ContriteTitForTat, SlowTitForTwoTats, AdaptiveTitForTat,SpitefulTitForTat)
from .worse_and_worse import (WorseAndWorse, KnowledgeableWorseAndWorse)

# Note: Meta* strategies are handled in .__init__.py
Expand Down Expand Up @@ -188,6 +188,7 @@
SoftJoss,
SolutionB1,
SolutionB5,
SpitefulTitForTat,
StochasticWSLS,
SuspiciousTitForTat,
Tester,
Expand Down
51 changes: 49 additions & 2 deletions axelrod/strategies/titfortat.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class TitForTat(Player):
def strategy(self, opponent):
"""This is the actual strategy"""
# First move
if len(self.history) == 0:
if not self.history:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 Thanks for spotting that one!

return C
# React to the opponent's last move
if opponent.history[-1] == D:
Expand Down Expand Up @@ -295,7 +295,7 @@ def __init__(self, deadlock_threshold=3, randomness_threshold=8):

def strategy(self, opponent):
# Cooperate on the first move
if len(self.history) == 0:
if not self.history:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 Again

return C
# TFT on round 2
if len(self.history) == 1:
Expand Down Expand Up @@ -554,3 +554,50 @@ def reset(self):
def __repr__(self):

return "%s: %s" % (self.name, round(self.rate, 2))

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PEP8 - Should be two blank lines before a class definition

class SpitefulTitForTat(Player):
"""
A player starts by cooperating and then mimics the previous action of the
opponent until opponent defects twice in a row, at which point player always defects

Names:

- Spiteful Tit For Tat [PRISON1998]_
"""

name = 'Spiteful Tit For Tat'
classifier = {
'memory_depth': float('inf'),
'stochastic': False,
'makes_use_of': set(),
'long_run_time': False,
'inspects_source': False,
'manipulates_source': False,
'manipulates_state': False
}

def __init__(self):
Player.__init__(self)
self.retaliating = False

def strategy(self, opponent):
# First move
if not self.history:
return C

if opponent.history[-2:] == [D,D]:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PEP8:
if opponent.history[-2:] == [D, D]:

self.retaliating=True
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we have some space around the '=' (PEP8)?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PEP8:
self.retaliating = True


if self.retaliating:
return D
else:
# React to the opponent's last move
if opponent.history[-1] == D:
return D
return C

def reset(self):
Player.reset(self)
self.retaliating = False


Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PEP8:

Should be one blank line at the end of file, not two.

28 changes: 28 additions & 0 deletions axelrod/tests/unit/test_titfortat.py
Original file line number Diff line number Diff line change
Expand Up @@ -497,4 +497,32 @@ def test_world_rate_reset(self):
self.assertEqual(p2.rate, 0.5)


Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are whitespace characters in these two lines that shouldn't be there.

class TestSpitefulTitForTat(TestPlayer):
name = "Spiteful Tit For Tat"
player = axelrod.SpitefulTitForTat
expected_classifier = {
'memory_depth': float('inf'),
'stochastic': False,
'makes_use_of': set(),
'inspects_source': False,
'manipulates_source': False,
'manipulates_state': False
}

def test_strategy(self):
"""Starts by cooperating."""
self.first_play_test(C)

def test_effect_of_strategy(self):
"""Repeats last action of opponent history until 2 consecutive defections, then always defects"""
self.markov_test([C, D, C, D])
self.responses_test([C] * 4, [C, C, C, C], [C], attrs = {"retaliating": False})
self.responses_test([C] * 5, [C, C, C, C, D], [D], attrs = {"retaliating": False})
self.responses_test([C] * 5, [C, C, D, D, C], [D], attrs = {"retaliating": True})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PEP8

The three lines above have spaces around the '=' which shouldn't be there.


def test_reset_retaliating(self):
player = self.player()
player.retaliating = True
player.reset()
self.assertFalse(player.retaliating)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is an extra blank line at the end of the file which shouldn't be there

2 changes: 1 addition & 1 deletion docs/tutorials/advanced/classification_of_strategies.rst
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ range of memory_depth values, we can use the 'min_memory_depth' and
... }
>>> strategies = axl.filtered_strategies(filterset)
>>> len(strategies)
42
43

We can also identify strategies that make use of particular properties of the
tournament. For example, here is the number of strategies that make use of the
Expand Down