diff --git a/axelrod/strategies/grudger.py b/axelrod/strategies/grudger.py index 73248ab1c..2f287905b 100644 --- a/axelrod/strategies/grudger.py +++ b/axelrod/strategies/grudger.py @@ -63,16 +63,16 @@ def __init__(self) -> None: def strategy(self, opponent: Player) -> Action: """Begins by playing C, then plays D for mem_length rounds if the opponent ever plays D.""" - if self.grudge_memory >= self.mem_length: + if self.grudge_memory == self.mem_length: self.grudge_memory = 0 self.grudged = False + if D in opponent.history[-1:]: + self.grudged = True + if self.grudged: self.grudge_memory += 1 return D - elif D in opponent.history[-1:]: - self.grudged = True - return D return C def reset(self): @@ -206,9 +206,9 @@ def strategy(self, opponent: Player) -> Action: """Begins by playing C, then plays Alternator for the remaining rounds if the opponent ever plays D.""" if opponent.defections: - if self.history[-1] == Actions.C: - return Actions.D - return Actions.C + if self.history[-1] == C: + return D + return C class EasyGo(Player): @@ -240,13 +240,14 @@ def strategy(opponent: Player) -> Action: return C return D + class GeneralSoftGrudger(Player): """ - A generalization of the SoftGrudger strategy. SoftGrudger punishes by playing: - D, D, D, D, C, C. after a defection by the opponent. GeneralSoftGrudger - only punishes after its opponent defects a specified amount of times consecutively. - The punishment is in the form of a series of defections followed by a 'penance' of - a series of consecutive cooperations. + A generalization of the SoftGrudger strategy. SoftGrudger punishes by + playing: D, D, D, D, C, C. after a defection by the opponent. + GeneralSoftGrudger only punishes after its opponent defects a specified + amount of times consecutively. The punishment is in the form of a series of + defections followed by a 'penance' of a series of consecutive cooperations. Names: @@ -268,11 +269,11 @@ def __init__(self, n: int=1, d: int=4, c: int=2) -> None: """ Parameters ---------- - n, int + n: int The number of defections by the opponent to trigger punishment - d, int + d: int The number of defections to punish the opponent - c, int + c: int The number of cooperations in the 'penance' stage Special Cases @@ -303,6 +304,7 @@ def strategy(self, opponent: Player) -> Action: elif [D] * self.n == opponent.history[-self.n:]: self.grudged = True return D + return C def reset(self): diff --git a/axelrod/tests/strategies/test_grudger.py b/axelrod/tests/strategies/test_grudger.py index b16832ea2..d9374517e 100644 --- a/axelrod/tests/strategies/test_grudger.py +++ b/axelrod/tests/strategies/test_grudger.py @@ -1,16 +1,15 @@ """Tests for Grudger strategies.""" -from random import randint -import axelrod +import axelrod as axl from .test_player import TestPlayer -C, D = axelrod.Actions.C, axelrod.Actions.D +C, D = axl.Actions.C, axl.Actions.D class TestGrudger(TestPlayer): name = "Grudger" - player = axelrod.Grudger + player = axl.Grudger expected_classifier = { 'memory_depth': float('inf'), # Long memory 'stochastic': False, @@ -22,17 +21,25 @@ class TestGrudger(TestPlayer): } def test_strategy(self): - # Starts by cooperating - self.first_play_test(C) # If opponent defects at any point then the player will defect forever. - self.responses_test([C], [C, D, D, D], [C, C, C, C]) - self.responses_test([D], [C, C, D, D, D], [C, D, C, C, C]) + opponent = axl.Cooperator() + actions = [(C, C)] * 20 + self.versus_test(opponent, expected_actions=actions) + + opponent = axl.Defector() + actions = [(C, D)] + [(D, D)] * 20 + self.versus_test(opponent, expected_actions=actions) + + opponent_actions = [C] * 10 + [D] + [C] * 20 + opponent = axl.MockPlayer(actions=opponent_actions) + actions = [(C, C)] * 10 + [(C, D)] + [(D, C)] * 20 + self.versus_test(opponent, expected_actions=actions) class TestForgetfulGrudger(TestPlayer): name = "Forgetful Grudger" - player = axelrod.ForgetfulGrudger + player = axl.ForgetfulGrudger expected_classifier = { 'memory_depth': 10, 'stochastic': False, @@ -44,34 +51,41 @@ class TestForgetfulGrudger(TestPlayer): } def test_strategy(self): - self.responses_test([C], attrs={"grudged": False}) - self.responses_test([C], [C], [C], attrs={"grudged": False}) - self.responses_test([D], [C], [D], attrs={"grudged": True}) - for i in range(10): - self.responses_test([D], [C, C] + [D] * i, [C, D] + [C] * i, - attrs={"grudged": True, "grudge_memory": i, - "mem_length": 10}) - # Forgets the grudge eventually - i = 10 - self.responses_test([C], [C, C] + [D] * i + [C], [C, D] + [C] * i + [C], - attrs={"grudged": False, "grudge_memory": 0, - "mem_length": 10}) + # If opponent defects at any point then the player will respond with + # D ten times and then continue to check for defections. + opponent = axl.Cooperator() + actions = [(C, C)] * 20 + attrs = {'grudged': False, 'mem_length': 10, 'grudge_memory': 0} + self.versus_test(opponent, expected_actions=actions, attrs=attrs) + + for i in range(1, 15): + opponent = axl.Defector() + actions = [(C, D)] + [(D, D)] * i + memory = i if i <= 10 else i - 10 + attrs = {'grudged': True, 'mem_length': 10, 'grudge_memory': memory} + self.versus_test(opponent, expected_actions=actions, attrs=attrs) + + opponent_actions = [C] * 2 + [D] + [C] * 10 + opponent = axl.MockPlayer(actions=opponent_actions) + actions = ([(C, C)] * 2 + [(C, D)] + [(D, C)] * 10) * 3 + [(C, C)] + attrs = {'grudged': False, 'mem_length': 10, 'grudge_memory': 0} + self.versus_test(opponent, expected_actions=actions, attrs=attrs) def test_reset_method(self): """Tests the reset method.""" - P1 = axelrod.ForgetfulGrudger() - P1.history = [C, D, D, D] - P1.grudged = True - P1.grudge_memory = 4 - P1.reset() - self.assertEqual(P1.grudged, False) - self.assertEqual(P1.grudge_memory, 0) + player = axl.ForgetfulGrudger() + player.history = [C, D, D, D] + player.grudged = True + player.grudge_memory = 4 + player.reset() + self.assertEqual(player.grudged, False) + self.assertEqual(player.grudge_memory, 0) class TestOppositeGrudger(TestPlayer): name = 'Opposite Grudger' - player = axelrod.OppositeGrudger + player = axl.OppositeGrudger expected_classifier = { 'memory_depth': float('inf'), # Long memory 'stochastic': False, @@ -83,18 +97,26 @@ class TestOppositeGrudger(TestPlayer): } def test_strategy(self): - # Starts by defecting. - self.first_play_test(D) # If opponent cooperates at any point then the player will cooperate # forever. - self.responses_test([D], [C, D, D, D], [D, D, D, D]) - self.responses_test([C], [C, C, D, D, D], [C, D, C, C, C]) + opponent = axl.Cooperator() + actions = [(D, C)] + [(C, C)] * 20 + self.versus_test(opponent, expected_actions=actions) + + opponent = axl.Defector() + actions = [(D, D)] * 20 + self.versus_test(opponent, expected_actions=actions) + + opponent_actions = [C] + [D] * 30 + opponent = axl.MockPlayer(actions=opponent_actions) + expected = [(D, C)] + [(C, D)] * 30 + self.versus_test(opponent, expected_actions=expected) class TestAggravater(TestPlayer): name = "Aggravater" - player = axelrod.Aggravater + player = axl.Aggravater expected_classifier = { 'memory_depth': float('inf'), # Long memory 'stochastic': False, @@ -106,17 +128,26 @@ class TestAggravater(TestPlayer): } def test_strategy(self): - # Starts by defecting - self.first_play_test(D) # If opponent defects at any point then the player will defect forever. - self.responses_test([C], [C, D, D, D], [C, C, C, C]) - self.responses_test([D], [C, C, D, D, D], [C, D, C, C, C]) + # Always defects on first three turns. + opponent = axl.Cooperator() + actions = [(D, C)] * 3 + [(C, C)] * 20 + self.versus_test(opponent, expected_actions=actions) + + opponent = axl.Defector() + actions = [(D, D)] * 20 + self.versus_test(opponent, expected_actions=actions) + + opponent_actions = [C] * 10 + [D] + [C] * 20 + opponent = axl.MockPlayer(actions=opponent_actions) + actions = [(D, C)] * 3 + [(C, C)] * 7 + [(C, D)] + [(D, C)] * 20 + self.versus_test(opponent, expected_actions=actions) class TestSoftGrudger(TestPlayer): name = "Soft Grudger" - player = axelrod.SoftGrudger + player = axl.SoftGrudger expected_classifier = { 'memory_depth': 6, 'stochastic': False, @@ -128,35 +159,39 @@ class TestSoftGrudger(TestPlayer): } def test_strategy(self): - # Starts by cooperating. - self.first_play_test(C) # If opponent defects at any point then the player will respond with - # D, D, D, D, C, C. - self.responses_test([C], [C], [C]) - self.responses_test([D], [C, C], [C, D]) - self.responses_test([D], [C, C, D], [C, D, C]) - self.responses_test([D], [C, C, D, D], [C, D, C, C]) - self.responses_test([D], [C, C, D, D, D], [C, D, C, C, C]) - self.responses_test([C], [C, C, D, D, D, D], [C, D, C, C, C, C]) - self.responses_test([C], [C, C, D, D, D, D, C], [C, D, C, C, C, C, C]) - self.responses_test([D], [C, C, D, D, D, D, C, C], - [C, D, C, C, C, C, C, D]) - self.responses_test([D], [C, C, D, D, D, D, C, C, D], - [C, D, C, C, C, C, C, D, C]) + # D, D, D, D, C, C and then continue to check for defections. + grudge_response_d = [(D, D)] * 4 + [(C, D)] * 2 + grudge_response_c = [(D, C)] * 4 + [(C, C)] * 2 + + opponent = axl.Cooperator() + actions = [(C, C)] * 20 + self.versus_test(opponent, expected_actions=actions) + + opponent = axl.Defector() + actions = [(C, D)] + grudge_response_d * 5 + self.versus_test(opponent, expected_actions=actions) + + opponent_actions = [C] * 10 + [D] + opponent = axl.MockPlayer(actions=opponent_actions) + actions_start = [(C, C)] * 10 + [(C, D)] + subsequent = grudge_response_c + [(C, C)] * 4 + [(C, D)] + actions = actions_start + subsequent * 5 + self.versus_test(opponent, expected_actions=actions) def test_reset(self): - p = axelrod.SoftGrudger() - p.grudged = True - p.grudge_memory = 5 - p.reset() - self.assertFalse(p.grudged) - self.assertEqual(p.grudge_memory, 0) + player = self.player() + player.grudged = True + player.grudge_memory = 5 + player.reset() + self.assertFalse(player.grudged) + self.assertEqual(player.grudge_memory, 0) class TestGrudgerAlternator(TestPlayer): name = "GrudgerAlternator" - player = axelrod.GrudgerAlternator + player = axl.GrudgerAlternator expected_classifier = { 'memory_depth': float('inf'), # Long memory 'stochastic': False, @@ -168,41 +203,25 @@ class TestGrudgerAlternator(TestPlayer): } def test_strategy(self): - # Starts by cooperating. - self.first_play_test(C) # If opponent defects at any point then the player will alternate D C. - self.responses_test([C], [C, C, C, C, C], [C, C, C, C, C]) - self.responses_test([D], [C, C, C, C, C, C], [C, C, C, C, C, D]) - self.responses_test([C], [C, C, C, C, C, C, D], [C, C, C, C, C, D, D]) - self.responses_test([D], [C, C, C, C, C, C, D, C], - [C, C, C, C, C, D, D, C]) - self.responses_test([C], [C, C, C, C, C, C, D, C, D], - [C, C, C, C, C, D, D, C, C]) - - def test_strategy_random_number_rounds(self): - """Runs test_strategy for a random number of rounds.""" - # Hasn't defected yet - for _ in range(20): - i = randint(1, 30) - j = randint(1, 30) - opp_hist = [C] * i - my_hist = [C] * i - self.responses_test([C] * j, my_hist, opp_hist) - - # Defected at least once - for _ in range(20): - i = randint(1, 30) - j = randint(1, 30) - opp_hist = [C for r in range(i)] + [D] - my_hist = [C] * (i + 1) - expected_response = [D if r % 2 == 0 else C for r in range(j)] - self.responses_test(expected_response, my_hist, opp_hist) + opponent = axl.Cooperator() + actions = [(C, C)] * 20 + self.versus_test(opponent, expected_actions=actions) + + opponent = axl.Defector() + actions = [(C, D)] + [(D, D), (C, D)] * 20 + self.versus_test(opponent, expected_actions=actions) + + opponent_actions = [C] * 10 + [D] + [C] * 20 + opponent = axl.MockPlayer(actions=opponent_actions) + actions = [(C, C)] * 10 + [(C, D)] + [(D, C), (C, C)] * 10 + self.versus_test(opponent, expected_actions=actions) class TestEasyGo(TestPlayer): name = "EasyGo" - player = axelrod.EasyGo + player = axl.EasyGo expected_classifier = { 'memory_depth': float('inf'), # Long memory 'stochastic': False, @@ -214,17 +233,26 @@ class TestEasyGo(TestPlayer): } def test_strategy(self): - # Starts by defecting. - self.first_play_test(D) # If opponent defects at any point then the player will cooperate # forever. - self.responses_test([D], [C, D, D, D], [C, C, C, C]) - self.responses_test([C], [C, C, D, D, D], [C, D, C, C, C]) + opponent = axl.Cooperator() + actions = [(D, C)] * 20 + self.versus_test(opponent, expected_actions=actions) + + opponent = axl.Defector() + actions = [(D, D)] + [(C, D)] * 20 + self.versus_test(opponent, expected_actions=actions) + + opponent_actions = [C] * 10 + [D, C] * 20 + opponent = axl.MockPlayer(actions=opponent_actions) + actions = [(D, C)] * 10 + [(D, D)] + [(C, C), (C, D)] * 19 + self.versus_test(opponent, expected_actions=actions) + class TestGeneralSoftGrudger(TestPlayer): name = "General Soft Grudger: n=1,d=4,c=2" - player = axelrod.GeneralSoftGrudger + player = axl.GeneralSoftGrudger expected_classifier = { 'memory_depth': float('inf'), 'stochastic': False, @@ -242,16 +270,21 @@ def test_strategy(self): self.first_play_test(C) # Testing default parameters of n=1, d=4, c=2 (same as Soft Grudger) - actions = [(C, D), (D, D), (D, C), (D, C), (D, D), (C, D), (C, C), (C, C)] - self.versus_test(axelrod.MockPlayer(actions=[D, D, C, C]), expected_actions=actions) + actions = [(C, D), (D, D), (D, C), (D, C), (D, D), (C, D), + (C, C), (C, C)] + self.versus_test(axl.MockPlayer(actions=[D, D, C, C]), + expected_actions=actions) # Testing n=2, d=4, c=2 - actions = [(C, D), (C, D), (D, C), (D, C), (D, D), (D, D), (C, C), (C, C)] - self.versus_test(axelrod.MockPlayer(actions=[D, D, C, C]), expected_actions=actions, + actions = [(C, D), (C, D), (D, C), (D, C), (D, D), (D, D), + (C, C), (C, C)] + self.versus_test(axl.MockPlayer(actions=[D, D, C, C]), + expected_actions=actions, init_kwargs={"n": 2}) # Testing n=1, d=1, c=1 - actions = [(C, D), (D, D), (C, C), (C, C), (C, D), (D, D), (C, C), (C, C)] - self.versus_test(axelrod.MockPlayer(actions=[D, D, C, C]), expected_actions=actions, + actions = [(C, D), (D, D), (C, C), (C, C), (C, D), (D, D), + (C, C), (C, C)] + self.versus_test(axl.MockPlayer(actions=[D, D, C, C]), + expected_actions=actions, init_kwargs={"n": 1, "d": 1, "c": 1}) -