diff --git a/axelrod/strategies/_strategies.py b/axelrod/strategies/_strategies.py index 4c28d72f5..ecef0e6f2 100644 --- a/axelrod/strategies/_strategies.py +++ b/axelrod/strategies/_strategies.py @@ -9,7 +9,8 @@ UnnamedStrategy, SteinAndRapoport, TidemanAndChieruzzi) from .axelrod_second import ( Champion, Eatherley, Tester, Gladstein, Tranquilizer, MoreGrofman, - Kluepfel, Borufsen, Cave, WmAdams, GraaskampKatzen, Weiner, Harrington) + Kluepfel, Borufsen, Cave, WmAdams, GraaskampKatzen, Weiner, Harrington, + MoreTidemanAndChieruzzi) from .backstabber import BackStabber, DoubleCrosser from .better_and_better import BetterAndBetter from .bush_mosteller import BushMosteller @@ -208,6 +209,7 @@ MindWarper, MirrorMindReader, MoreGrofman, + MoreTidemanAndChieruzzi, Negation, NiceAverageCopier, NTitsForMTats, diff --git a/axelrod/strategies/axelrod_second.py b/axelrod/strategies/axelrod_second.py index eedf2d1d6..bdfbca6cf 100644 --- a/axelrod/strategies/axelrod_second.py +++ b/axelrod/strategies/axelrod_second.py @@ -1305,3 +1305,114 @@ def strategy(self, opponent: Player) -> Action: self.prob += 0.05 self.more_coop, self.last_generous_n_turns_ago = 2, 1 return self.try_return(D, lower_flags=False) + + +class MoreTidemanAndChieruzzi(Player): + """ + Strategy submitted to Axelrod's second tournament by T. Nicolaus Tideman + and Paula Chieruzzi (K84R) and came in ninth in that tournament. + + This strategy Cooperates if this player's score exceeds the opponent's + score by at least `score_to_beat`. `score_to_beat` starts at zero and + increases by `score_to_beat_inc` every time the opponent's last two moves + are a Cooperation and Defection in that order. `score_to_beat_inc` itself + increase by 5 every time the opponent's last two moves are a Cooperation + and Defection in that order. + + Additionally, the strategy executes a "fresh start" if the following hold: + + - The strategy would Defect by score (difference less than `score_to_beat`) + - The opponent did not Cooperate and Defect (in order) in the last two + turns. + - It's been at least 10 turns since the last fresh start. Or since the + match started if there hasn't been a fresh start yet. + + A "fresh start" entails two Cooperations and resetting scores, + `scores_to_beat` and `scores_to_beat_inc`. + + Names: + + - MoreTidemanAndChieruzzi: [Axelrod1980b]_ + """ + + name = 'More Tideman and Chieruzzi' + classifier = { + 'memory_depth': float('inf'), + 'stochastic': False, + 'makes_use_of': {"game"}, + 'long_run_time': False, + 'inspects_source': False, + 'manipulates_source': False, + 'manipulates_state': False + } + + def __init__(self) -> None: + super().__init__() + self.current_score = 0 + self.opponent_score = 0 + self.last_fresh_start = 0 + self.fresh_start = False + self.score_to_beat = 0 + self.score_to_beat_inc = 0 + + def _fresh_start(self): + """Give the opponent a fresh start by forgetting the past""" + self.current_score = 0 + self.opponent_score = 0 + self.score_to_beat = 0 + self.score_to_beat_inc = 0 + + def _score_last_round(self, opponent: Player): + """Updates the scores for each player.""" + # Load the default game if not supplied by a tournament. + game = self.match_attributes["game"] + last_round = (self.history[-1], opponent.history[-1]) + scores = game.score(last_round) + self.current_score += scores[0] + self.opponent_score += scores[1] + + def strategy(self, opponent: Player) -> Action: + current_round = len(self.history) + 1 + + if current_round == 1: + return C + + # Calculate the scores. + self._score_last_round(opponent) + + # Check if we have recently given the strategy a fresh start. + if self.fresh_start: + self._fresh_start() + self.last_fresh_start = current_round + self.fresh_start = False + return C # Second cooperation + + opponent_CDd = False + + opponent_two_turns_ago = C # Default value for second turn. + if len(opponent.history) >= 2: + opponent_two_turns_ago = opponent.history[-2] + # If opponent's last two turns are C and D in that order. + if opponent_two_turns_ago == C and opponent.history[-1] == D: + opponent_CDd = True + self.score_to_beat += self.score_to_beat_inc + self.score_to_beat_inc += 5 + + # Cooperate if we're beating opponent by at least `score_to_beat` + if self.current_score - self.opponent_score >= self.score_to_beat: + return C + + # Wait at least ten turns for another fresh start. + if (not opponent_CDd) and current_round - self.last_fresh_start >= 10: + # 50-50 split is based off the binomial distribution. + N = opponent.cooperations + opponent.defections + # std_dev = sqrt(N*p*(1-p)) where p is 1 / 2. + std_deviation = (N ** (1 / 2)) / 2 + lower = N / 2 - 3 * std_deviation + upper = N / 2 + 3 * std_deviation + if opponent.defections <= lower or opponent.defections >= upper: + # Opponent deserves a fresh start + self.fresh_start = True + return C # First cooperation + + return D diff --git a/axelrod/tests/strategies/test_axelrod_second.py b/axelrod/tests/strategies/test_axelrod_second.py index c91d280ee..bc2ee8da7 100644 --- a/axelrod/tests/strategies/test_axelrod_second.py +++ b/axelrod/tests/strategies/test_axelrod_second.py @@ -854,3 +854,55 @@ def test_strategy(self): actions += [(C, D)] * 2 self.versus_test(Rand_Then_Def, expected_actions=actions, seed=10, attrs={"mode": "Normal", "was_defective": True}) + + +class TestMoreTidemanAndChieruzzi(TestPlayer): + name = 'More Tideman and Chieruzzi' + player = axelrod.MoreTidemanAndChieruzzi + expected_classifier = { + 'memory_depth': float('inf'), + 'stochastic': False, + 'makes_use_of': {"game"}, + 'long_run_time': False, + 'inspects_source': False, + 'manipulates_source': False, + 'manipulates_state': False + } + + def test_strategy(self): + actions = [(C, C)] * 100 + self.versus_test(axelrod.Cooperator(), expected_actions=actions) + + actions = [(C, D)] + [(D, D)] * 8 + self.versus_test(axelrod.Defector(), expected_actions=actions, attrs={"score_to_beat_inc": 5}) + + actions = [(C, D)] + [(D, D)] * 8 + # On tenth turn, try a fresh start + actions += [(C, D), (C, D)] + [(D, D)] * 2 + self.versus_test(axelrod.Defector(), expected_actions=actions, attrs={"last_fresh_start": 11}) + + actions = [(C, C), (C, D)] + # Scores and score_to_beat variables are a turn behind + self.versus_test(axelrod.Alternator(), expected_actions=actions, + attrs={"current_score": 3, "opponent_score": 3, "score_to_beat": 0, "score_to_beat_inc": 0}) + actions += [(D, C), (C, D)] + self.versus_test(axelrod.Alternator(), expected_actions=actions, + attrs={"current_score": 8, "opponent_score": 8, "score_to_beat": 0, "score_to_beat_inc": 5}) + actions += [(D, C), (D, D)] + self.versus_test(axelrod.Alternator(), expected_actions=actions, + attrs={"current_score": 13, "opponent_score": 13, "score_to_beat": 5, "score_to_beat_inc": 10}) + actions += [(D, C), (D, D)] + self.versus_test(axelrod.Alternator(), expected_actions=actions, + attrs={"current_score": 19, "opponent_score": 14, "score_to_beat": 15, "score_to_beat_inc": 15}) + actions += [(D, C), (D, D)] + self.versus_test(axelrod.Alternator(), expected_actions=actions, + attrs={"current_score": 25, "opponent_score": 15, "score_to_beat": 30, "score_to_beat_inc": 20}) + + # Build an opponent who will cause us to consider a Fresh Start, but + # will fail the binomial test. + opponent_actions = [C] * 5 + [D] * 5 + C5D5_Player = axelrod.MockPlayer(actions=opponent_actions) + actions = [(C, C)] * 5 + [(C, D)] + [(D, D)] * 3 + actions += [(D, D)] # No Defection here means no Fresh Start. + self.versus_test(C5D5_Player, expected_actions=actions) + diff --git a/docs/reference/overview_of_strategies.rst b/docs/reference/overview_of_strategies.rst index bfbe9193b..892fc407a 100644 --- a/docs/reference/overview_of_strategies.rst +++ b/docs/reference/overview_of_strategies.rst @@ -36,8 +36,6 @@ An indication is given as to whether or not this strategy is implemented in the Tideman and Chieruzzi ^^^^^^^^^^^^^^^^^^^^^ -**Not implemented yet** - This strategy begins by playing Tit For Tat and then things get slightly complicated: @@ -155,7 +153,7 @@ repository. "K81R_", "Martyn Jones", "Not Implemented" "K82R_", "Robert A Leyland", "Not Implemented" "K83R_", "Paul E Black", "Not Implemented" - "K84R_", "T Nicolaus Tideman and Paula Chieruzz", "Not Implemented" + "K84R_", "T Nicolaus Tideman and Paula Chieruzzi", ":class:`More Tideman And Chieruzzi `" "K85R_", "Robert B Falk and James M Langsted", "Not Implemented" "K86R_", "Bernard Grofman", "Not Implemented" "K87R_", "E E H Schurmann", "Not Implemented"