Skip to content

Commit 20b4a8c

Browse files
authored
Merge pull request #1012 from MariosZoulias/SteinRap
SteinAndRapoport Strategy
2 parents 7f005a0 + b72e03c commit 20b4a8c

File tree

9 files changed

+167
-14
lines changed

9 files changed

+167
-14
lines changed

appveyor.yml

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
11
environment:
22
matrix:
3-
- PYTHON: "C:\\Python35"
4-
- PYTHON: "C:\\Python36"
3+
- PYTHON_VERSION: "3.5"
4+
MINICONDA: "C:\\Miniconda35"
5+
- PYTHON_VERSION: "3.6"
6+
MINICONDA: "C:\\Miniconda36"
57
install:
6-
- "%PYTHON%\\python.exe -m pip install -r requirements.txt"
8+
- "set PATH=%MINICONDA%;%MINICONDA%\\Scripts;%PATH%"
9+
- "conda config --set always_yes yes --set changeps1 no"
10+
- "conda update -q conda"
11+
- "conda create -q -n test-environment python=%PYTHON_VERSION% scipy>=0.19.0 numpy>=1.9.2"
12+
- "activate test-environment"
13+
- "python -m pip install -r requirements.txt"
714
build: off
815
test_script:
9-
- "%PYTHON%\\python.exe -m unittest discover"
10-
- "%PYTHON%\\python.exe doctests.py"
11-
- "%PYTHON%\\python.exe setup.py install"
16+
- "python -m unittest discover"
17+
- "python doctests.py"
18+
- "python setup.py install"

axelrod/player.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,8 @@ def clone(self):
241241
return new_player
242242

243243
def reset(self):
244-
"""Resets history.
244+
"""
245+
Resets history.
245246
When creating strategies that create new attributes then this method
246247
should be re-written (in the inherited class) and should not only reset
247248
history but also rest all other attributes.

axelrod/strategies/_strategies.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from .averagecopier import AverageCopier, NiceAverageCopier
77
from .axelrod_first import (
88
Davis, RevisedDowning, Feld, Grofman, Nydegger, Joss, Shubik, Tullock,
9-
UnnamedStrategy)
9+
UnnamedStrategy, SteinAndRapoport)
1010
from .axelrod_second import Champion, Eatherley, Tester
1111
from .backstabber import BackStabber, DoubleCrosser
1212
from .better_and_better import BetterAndBetter
@@ -232,6 +232,7 @@
232232
SolutionB5,
233233
SpitefulTitForTat,
234234
Stalker,
235+
SteinAndRapoport,
235236
StochasticCooperator,
236237
StochasticWSLS,
237238
SuspiciousTitForTat,

axelrod/strategies/axelrod_first.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
from axelrod.player import Player
99
from axelrod.random_ import random_choice
1010
from.memoryone import MemoryOnePlayer
11+
from axelrod.strategy_transformers import FinalTransformer
12+
from scipy.stats import chisquare
1113

1214
from typing import List, Dict, Tuple
1315

@@ -487,3 +489,67 @@ class UnnamedStrategy(Player):
487489
def strategy(opponent: Player) -> Action:
488490
r = random.uniform(3, 7) / 10
489491
return random_choice(r)
492+
493+
@FinalTransformer((D, D), name_prefix=None)
494+
class SteinAndRapoport(Player):
495+
"""
496+
A player who plays according to statistic methods.
497+
Begins by playing C for the first four (4) rounds, then it plays
498+
tit for tat and at the last 2 round it Defects. Every 15 turns it
499+
runs a chi-squared test to check whether the opponent behaves randomly
500+
or not. In case the opponent behaves randomly then Stein and Rapoport
501+
Defects until the next 15 round (where we check again), otherwise it
502+
still plays TitForTat.0
503+
504+
Names:
505+
506+
- SteinAndRapoport [Axelrod1980]_
507+
"""
508+
509+
name = 'Stein and Rapoport'
510+
classifier = {
511+
'memory_depth': 15,
512+
'stochastic': False,
513+
'makes_use_of': {"length"},
514+
'long_run_time': False,
515+
'inspects_source': False,
516+
'manipulates_source': False,
517+
'manipulates_state': False
518+
}
519+
520+
def __init__(self, alpha: float=0.05) -> None:
521+
"""
522+
Parameters
523+
----------
524+
alpha, float
525+
The significant level of pvalue from chi-squared test
526+
0.05 by default according to literature
527+
"""
528+
super().__init__()
529+
self.alpha = alpha
530+
self.opponent_is_random = None
531+
532+
def strategy(self , opponent: Player) -> Action:
533+
round_number = len(self.history) + 1
534+
535+
# First 4 moves
536+
if round_number < 5:
537+
return C
538+
# For first 15 rounds tit for tat as we do not know opponents strategy
539+
elif round_number < 15:
540+
return opponent.history[-1]
541+
542+
if round_number % 15 == 0:
543+
p_value = chisquare([opponent.cooperations,
544+
opponent.defections]).pvalue
545+
self.opponent_is_random = p_value >= self.alpha
546+
547+
if self.opponent_is_random:
548+
# Defect if opponent plays randomly
549+
return D
550+
else: # TitForTatat if opponent plays not randomly
551+
return opponent.history[-1]
552+
553+
def reset(self):
554+
super().reset()
555+
self.random_opponent = None

axelrod/tests/strategies/test_axelrod_first.py

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,3 +387,80 @@ def test_strategy(self):
387387
actions = [(C, C), (C, C), (D, C), (C, C), (C, C), (D, C)]
388388
self.versus_test(axelrod.Cooperator(), expected_actions=actions,
389389
seed=10)
390+
391+
392+
class SteinAndRapoport(TestPlayer):
393+
394+
name = "Stein and Rapoport: 0.05: ('D', 'D')"
395+
player = axelrod.SteinAndRapoport
396+
expected_classifier = {
397+
'memory_depth': 15,
398+
'long_run_time': False,
399+
'stochastic': False,
400+
'makes_use_of': {'length'},
401+
'inspects_source': False,
402+
'manipulates_source': False,
403+
'manipulates_state': False
404+
}
405+
406+
def test_init(self):
407+
player = self.player()
408+
self.assertEqual(player.alpha, 0.05)
409+
self.assertIsNone(player.opponent_is_random)
410+
411+
player = self.player(alpha=.5)
412+
self.assertEqual(player.alpha, 0.5)
413+
self.assertIsNone(player.opponent_is_random)
414+
415+
def test_strategy(self):
416+
self.first_play_test(C)
417+
418+
# Our Player (SteinAndRapoport) vs Cooperator
419+
# After 15th round (pvalue < alpha) still plays titfortat
420+
# Note it always defects on the last two rounds
421+
opponent = axelrod.Cooperator()
422+
actions = [(C, C)] * 17 + [(D, C)] * 2
423+
self.versus_test(opponent, expected_actions=actions,
424+
attrs={"opponent_is_random": False})
425+
426+
actions = actions[:-2] + [(C, C)] * 2
427+
self.versus_test(opponent, expected_actions=actions[:-2],
428+
match_attributes={"length": -1},
429+
attrs={"opponent_is_random": False})
430+
431+
# Our Player (SteinAndRapoport) vs Defector
432+
# After 15th round (pvalue < alpha) still plays titfortat
433+
opponent = axelrod.Defector()
434+
actions = [(C, D)] * 4 + [(D, D)] * 15
435+
self.versus_test(opponent, expected_actions=actions,
436+
attrs={"opponent_is_random": False})
437+
438+
# Our Player (SteinAndRapoport) vs Alternator
439+
# After 15th round (pvalue > alpha) starts defect
440+
opponent = axelrod.Alternator()
441+
actions = [(C, C), (C, D), (C, C), (C, D)]
442+
443+
# On 15th round carry out chisquare test
444+
actions += [(D, C), (C, D)] * 5 + [(D, C)]
445+
446+
# Defect throughout
447+
actions += [(D, D), (D, C), (D, D), (D, C)]
448+
449+
self.versus_test(opponent, expected_actions=actions,
450+
attrs={"opponent_is_random": True})
451+
452+
# The test is carried out again every 15 rounds.
453+
# If the strategy alternates for the first 12 rounds and then cooperates
454+
# it is no longer recognised as random
455+
opponent = axelrod.MockPlayer([C, D] * 6 + [C] * 50)
456+
457+
actions = [(C, C), (C, D), (C, C), (C, D)]
458+
# On 15th round carry out chisquare test
459+
actions += [(D, C), (C, D)] * 4 + [(D, C), (C, C), (D, C)]
460+
# Defect throughout and carry out chisquare test on round 30
461+
# Opponent is no longer recognised as random, revert to TfT
462+
actions += [(D, C)] * 14 + [(C, C)]
463+
self.versus_test(opponent, expected_actions=actions,
464+
match_attributes={"length": -1},
465+
attrs={"opponent_is_random": False})
466+

docs/conf.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@
1818
import mock
1919

2020
MOCK_MODULES = [
21-
'numpy', 'numpy.linalg', 'numpy.random', 'matplotlib.pyplot', 'matplotlib',
22-
'matplotlib.transforms', 'mpl_toolkits.axes_grid1', 'dill', 'multiprocess',
23-
'prompt_toolkit', 'prompt_toolkit.token', 'prompt_toolkit.styles',
24-
'prompt_toolkit.validation']
21+
'scipy', 'scipy.stats','numpy', 'numpy.linalg', 'numpy.random',
22+
'matplotlib.pyplot', 'matplotlib','matplotlib.transforms',
23+
'mpl_toolkits.axes_grid1', 'dill', 'multiprocess','prompt_toolkit',
24+
'prompt_toolkit.token', 'prompt_toolkit.styles','prompt_toolkit.validation']
2525
for mod_name in MOCK_MODULES:
2626
sys.modules[mod_name] = mock.Mock()
2727

docs/reference/overview_of_strategies.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ Strategies in the Axelrod's first tournament:
2828
+--------------------------+-------------------------------------------+--------------------------+
2929
| `Shubik`_ | Shubik (author's name) | :code:`Shubik` |
3030
+--------------------------+-------------------------------------------+--------------------------+
31-
| `Stein and Rapoport`_ | Stein and Rapoport (authors' names) | Not Implemented |
31+
| `Stein and Rapoport`_ | Stein and Rapoport (authors' names) | :code:`SteinAndRapoport` |
3232
+--------------------------+-------------------------------------------+--------------------------+
3333
| `Grudger`_ | Grudger (by Friedman) | :code:`Grudger` |
3434
+--------------------------+-------------------------------------------+--------------------------+

docs/tutorials/advanced/classification_of_strategies.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ length of each match of the tournament::
8080
... }
8181
>>> strategies = axl.filtered_strategies(filterset)
8282
>>> len(strategies)
83-
26
83+
27
8484

8585
Note that in the filterset dictionary, the value for the 'makes_use_of' key
8686
must be a list. Here is how we might identify the number of strategies that use

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ matplotlib>=1.4.2
33
hypothesis>=3.2
44
tqdm>=3.4.0
55
prompt-toolkit>=1.0.7
6+
scipy>=0.19.0

0 commit comments

Comments
 (0)