Skip to content

N tit(s) for M tat(s) #1084

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

Merged
merged 12 commits into from
Jul 25, 2017
6 changes: 3 additions & 3 deletions axelrod/strategies/_strategies.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,8 @@
from .titfortat import (
TitForTat, TitFor2Tats, TwoTitsForTat, Bully, SneakyTitForTat,
SuspiciousTitForTat, AntiTitForTat, HardTitForTat, HardTitFor2Tats,
OmegaTFT, Gradual, ContriteTitForTat, SlowTitForTwoTats, AdaptiveTitForTat,
SpitefulTitForTat, SlowTitForTwoTats2, Alexei, EugineNier, DynamicTwoTitsForTat)
OmegaTFT, Gradual, ContriteTitForTat, AdaptiveTitForTat,
SpitefulTitForTat, SlowTitForTwoTats2, Alexei, EugineNier, DynamicTwoTitsForTat, NTitsForMTats)
from .verybad import VeryBad
from .worse_and_worse import (WorseAndWorse, KnowledgeableWorseAndWorse,
WorseAndWorse2, WorseAndWorse3)
Expand Down Expand Up @@ -193,6 +193,7 @@
MirrorMindReader,
Negation,
NiceAverageCopier,
NTitsForMTats,
Nydegger,
OmegaTFT,
OnceBitten,
Expand Down Expand Up @@ -224,7 +225,6 @@
SelfSteem,
ShortMem,
Shubik,
SlowTitForTwoTats,
SlowTitForTwoTats2,
SneakyTitForTat,
SoftGrudger,
Expand Down
92 changes: 57 additions & 35 deletions axelrod/strategies/titfortat.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ class TitFor2Tats(Player):
Names:

- Tit for two Tats: [Axelrod1984]_
- Slow tit for two tats: Original name by Ranjini Das
"""

name = "Tit For 2 Tats"
Expand Down Expand Up @@ -496,41 +497,6 @@ def reset(self):
self._recorded_history = []


class SlowTitForTwoTats(Player):
"""
A player plays C twice, then if the opponent plays the same move twice,
plays that move.

Names:

- Slow tit for two tats: Original name by Ranjini Das
"""

name = 'Slow Tit For Two Tats'
classifier = {
'memory_depth': 2,
'stochastic': False,
'makes_use_of': set(),
'long_run_time': False,
'inspects_source': False,
'manipulates_source': False,
'manipulates_state': False
}

def strategy(self, opponent: Player) -> Action:

# Start with two cooperations
if len(self.history) < 2:
return C

# Mimic if opponent plays the same move twice
if opponent.history[-2] == opponent.history[-1]:
return opponent.history[-1]

# Otherwise cooperate
return C


class AdaptiveTitForTat(Player):
"""ATFT - Adaptive Tit For Tat (Basic Model)

Expand Down Expand Up @@ -750,3 +716,59 @@ def strategy(self, opponent: Player) -> Action:
def reset(self):
super().reset()
self.is_defector = False


class NTitsForMTats(Player):
"""
A parameterizable Tit-for-Tat,
The arguments are :
1) the number of defection before retaliation
2) the number of retaliations

Names:

- N Tit(s) For M Tat(s): Original name by Marc Harper
"""

name = 'N Tit(s) For M Tat(s)'
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, N: int=3, M: int=2) -> None:
"""
Parameters
----------
N
Number of retaliations
M
Number of defection before retaliation

Special Cases
-------------
NTitsForMTats(1,1) is equivalent to TitForTat
NTitsForMTats(1,2) is equivalent to TitFor2Tats
NTitsForMTats(2,1) is equivalent to TwoTitsForTat
NTitsForMTats(0,*) is equivalent to Cooperator
NTitsForMTats(*,0) is equivalent to Defector
"""
super().__init__()
self.N = N
self.M = M
self.classifier['memory_depth'] = max([M,N])
self.retaliate_count = 0

def strategy(self, opponent: Player) -> Action:
# if opponent defected consecutively M times, start the retaliation
if not self.M or opponent.history[-self.M:].count(D) == self.M:
self.retaliate_count = self.N
if self.retaliate_count:
self.retaliate_count -= 1
return D
return C
121 changes: 98 additions & 23 deletions axelrod/tests/strategies/test_titfortat.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
"""Tests for the tit for tat strategies."""

import random

import copy
from hypothesis import given
from hypothesis.strategies import integers
import axelrod
Expand Down Expand Up @@ -79,10 +81,14 @@ class TestTitFor2Tats(TestPlayer):
}

def test_strategy(self):
# Will punish sequence of 2 defections but will forgive
# Will punish sequence of 2 defections but will forgive one
opponent = axelrod.MockPlayer(actions=[D, D, D, C, C])
actions = [(C, D), (C, D), (D, D), (D, C), (C, C), (C, D)]
self.versus_test(opponent, expected_actions=actions)
opponent = axelrod.MockPlayer(actions=[C, C, D, D, C, D, D, C, C, D, D])
actions = [(C, C), (C, C), (C, D), (C, D), (D, C), (C, D), (C, D),
(D, C), (C, C), (C, D), (C, D)]
self.versus_test(opponent, expected_actions=actions)


class TestTwoTitsForTat(TestPlayer):
Expand Down Expand Up @@ -473,28 +479,6 @@ def test_strategy_with_noise(self):
self.assertFalse(ctft.contrite)


class TestSlowTitForTwoTats(TestPlayer):

name = "Slow Tit For Two Tats"
player = axelrod.SlowTitForTwoTats
expected_classifier = {
'memory_depth': 2,
'stochastic': False,
'makes_use_of': set(),
'inspects_source': False,
'manipulates_source': False,
'manipulates_state': False
}

def test_strategy(self):
# If opponent plays the same move twice, repeats last action of
# opponent history.
opponent = axelrod.MockPlayer(actions=[C, C, D, D, C, D, D, C, C, D, D])
actions = [(C, C), (C, C), (C, D), (C, D), (D, C), (C, D), (C, D),
(D, C), (C, C), (C, D), (C, D)]
self.versus_test(opponent, expected_actions=actions)


class TestAdaptiveTitForTat(TestPlayer):

name = "Adaptive Tit For Tat: 0.5"
Expand Down Expand Up @@ -642,3 +626,94 @@ def test_strategy(self):
actions = [(C, D), (D, C), (C, D), (D, D),
(D, D), (D, D), (D, C), (D, C)]
self.versus_test(opponent, expected_actions=actions)



class TestNTitsForMTats(TestPlayer):
"""
Tests for the N Tit(s) For M Tat(s) strategy
"""

name = 'N Tit(s) For M Tat(s): 3, 2'
player = axelrod.NTitsForMTats
expected_classifier = {
'memory_depth': 3,
'stochastic': False,
'makes_use_of': set(),
'long_run_time': False,
'inspects_source': False,
'manipulates_source': False,
'manipulates_state': False
}

expected_class_classifier = copy.copy(expected_classifier)
expected_class_classifier['memory_depth'] = float('inf')

def test_strategy(self):
# TitForTat test_strategy
init_kwargs = {'N': 1, 'M': 1}
actions = [(C, C), (C, D), (D, C), (C, D), (D, C)]
self.versus_test(axelrod.Alternator(), expected_actions=actions, init_kwargs=init_kwargs)
actions = [(C, C), (C, C), (C, C), (C, C), (C, C)]
self.versus_test(axelrod.Cooperator(), expected_actions=actions, init_kwargs=init_kwargs)
actions = [(C, D), (D, D), (D, D), (D, D), (D, D)]
self.versus_test(axelrod.Defector(), expected_actions=actions, init_kwargs=init_kwargs)
actions = [(C, C), (C, D), (D, C), (C, D), (D, C)]
self.versus_test(axelrod.Alternator(), expected_actions=actions,
match_attributes={"length": -1}, init_kwargs=init_kwargs)
actions = [(C, D), (D, D), (D, C), (C, C), (C, D)]
self.versus_test(axelrod.Random(), expected_actions=actions,
seed=0, init_kwargs=init_kwargs)
actions = [(C, C), (C, D), (D, D), (D, C)]
self.versus_test(axelrod.Random(), expected_actions=actions,
seed=1, init_kwargs=init_kwargs)
opponent = axelrod.MockPlayer(actions=[C, D])
actions = [(C, C), (C, D), (D, C), (C, D)]
self.versus_test(opponent, expected_actions=actions, init_kwargs=init_kwargs)
opponent = axelrod.MockPlayer(actions=[C, C, D, D, C, D])
actions = [(C, C), (C, C), (C, D), (D, D), (D, C), (C, D)]
self.versus_test(opponent, expected_actions=actions, init_kwargs=init_kwargs)

# TitFor2Tats test_strategy
init_kwargs = {'N': 1, 'M': 2}
opponent = axelrod.MockPlayer(actions=[D, D, D, C, C])
actions = [(C, D), (C, D), (D, D), (D, C), (C, C), (C, D)]
self.versus_test(opponent, expected_actions=actions, init_kwargs=init_kwargs)

# TwoTitsForTat test_strategy
init_kwargs = {'N': 2, 'M': 1}
opponent = axelrod.MockPlayer(actions=[D, C, C, D, C])
actions = [(C, D), (D, C), (D, C), (C, D), (D, C)]
self.versus_test(opponent, expected_actions=actions, init_kwargs=init_kwargs)
actions = [(C, C), (C, C)]
self.versus_test(opponent=axelrod.Cooperator(),
expected_actions=actions, init_kwargs=init_kwargs)
actions = [(C, D), (D, D), (D, D)]
self.versus_test(opponent=axelrod.Defector(),
expected_actions=actions, init_kwargs=init_kwargs)

# Cooperator test_strategy
actions = [(C, C)] + [(C, D), (C, C)] * 9
self.versus_test(opponent=axelrod.Alternator(),
expected_actions=actions, init_kwargs={'N': 0, 'M': 1})
self.versus_test(opponent=axelrod.Alternator(),
expected_actions=actions, init_kwargs={'N': 0, 'M': 5})
self.versus_test(opponent=axelrod.Alternator(),
expected_actions=actions, init_kwargs={'N': 0, 'M': 0})

# Defector test_strategy
actions = [(D, C)] + [(D, D), (D, C)] * 9
self.versus_test(opponent=axelrod.Alternator(),
expected_actions=actions, init_kwargs={'N': 1, 'M': 0})
self.versus_test(opponent=axelrod.Alternator(),
expected_actions=actions, init_kwargs={'N': 5, 'M': 0})

# Default init args
actions = [(C, C), (C, D), (C, D), (D, C), (D, C), (D, D), (C, C)]
opponent = axelrod.MockPlayer(actions=[acts[1] for acts in actions])
self.versus_test(opponent=opponent, expected_actions=actions)

def test_varying_memory_depth(self):
self.assertEqual(self.player(1, 1).classifier['memory_depth'], 1)
self.assertEqual(self.player(0, 3).classifier['memory_depth'], 3)
self.assertEqual(self.player(5, 3).classifier['memory_depth'], 5)