From 4c5f80063e1c971f0e5486fd32787cfa1c312487 Mon Sep 17 00:00:00 2001 From: Marc Harper Date: Mon, 14 Sep 2015 14:27:55 -0700 Subject: [PATCH 01/16] Various reorganization of strategies to eliminate dependency loops. Defines basic_strategies automatically. Handles dependency issues with Meta strategies. --- axelrod/__init__.py | 4 +- axelrod/player.py | 23 +++++ axelrod/strategies/__init__.py | 17 +++- axelrod/strategies/_strategies.py | 98 +++++++++------------ axelrod/strategies/axelrod_tournaments.py | 2 + axelrod/strategies/gobymajority.py | 4 +- axelrod/strategies/hunter.py | 54 +----------- axelrod/strategies/mathematicalconstants.py | 1 + axelrod/strategies/memoryone.py | 2 +- axelrod/strategies/meta.py | 57 +++++++++--- axelrod/strategies/prober.py | 4 + 11 files changed, 142 insertions(+), 124 deletions(-) diff --git a/axelrod/__init__.py b/axelrod/__init__.py index 82ecad3cf..4cff03833 100644 --- a/axelrod/__init__.py +++ b/axelrod/__init__.py @@ -1,13 +1,13 @@ from __future__ import absolute_import +# The order of imports matters! from .random_ import random_choice - from .plot import * from .game import * from .player import * from .mock_player import * from .round_robin import * +from .strategies import * from .tournament import * from .tournament_manager import * -from .strategies import * from .ecosystem import * diff --git a/axelrod/player.py b/axelrod/player.py index 55488b005..20a6495fe 100644 --- a/axelrod/player.py +++ b/axelrod/player.py @@ -6,6 +6,29 @@ flip_dict = {C: D, D: C} +# Strategy classifiers + +def is_basic(s): + """ + Defines criteria for a strategy to be considered 'basic' + """ + stochastic = s.classifier['stochastic'] + depth = s.classifier['memory_depth'] + inspects_source = s.classifier['inspects_source'] + manipulates_source = s.classifier['manipulates_source'] + manipulates_state = s.classifier['manipulates_state'] + return not (stochastic or inspects_source or manipulates_source or manipulates_state) and (depth in (0, 1)) + +def is_cheater(s): + """ + A function to check if a strategy cheats. + """ + classifier = s.classifier + return classifier['inspects_source'] or\ + classifier['manipulates_source'] or\ + classifier['manipulates_state'] + + def update_histories(player1, player2, move1, move2): """Updates histories and cooperation / defections counts following play.""" # Update histories diff --git a/axelrod/strategies/__init__.py b/axelrod/strategies/__init__.py index cf9bbc761..7f265abc4 100644 --- a/axelrod/strategies/__init__.py +++ b/axelrod/strategies/__init__.py @@ -1 +1,16 @@ -from ._strategies import * +from ..player import is_basic, is_cheater +from ._strategies import strategies + +# `from ._strategies import strategies` import the collection `strategies` +# Now import the Meta strategies. This cannot be done in _strategies +# because it creates circular dependencies + +from .meta import MetaMajority, MetaMinority, MetaWinner, MetaHunter +strategies.extend((MetaHunter, MetaMajority, MetaMinority, MetaWinner)) + +# Distinguished strategy collections in addition to +# `strategies` from _strategies.py + +basic_strategies = [s for s in strategies if is_basic(s())] +ordinary_strategies = [s for s in strategies if not is_cheater(s())] +cheating_strategies = [s for s in strategies if is_cheater(s())] diff --git a/axelrod/strategies/_strategies.py b/axelrod/strategies/_strategies.py index 8a44df298..cf7b3bc6d 100644 --- a/axelrod/strategies/_strategies.py +++ b/axelrod/strategies/_strategies.py @@ -1,47 +1,50 @@ from __future__ import absolute_import -from .alternator import * -from .appeaser import * -from .averagecopier import * -from .axelrod_tournaments import * -from .backstabber import * +from .alternator import Alternator +from .appeaser import Appeaser +from .averagecopier import AverageCopier, NiceAverageCopier +from .axelrod_tournaments import ( + Davis, Feld, Shubik, Tullock, Champion, Eatherley, Tester) +from .backstabber import BackStabber, DoubleCrosser from .calculator import Calculator -from .cooperator import * -from .cycler import * -from .darwin import * -from .defector import * -from .forgiver import * -from .geller import * -from .gobymajority import * -from .grudger import * -from .grumpy import * -from .hunter import * -from .inverse import * -from .mathematicalconstants import * -from .memoryone import * -from .meta import * -from .mindcontrol import * -from .mindreader import * -from .oncebitten import * -from .prober import * -from .punisher import * -from .qlearner import * -from .rand import * -from .retaliate import * -from .titfortat import * +from .cooperator import Cooperator, TrickyCooperator +from .cycler import AntiCycler, CyclerCCD, CyclerCCCD, CyclerCCCCCD +from .darwin import Darwin +from .defector import Defector, TrickyDefector +from .forgiver import Forgiver, ForgivingTitForTat +from .geller import Geller, GellerCooperator, GellerDefector +from .gobymajority import ( + GoByMajority, GoByMajority10, GoByMajority20, GoByMajority40, + GoByMajority5) +from .grudger import Grudger, ForgetfulGrudger, OppositeGrudger, Aggravater +from .grumpy import Grumpy +from .hunter import ( + DefectorHunter, CooperatorHunter, AlternatorHunter, MathConstantHunter, + RandomHunter) +from .inverse import Inverse +from .mathematicalconstants import Golden, Pi, e +from .memoryone import ( + WinStayLoseShift,GTFT, StochasticCooperator, StochasticWSLS, ZDGTFT2, + ZDExtort2, Grofman, Joss, SoftJoss ) +from .mindcontrol import MindController, MindWarper, MindBender +from .mindreader import MindReader, ProtectedMindReader +from .oncebitten import OnceBitten, FoolMeOnce, ForgetfulFoolMeOnce, FoolMeForever +from .prober import Prober, Prober2, Prober3, HardProber +from .punisher import Punisher, InversePunisher +from .qlearner import RiskyQLearner, ArrogantQLearner, HesitantQLearner, CautiousQLearner +from .rand import Random +from .retaliate import ( + Retaliate, Retaliate2, Retaliate3, LimitedRetaliate, LimitedRetaliate2, + LimitedRetaliate3) +from .titfortat import ( + TitForTat, TitFor2Tats, TwoTitsForTat, Bully, SneakyTitForTat, + SuspiciousTitForTat, AntiTitForTat, HardTitForTat, HardTitFor2Tats) -# A list of strategies to quickly create a tournament -basic_strategies = [ - Alternator, - Cooperator, - Defector, - Random, - TitForTat, -] # All the strategies in the tournament -strategies = basic_strategies + [ +strategies = [ Aggravater, + Alternator, AlternatorHunter, AntiCycler, AntiTitForTat, @@ -53,12 +56,14 @@ Calculator, CautiousQLearner, Champion, + Cooperator, CooperatorHunter, CyclerCCCCCD, CyclerCCCD, CyclerCCD, Darwin, Davis, + Defector, DefectorHunter, DoubleCrosser, Eatherley, @@ -93,10 +98,6 @@ LimitedRetaliate2, LimitedRetaliate3, MathConstantHunter, - MetaHunter, - MetaMajority, - MetaMinority, - MetaWinner, MindBender, MindController, MindReader, @@ -110,6 +111,7 @@ Prober3, ProtectedMindReader, Punisher, + Random, RandomHunter, Retaliate, Retaliate2, @@ -121,6 +123,7 @@ StochasticWSLS, SuspiciousTitForTat, Tester, + TitForTat, TitFor2Tats, TrickyCooperator, TrickyDefector, @@ -131,16 +134,3 @@ ZDGTFT2, e, ] - - -def is_cheater(s): - """ - A function to check if a strategy cheats. - """ - classifier = s.classifier - return classifier['inspects_source'] or\ - classifier['manipulates_source'] or\ - classifier['manipulates_state'] - -ordinary_strategies = [s for s in strategies if not is_cheater(s)] -cheating_strategies = [s for s in strategies if is_cheater(s)] diff --git a/axelrod/strategies/axelrod_tournaments.py b/axelrod/strategies/axelrod_tournaments.py index 1b46773f6..fe0b923e4 100644 --- a/axelrod/strategies/axelrod_tournaments.py +++ b/axelrod/strategies/axelrod_tournaments.py @@ -9,6 +9,7 @@ flip_dict = {'C': 'D', 'D': 'C'} + ## First Tournament class Davis(Player): @@ -141,6 +142,7 @@ def reset(self): self.retaliation_length = 0 self.retaliation_remaining = 0 + class Tullock(Player): """ Cooperates for the first 11 rounds then randomly cooperates 10% less often diff --git a/axelrod/strategies/gobymajority.py b/axelrod/strategies/gobymajority.py index 15ab4d2c4..b607af881 100644 --- a/axelrod/strategies/gobymajority.py +++ b/axelrod/strategies/gobymajority.py @@ -13,9 +13,9 @@ class GoByMajority(Player): 'stochastic': False, 'inspects_source': False, 'manipulates_source': False, - 'manipulates_state': False + 'manipulates_state': False, + 'memory_depth': 0 # memory_depth may be altered by __init__ } - # memory_depth is set by __init__ def __init__(self, memory_depth=0, soft=True): Player.__init__(self) diff --git a/axelrod/strategies/hunter.py b/axelrod/strategies/hunter.py index 044e2d090..61d88e2be 100644 --- a/axelrod/strategies/hunter.py +++ b/axelrod/strategies/hunter.py @@ -1,7 +1,6 @@ from __future__ import absolute_import from axelrod import Player -from .meta import MetaPlayer class DefectorHunter(Player): @@ -65,16 +64,12 @@ class MathConstantHunter(Player): name = "Math Constant Hunter" classifier = { 'memory_depth': float('inf'), # Long memory + 'stochastic': False, 'inspects_source': False, 'manipulates_source': False, 'manipulates_state': False } - # We need to make sure this is not marked as stochastic. - def __init__(self): - Player.__init__(self) - self.classifier['stochastic'] = False - def strategy(self, opponent): """ Check whether the number of cooperations in the first and second halves @@ -107,16 +102,12 @@ class RandomHunter(Player): name = "Random Hunter" classifier = { 'memory_depth': float('inf'), # Long memory + 'stochastic': False, 'inspects_source': False, 'manipulates_source': False, 'manipulates_state': False } - # We need to make sure this is not marked as stochastic. - def __init__(self): - Player.__init__(self) - self.classifier['stochastic'] = False - def strategy(self, opponent): """ A random player is unpredictable, which means the conditional frequency @@ -138,44 +129,3 @@ def strategy(self, opponent): return 'D' return 'C' - - -class MetaHunter(MetaPlayer): - """A player who uses a selection of hunters.""" - - name = "Meta Hunter" - classifier = { - 'memory_depth': float('inf'), # Long memory - 'inspects_source': False, - 'manipulates_source': False, - 'manipulates_state': False - } - - def __init__(self): - - # We need to make sure this is not marked as stochastic. - self.classifier['stochastic'] = False - - # Notice that we don't include the cooperator hunter, because it leads to excessive - # defection and therefore bad performance against unforgiving strategies. We will stick - # to hunters that use defections as cues. However, a really tangible benefit comes from - # combining Random Hunter and Math Constant Hunter, since together they catch strategies - # that are lightly randomized but still quite constant (the tricky/suspecious ones). - self.team = [DefectorHunter, AlternatorHunter, RandomHunter, MathConstantHunter] - - MetaPlayer.__init__(self) - - @staticmethod - def meta_strategy(results, opponent): - - # If any of the hunters smells prey, then defect! - if 'D' in results: - return 'D' - - # Tit-for-tat might seem like a better default choice, but in many cases it complicates - # the heuristics of hunting and creates fale-positives. So go ahead and use it, but only - # for longer histories. - if len(opponent.history) > 100: - return 'D' if opponent.history[-1:] == ['D'] else 'C' - else: - return 'C' diff --git a/axelrod/strategies/mathematicalconstants.py b/axelrod/strategies/mathematicalconstants.py index d03559bdc..450407624 100644 --- a/axelrod/strategies/mathematicalconstants.py +++ b/axelrod/strategies/mathematicalconstants.py @@ -7,6 +7,7 @@ class CotoDeRatio(Player): defections closer to the ratio as given in a sub class""" classifier = { + 'stochastic': False, 'memory_depth': float('inf'), # Long memory 'inspects_source': False, 'manipulates_source': False, diff --git a/axelrod/strategies/memoryone.py b/axelrod/strategies/memoryone.py index 4c6f87d97..4bc4dabfc 100644 --- a/axelrod/strategies/memoryone.py +++ b/axelrod/strategies/memoryone.py @@ -47,7 +47,7 @@ class MemoryOnePlayer(Player): name = 'Generic Memory One Player' classifier = { 'memory_depth': 1, # Memory-one Four-Vector - 'stochastic': False, + 'stochastic': True, 'inspects_source': False, 'manipulates_source': False, 'manipulates_state': False diff --git a/axelrod/strategies/meta.py b/axelrod/strategies/meta.py index 3922f88ba..a77dba41b 100644 --- a/axelrod/strategies/meta.py +++ b/axelrod/strategies/meta.py @@ -1,6 +1,11 @@ -from axelrod import Player +from axelrod import Player, is_cheater +from ._strategies import strategies +from .hunter import DefectorHunter, AlternatorHunter, RandomHunter, MathConstantHunter +# Needs to be computed manually to prevent circular dependency +ordinary_strategies = [s for s in strategies if not is_cheater(s)] + class MetaPlayer(Player): """A generic player that has its own team of players.""" @@ -51,12 +56,7 @@ class MetaMajority(MetaPlayer): name = "Meta Majority" def __init__(self): - - # We need to import the list of strategies at runtime, since - # _strategies import also _this_ module before defining the list. - from ._strategies import ordinary_strategies self.team = ordinary_strategies - MetaPlayer.__init__(self) def meta_strategy(self, results, opponent): @@ -71,12 +71,7 @@ class MetaMinority(MetaPlayer): name = "Meta Minority" def __init__(self): - - # We need to import the list of strategies at runtime, since - # _strategies import also _this_ module before defining the list. - from ._strategies import ordinary_strategies self.team = ordinary_strategies - MetaPlayer.__init__(self) def meta_strategy(self, results, opponent): @@ -97,7 +92,7 @@ def __init__(self, team=None): if team: self.team = team else: - from ._strategies import ordinary_strategies + # Needs to be computed manually to prevent circular dependency self.team = ordinary_strategies MetaPlayer.__init__(self) @@ -134,3 +129,41 @@ def meta_strategy(self, results, opponent): t.proposed_history.append(r) return bestresult + + +class MetaHunter(MetaPlayer): + """A player who uses a selection of hunters.""" + + name = "Meta Hunter" + classifier = { + 'memory_depth': float('inf'), # Long memory + 'stochastic': False, + 'inspects_source': False, + 'manipulates_source': False, + 'manipulates_state': False + } + + def __init__(self): + # Notice that we don't include the cooperator hunter, because it leads to excessive + # defection and therefore bad performance against unforgiving strategies. We will stick + # to hunters that use defections as cues. However, a really tangible benefit comes from + # combining Random Hunter and Math Constant Hunter, since together they catch strategies + # that are lightly randomized but still quite constant (the tricky/suspecious ones). + self.team = [DefectorHunter, AlternatorHunter, RandomHunter, MathConstantHunter] + + MetaPlayer.__init__(self) + + @staticmethod + def meta_strategy(results, opponent): + + # If any of the hunters smells prey, then defect! + if 'D' in results: + return 'D' + + # Tit-for-tat might seem like a better default choice, but in many cases it complicates + # the heuristics of hunting and creates fale-positives. So go ahead and use it, but only + # for longer histories. + if len(opponent.history) > 100: + return 'D' if opponent.history[-1:] == ['D'] else 'C' + else: + return 'C' diff --git a/axelrod/strategies/prober.py b/axelrod/strategies/prober.py index 2926ef09e..27661a659 100644 --- a/axelrod/strategies/prober.py +++ b/axelrod/strategies/prober.py @@ -9,6 +9,7 @@ class Prober(Player): name = 'Prober' classifier = { + 'stochastic': False, 'memory_depth': float('inf'), # Long memory 'inspects_source': False, 'manipulates_source': False, @@ -39,6 +40,7 @@ class Prober2(Player): name = 'Prober 2' classifier = { + 'stochastic': False, 'memory_depth': float('inf'), # Long memory 'inspects_source': False, 'manipulates_source': False, @@ -69,6 +71,7 @@ class Prober3(Player): name = 'Prober 3' classifier = { + 'stochastic': False, 'memory_depth': float('inf'), # Long memory 'inspects_source': False, 'manipulates_source': False, @@ -97,6 +100,7 @@ class HardProber(Player): name = 'Hard Prober' classifier = { + 'stochastic': False, 'memory_depth': float('inf'), # Long memory 'inspects_source': False, 'manipulates_source': False, From 9ce155c2e135d1260f6f1ed054ae13d542474e52 Mon Sep 17 00:00:00 2001 From: Marc Harper Date: Mon, 14 Sep 2015 14:31:21 -0700 Subject: [PATCH 02/16] Move Grofman and Joss to axelrod_tournament.py --- axelrod/strategies/_strategies.py | 4 +-- axelrod/strategies/axelrod_tournaments.py | 35 +++++++++++++++++++++-- axelrod/strategies/memoryone.py | 31 -------------------- 3 files changed, 35 insertions(+), 35 deletions(-) diff --git a/axelrod/strategies/_strategies.py b/axelrod/strategies/_strategies.py index cf7b3bc6d..5ae66144b 100644 --- a/axelrod/strategies/_strategies.py +++ b/axelrod/strategies/_strategies.py @@ -4,7 +4,7 @@ from .appeaser import Appeaser from .averagecopier import AverageCopier, NiceAverageCopier from .axelrod_tournaments import ( - Davis, Feld, Shubik, Tullock, Champion, Eatherley, Tester) + Davis, Feld, Grofman, Joss, Shubik, Tullock, Champion, Eatherley, Tester) from .backstabber import BackStabber, DoubleCrosser from .calculator import Calculator from .cooperator import Cooperator, TrickyCooperator @@ -25,7 +25,7 @@ from .mathematicalconstants import Golden, Pi, e from .memoryone import ( WinStayLoseShift,GTFT, StochasticCooperator, StochasticWSLS, ZDGTFT2, - ZDExtort2, Grofman, Joss, SoftJoss ) + ZDExtort2, SoftJoss ) from .mindcontrol import MindController, MindWarper, MindBender from .mindreader import MindReader, ProtectedMindReader from .oncebitten import OnceBitten, FoolMeOnce, ForgetfulFoolMeOnce, FoolMeForever diff --git a/axelrod/strategies/axelrod_tournaments.py b/axelrod/strategies/axelrod_tournaments.py index fe0b923e4..c5fbf2bb7 100644 --- a/axelrod/strategies/axelrod_tournaments.py +++ b/axelrod/strategies/axelrod_tournaments.py @@ -2,10 +2,11 @@ Additional strategies from Axelrod's two tournaments. """ -from axelrod import Player - import random +from axelrod import Player +from.memoryone import MemoryOnePlayer + flip_dict = {'C': 'D', 'D': 'C'} @@ -83,6 +84,36 @@ def strategy(self, opponent): return 'D' +class Grofman(MemoryOnePlayer): + """ + Cooperates with probability 2/7. + """ + + name = "Grofman" + + def __init__(self): + p = float(2) / 7 + four_vector = (p, p, p, p) + super(self.__class__, self).__init__(four_vector) + + +class Joss(MemoryOnePlayer): + """ + Cooperates with probability 0.9 when the opponent cooperates, otherwise + emulates Tit-For-Tat. + """ + + name = "Joss" + + def __init__(self, p=0.9): + four_vector = (p, 0, p, 0) + self.p = p + super(self.__class__, self).__init__(four_vector) + + def __repr__(self): + return "%s: %s" % (self.name, round(self.p, 2)) + + class Shubik(Player): """ Plays like Tit-For-Tat with the following modification. After diff --git a/axelrod/strategies/memoryone.py b/axelrod/strategies/memoryone.py index 4bc4dabfc..477c55657 100644 --- a/axelrod/strategies/memoryone.py +++ b/axelrod/strategies/memoryone.py @@ -161,37 +161,6 @@ def __init__(self): ### Strategies for recreating Axelrod's tournament ### - -class Grofman(MemoryOnePlayer): - """ - Cooperates with probability 2/7. - """ - - name = "Grofman" - - def __init__(self): - p = float(2) / 7 - four_vector = (p, p, p, p) - super(self.__class__, self).__init__(four_vector) - - -class Joss(MemoryOnePlayer): - """ - Cooperates with probability 0.9 when the opponent cooperates, otherwise - emulates Tit-For-Tat. - """ - - name = "Joss" - - def __init__(self, p=0.9): - four_vector = (p, 0, p, 0) - self.p = p - super(self.__class__, self).__init__(four_vector) - - def __repr__(self): - return "%s: %s" % (self.name, round(self.p, 2)) - - class SoftJoss(MemoryOnePlayer): """ Defects with probability 0.9 when the opponent defects, otherwise From 0c63922d97f0be8991b7bcded933dbd35860ad62 Mon Sep 17 00:00:00 2001 From: Marc Harper Date: Mon, 14 Sep 2015 14:41:19 -0700 Subject: [PATCH 03/16] Update import of Joss in Calculator; update readme docs for new classifier dictionary; various PEP8 --- README.rst | 8 +++----- axelrod/strategies/_strategies.py | 5 +++-- axelrod/strategies/calculator.py | 2 +- axelrod/strategies/geller.py | 2 ++ axelrod/strategies/memoryone.py | 3 ++- axelrod/strategies/meta.py | 1 + 6 files changed, 12 insertions(+), 9 deletions(-) diff --git a/README.rst b/README.rst index 8a48507f5..a738a3c1b 100644 --- a/README.rst +++ b/README.rst @@ -57,7 +57,7 @@ The documentation includes details of how to setup a tournament but here is an example showing how to create a tournament with all stochastic strategies:: import axelrod - strategies = [s() for s in axelrod.ordinary_strategies if s().stochastic] + strategies = [s() for s in axelrod.ordinary_strategies if s().classifier['stochastic']] tournament = axelrod.Tournament(strategies) results = tournament.play() @@ -67,10 +67,8 @@ The :code:`results` object now contains all the results we could need:: gives:: - ['Inverse', 'Forgetful Fool Me Once', 'Nice Average Copier', 'Champion', - 'Generous Tit-For-Tat', 'Eatherley', 'ZD-GTFT-2', 'Meta Majority', 'Soft Joss', - 'Average Copier', 'Feld', 'Stochastic WSLS', 'Tullock', 'Joss', 'ZD-Extort-2', - 'Grofman', 'Random', 'Meta Winner', 'Meta Minority'] + ['Meta Hunter', 'Inverse', 'Forgetful Fool Me Once', 'GTFT: 0.33', 'Champion', 'ZD-GTFT-2', 'Eatherley', 'Math Constant Hunter', 'Random Hunter', 'Soft Joss: 0.9', 'Meta Majority', 'Nice Average Copier', 'Feld', 'Meta Minority', 'Grofman', 'Stochastic WSLS', 'ZD-Extort-2', 'Tullock', 'Joss: 0.9', 'Arrogant QLearner', 'Average Copier', 'Cautious QLearner', 'Hesitant QLearner', 'Risky QLearner', 'Random: 0.5', 'Meta Winner'] + Results ======= diff --git a/axelrod/strategies/_strategies.py b/axelrod/strategies/_strategies.py index 5ae66144b..4829b7203 100644 --- a/axelrod/strategies/_strategies.py +++ b/axelrod/strategies/_strategies.py @@ -41,7 +41,8 @@ SuspiciousTitForTat, AntiTitForTat, HardTitForTat, HardTitFor2Tats) -# All the strategies in the tournament +# Note: Meta* strategies are handled in .__init__.py + strategies = [ Aggravater, Alternator, @@ -133,4 +134,4 @@ ZDExtort2, ZDGTFT2, e, - ] +] diff --git a/axelrod/strategies/calculator.py b/axelrod/strategies/calculator.py index 8b61f1424..2998f13a8 100644 --- a/axelrod/strategies/calculator.py +++ b/axelrod/strategies/calculator.py @@ -1,7 +1,7 @@ import itertools from axelrod import Player -from .memoryone import Joss +from .axelrod_tournaments import Joss class Calculator(Player): diff --git a/axelrod/strategies/geller.py b/axelrod/strategies/geller.py index e712e310f..0addb51b3 100644 --- a/axelrod/strategies/geller.py +++ b/axelrod/strategies/geller.py @@ -55,6 +55,7 @@ def strategy(self, opponent): else: return opponent.strategy(self) + class GellerCooperator(Geller): """Observes what the payer will do (like :code:`Geller`) but if unable to will cooperate. @@ -69,6 +70,7 @@ class GellerCooperator(Geller): 'manipulates_state': False } + class GellerDefector(Geller): """Observes what the payer will do (like :code:`Geller`) but if unable to will defect. diff --git a/axelrod/strategies/memoryone.py b/axelrod/strategies/memoryone.py index 477c55657..f3e08bb94 100644 --- a/axelrod/strategies/memoryone.py +++ b/axelrod/strategies/memoryone.py @@ -159,7 +159,8 @@ def __init__(self): ZeroDeterminantPlayer.__init__(self, phi=1./9, s=0.5, l=P) -### Strategies for recreating Axelrod's tournament ### +### Strategies for recreating tournaments +# See also Joss in axelrod_tournaments.py class SoftJoss(MemoryOnePlayer): """ diff --git a/axelrod/strategies/meta.py b/axelrod/strategies/meta.py index a77dba41b..5017d72b9 100644 --- a/axelrod/strategies/meta.py +++ b/axelrod/strategies/meta.py @@ -6,6 +6,7 @@ # Needs to be computed manually to prevent circular dependency ordinary_strategies = [s for s in strategies if not is_cheater(s)] + class MetaPlayer(Player): """A generic player that has its own team of players.""" From 784e0c32f68bc9f4ea22071f6e2e20091c1a11de Mon Sep 17 00:00:00 2001 From: Marc Harper Date: Mon, 14 Sep 2015 14:50:44 -0700 Subject: [PATCH 04/16] Update strategies.__init__.py to import all strategies as expected --- axelrod/strategies/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/axelrod/strategies/__init__.py b/axelrod/strategies/__init__.py index 7f265abc4..8d89d4deb 100644 --- a/axelrod/strategies/__init__.py +++ b/axelrod/strategies/__init__.py @@ -1,7 +1,7 @@ from ..player import is_basic, is_cheater -from ._strategies import strategies +from ._strategies import * -# `from ._strategies import strategies` import the collection `strategies` +# `from ._strategies import *` import the collection `strategies` # Now import the Meta strategies. This cannot be done in _strategies # because it creates circular dependencies From bc4cbc4bb8e2451f9a60bd7581ec6758c7cf7ec7 Mon Sep 17 00:00:00 2001 From: Marc Harper Date: Mon, 14 Sep 2015 15:05:09 -0700 Subject: [PATCH 05/16] Tests for strategy lists and classifiers --- axelrod/tests/unit/test_strategies.py | 37 +++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 axelrod/tests/unit/test_strategies.py diff --git a/axelrod/tests/unit/test_strategies.py b/axelrod/tests/unit/test_strategies.py new file mode 100644 index 000000000..b4514d480 --- /dev/null +++ b/axelrod/tests/unit/test_strategies.py @@ -0,0 +1,37 @@ +"""Test for the random strategy.""" + +import unittest + +import axelrod + + +class TestStrategies(unittest.TestCase): + + def test_strategy_list(self): + self.assertTrue(hasattr(axelrod, "strategies")) + self.assertTrue(hasattr(axelrod, "basic_strategies")) + self.assertTrue(hasattr(axelrod, "ordinary_strategies")) + self.assertTrue(hasattr(axelrod, "cheating_strategies")) + + def test_lists_not_empty(self): + self.assertTrue(len(axelrod.strategies) > 0) + self.assertTrue(len(axelrod.basic_strategies) > 0) + self.assertTrue(len(axelrod.ordinary_strategies) > 0) + self.assertTrue(len(axelrod.cheating_strategies) > 0) + + def test_meta_inclusion(self): + self.assertTrue(axelrod.MetaMajority in axelrod.strategies) + + +class TestFilters(unittest.TestCase): + + def test_is_basic(self): + self.assertTrue(axelrod.is_basic(axelrod.Cooperator)) + self.assertTrue(axelrod.is_basic(axelrod.TitForTat)) + self.assertFalse(axelrod.is_basic(axelrod.MindWarper)) + + + def test_is_cheater(self): + self.assertFalse(axelrod.is_cheater(axelrod.Cooperator)) + self.assertFalse(axelrod.is_cheater(axelrod.TitForTat)) + self.assertTrue(axelrod.is_cheater(axelrod.MindWarper)) From d0ae3c06c9b1b0e317f29702fac5effececeb46e Mon Sep 17 00:00:00 2001 From: Marc Harper Date: Mon, 14 Sep 2015 15:19:36 -0700 Subject: [PATCH 06/16] Fix various tests; removed inspection to determine if strategy is stochastic due to inconsistency and favoring explicit over implicit --- axelrod/player.py | 2 - axelrod/strategies/_strategies.py | 2 +- axelrod/strategies/axelrod_tournaments.py | 8 ++-- axelrod/strategies/hunter.py | 2 +- .../tests/unit/test_axelrod_tournaments.py | 45 ++++++++++++++++++- axelrod/tests/unit/test_calculator.py | 2 +- axelrod/tests/unit/test_memoryone.py | 43 ------------------ 7 files changed, 51 insertions(+), 53 deletions(-) diff --git a/axelrod/player.py b/axelrod/player.py index 20a6495fe..294d25020 100644 --- a/axelrod/player.py +++ b/axelrod/player.py @@ -65,8 +65,6 @@ def __init__(self): """Initiates an empty history and 0 score for a player.""" self.history = [] self.classifier = copy.copy(self.classifier) - self.classifier['stochastic'] = ( - "random" in inspect.getsource(self.__class__)) if self.name == "Player": self.classifier['stochastic'] = False for dimension in self.default_classifier: diff --git a/axelrod/strategies/_strategies.py b/axelrod/strategies/_strategies.py index 4829b7203..5c9c3626d 100644 --- a/axelrod/strategies/_strategies.py +++ b/axelrod/strategies/_strategies.py @@ -25,7 +25,7 @@ from .mathematicalconstants import Golden, Pi, e from .memoryone import ( WinStayLoseShift,GTFT, StochasticCooperator, StochasticWSLS, ZDGTFT2, - ZDExtort2, SoftJoss ) + ZDExtort2, SoftJoss, MemoryOnePlayer) from .mindcontrol import MindController, MindWarper, MindBender from .mindreader import MindReader, ProtectedMindReader from .oncebitten import OnceBitten, FoolMeOnce, ForgetfulFoolMeOnce, FoolMeForever diff --git a/axelrod/strategies/axelrod_tournaments.py b/axelrod/strategies/axelrod_tournaments.py index c5fbf2bb7..cbf0409d0 100644 --- a/axelrod/strategies/axelrod_tournaments.py +++ b/axelrod/strategies/axelrod_tournaments.py @@ -49,7 +49,7 @@ class Feld(Player): name = "Feld" classifier = { 'memory_depth': 200, # Varies actually, eventually becomes depth 1 - 'stochastic': False, + 'stochastic': True, 'inspects_source': False, 'manipulates_source': False, 'manipulates_state': False @@ -182,7 +182,7 @@ class Tullock(Player): name = "Tullock" classifier = { 'memory_depth': 11, # long memory, modified by init - 'stochastic': False, + 'stochastic': True, 'inspects_source': False, 'manipulates_source': False, 'manipulates_state': False @@ -216,7 +216,7 @@ class Champion(Player): name = "Champion" classifier = { 'memory_depth': float('inf'), - 'stochastic': False, + 'stochastic': True, 'inspects_source': False, 'manipulates_source': False, 'manipulates_state': False @@ -250,7 +250,7 @@ class Eatherley(Player): name = "Eatherley" classifier = { 'memory_depth': float('inf'), - 'stochastic': False, + 'stochastic': True, 'inspects_source': False, 'manipulates_source': False, 'manipulates_state': False diff --git a/axelrod/strategies/hunter.py b/axelrod/strategies/hunter.py index 61d88e2be..a35c2789e 100644 --- a/axelrod/strategies/hunter.py +++ b/axelrod/strategies/hunter.py @@ -75,7 +75,7 @@ def strategy(self, opponent): Check whether the number of cooperations in the first and second halves of the history are close. The variance of the uniform distribution (1/4) is a reasonable delta but use something lower for certainty and avoiding - false positives. Rhis approach will also detect a lot of random players. + false positives. This approach will also detect a lot of random players. """ n = len(self.history) diff --git a/axelrod/tests/unit/test_axelrod_tournaments.py b/axelrod/tests/unit/test_axelrod_tournaments.py index 21c61efa0..1739384db 100644 --- a/axelrod/tests/unit/test_axelrod_tournaments.py +++ b/axelrod/tests/unit/test_axelrod_tournaments.py @@ -4,7 +4,7 @@ import axelrod -from .test_player import TestPlayer +from .test_player import TestPlayer, test_four_vector C, D = 'C', 'D' @@ -85,6 +85,49 @@ def test_strategy(self): self.responses_test(history_1, history_2, [D, D, D, D], random_seed=50) +class TestGrofman(TestPlayer): + + name = "Grofman" + player = axelrod.Grofman + expected_classifier = { + 'memory_depth': 1, + 'stochastic': True, + 'inspects_source': False, + 'manipulates_source': False, + 'manipulates_state': False + } + + def test_four_vector(self): + p = float(2) / 7 + expected_dictionary = {(C, C): p, (C, D): p, (D, C): p, (D, D): p} + test_four_vector(self, expected_dictionary) + + def test_strategy(self): + self.responses_test([C], [C], [D, D, C], random_seed=2) + self.responses_test([C], [D], [C, D, D]) + + +class TestJoss(TestPlayer): + + name = "Joss: 0.9" + player = axelrod.Joss + expected_classifier = { + 'memory_depth': 1, + 'stochastic': True, + 'inspects_source': False, + 'manipulates_source': False, + 'manipulates_state': False + } + + def test_four_vector(self): + expected_dictionary = {(C, C): 0.9, (C, D): 0, (D, C): 0.9, (D, D): 0} + test_four_vector(self, expected_dictionary) + + def test_strategy(self): + self.responses_test([C], [C], [D], random_seed=2) + self.responses_test([C], [D], [D], random_seed=4) + + class TestShubik(TestPlayer): name = 'Shubik' diff --git a/axelrod/tests/unit/test_calculator.py b/axelrod/tests/unit/test_calculator.py index 5fc6caed5..7598143fc 100644 --- a/axelrod/tests/unit/test_calculator.py +++ b/axelrod/tests/unit/test_calculator.py @@ -15,7 +15,7 @@ class TestCalculator(TestPlayer): player = axelrod.Calculator expected_classifier = { 'memory_depth': float('inf'), - 'stochastic': False, + 'stochastic': True, 'inspects_source': False, 'manipulates_source': False, 'manipulates_state': False diff --git a/axelrod/tests/unit/test_memoryone.py b/axelrod/tests/unit/test_memoryone.py index 01e0844fe..801f24dec 100644 --- a/axelrod/tests/unit/test_memoryone.py +++ b/axelrod/tests/unit/test_memoryone.py @@ -165,49 +165,6 @@ def test_effect_of_strategy(self): self.responses_test([D], [D], [D]) -class TestGrofman(TestPlayer): - - name = "Grofman" - player = axelrod.Grofman - expected_classifier = { - 'memory_depth': 1, - 'stochastic': True, - 'inspects_source': False, - 'manipulates_source': False, - 'manipulates_state': False - } - - def test_four_vector(self): - p = float(2) / 7 - expected_dictionary = {(C, C): p, (C, D): p, (D, C): p, (D, D): p} - test_four_vector(self, expected_dictionary) - - def test_strategy(self): - self.responses_test([C], [C], [D, D, C], random_seed=2) - self.responses_test([C], [D], [C, D, D]) - - -class TestJoss(TestPlayer): - - name = "Joss: 0.9" - player = axelrod.Joss - expected_classifier = { - 'memory_depth': 1, - 'stochastic': True, - 'inspects_source': False, - 'manipulates_source': False, - 'manipulates_state': False - } - - def test_four_vector(self): - expected_dictionary = {(C, C): 0.9, (C, D): 0, (D, C): 0.9, (D, D): 0} - test_four_vector(self, expected_dictionary) - - def test_strategy(self): - self.responses_test([C], [C], [D], random_seed=2) - self.responses_test([C], [D], [D], random_seed=4) - - class TestSoftJoss(TestPlayer): name = "Soft Joss: 0.9" From 63bb40ab447b12700b3b3188c89cce57d5b9f7f1 Mon Sep 17 00:00:00 2001 From: Marc Harper Date: Tue, 15 Sep 2015 08:32:30 -0700 Subject: [PATCH 07/16] Update classification tests for is_basic --- axelrod/strategies/__init__.py | 3 ++ axelrod/tests/unit/test_classification.py | 42 ++++++++++++++++++++++- axelrod/tests/unit/test_strategies.py | 37 -------------------- 3 files changed, 44 insertions(+), 38 deletions(-) delete mode 100644 axelrod/tests/unit/test_strategies.py diff --git a/axelrod/strategies/__init__.py b/axelrod/strategies/__init__.py index 8d89d4deb..859587284 100644 --- a/axelrod/strategies/__init__.py +++ b/axelrod/strategies/__init__.py @@ -14,3 +14,6 @@ basic_strategies = [s for s in strategies if is_basic(s())] ordinary_strategies = [s for s in strategies if not is_cheater(s())] cheating_strategies = [s for s in strategies if is_cheater(s())] + +for x in basic_strategies: + print x \ No newline at end of file diff --git a/axelrod/tests/unit/test_classification.py b/axelrod/tests/unit/test_classification.py index 3084a4fbd..89bb99e87 100644 --- a/axelrod/tests/unit/test_classification.py +++ b/axelrod/tests/unit/test_classification.py @@ -62,8 +62,48 @@ def test_is_cheater(self): axelrod.MindWarper, axelrod.MindReader] + known_basic = [axelrod.Alternator, + axelrod.AntiTitForTat, + axelrod.Bully, + axelrod.Cooperator, + axelrod.CyclerCCCCCD, + axelrod.CyclerCCCD, + axelrod.CyclerCCD, + axelrod.Defector, + axelrod.GoByMajority, + axelrod.SuspiciousTitForTat, + axelrod.TitForTat, + axelrod.WinStayLoseShift] + for strategy in known_cheaters: self.assertTrue(axelrod.is_cheater(strategy), msg=strategy) + self.assertFalse(axelrod.is_basic(strategy), msg=strategy) - for strategy in axelrod.basic_strategies: + for strategy in known_basic: + self.assertTrue(axelrod.is_basic(strategy), msg=strategy) self.assertFalse(axelrod.is_cheater(strategy), msg=strategy) + + + def test_is_basic(self): + pass + + + +class TestStrategies(unittest.TestCase): + + def test_strategy_list(self): + self.assertTrue(hasattr(axelrod, "strategies")) + self.assertTrue(hasattr(axelrod, "basic_strategies")) + self.assertTrue(hasattr(axelrod, "ordinary_strategies")) + self.assertTrue(hasattr(axelrod, "cheating_strategies")) + + def test_lists_not_empty(self): + self.assertTrue(len(axelrod.strategies) > 0) + self.assertTrue(len(axelrod.basic_strategies) > 0) + self.assertTrue(len(axelrod.ordinary_strategies) > 0) + self.assertTrue(len(axelrod.cheating_strategies) > 0) + + def test_meta_inclusion(self): + self.assertTrue(axelrod.MetaMajority in axelrod.strategies) + + diff --git a/axelrod/tests/unit/test_strategies.py b/axelrod/tests/unit/test_strategies.py deleted file mode 100644 index b4514d480..000000000 --- a/axelrod/tests/unit/test_strategies.py +++ /dev/null @@ -1,37 +0,0 @@ -"""Test for the random strategy.""" - -import unittest - -import axelrod - - -class TestStrategies(unittest.TestCase): - - def test_strategy_list(self): - self.assertTrue(hasattr(axelrod, "strategies")) - self.assertTrue(hasattr(axelrod, "basic_strategies")) - self.assertTrue(hasattr(axelrod, "ordinary_strategies")) - self.assertTrue(hasattr(axelrod, "cheating_strategies")) - - def test_lists_not_empty(self): - self.assertTrue(len(axelrod.strategies) > 0) - self.assertTrue(len(axelrod.basic_strategies) > 0) - self.assertTrue(len(axelrod.ordinary_strategies) > 0) - self.assertTrue(len(axelrod.cheating_strategies) > 0) - - def test_meta_inclusion(self): - self.assertTrue(axelrod.MetaMajority in axelrod.strategies) - - -class TestFilters(unittest.TestCase): - - def test_is_basic(self): - self.assertTrue(axelrod.is_basic(axelrod.Cooperator)) - self.assertTrue(axelrod.is_basic(axelrod.TitForTat)) - self.assertFalse(axelrod.is_basic(axelrod.MindWarper)) - - - def test_is_cheater(self): - self.assertFalse(axelrod.is_cheater(axelrod.Cooperator)) - self.assertFalse(axelrod.is_cheater(axelrod.TitForTat)) - self.assertTrue(axelrod.is_cheater(axelrod.MindWarper)) From 009a40b928c42ef1033a768e291f7c34e6837a0d Mon Sep 17 00:00:00 2001 From: Marc Harper Date: Tue, 15 Sep 2015 08:34:11 -0700 Subject: [PATCH 08/16] Remove print statement --- axelrod/strategies/__init__.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/axelrod/strategies/__init__.py b/axelrod/strategies/__init__.py index 859587284..8d89d4deb 100644 --- a/axelrod/strategies/__init__.py +++ b/axelrod/strategies/__init__.py @@ -14,6 +14,3 @@ basic_strategies = [s for s in strategies if is_basic(s())] ordinary_strategies = [s for s in strategies if not is_cheater(s())] cheating_strategies = [s for s in strategies if is_cheater(s())] - -for x in basic_strategies: - print x \ No newline at end of file From 629d25dfda6077d63bed2afa74bf4547e951da36 Mon Sep 17 00:00:00 2001 From: Marc Harper Date: Tue, 15 Sep 2015 08:36:40 -0700 Subject: [PATCH 09/16] Remove empty test --- axelrod/tests/unit/test_classification.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/axelrod/tests/unit/test_classification.py b/axelrod/tests/unit/test_classification.py index 89bb99e87..36c41f79d 100644 --- a/axelrod/tests/unit/test_classification.py +++ b/axelrod/tests/unit/test_classification.py @@ -84,11 +84,6 @@ def test_is_cheater(self): self.assertFalse(axelrod.is_cheater(strategy), msg=strategy) - def test_is_basic(self): - pass - - - class TestStrategies(unittest.TestCase): def test_strategy_list(self): From b592b22d783141e094f6b3bf9948470c60202d57 Mon Sep 17 00:00:00 2001 From: Marc Harper Date: Tue, 15 Sep 2015 08:43:44 -0700 Subject: [PATCH 10/16] More classification testing --- axelrod/tests/unit/test_classification.py | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/axelrod/tests/unit/test_classification.py b/axelrod/tests/unit/test_classification.py index 36c41f79d..bb3ef2e4f 100644 --- a/axelrod/tests/unit/test_classification.py +++ b/axelrod/tests/unit/test_classification.py @@ -75,13 +75,25 @@ def test_is_cheater(self): axelrod.TitForTat, axelrod.WinStayLoseShift] + known_ordinary = [axelrod.AverageCopier, + axelrod.ForgivingTitForTat, + axelrod.GoByMajority20, + axelrod.GTFT, + axelrod.Grudger, + axelrod.Inverse, + axelrod.Random] + for strategy in known_cheaters: - self.assertTrue(axelrod.is_cheater(strategy), msg=strategy) - self.assertFalse(axelrod.is_basic(strategy), msg=strategy) + self.assertTrue(axelrod.is_cheater(strategy()), msg=strategy) + self.assertFalse(axelrod.is_basic(strategy()), msg=strategy) for strategy in known_basic: - self.assertTrue(axelrod.is_basic(strategy), msg=strategy) - self.assertFalse(axelrod.is_cheater(strategy), msg=strategy) + self.assertTrue(axelrod.is_basic(strategy()), msg=strategy) + self.assertFalse(axelrod.is_cheater(strategy()), msg=strategy) + + for strategy in known_ordinary: + self.assertFalse(axelrod.is_basic(strategy()), msg=strategy) + self.assertFalse(axelrod.is_cheater(strategy()), msg=strategy) class TestStrategies(unittest.TestCase): From 156e1ef2472d1254110d33cd99b22c57f8829822 Mon Sep 17 00:00:00 2001 From: Marc Harper Date: Tue, 15 Sep 2015 08:55:36 -0700 Subject: [PATCH 11/16] Update usage.rst docs --- docs/usage.rst | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/docs/usage.rst b/docs/usage.rst index eca85d0da..d490bfd34 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -52,10 +52,18 @@ We can list the so called 'basic strategies' by doing the following:: which gives:: Alternator + Anti Tit For Tat + Bully Cooperator + Cycler CCCCCD + Cycler CCCD + Cycler CCD Defector - Random + Soft Go By Majority + Suspicious Tit For Tat Tit For Tat + Win-Stay Lose-Shift + Before creating a tournament let us add another :code:`Defector` to our strategies:: @@ -71,7 +79,7 @@ To view the player types in our tournament:: which gives:: - [Alternator, Cooperator, Defector, Random, Tit For Tat, Defector] + [Alternator, Anti Tit For Tat, Bully, Cooperator, Cycler CCCCCD, Cycler CCCD, Cycler CCD, Defector, Soft Go By Majority, Suspicious Tit For Tat, Tit For Tat, Win-Stay Lose-Shift, Defector] Now to run the tournament and save the results:: @@ -84,7 +92,8 @@ First, let us view the scores:: which gives:: - [[1.952, 1.943, 1.951, 1.96, 1.924, 1.943, 2.007, 1.966, 2.003, 1.963], [1.221, 1.185, 1.173, 1.218, 1.206, 1.218, 1.221, 1.224, 1.188, 1.221], [2.588, 2.616, 2.608, 2.632, 2.588, 2.624, 2.612, 2.532, 2.588, 2.564], [1.917, 1.896, 1.901, 1.884, 1.931, 1.896, 1.87, 1.912, 1.886, 1.899], [1.967, 1.94, 1.929, 1.934, 1.957, 1.959, 1.948, 1.95, 1.937, 1.955], [2.636, 2.664, 2.632, 2.592, 2.588, 2.644, 2.604, 2.572, 2.612, 2.588]] + [[2.452916666666667, 2.452916666666667, 2.452916666666667, 2.452916666666667, 2.452916666666667, 2.452916666666667, 2.452916666666667, 2.452916666666667, 2.452916666666667, 2.452916666666667], [2.28, 2.28, 2.28, 2.28, 2.28, 2.28, 2.28, 2.28, 2.28, 2.28], [2.7, 2.7, 2.7, 2.7, 2.7, 2.7, 2.7, 2.7, 2.7, 2.7], [1.68875, 1.68875, 1.68875, 1.68875, 1.68875, 1.68875, 1.68875, 1.68875, 1.68875, 1.68875], [1.8416666666666666, 1.8416666666666666, 1.8416666666666666, 1.8416666666666666, 1.8416666666666666, 1.8416666666666666, 1.8416666666666666, 1.8416666666666666, 1.8416666666666666, 1.8416666666666666], [1.9983333333333333, 1.9983333333333333, 1.9983333333333333, 1.9983333333333333, 1.9983333333333333, 1.9983333333333333, 1.9983333333333333, 1.9983333333333333, 1.9983333333333333, 1.9983333333333333], [2.149583333333333, 2.149583333333333, 2.149583333333333, 2.149583333333333, 2.149583333333333, 2.149583333333333, 2.149583333333333, 2.149583333333333, 2.149583333333333, 2.149583333333333], [3.0866666666666664, 3.0866666666666664, 3.0866666666666664, 3.0866666666666664, 3.0866666666666664, 3.0866666666666664, 3.0866666666666664, 3.0866666666666664, 3.0866666666666664, 3.0866666666666664], [2.1995833333333334, 2.1995833333333334, 2.1995833333333334, 2.1995833333333334, 2.1995833333333334, 2.1995833333333334, 2.1995833333333334, 2.1995833333333334, 2.1995833333333334, 2.1995833333333334], [2.2741666666666664, 2.2741666666666664, 2.2741666666666664, 2.2741666666666664, 2.2741666666666664, 2.2741666666666664, 2.2741666666666664, 2.2741666666666664, 2.2741666666666664, 2.2741666666666664], [2.39375, 2.39375, 2.39375, 2.39375, 2.39375, 2.39375, 2.39375, 2.39375, 2.39375, 2.39375], [2.19, 2.19, 2.19, 2.19, 2.19, 2.19, 2.19, 2.19, 2.19, 2.19], [3.0866666666666664, 3.0866666666666664, 3.0866666666666664, 3.0866666666666664, 3.0866666666666664, 3.0866666666666664, 3.0866666666666664, 3.0866666666666664, 3.0866666666666664, 3.0866666666666664]] + We see here that when we ran :code:`tournament.play()` it automatically repeated the round robin tournament 10 times (this is to deal with the stochasticity of the random players). The :code:`normalised_scores` contains a list of normalized scores for all players. @@ -95,7 +104,7 @@ To view a ranking based on median score:: which gives:: - [2, 5, 0, 4, 3, 1] + [7, 12, 2, 0, 10, 1, 9, 8, 11, 6, 5, 4, 3] Finally, to obtain the ranking in a helpful format with all the names:: @@ -103,24 +112,23 @@ Finally, to obtain the ranking in a helpful format with all the names:: which gives:: - ['Defector', 'Defector', 'Alternator', 'Tit For Tat', 'Random', 'Cooperator'] + ['Defector', 'Defector', 'Bully', 'Alternator', 'Tit For Tat', 'Anti Tit For Tat', 'Suspicious Tit For Tat', 'Soft Go By Majority', 'Win-Stay Lose-Shift', 'Cycler CCD', 'Cycler CCCD', 'Cycler CCCCCD', 'Cooperator'] So in this particular instance our two defectors have won. -Let us write a little script that will throw in a new :code:`TitForTat` player until the tit for tat player wins:: +Let us write a little script that will throw in a new :code:`TitForTat` player until the Tit-For-Tat player wins:: - while ranks[0] == 'Defector': + while results.ranked_names[0] == 'Defector': strategies.append(axelrod.TitForTat()) # Adding a new tit for tat player tournament = axelrod.Tournament(strategies) results = tournament.play() - ranks = results.ranked_names Once that has run let us see how many :code:`TitForTat` players were required:: - ranks.count('Tit For Tat') + results.ranked_names.count('Tit For Tat') which gives:: - 3 + 5 We can wrap all this in a function and use it to see how many :code:`TitForTat` are needed to overcome a varying number :code:`Defector`:: @@ -149,7 +157,7 @@ By viewing :code:`t` we actually see that even with 50 :code:`Defector` 3 :code: gives:: - 3 + 6 So even with a large quantity of :code:`Defector` only a small number of :code:`TitForTat` strategies is required. From fc62b11f01257031a94660717c991d4022dce1c9 Mon Sep 17 00:00:00 2001 From: Marc Harper Date: Tue, 15 Sep 2015 13:12:43 -0700 Subject: [PATCH 12/16] Correct memory_depth for cyclers and update docs and tests --- axelrod/player.py | 2 +- axelrod/strategies/cycler.py | 2 +- axelrod/tests/unit/test_classification.py | 3 --- docs/usage.rst | 16 ++++++---------- 4 files changed, 8 insertions(+), 15 deletions(-) diff --git a/axelrod/player.py b/axelrod/player.py index 294d25020..1ae55b799 100644 --- a/axelrod/player.py +++ b/axelrod/player.py @@ -17,7 +17,7 @@ def is_basic(s): inspects_source = s.classifier['inspects_source'] manipulates_source = s.classifier['manipulates_source'] manipulates_state = s.classifier['manipulates_state'] - return not (stochastic or inspects_source or manipulates_source or manipulates_state) and (depth in (0, 1)) + return (not stochastic) and (not inspects_source) and (not manipulates_source) and (not manipulates_state) and (depth in (0, 1)) def is_cheater(s): """ diff --git a/axelrod/strategies/cycler.py b/axelrod/strategies/cycler.py index be268f9e0..d7ac106e5 100644 --- a/axelrod/strategies/cycler.py +++ b/axelrod/strategies/cycler.py @@ -53,7 +53,7 @@ def __init__(self, cycle="CCD"): Player.__init__(self) self.cycle = cycle self.name += " " + cycle - self.memory_depth = len(cycle) + self.classifier['memory_depth'] = len(cycle) def strategy(self, opponent): curent_round = len(self.history) diff --git a/axelrod/tests/unit/test_classification.py b/axelrod/tests/unit/test_classification.py index bb3ef2e4f..78d3be829 100644 --- a/axelrod/tests/unit/test_classification.py +++ b/axelrod/tests/unit/test_classification.py @@ -66,9 +66,6 @@ def test_is_cheater(self): axelrod.AntiTitForTat, axelrod.Bully, axelrod.Cooperator, - axelrod.CyclerCCCCCD, - axelrod.CyclerCCCD, - axelrod.CyclerCCD, axelrod.Defector, axelrod.GoByMajority, axelrod.SuspiciousTitForTat, diff --git a/docs/usage.rst b/docs/usage.rst index d490bfd34..62e7d1180 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -55,16 +55,12 @@ which gives:: Anti Tit For Tat Bully Cooperator - Cycler CCCCCD - Cycler CCCD - Cycler CCD Defector Soft Go By Majority Suspicious Tit For Tat Tit For Tat Win-Stay Lose-Shift - Before creating a tournament let us add another :code:`Defector` to our strategies:: strategies.append(axelrod.Defector()) @@ -79,7 +75,7 @@ To view the player types in our tournament:: which gives:: - [Alternator, Anti Tit For Tat, Bully, Cooperator, Cycler CCCCCD, Cycler CCCD, Cycler CCD, Defector, Soft Go By Majority, Suspicious Tit For Tat, Tit For Tat, Win-Stay Lose-Shift, Defector] + [Alternator, Anti Tit For Tat, Bully, Cooperator, Defector, Soft Go By Majority, Suspicious Tit For Tat, Tit For Tat, Win-Stay Lose-Shift, Defector] Now to run the tournament and save the results:: @@ -92,7 +88,7 @@ First, let us view the scores:: which gives:: - [[2.452916666666667, 2.452916666666667, 2.452916666666667, 2.452916666666667, 2.452916666666667, 2.452916666666667, 2.452916666666667, 2.452916666666667, 2.452916666666667, 2.452916666666667], [2.28, 2.28, 2.28, 2.28, 2.28, 2.28, 2.28, 2.28, 2.28, 2.28], [2.7, 2.7, 2.7, 2.7, 2.7, 2.7, 2.7, 2.7, 2.7, 2.7], [1.68875, 1.68875, 1.68875, 1.68875, 1.68875, 1.68875, 1.68875, 1.68875, 1.68875, 1.68875], [1.8416666666666666, 1.8416666666666666, 1.8416666666666666, 1.8416666666666666, 1.8416666666666666, 1.8416666666666666, 1.8416666666666666, 1.8416666666666666, 1.8416666666666666, 1.8416666666666666], [1.9983333333333333, 1.9983333333333333, 1.9983333333333333, 1.9983333333333333, 1.9983333333333333, 1.9983333333333333, 1.9983333333333333, 1.9983333333333333, 1.9983333333333333, 1.9983333333333333], [2.149583333333333, 2.149583333333333, 2.149583333333333, 2.149583333333333, 2.149583333333333, 2.149583333333333, 2.149583333333333, 2.149583333333333, 2.149583333333333, 2.149583333333333], [3.0866666666666664, 3.0866666666666664, 3.0866666666666664, 3.0866666666666664, 3.0866666666666664, 3.0866666666666664, 3.0866666666666664, 3.0866666666666664, 3.0866666666666664, 3.0866666666666664], [2.1995833333333334, 2.1995833333333334, 2.1995833333333334, 2.1995833333333334, 2.1995833333333334, 2.1995833333333334, 2.1995833333333334, 2.1995833333333334, 2.1995833333333334, 2.1995833333333334], [2.2741666666666664, 2.2741666666666664, 2.2741666666666664, 2.2741666666666664, 2.2741666666666664, 2.2741666666666664, 2.2741666666666664, 2.2741666666666664, 2.2741666666666664, 2.2741666666666664], [2.39375, 2.39375, 2.39375, 2.39375, 2.39375, 2.39375, 2.39375, 2.39375, 2.39375, 2.39375], [2.19, 2.19, 2.19, 2.19, 2.19, 2.19, 2.19, 2.19, 2.19, 2.19], [3.0866666666666664, 3.0866666666666664, 3.0866666666666664, 3.0866666666666664, 3.0866666666666664, 3.0866666666666664, 3.0866666666666664, 3.0866666666666664, 3.0866666666666664, 3.0866666666666664]] + [[2.25, 2.25, 2.25, 2.25, 2.25, 2.25, 2.25, 2.25, 2.25, 2.25], [1.8722222222222222, 1.8722222222222222, 1.8722222222222222, 1.8722222222222222, 1.8722222222222222, 1.8722222222222222, 1.8722222222222222, 1.8722222222222222, 1.8722222222222222, 1.8722222222222222], [2.428888888888889, 2.428888888888889, 2.428888888888889, 2.428888888888889, 2.428888888888889, 2.428888888888889, 2.428888888888889, 2.428888888888889, 2.428888888888889, 2.428888888888889], [1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5], [2.78, 2.78, 2.78, 2.78, 2.78, 2.78, 2.78, 2.78, 2.78, 2.78], [2.181111111111111, 2.181111111111111, 2.181111111111111, 2.181111111111111, 2.181111111111111, 2.181111111111111, 2.181111111111111, 2.181111111111111, 2.181111111111111, 2.181111111111111], [2.1127777777777776, 2.1127777777777776, 2.1127777777777776, 2.1127777777777776, 2.1127777777777776, 2.1127777777777776, 2.1127777777777776, 2.1127777777777776, 2.1127777777777776, 2.1127777777777776], [2.2755555555555556, 2.2755555555555556, 2.2755555555555556, 2.2755555555555556, 2.2755555555555556, 2.2755555555555556, 2.2755555555555556, 2.2755555555555556, 2.2755555555555556, 2.2755555555555556], [1.8794444444444445, 1.8794444444444445, 1.8794444444444445, 1.8794444444444445, 1.8794444444444445, 1.8794444444444445, 1.8794444444444445, 1.8794444444444445, 1.8794444444444445, 1.8794444444444445], [2.78, 2.78, 2.78, 2.78, 2.78, 2.78, 2.78, 2.78, 2.78, 2.78]] We see here that when we ran :code:`tournament.play()` it automatically repeated the round robin tournament 10 times (this is to deal with the stochasticity of the random players). @@ -104,7 +100,7 @@ To view a ranking based on median score:: which gives:: - [7, 12, 2, 0, 10, 1, 9, 8, 11, 6, 5, 4, 3] + [4, 9, 2, 7, 0, 5, 6, 8, 1, 3] Finally, to obtain the ranking in a helpful format with all the names:: @@ -112,7 +108,7 @@ Finally, to obtain the ranking in a helpful format with all the names:: which gives:: - ['Defector', 'Defector', 'Bully', 'Alternator', 'Tit For Tat', 'Anti Tit For Tat', 'Suspicious Tit For Tat', 'Soft Go By Majority', 'Win-Stay Lose-Shift', 'Cycler CCD', 'Cycler CCCD', 'Cycler CCCCCD', 'Cooperator'] + ['Defector', 'Defector', 'Bully', 'Tit For Tat', 'Alternator', 'Soft Go By Majority', 'Suspicious Tit For Tat', 'Win-Stay Lose-Shift', 'Anti Tit For Tat', 'Cooperator'] So in this particular instance our two defectors have won. Let us write a little script that will throw in a new :code:`TitForTat` player until the Tit-For-Tat player wins:: @@ -128,7 +124,7 @@ Once that has run let us see how many :code:`TitForTat` players were required:: which gives:: - 5 + 4 We can wrap all this in a function and use it to see how many :code:`TitForTat` are needed to overcome a varying number :code:`Defector`:: @@ -157,7 +153,7 @@ By viewing :code:`t` we actually see that even with 50 :code:`Defector` 3 :code: gives:: - 6 + 4 So even with a large quantity of :code:`Defector` only a small number of :code:`TitForTat` strategies is required. From 03951a227bfafb0b1017354bdbf3a1247322fc9b Mon Sep 17 00:00:00 2001 From: Marc Harper Date: Tue, 15 Sep 2015 13:17:53 -0700 Subject: [PATCH 13/16] Fix cycler tests --- axelrod/tests/unit/test_cycler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/axelrod/tests/unit/test_cycler.py b/axelrod/tests/unit/test_cycler.py index c9fd5bc23..6594fc67b 100644 --- a/axelrod/tests/unit/test_cycler.py +++ b/axelrod/tests/unit/test_cycler.py @@ -32,7 +32,7 @@ class TestCycler(TestPlayer): name = "Cycler %s" % cycle player = getattr(axelrod, 'Cycler%s' % cycle) expected_classifier = { - 'memory_depth': 1, + 'memory_depth': len(cycle), 'stochastic': False, 'inspects_source': False, 'manipulates_source': False, From ce90eb89c2d14fd90a26687044f0a92210c28b58 Mon Sep 17 00:00:00 2001 From: Marc Harper Date: Tue, 15 Sep 2015 13:19:10 -0700 Subject: [PATCH 14/16] Fix cycler tests (again) --- axelrod/strategies/cycler.py | 2 +- axelrod/tests/unit/test_cycler.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/axelrod/strategies/cycler.py b/axelrod/strategies/cycler.py index d7ac106e5..c06117385 100644 --- a/axelrod/strategies/cycler.py +++ b/axelrod/strategies/cycler.py @@ -53,7 +53,7 @@ def __init__(self, cycle="CCD"): Player.__init__(self) self.cycle = cycle self.name += " " + cycle - self.classifier['memory_depth'] = len(cycle) + self.classifier['memory_depth'] = len(cycle) - 1 def strategy(self, opponent): curent_round = len(self.history) diff --git a/axelrod/tests/unit/test_cycler.py b/axelrod/tests/unit/test_cycler.py index 6594fc67b..b4bd8c1aa 100644 --- a/axelrod/tests/unit/test_cycler.py +++ b/axelrod/tests/unit/test_cycler.py @@ -32,7 +32,7 @@ class TestCycler(TestPlayer): name = "Cycler %s" % cycle player = getattr(axelrod, 'Cycler%s' % cycle) expected_classifier = { - 'memory_depth': len(cycle), + 'memory_depth': len(cycle) - 1, 'stochastic': False, 'inspects_source': False, 'manipulates_source': False, From 30f6cefd12a00ec82ea0689e42a7540c04d55bf9 Mon Sep 17 00:00:00 2001 From: Marc Harper Date: Tue, 15 Sep 2015 13:43:26 -0700 Subject: [PATCH 15/16] Update tournament tests now that basic_strategies is a subset of ordinary_strategies --- axelrod/tests/integration/test_tournament.py | 2 +- axelrod/tests/unit/test_tournament_manager_factory.py | 2 -- axelrod/tournament_manager_factory.py | 2 -- 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/axelrod/tests/integration/test_tournament.py b/axelrod/tests/integration/test_tournament.py index 8692d8356..a43063e1f 100644 --- a/axelrod/tests/integration/test_tournament.py +++ b/axelrod/tests/integration/test_tournament.py @@ -27,7 +27,7 @@ def setUpClass(cls): def test_full_tournament(self): """A test to check that tournament runs with all non cheating strategies.""" - strategies = [strategy() for strategy in axelrod.basic_strategies + axelrod.ordinary_strategies] + strategies = [strategy() for strategy in axelrod.ordinary_strategies] tournament = axelrod.Tournament(name='test', players=strategies, game=self.game, turns=500, repetitions=2) output_of_tournament = tournament.play().results self.assertEqual(type(output_of_tournament), dict) diff --git a/axelrod/tests/unit/test_tournament_manager_factory.py b/axelrod/tests/unit/test_tournament_manager_factory.py index 6f97bc8be..fd084af65 100644 --- a/axelrod/tests/unit/test_tournament_manager_factory.py +++ b/axelrod/tests/unit/test_tournament_manager_factory.py @@ -22,11 +22,9 @@ def setUpClass(cls): cls.expected_basic_strategies = axelrod.basic_strategies cls.expected_strategies = ( - axelrod.basic_strategies + axelrod.ordinary_strategies) cls.expected_cheating_strategies = axelrod.cheating_strategies cls.expected_all_strategies = ( - axelrod.basic_strategies + axelrod.ordinary_strategies + axelrod.cheating_strategies) diff --git a/axelrod/tournament_manager_factory.py b/axelrod/tournament_manager_factory.py index 6731c8567..ab439776a 100644 --- a/axelrod/tournament_manager_factory.py +++ b/axelrod/tournament_manager_factory.py @@ -53,11 +53,9 @@ def _tournaments_dict(exclusions=None): tournaments = OrderedDict([ ('basic_strategies', axelrod.basic_strategies), ('strategies', - axelrod.basic_strategies + axelrod.ordinary_strategies), ('cheating_strategies', axelrod.cheating_strategies), ('all_strategies', - axelrod.basic_strategies + axelrod.ordinary_strategies + axelrod.cheating_strategies) ]) From c031c23ce0d5d2984b6cce60dc32f0c0f3cff3a0 Mon Sep 17 00:00:00 2001 From: Marc Harper Date: Tue, 15 Sep 2015 16:01:42 -0700 Subject: [PATCH 16/16] Revert basic_strategies to a basic list --- axelrod/player.py | 11 ----------- axelrod/strategies/__init__.py | 6 ++++-- axelrod/strategies/_strategies.py | 3 ++- axelrod/tests/unit/test_classification.py | 16 ---------------- docs/usage.rst | 19 ++++++++----------- 5 files changed, 14 insertions(+), 41 deletions(-) diff --git a/axelrod/player.py b/axelrod/player.py index 1ae55b799..49b08e4f2 100644 --- a/axelrod/player.py +++ b/axelrod/player.py @@ -8,17 +8,6 @@ # Strategy classifiers -def is_basic(s): - """ - Defines criteria for a strategy to be considered 'basic' - """ - stochastic = s.classifier['stochastic'] - depth = s.classifier['memory_depth'] - inspects_source = s.classifier['inspects_source'] - manipulates_source = s.classifier['manipulates_source'] - manipulates_state = s.classifier['manipulates_state'] - return (not stochastic) and (not inspects_source) and (not manipulates_source) and (not manipulates_state) and (depth in (0, 1)) - def is_cheater(s): """ A function to check if a strategy cheats. diff --git a/axelrod/strategies/__init__.py b/axelrod/strategies/__init__.py index 8d89d4deb..6deedaf05 100644 --- a/axelrod/strategies/__init__.py +++ b/axelrod/strategies/__init__.py @@ -1,4 +1,4 @@ -from ..player import is_basic, is_cheater +from ..player import is_cheater from ._strategies import * # `from ._strategies import *` import the collection `strategies` @@ -11,6 +11,8 @@ # Distinguished strategy collections in addition to # `strategies` from _strategies.py -basic_strategies = [s for s in strategies if is_basic(s())] ordinary_strategies = [s for s in strategies if not is_cheater(s())] cheating_strategies = [s for s in strategies if is_cheater(s())] + +# Defined by fiat for demo purposes +basic_strategies = [Alternator, Cooperator, Defector, Random, TitForTat] diff --git a/axelrod/strategies/_strategies.py b/axelrod/strategies/_strategies.py index 5c9c3626d..e9409e788 100644 --- a/axelrod/strategies/_strategies.py +++ b/axelrod/strategies/_strategies.py @@ -41,7 +41,8 @@ SuspiciousTitForTat, AntiTitForTat, HardTitForTat, HardTitFor2Tats) -# Note: Meta* strategies are handled in .__init__.py +# Note: Meta* strategies are handled in .__init__.py, so this is not the +# final form of the list strategies = [ Aggravater, diff --git a/axelrod/tests/unit/test_classification.py b/axelrod/tests/unit/test_classification.py index 78d3be829..b6c7baec3 100644 --- a/axelrod/tests/unit/test_classification.py +++ b/axelrod/tests/unit/test_classification.py @@ -62,16 +62,6 @@ def test_is_cheater(self): axelrod.MindWarper, axelrod.MindReader] - known_basic = [axelrod.Alternator, - axelrod.AntiTitForTat, - axelrod.Bully, - axelrod.Cooperator, - axelrod.Defector, - axelrod.GoByMajority, - axelrod.SuspiciousTitForTat, - axelrod.TitForTat, - axelrod.WinStayLoseShift] - known_ordinary = [axelrod.AverageCopier, axelrod.ForgivingTitForTat, axelrod.GoByMajority20, @@ -82,14 +72,8 @@ def test_is_cheater(self): for strategy in known_cheaters: self.assertTrue(axelrod.is_cheater(strategy()), msg=strategy) - self.assertFalse(axelrod.is_basic(strategy()), msg=strategy) - - for strategy in known_basic: - self.assertTrue(axelrod.is_basic(strategy()), msg=strategy) - self.assertFalse(axelrod.is_cheater(strategy()), msg=strategy) for strategy in known_ordinary: - self.assertFalse(axelrod.is_basic(strategy()), msg=strategy) self.assertFalse(axelrod.is_cheater(strategy()), msg=strategy) diff --git a/docs/usage.rst b/docs/usage.rst index 62e7d1180..a34f12a9f 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -52,14 +52,10 @@ We can list the so called 'basic strategies' by doing the following:: which gives:: Alternator - Anti Tit For Tat - Bully Cooperator Defector - Soft Go By Majority - Suspicious Tit For Tat + Random: 0.5 Tit For Tat - Win-Stay Lose-Shift Before creating a tournament let us add another :code:`Defector` to our strategies:: @@ -75,7 +71,7 @@ To view the player types in our tournament:: which gives:: - [Alternator, Anti Tit For Tat, Bully, Cooperator, Defector, Soft Go By Majority, Suspicious Tit For Tat, Tit For Tat, Win-Stay Lose-Shift, Defector] + [Alternator, Cooperator, Defector, Random: 0.5, Tit For Tat, Defector] Now to run the tournament and save the results:: @@ -88,7 +84,7 @@ First, let us view the scores:: which gives:: - [[2.25, 2.25, 2.25, 2.25, 2.25, 2.25, 2.25, 2.25, 2.25, 2.25], [1.8722222222222222, 1.8722222222222222, 1.8722222222222222, 1.8722222222222222, 1.8722222222222222, 1.8722222222222222, 1.8722222222222222, 1.8722222222222222, 1.8722222222222222, 1.8722222222222222], [2.428888888888889, 2.428888888888889, 2.428888888888889, 2.428888888888889, 2.428888888888889, 2.428888888888889, 2.428888888888889, 2.428888888888889, 2.428888888888889, 2.428888888888889], [1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5], [2.78, 2.78, 2.78, 2.78, 2.78, 2.78, 2.78, 2.78, 2.78, 2.78], [2.181111111111111, 2.181111111111111, 2.181111111111111, 2.181111111111111, 2.181111111111111, 2.181111111111111, 2.181111111111111, 2.181111111111111, 2.181111111111111, 2.181111111111111], [2.1127777777777776, 2.1127777777777776, 2.1127777777777776, 2.1127777777777776, 2.1127777777777776, 2.1127777777777776, 2.1127777777777776, 2.1127777777777776, 2.1127777777777776, 2.1127777777777776], [2.2755555555555556, 2.2755555555555556, 2.2755555555555556, 2.2755555555555556, 2.2755555555555556, 2.2755555555555556, 2.2755555555555556, 2.2755555555555556, 2.2755555555555556, 2.2755555555555556], [1.8794444444444445, 1.8794444444444445, 1.8794444444444445, 1.8794444444444445, 1.8794444444444445, 1.8794444444444445, 1.8794444444444445, 1.8794444444444445, 1.8794444444444445, 1.8794444444444445], [2.78, 2.78, 2.78, 2.78, 2.78, 2.78, 2.78, 2.78, 2.78, 2.78]] + [[1.94, 1.927, 1.929, 1.962, 1.961, 1.977, 1.907, 1.948, 1.954, 1.967], [1.2, 1.146, 1.194, 1.215, 1.188, 1.191, 1.203, 1.2, 1.191, 1.179], [2.588, 2.588, 2.608, 2.624, 2.632, 2.596, 2.62, 2.592, 2.612, 2.572], [1.889, 1.976, 1.91, 1.888, 1.899, 1.893, 1.919, 1.926, 1.921, 1.912], [1.927, 1.95, 1.943, 1.944, 1.941, 1.943, 1.942, 1.942, 1.957, 1.951], [2.636, 2.532, 2.628, 2.58, 2.604, 2.648, 2.584, 2.556, 2.584, 2.62]] We see here that when we ran :code:`tournament.play()` it automatically repeated the round robin tournament 10 times (this is to deal with the stochasticity of the random players). @@ -100,7 +96,7 @@ To view a ranking based on median score:: which gives:: - [4, 9, 2, 7, 0, 5, 6, 8, 1, 3] + [2, 5, 0, 4, 3, 1] Finally, to obtain the ranking in a helpful format with all the names:: @@ -108,7 +104,8 @@ Finally, to obtain the ranking in a helpful format with all the names:: which gives:: - ['Defector', 'Defector', 'Bully', 'Tit For Tat', 'Alternator', 'Soft Go By Majority', 'Suspicious Tit For Tat', 'Win-Stay Lose-Shift', 'Anti Tit For Tat', 'Cooperator'] + ['Defector', 'Defector', 'Alternator', 'Tit For Tat', 'Random: 0.5', 'Cooperator'] + So in this particular instance our two defectors have won. Let us write a little script that will throw in a new :code:`TitForTat` player until the Tit-For-Tat player wins:: @@ -124,7 +121,7 @@ Once that has run let us see how many :code:`TitForTat` players were required:: which gives:: - 4 + 3 We can wrap all this in a function and use it to see how many :code:`TitForTat` are needed to overcome a varying number :code:`Defector`:: @@ -153,7 +150,7 @@ By viewing :code:`t` we actually see that even with 50 :code:`Defector` 3 :code: gives:: - 4 + 3 So even with a large quantity of :code:`Defector` only a small number of :code:`TitForTat` strategies is required.