diff --git a/axelrod/strategies/_strategies.py b/axelrod/strategies/_strategies.py index 59b7e941f..32dfef720 100644 --- a/axelrod/strategies/_strategies.py +++ b/axelrod/strategies/_strategies.py @@ -46,7 +46,7 @@ from .mindreader import MindReader, ProtectedMindReader, MirrorMindReader from .mutual import Desperate, Hopeless, Willing from .oncebitten import OnceBitten, FoolMeOnce, ForgetfulFoolMeOnce, FoolMeForever -from .prober import (Prober, Prober2, Prober3, HardProber, +from .prober import (Prober, Prober2, Prober3, Prober4, HardProber, NaiveProber, RemorsefulProber) from .punisher import Punisher, InversePunisher from .qlearner import RiskyQLearner, ArrogantQLearner, HesitantQLearner, CautiousQLearner @@ -164,6 +164,7 @@ Prober, Prober2, Prober3, + Prober4, ProtectedMindReader, Punisher, Raider, diff --git a/axelrod/strategies/prober.py b/axelrod/strategies/prober.py index a6306ec7e..ccc2df3c8 100644 --- a/axelrod/strategies/prober.py +++ b/axelrod/strategies/prober.py @@ -102,6 +102,68 @@ def strategy(self, opponent): return D if opponent.history[-1:] == [D] else C +class Prober4(Player): + """ + Plays C, C, D, C, D, D, D, C, C, D, C, D, C, C, D, C, D, D, C, D initially. + Counts retaliating and provocative defections of the opponent. + If the absolute difference between the counts is smaller or equal to 2, + defects forever. + Otherwise plays C for the next 5 turns and TFT for the rest of the game. + + Names: + + - prober4: [PRISON1998]_ + """ + + name = 'Prober 4' + classifier = { + 'stochastic': False, + 'memory_depth': float('inf'), + 'makes_use_of': set(), + 'long_run_time': False, + 'inspects_source': False, + 'manipulates_source': False, + 'manipulates_state': False + } + + @init_args + def __init__(self): + Player.__init__(self) + self.init_sequence = [ + C, C, D, C, D, D, D, C, C, D, C, D, C, C, D, C, D, D, C, D + ] + self.just_Ds = 0 + self.unjust_Ds = 0 + self.turned_defector = False + + def strategy(self, opponent): + if not self.history: + return self.init_sequence[0] + turn = len(self.history) + if turn < len(self.init_sequence): + if opponent.history[-1] == D: + if self.history[-1] == D: + self.just_Ds += 1 + if self.history[-1] == C: + self.unjust_Ds += 1 + return self.init_sequence[turn] + if turn == len(self.init_sequence): + diff_in_Ds = abs(self.just_Ds - self.unjust_Ds) + self.turned_defector = (diff_in_Ds <= 2) + if self.turned_defector: + return D + if not self.turned_defector: + if turn < len(self.init_sequence) + 5: + return C + return D if opponent.history[-1] == D else C + + def reset(self): + Player.reset(self) + self.just_Ds = 0 + self.unjust_Ds = 0 + self.turned_defector = False + + class HardProber(Player): """ Plays D, D, C, C initially. Defects forever if opponent cooperated in moves diff --git a/axelrod/tests/unit/test_prober.py b/axelrod/tests/unit/test_prober.py index 0f2cb7c67..c53fde0b1 100644 --- a/axelrod/tests/unit/test_prober.py +++ b/axelrod/tests/unit/test_prober.py @@ -99,6 +99,79 @@ def test_strategy(self): self.responses_test([D, C, C, D, C], [C, D, C, C], [C]) +class TestProber4(TestPlayer): + + name = "Prober 4" + player = axelrod.Prober4 + expected_classifier = { + 'stochastic': False, + 'memory_depth': float('inf'), + 'makes_use_of': set(), + 'long_run_time': False, + 'inspects_source': False, + 'manipulates_source': False, + 'manipulates_state': False + } + initial_sequence = [ + C, C, D, C, D, D, D, C, C, D, C, D, C, C, D, C, D, D, C, D + ] + + def test_initial_strategy(self): + """Starts by playing CCDCDDDCCDCDCCDCDDCD.""" + self.responses_test([], [], self.initial_sequence) + + def test_strategy(self): + # After playing the initial sequence defects forever + # if the absolute difference in the number of retaliating + # and provocative defections of the opponent is smaller or equal to 2 + + provocative_histories = [ + [C, C, C, C, C, C, C, C, C, C, C, C, C, C, C, C, C, C, C, C], + [C, D, C, C, C, C, C, C, C, C, C, C, C, C, C, C, C, C, C, C], + [C, D, C, D, C, C, C, C, C, C, C, C, C, C, C, C, C, C, C, C], + [C, C, D, C, C, C, C, C, C, C, C, C, C, C, C, C, C, C, C, C], + [C, C, D, C, D, C, C, C, C, C, C, C, C, C, C, C, C, C, C, C], + [D, D, D, D, D, D, D, D, D, D, D, D, D, D, D, D, D, D, D, D], + ] + + history1 = self.initial_sequence + responses = [D] * 10 + attrs = {'turned_defector': True} + for history2 in provocative_histories: + self.responses_test(history1, history2, responses, attrs=attrs) + + # Otherwise cooperates for 5 rounds + unprovocative_histories = [ + [C, C, D, C, D, D, D, C, C, D, C, D, C, C, D, C, D, D, C, D], + [D, D, C, D, C, C, C, D, D, C, D, C, D, D, C, D, C, C, D, C], + [C, C, D, C, D, D, C, C, C, C, C, C, C, C, C, C, C, C, C, C], + [C, C, D, C, D, D, C, C, D, C, C, C, C, C, C, D, D, D, C, C], + [C, C, C, C, D, D, C, C, D, C, C, D, D, C, D, C, D, C, C, C], + ] + + responses = [C] * 5 + attrs = {'turned_defector': False} + for history2 in unprovocative_histories: + self.responses_test(history1, history2, responses, attrs=attrs) + + # and plays like TFT afterwards + history1 += responses + history2 += responses + self.responses_test(history1, history2, [C], attrs=attrs) + + history1 += [C] + history2 += [D] + self.responses_test(history1, history2, [D], attrs=attrs) + + history1 += [D] + history2 += [C] + self.responses_test(history1, history2, [C], attrs=attrs) + + history1 += [C] + history2 += [D] + self.responses_test(history1, history2, [D], attrs=attrs) + + class TestHardProber(TestPlayer): name = "Hard Prober"