Skip to content

Commit e52b5cf

Browse files
authored
Merge pull request #1084 from Chadys/NTitForMTat
N tit(s) for M tat(s)
2 parents 3ac5965 + 0b676d2 commit e52b5cf

File tree

3 files changed

+158
-61
lines changed

3 files changed

+158
-61
lines changed

axelrod/strategies/_strategies.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,8 @@
7575
from .titfortat import (
7676
TitForTat, TitFor2Tats, TwoTitsForTat, Bully, SneakyTitForTat,
7777
SuspiciousTitForTat, AntiTitForTat, HardTitForTat, HardTitFor2Tats,
78-
OmegaTFT, Gradual, ContriteTitForTat, SlowTitForTwoTats, AdaptiveTitForTat,
79-
SpitefulTitForTat, SlowTitForTwoTats2, Alexei, EugineNier, DynamicTwoTitsForTat)
78+
OmegaTFT, Gradual, ContriteTitForTat, AdaptiveTitForTat,
79+
SpitefulTitForTat, SlowTitForTwoTats2, Alexei, EugineNier, DynamicTwoTitsForTat, NTitsForMTats)
8080
from .verybad import VeryBad
8181
from .worse_and_worse import (WorseAndWorse, KnowledgeableWorseAndWorse,
8282
WorseAndWorse2, WorseAndWorse3)
@@ -193,6 +193,7 @@
193193
MirrorMindReader,
194194
Negation,
195195
NiceAverageCopier,
196+
NTitsForMTats,
196197
Nydegger,
197198
OmegaTFT,
198199
OnceBitten,
@@ -224,7 +225,6 @@
224225
SelfSteem,
225226
ShortMem,
226227
Shubik,
227-
SlowTitForTwoTats,
228228
SlowTitForTwoTats2,
229229
SneakyTitForTat,
230230
SoftGrudger,

axelrod/strategies/titfortat.py

Lines changed: 57 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ class TitFor2Tats(Player):
5555
Names:
5656
5757
- Tit for two Tats: [Axelrod1984]_
58+
- Slow tit for two tats: Original name by Ranjini Das
5859
"""
5960

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

498499

499-
class SlowTitForTwoTats(Player):
500-
"""
501-
A player plays C twice, then if the opponent plays the same move twice,
502-
plays that move.
503-
504-
Names:
505-
506-
- Slow tit for two tats: Original name by Ranjini Das
507-
"""
508-
509-
name = 'Slow Tit For Two Tats'
510-
classifier = {
511-
'memory_depth': 2,
512-
'stochastic': False,
513-
'makes_use_of': set(),
514-
'long_run_time': False,
515-
'inspects_source': False,
516-
'manipulates_source': False,
517-
'manipulates_state': False
518-
}
519-
520-
def strategy(self, opponent: Player) -> Action:
521-
522-
# Start with two cooperations
523-
if len(self.history) < 2:
524-
return C
525-
526-
# Mimic if opponent plays the same move twice
527-
if opponent.history[-2] == opponent.history[-1]:
528-
return opponent.history[-1]
529-
530-
# Otherwise cooperate
531-
return C
532-
533-
534500
class AdaptiveTitForTat(Player):
535501
"""ATFT - Adaptive Tit For Tat (Basic Model)
536502
@@ -750,3 +716,59 @@ def strategy(self, opponent: Player) -> Action:
750716
def reset(self):
751717
super().reset()
752718
self.is_defector = False
719+
720+
721+
class NTitsForMTats(Player):
722+
"""
723+
A parameterizable Tit-for-Tat,
724+
The arguments are :
725+
1) the number of defection before retaliation
726+
2) the number of retaliations
727+
728+
Names:
729+
730+
- N Tit(s) For M Tat(s): Original name by Marc Harper
731+
"""
732+
733+
name = 'N Tit(s) For M Tat(s)'
734+
classifier = {
735+
'memory_depth': float('inf'),
736+
'stochastic': False,
737+
'makes_use_of': set(),
738+
'long_run_time': False,
739+
'inspects_source': False,
740+
'manipulates_source': False,
741+
'manipulates_state': False
742+
}
743+
744+
def __init__(self, N: int=3, M: int=2) -> None:
745+
"""
746+
Parameters
747+
----------
748+
N
749+
Number of retaliations
750+
M
751+
Number of defection before retaliation
752+
753+
Special Cases
754+
-------------
755+
NTitsForMTats(1,1) is equivalent to TitForTat
756+
NTitsForMTats(1,2) is equivalent to TitFor2Tats
757+
NTitsForMTats(2,1) is equivalent to TwoTitsForTat
758+
NTitsForMTats(0,*) is equivalent to Cooperator
759+
NTitsForMTats(*,0) is equivalent to Defector
760+
"""
761+
super().__init__()
762+
self.N = N
763+
self.M = M
764+
self.classifier['memory_depth'] = max([M,N])
765+
self.retaliate_count = 0
766+
767+
def strategy(self, opponent: Player) -> Action:
768+
# if opponent defected consecutively M times, start the retaliation
769+
if not self.M or opponent.history[-self.M:].count(D) == self.M:
770+
self.retaliate_count = self.N
771+
if self.retaliate_count:
772+
self.retaliate_count -= 1
773+
return D
774+
return C

axelrod/tests/strategies/test_titfortat.py

Lines changed: 98 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
"""Tests for the tit for tat strategies."""
22

33
import random
4+
5+
import copy
46
from hypothesis import given
57
from hypothesis.strategies import integers
68
import axelrod
@@ -79,10 +81,14 @@ class TestTitFor2Tats(TestPlayer):
7981
}
8082

8183
def test_strategy(self):
82-
# Will punish sequence of 2 defections but will forgive
84+
# Will punish sequence of 2 defections but will forgive one
8385
opponent = axelrod.MockPlayer(actions=[D, D, D, C, C])
8486
actions = [(C, D), (C, D), (D, D), (D, C), (C, C), (C, D)]
8587
self.versus_test(opponent, expected_actions=actions)
88+
opponent = axelrod.MockPlayer(actions=[C, C, D, D, C, D, D, C, C, D, D])
89+
actions = [(C, C), (C, C), (C, D), (C, D), (D, C), (C, D), (C, D),
90+
(D, C), (C, C), (C, D), (C, D)]
91+
self.versus_test(opponent, expected_actions=actions)
8692

8793

8894
class TestTwoTitsForTat(TestPlayer):
@@ -473,28 +479,6 @@ def test_strategy_with_noise(self):
473479
self.assertFalse(ctft.contrite)
474480

475481

476-
class TestSlowTitForTwoTats(TestPlayer):
477-
478-
name = "Slow Tit For Two Tats"
479-
player = axelrod.SlowTitForTwoTats
480-
expected_classifier = {
481-
'memory_depth': 2,
482-
'stochastic': False,
483-
'makes_use_of': set(),
484-
'inspects_source': False,
485-
'manipulates_source': False,
486-
'manipulates_state': False
487-
}
488-
489-
def test_strategy(self):
490-
# If opponent plays the same move twice, repeats last action of
491-
# opponent history.
492-
opponent = axelrod.MockPlayer(actions=[C, C, D, D, C, D, D, C, C, D, D])
493-
actions = [(C, C), (C, C), (C, D), (C, D), (D, C), (C, D), (C, D),
494-
(D, C), (C, C), (C, D), (C, D)]
495-
self.versus_test(opponent, expected_actions=actions)
496-
497-
498482
class TestAdaptiveTitForTat(TestPlayer):
499483

500484
name = "Adaptive Tit For Tat: 0.5"
@@ -642,3 +626,94 @@ def test_strategy(self):
642626
actions = [(C, D), (D, C), (C, D), (D, D),
643627
(D, D), (D, D), (D, C), (D, C)]
644628
self.versus_test(opponent, expected_actions=actions)
629+
630+
631+
632+
class TestNTitsForMTats(TestPlayer):
633+
"""
634+
Tests for the N Tit(s) For M Tat(s) strategy
635+
"""
636+
637+
name = 'N Tit(s) For M Tat(s): 3, 2'
638+
player = axelrod.NTitsForMTats
639+
expected_classifier = {
640+
'memory_depth': 3,
641+
'stochastic': False,
642+
'makes_use_of': set(),
643+
'long_run_time': False,
644+
'inspects_source': False,
645+
'manipulates_source': False,
646+
'manipulates_state': False
647+
}
648+
649+
expected_class_classifier = copy.copy(expected_classifier)
650+
expected_class_classifier['memory_depth'] = float('inf')
651+
652+
def test_strategy(self):
653+
# TitForTat test_strategy
654+
init_kwargs = {'N': 1, 'M': 1}
655+
actions = [(C, C), (C, D), (D, C), (C, D), (D, C)]
656+
self.versus_test(axelrod.Alternator(), expected_actions=actions, init_kwargs=init_kwargs)
657+
actions = [(C, C), (C, C), (C, C), (C, C), (C, C)]
658+
self.versus_test(axelrod.Cooperator(), expected_actions=actions, init_kwargs=init_kwargs)
659+
actions = [(C, D), (D, D), (D, D), (D, D), (D, D)]
660+
self.versus_test(axelrod.Defector(), expected_actions=actions, init_kwargs=init_kwargs)
661+
actions = [(C, C), (C, D), (D, C), (C, D), (D, C)]
662+
self.versus_test(axelrod.Alternator(), expected_actions=actions,
663+
match_attributes={"length": -1}, init_kwargs=init_kwargs)
664+
actions = [(C, D), (D, D), (D, C), (C, C), (C, D)]
665+
self.versus_test(axelrod.Random(), expected_actions=actions,
666+
seed=0, init_kwargs=init_kwargs)
667+
actions = [(C, C), (C, D), (D, D), (D, C)]
668+
self.versus_test(axelrod.Random(), expected_actions=actions,
669+
seed=1, init_kwargs=init_kwargs)
670+
opponent = axelrod.MockPlayer(actions=[C, D])
671+
actions = [(C, C), (C, D), (D, C), (C, D)]
672+
self.versus_test(opponent, expected_actions=actions, init_kwargs=init_kwargs)
673+
opponent = axelrod.MockPlayer(actions=[C, C, D, D, C, D])
674+
actions = [(C, C), (C, C), (C, D), (D, D), (D, C), (C, D)]
675+
self.versus_test(opponent, expected_actions=actions, init_kwargs=init_kwargs)
676+
677+
# TitFor2Tats test_strategy
678+
init_kwargs = {'N': 1, 'M': 2}
679+
opponent = axelrod.MockPlayer(actions=[D, D, D, C, C])
680+
actions = [(C, D), (C, D), (D, D), (D, C), (C, C), (C, D)]
681+
self.versus_test(opponent, expected_actions=actions, init_kwargs=init_kwargs)
682+
683+
# TwoTitsForTat test_strategy
684+
init_kwargs = {'N': 2, 'M': 1}
685+
opponent = axelrod.MockPlayer(actions=[D, C, C, D, C])
686+
actions = [(C, D), (D, C), (D, C), (C, D), (D, C)]
687+
self.versus_test(opponent, expected_actions=actions, init_kwargs=init_kwargs)
688+
actions = [(C, C), (C, C)]
689+
self.versus_test(opponent=axelrod.Cooperator(),
690+
expected_actions=actions, init_kwargs=init_kwargs)
691+
actions = [(C, D), (D, D), (D, D)]
692+
self.versus_test(opponent=axelrod.Defector(),
693+
expected_actions=actions, init_kwargs=init_kwargs)
694+
695+
# Cooperator test_strategy
696+
actions = [(C, C)] + [(C, D), (C, C)] * 9
697+
self.versus_test(opponent=axelrod.Alternator(),
698+
expected_actions=actions, init_kwargs={'N': 0, 'M': 1})
699+
self.versus_test(opponent=axelrod.Alternator(),
700+
expected_actions=actions, init_kwargs={'N': 0, 'M': 5})
701+
self.versus_test(opponent=axelrod.Alternator(),
702+
expected_actions=actions, init_kwargs={'N': 0, 'M': 0})
703+
704+
# Defector test_strategy
705+
actions = [(D, C)] + [(D, D), (D, C)] * 9
706+
self.versus_test(opponent=axelrod.Alternator(),
707+
expected_actions=actions, init_kwargs={'N': 1, 'M': 0})
708+
self.versus_test(opponent=axelrod.Alternator(),
709+
expected_actions=actions, init_kwargs={'N': 5, 'M': 0})
710+
711+
# Default init args
712+
actions = [(C, C), (C, D), (C, D), (D, C), (D, C), (D, D), (C, C)]
713+
opponent = axelrod.MockPlayer(actions=[acts[1] for acts in actions])
714+
self.versus_test(opponent=opponent, expected_actions=actions)
715+
716+
def test_varying_memory_depth(self):
717+
self.assertEqual(self.player(1, 1).classifier['memory_depth'], 1)
718+
self.assertEqual(self.player(0, 3).classifier['memory_depth'], 3)
719+
self.assertEqual(self.player(5, 3).classifier['memory_depth'], 5)

0 commit comments

Comments
 (0)