From 204d498bd08221f606c4b878b3ee99d31021c886 Mon Sep 17 00:00:00 2001 From: Marios Zoulias Date: Wed, 17 May 2017 22:30:34 +0300 Subject: [PATCH 1/6] SteinAndRapoport Strategy --- axelrod/strategies/_strategies.py | 2 + axelrod/strategies/axelrod_first.py | 68 +++++++++++++++++++ .../tests/strategies/test_axelrod_first.py | 38 +++++++++++ 3 files changed, 108 insertions(+) diff --git a/axelrod/strategies/_strategies.py b/axelrod/strategies/_strategies.py index b1ccc27f4..00a00062c 100644 --- a/axelrod/strategies/_strategies.py +++ b/axelrod/strategies/_strategies.py @@ -72,6 +72,7 @@ from .selfsteem import SelfSteem from .shortmem import ShortMem from .stalker import Stalker +from .axelrod_first import SteinAndRapoport from .titfortat import ( TitForTat, TitFor2Tats, TwoTitsForTat, Bully, SneakyTitForTat, SuspiciousTitForTat, AntiTitForTat, HardTitForTat, HardTitFor2Tats, @@ -232,6 +233,7 @@ SolutionB5, SpitefulTitForTat, Stalker, + SteinAndRapoport, StochasticCooperator, StochasticWSLS, SuspiciousTitForTat, diff --git a/axelrod/strategies/axelrod_first.py b/axelrod/strategies/axelrod_first.py index d48a5c64e..6d1304704 100644 --- a/axelrod/strategies/axelrod_first.py +++ b/axelrod/strategies/axelrod_first.py @@ -8,6 +8,8 @@ from axelrod.player import Player from axelrod.random_ import random_choice from.memoryone import MemoryOnePlayer +from axelrod.strategy_transformers import FinalTransformer +from scipy.stats import chisquare from typing import List, Dict, Tuple @@ -487,3 +489,69 @@ class UnnamedStrategy(Player): def strategy(opponent: Player) -> Action: r = random.uniform(3, 7) / 10 return random_choice(r) + +@FinalTransformer((D, D), name_prefix=None) +class SteinAndRapoport(Player): + """ + A player who plays according to statistic methods. + Begins by playing C for the first four (4) rounds , then it plays + tit for tat and at the last 2 round it Defects. Every 15 turns it + run a chi-squared test to check whether the opponent behaves randomly + or not . In case the opponent behaves randomly then Stein_and_Rapoport + Defects untill the next 15 round (where we check again), otherwise he + still plays TitForTat. + """ + + name = 'Stein and Rapoport' + classifier = { + 'memory_depth': 15, + 'stochastic': False, + 'makes_use_of': set(), + 'long_run_time': False, + 'inspects_source': False, + 'manipulates_source': False, + 'manipulates_state': False + } + + def __init__(self, alpha: float=0.05) -> None: + """ + Parameters + ---------- + alpha, float + The significant level of pvalue from chi-squared test + 0.05 by default according to literature + """ + super().__init__() + self.alpha = alpha + if (self.alpha > 1) or (self.alpha < 0): + self.alpha = 0.05 + + def strategy(self , opponent: Player , chi_tests = [0]) -> Action: + # chi-tests == 0 then we play TitForTat + round_number = len(self.history) + 1 + + # First 4 moves + if round_number < 5 : + return C + + # For first 15 rounds tit for tat as we dont know opponents strategy + if round_number < 16 : + if opponent.history[-1] == 'D' : + return D + else : + return C + + if len(self.history) % 15 == 0 : + if chisquare([opponent.cooperations, opponent.defections]).pvalue >= self.alpha : + chi_tests.append(1) + else : + chi_tests.append(0) + + if chi_tests[-1] == 1 : + # Defect if opponent plays randomly + return D + else : # TitForTatat if opponent plays not randomly + if opponent.history[-1] == 'D' : + return D + else : + return C diff --git a/axelrod/tests/strategies/test_axelrod_first.py b/axelrod/tests/strategies/test_axelrod_first.py index 2ad4e316e..d098cfe1f 100644 --- a/axelrod/tests/strategies/test_axelrod_first.py +++ b/axelrod/tests/strategies/test_axelrod_first.py @@ -387,3 +387,41 @@ def test_strategy(self): actions = [(C, C), (C, C), (D, C), (C, C), (C, C), (D, C)] self.versus_test(axelrod.Cooperator(), expected_actions=actions, seed=10) + + +class SteinAndRapoport(TestPlayer): + + name = "SteinAndRapoport" + player = axelrod.SteinAndRapoport + expected_classifier = { + 'memory_depth': 15, + 'long_run_time': False, + 'stochastic': False, + 'makes_use_of': set(), + 'inspects_source': False, + 'manipulates_source': False, + 'manipulates_state': False + } + + def test_strategy(self): + self.first_play_test(C) + + # Our Player (SteinAndRapoport) vs Cooperator + # After 15th round (pvalue < alpha) still plays titfortat + opponent = axelrod.Cooperator() + actions = [(C, C)] * 17 + [(D, C)] * 2 + self.versus_test(opponent, expected_actions=actions) + + # Our Player (SteinAndRapoport) vs Defector + # After 15th round (pvalue < alpha) still plays titfortat + opponent = axelrod.Cooperator() + actions = [(C, D)] * 4 + [(D, D)] * 15 + self.versus_test(opponent, expected_actions=actions) + + # Our Player (SteinAndRapoport) vs Alternator + # After 15th round (pvalue > alpha) starts defect + opponent = axelrod.Alternator() + actions = [(C, C), (C, D), (C, C), (C, D), (D, C), (C, D), (D, C), + (C, D), (D, C), (C, D), (D, C), (C, D),(D, C), (C, D), + (D, C), (D, D), (D, C), (D, D), (D, C)] + self.versus_test(opponent, expected_actions=actions) From 100e511535865d058661b88383d8841f98a6ff26 Mon Sep 17 00:00:00 2001 From: Marios Zoulias Date: Fri, 19 May 2017 04:20:04 +0300 Subject: [PATCH 2/6] New changes --- axelrod/strategies/_strategies.py | 3 +-- axelrod/strategies/axelrod_first.py | 40 +++++++++++++++-------------- requirements.txt | 1 + 3 files changed, 23 insertions(+), 21 deletions(-) diff --git a/axelrod/strategies/_strategies.py b/axelrod/strategies/_strategies.py index 00a00062c..86378d011 100644 --- a/axelrod/strategies/_strategies.py +++ b/axelrod/strategies/_strategies.py @@ -6,7 +6,7 @@ from .averagecopier import AverageCopier, NiceAverageCopier from .axelrod_first import ( Davis, RevisedDowning, Feld, Grofman, Nydegger, Joss, Shubik, Tullock, - UnnamedStrategy) + UnnamedStrategy, SteinAndRapoport) from .axelrod_second import Champion, Eatherley, Tester from .backstabber import BackStabber, DoubleCrosser from .better_and_better import BetterAndBetter @@ -72,7 +72,6 @@ from .selfsteem import SelfSteem from .shortmem import ShortMem from .stalker import Stalker -from .axelrod_first import SteinAndRapoport from .titfortat import ( TitForTat, TitFor2Tats, TwoTitsForTat, Bully, SneakyTitForTat, SuspiciousTitForTat, AntiTitForTat, HardTitForTat, HardTitFor2Tats, diff --git a/axelrod/strategies/axelrod_first.py b/axelrod/strategies/axelrod_first.py index 6d1304704..f8767e7c5 100644 --- a/axelrod/strategies/axelrod_first.py +++ b/axelrod/strategies/axelrod_first.py @@ -492,15 +492,18 @@ def strategy(opponent: Player) -> Action: @FinalTransformer((D, D), name_prefix=None) class SteinAndRapoport(Player): - """ - A player who plays according to statistic methods. - Begins by playing C for the first four (4) rounds , then it plays - tit for tat and at the last 2 round it Defects. Every 15 turns it - run a chi-squared test to check whether the opponent behaves randomly - or not . In case the opponent behaves randomly then Stein_and_Rapoport - Defects untill the next 15 round (where we check again), otherwise he - still plays TitForTat. - """ +""" +A player who plays according to statistic methods. +Begins by playing C for the first four (4) rounds , then it plays +tit for tat and at the last 2 round it Defects. Every 15 turns it +run a chi-squared test to check whether the opponent behaves randomly +or not . In case the opponent behaves randomly then Stein_and_Rapoport +Defects untill the next 15 round (where we check again), otherwise he +still plays TitForTat. + +Names: +- SteinAndRapoport [Axelrod1980]_ +""" name = 'Stein and Rapoport' classifier = { @@ -527,31 +530,30 @@ def __init__(self, alpha: float=0.05) -> None: self.alpha = 0.05 def strategy(self , opponent: Player , chi_tests = [0]) -> Action: - # chi-tests == 0 then we play TitForTat round_number = len(self.history) + 1 # First 4 moves - if round_number < 5 : + if round_number < 5: return C # For first 15 rounds tit for tat as we dont know opponents strategy - if round_number < 16 : - if opponent.history[-1] == 'D' : + if round_number < 16: + if opponent.history[-1] == 'D': return D else : return C - if len(self.history) % 15 == 0 : - if chisquare([opponent.cooperations, opponent.defections]).pvalue >= self.alpha : + if len(self.history) % 15 == 0: + if chisquare([opponent.cooperations, opponent.defections]).pvalue >= self.alpha: chi_tests.append(1) - else : + else: chi_tests.append(0) - if chi_tests[-1] == 1 : + if chi_tests[-1] == 1: # Defect if opponent plays randomly return D else : # TitForTatat if opponent plays not randomly - if opponent.history[-1] == 'D' : + if opponent.history[-1] == 'D': return D - else : + else: return C diff --git a/requirements.txt b/requirements.txt index 82b3933d6..5cc9fdfee 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,3 +3,4 @@ matplotlib>=1.4.2 hypothesis>=3.2 tqdm>=3.4.0 prompt-toolkit>=1.0.7 +scipy>=0.19.0 \ No newline at end of file From 23840d8e2fb3de2186b1253f40b54cf33c85632c Mon Sep 17 00:00:00 2001 From: Marios Zoulias Date: Fri, 19 May 2017 22:23:51 +0300 Subject: [PATCH 3/6] More changes --- axelrod/strategies/axelrod_first.py | 24 +++++++++++------------ docs/reference/overview_of_strategies.rst | 2 +- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/axelrod/strategies/axelrod_first.py b/axelrod/strategies/axelrod_first.py index f8767e7c5..859b2f45e 100644 --- a/axelrod/strategies/axelrod_first.py +++ b/axelrod/strategies/axelrod_first.py @@ -492,18 +492,18 @@ def strategy(opponent: Player) -> Action: @FinalTransformer((D, D), name_prefix=None) class SteinAndRapoport(Player): -""" -A player who plays according to statistic methods. -Begins by playing C for the first four (4) rounds , then it plays -tit for tat and at the last 2 round it Defects. Every 15 turns it -run a chi-squared test to check whether the opponent behaves randomly -or not . In case the opponent behaves randomly then Stein_and_Rapoport -Defects untill the next 15 round (where we check again), otherwise he -still plays TitForTat. - -Names: -- SteinAndRapoport [Axelrod1980]_ -""" + """ + A player who plays according to statistic methods. + Begins by playing C for the first four (4) rounds , then it plays + tit for tat and at the last 2 round it Defects. Every 15 turns it + run a chi-squared test to check whether the opponent behaves randomly + or not . In case the opponent behaves randomly then Stein_and_Rapoport + Defects untill the next 15 round (where we check again), otherwise he + still plays TitForTat.0 + + Names: + - SteinAndRapoport [Axelrod1980]_ + """ name = 'Stein and Rapoport' classifier = { diff --git a/docs/reference/overview_of_strategies.rst b/docs/reference/overview_of_strategies.rst index 3b142ea49..feaa098c1 100644 --- a/docs/reference/overview_of_strategies.rst +++ b/docs/reference/overview_of_strategies.rst @@ -28,7 +28,7 @@ Strategies in the Axelrod's first tournament: +--------------------------+-------------------------------------------+--------------------------+ | `Shubik`_ | Shubik (author's name) | :code:`Shubik` | +--------------------------+-------------------------------------------+--------------------------+ -| `Stein and Rapoport`_ | Stein and Rapoport (authors' names) | Not Implemented | +| `Stein and Rapoport`_ | Stein and Rapoport (authors' names) | :code:`SteinAndRapoport` | +--------------------------+-------------------------------------------+--------------------------+ | `Grudger`_ | Grudger (by Friedman) | :code:`Grudger` | +--------------------------+-------------------------------------------+--------------------------+ From dc0fb135096529c2f78683eacf4a7eb025b87f8d Mon Sep 17 00:00:00 2001 From: Marios Zoulias Date: Sat, 20 May 2017 20:11:02 +0300 Subject: [PATCH 4/6] More and More Changes --- docs/conf.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 0ba675c31..41ece8ff4 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -18,10 +18,10 @@ import mock MOCK_MODULES = [ - 'numpy', 'numpy.linalg', 'numpy.random', 'matplotlib.pyplot', 'matplotlib', - 'matplotlib.transforms', 'mpl_toolkits.axes_grid1', 'dill', 'multiprocess', - 'prompt_toolkit', 'prompt_toolkit.token', 'prompt_toolkit.styles', - 'prompt_toolkit.validation'] + 'scipy', 'scipy.stats','numpy', 'numpy.linalg', 'numpy.random', + 'matplotlib.pyplot', 'matplotlib','matplotlib.transforms', + 'mpl_toolkits.axes_grid1', 'dill', 'multiprocess','prompt_toolkit', + 'prompt_toolkit.token', 'prompt_toolkit.styles','prompt_toolkit.validation'] for mod_name in MOCK_MODULES: sys.modules[mod_name] = mock.Mock() From 5a6cc56b0a5f89edb5955c3b6255b8f8f2fe9523 Mon Sep 17 00:00:00 2001 From: Vince Knight Date: Sun, 21 May 2017 14:33:55 +0100 Subject: [PATCH 5/6] Change build command for appveyor This is due to an error that was occurring with scipy. The solution is to use a conda environment as described in http://tjelvarolsson.com/blog/how-to-continuously-test-your-python-code-on-windows-using-appveyor/ --- appveyor.yml | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index dbe222f36..9455dd287 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,11 +1,18 @@ environment: matrix: - - PYTHON: "C:\\Python35" - - PYTHON: "C:\\Python36" + - PYTHON_VERSION: "3.5" + MINICONDA: "C:\\Miniconda35" + - PYTHON_VERSION: "3.6" + MINICONDA: "C:\\Miniconda36" install: - - "%PYTHON%\\python.exe -m pip install -r requirements.txt" + - "set PATH=%MINICONDA%;%MINICONDA%\\Scripts;%PATH%" + - "conda config --set always_yes yes --set changeps1 no" + - "conda update -q conda" + - "conda create -q -n test-environment python=%PYTHON_VERSION% scipy>=0.19.0 numpy>=1.9.2" + - "activate test-environment" + - "python -m pip install -r requirements.txt" build: off test_script: - - "%PYTHON%\\python.exe -m unittest discover" - - "%PYTHON%\\python.exe doctests.py" - - "%PYTHON%\\python.exe setup.py install" + - "python -m unittest discover" + - "python doctests.py" + - "python setup.py install" From 9c036156de4823e747f920d23004b6f00fc76ab7 Mon Sep 17 00:00:00 2001 From: Vince Knight Date: Sun, 21 May 2017 14:34:55 +0100 Subject: [PATCH 6/6] Correct SteinRap strategy and add relevant test. The strategy wasn't quite correct: it would in fact not remember the result of the chisquared test from one call to another. I've fixed that here. I've also included a few more tests including a test that the chi squared test gets run again and that it can change it's mind. --- axelrod/player.py | 3 +- axelrod/strategies/axelrod_first.py | 48 +++++++--------- .../tests/strategies/test_axelrod_first.py | 57 ++++++++++++++++--- .../advanced/classification_of_strategies.rst | 2 +- 4 files changed, 73 insertions(+), 37 deletions(-) diff --git a/axelrod/player.py b/axelrod/player.py index 0025dacca..2088495ea 100644 --- a/axelrod/player.py +++ b/axelrod/player.py @@ -241,7 +241,8 @@ def clone(self): return new_player def reset(self): - """Resets history. + """ + Resets history. When creating strategies that create new attributes then this method should be re-written (in the inherited class) and should not only reset history but also rest all other attributes. diff --git a/axelrod/strategies/axelrod_first.py b/axelrod/strategies/axelrod_first.py index 859b2f45e..09750c4be 100644 --- a/axelrod/strategies/axelrod_first.py +++ b/axelrod/strategies/axelrod_first.py @@ -494,14 +494,15 @@ def strategy(opponent: Player) -> Action: class SteinAndRapoport(Player): """ A player who plays according to statistic methods. - Begins by playing C for the first four (4) rounds , then it plays + Begins by playing C for the first four (4) rounds, then it plays tit for tat and at the last 2 round it Defects. Every 15 turns it - run a chi-squared test to check whether the opponent behaves randomly - or not . In case the opponent behaves randomly then Stein_and_Rapoport - Defects untill the next 15 round (where we check again), otherwise he + runs a chi-squared test to check whether the opponent behaves randomly + or not. In case the opponent behaves randomly then Stein and Rapoport + Defects until the next 15 round (where we check again), otherwise it still plays TitForTat.0 Names: + - SteinAndRapoport [Axelrod1980]_ """ @@ -509,7 +510,7 @@ class SteinAndRapoport(Player): classifier = { 'memory_depth': 15, 'stochastic': False, - 'makes_use_of': set(), + 'makes_use_of': {"length"}, 'long_run_time': False, 'inspects_source': False, 'manipulates_source': False, @@ -526,34 +527,29 @@ def __init__(self, alpha: float=0.05) -> None: """ super().__init__() self.alpha = alpha - if (self.alpha > 1) or (self.alpha < 0): - self.alpha = 0.05 + self.opponent_is_random = None - def strategy(self , opponent: Player , chi_tests = [0]) -> Action: + def strategy(self , opponent: Player) -> Action: round_number = len(self.history) + 1 # First 4 moves if round_number < 5: return C + # For first 15 rounds tit for tat as we do not know opponents strategy + elif round_number < 15: + return opponent.history[-1] - # For first 15 rounds tit for tat as we dont know opponents strategy - if round_number < 16: - if opponent.history[-1] == 'D': - return D - else : - return C - - if len(self.history) % 15 == 0: - if chisquare([opponent.cooperations, opponent.defections]).pvalue >= self.alpha: - chi_tests.append(1) - else: - chi_tests.append(0) + if round_number % 15 == 0: + p_value = chisquare([opponent.cooperations, + opponent.defections]).pvalue + self.opponent_is_random = p_value >= self.alpha - if chi_tests[-1] == 1: + if self.opponent_is_random: # Defect if opponent plays randomly return D - else : # TitForTatat if opponent plays not randomly - if opponent.history[-1] == 'D': - return D - else: - return C + else: # TitForTatat if opponent plays not randomly + return opponent.history[-1] + + def reset(self): + super().reset() + self.random_opponent = None diff --git a/axelrod/tests/strategies/test_axelrod_first.py b/axelrod/tests/strategies/test_axelrod_first.py index d098cfe1f..47fe81c83 100644 --- a/axelrod/tests/strategies/test_axelrod_first.py +++ b/axelrod/tests/strategies/test_axelrod_first.py @@ -391,37 +391,76 @@ def test_strategy(self): class SteinAndRapoport(TestPlayer): - name = "SteinAndRapoport" + name = "Stein and Rapoport: 0.05: ('D', 'D')" player = axelrod.SteinAndRapoport expected_classifier = { 'memory_depth': 15, 'long_run_time': False, 'stochastic': False, - 'makes_use_of': set(), + 'makes_use_of': {'length'}, 'inspects_source': False, 'manipulates_source': False, 'manipulates_state': False } + def test_init(self): + player = self.player() + self.assertEqual(player.alpha, 0.05) + self.assertIsNone(player.opponent_is_random) + + player = self.player(alpha=.5) + self.assertEqual(player.alpha, 0.5) + self.assertIsNone(player.opponent_is_random) + def test_strategy(self): self.first_play_test(C) # Our Player (SteinAndRapoport) vs Cooperator # After 15th round (pvalue < alpha) still plays titfortat + # Note it always defects on the last two rounds opponent = axelrod.Cooperator() actions = [(C, C)] * 17 + [(D, C)] * 2 - self.versus_test(opponent, expected_actions=actions) + self.versus_test(opponent, expected_actions=actions, + attrs={"opponent_is_random": False}) + + actions = actions[:-2] + [(C, C)] * 2 + self.versus_test(opponent, expected_actions=actions[:-2], + match_attributes={"length": -1}, + attrs={"opponent_is_random": False}) # Our Player (SteinAndRapoport) vs Defector # After 15th round (pvalue < alpha) still plays titfortat - opponent = axelrod.Cooperator() + opponent = axelrod.Defector() actions = [(C, D)] * 4 + [(D, D)] * 15 - self.versus_test(opponent, expected_actions=actions) + self.versus_test(opponent, expected_actions=actions, + attrs={"opponent_is_random": False}) # Our Player (SteinAndRapoport) vs Alternator # After 15th round (pvalue > alpha) starts defect opponent = axelrod.Alternator() - actions = [(C, C), (C, D), (C, C), (C, D), (D, C), (C, D), (D, C), - (C, D), (D, C), (C, D), (D, C), (C, D),(D, C), (C, D), - (D, C), (D, D), (D, C), (D, D), (D, C)] - self.versus_test(opponent, expected_actions=actions) + actions = [(C, C), (C, D), (C, C), (C, D)] + + # On 15th round carry out chisquare test + actions += [(D, C), (C, D)] * 5 + [(D, C)] + + # Defect throughout + actions += [(D, D), (D, C), (D, D), (D, C)] + + self.versus_test(opponent, expected_actions=actions, + attrs={"opponent_is_random": True}) + + # The test is carried out again every 15 rounds. + # If the strategy alternates for the first 12 rounds and then cooperates + # it is no longer recognised as random + opponent = axelrod.MockPlayer([C, D] * 6 + [C] * 50) + + actions = [(C, C), (C, D), (C, C), (C, D)] + # On 15th round carry out chisquare test + actions += [(D, C), (C, D)] * 4 + [(D, C), (C, C), (D, C)] + # Defect throughout and carry out chisquare test on round 30 + # Opponent is no longer recognised as random, revert to TfT + actions += [(D, C)] * 14 + [(C, C)] + self.versus_test(opponent, expected_actions=actions, + match_attributes={"length": -1}, + attrs={"opponent_is_random": False}) + diff --git a/docs/tutorials/advanced/classification_of_strategies.rst b/docs/tutorials/advanced/classification_of_strategies.rst index a2a268a4e..4d04ca822 100644 --- a/docs/tutorials/advanced/classification_of_strategies.rst +++ b/docs/tutorials/advanced/classification_of_strategies.rst @@ -80,7 +80,7 @@ length of each match of the tournament:: ... } >>> strategies = axl.filtered_strategies(filterset) >>> len(strategies) - 26 + 27 Note that in the filterset dictionary, the value for the 'makes_use_of' key must be a list. Here is how we might identify the number of strategies that use