From 2bb0b6cc996714b4115e95758d878eef07a47e39 Mon Sep 17 00:00:00 2001 From: James Campbell Date: Wed, 19 Oct 2016 12:44:06 +0100 Subject: [PATCH 01/25] Creates dual_wrapper - not yet tested --- axelrod/strategy_transformers.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/axelrod/strategy_transformers.py b/axelrod/strategy_transformers.py index 9ea5be8fb..08820288e 100644 --- a/axelrod/strategy_transformers.py +++ b/axelrod/strategy_transformers.py @@ -13,6 +13,7 @@ from .actions import Actions, flip_action from .random_ import random_choice +from mock_player import simulate_play C, D = Actions.C, Actions.D @@ -161,6 +162,20 @@ def flip_wrapper(player, opponent, action): flip_wrapper, name_prefix="Flipped") +def dual_wrapper(player, opponent, action): + """Applies flip_action at the class level.""" + original_history = [flip_action(a) for a in player.history] + opponent_history = opponent.history + simulate_play(player, opponent, h1=original_history, h2=opponent_history) + player.play(opponent) + action = player.history[-1][0] + return flip_action(action) + + +DualTransformer = StrategyTransformerFactory( + dual_wrapper, name_prefix="Dual") + + def noisy_wrapper(player, opponent, action, noise=0.05): """Applies flip_action at the class level.""" r = random.random() From 9071963f10031384cf34ee4f72895caad11bf5f8 Mon Sep 17 00:00:00 2001 From: James Campbell Date: Thu, 20 Oct 2016 21:08:16 +0100 Subject: [PATCH 02/25] Begin work on DualTransformer --- axelrod/strategy_transformers.py | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/axelrod/strategy_transformers.py b/axelrod/strategy_transformers.py index 08820288e..a5faf98f8 100644 --- a/axelrod/strategy_transformers.py +++ b/axelrod/strategy_transformers.py @@ -13,7 +13,7 @@ from .actions import Actions, flip_action from .random_ import random_choice -from mock_player import simulate_play +from axelrod import simulate_play C, D = Actions.C, Actions.D @@ -162,14 +162,20 @@ def flip_wrapper(player, opponent, action): flip_wrapper, name_prefix="Flipped") -def dual_wrapper(player, opponent, action): +def dual_wrapper(player, opp, proposed_action, strategyclass, opponentclass): """Applies flip_action at the class level.""" - original_history = [flip_action(a) for a in player.history] - opponent_history = opponent.history - simulate_play(player, opponent, h1=original_history, h2=opponent_history) - player.play(opponent) - action = player.history[-1][0] - return flip_action(action) + if len(player.history) == 0: + return flip_action(proposed_action) + + orig_hist = [flip_action(a) for a in player.history] + oppo_hist = opp.history + P1 = strategyclass.clone() + P2 = opponentclass.clone() + new_orig_hist, new_oppo_hist = simulate_play(P1, P2, h1=orig_hist, h2=oppo_hist) + P1.history = new_orig_hist + P2.history = new_oppo_hist + P1.play(P2) + return flip_action(P1.history[-1]) DualTransformer = StrategyTransformerFactory( From af03cb434e21d38ccc910650668c533823916048 Mon Sep 17 00:00:00 2001 From: James Campbell Date: Fri, 21 Oct 2016 19:14:23 +0100 Subject: [PATCH 03/25] Create copy of histories to avoid weird issues with history lengths --- axelrod/strategy_transformers.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/axelrod/strategy_transformers.py b/axelrod/strategy_transformers.py index a5faf98f8..e940b2b85 100644 --- a/axelrod/strategy_transformers.py +++ b/axelrod/strategy_transformers.py @@ -168,13 +168,22 @@ def dual_wrapper(player, opp, proposed_action, strategyclass, opponentclass): return flip_action(proposed_action) orig_hist = [flip_action(a) for a in player.history] - oppo_hist = opp.history + oppo_hist = opp.history[:] # prevents weird copy issues + print(orig_hist) + print(oppo_hist) P1 = strategyclass.clone() P2 = opponentclass.clone() new_orig_hist, new_oppo_hist = simulate_play(P1, P2, h1=orig_hist, h2=oppo_hist) + print(new_orig_hist) + print(new_oppo_hist) P1.history = new_orig_hist P2.history = new_oppo_hist + print(P1.history[-1], P2.history[-1]) P1.play(P2) + print(P1.history) + print(P2.history) + print(opp.history) + print("------") return flip_action(P1.history[-1]) From 607d713ba27343727ebb8b154cb6a7141ca0cc55 Mon Sep 17 00:00:00 2001 From: James Campbell Date: Sat, 22 Oct 2016 09:25:15 +0100 Subject: [PATCH 04/25] Remove need to pass through the opponent class for DualTransformer --- axelrod/strategy_transformers.py | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/axelrod/strategy_transformers.py b/axelrod/strategy_transformers.py index e940b2b85..2aa6c3ba0 100644 --- a/axelrod/strategy_transformers.py +++ b/axelrod/strategy_transformers.py @@ -13,7 +13,8 @@ from .actions import Actions, flip_action from .random_ import random_choice -from axelrod import simulate_play +from axelrod import simulate_play, strategies + C, D = Actions.C, Actions.D @@ -151,6 +152,7 @@ def generic_strategy_wrapper(player, opponent, proposed_action, *args, **kwargs) # This example just passes through the proposed_action return proposed_action + IdentityTransformer = StrategyTransformerFactory(generic_strategy_wrapper) @@ -158,32 +160,25 @@ def flip_wrapper(player, opponent, action): """Applies flip_action at the class level.""" return flip_action(action) + FlipTransformer = StrategyTransformerFactory( flip_wrapper, name_prefix="Flipped") -def dual_wrapper(player, opp, proposed_action, strategyclass, opponentclass): +def dual_wrapper(player, opp, proposed_action, strategyclass): """Applies flip_action at the class level.""" if len(player.history) == 0: return flip_action(proposed_action) orig_hist = [flip_action(a) for a in player.history] - oppo_hist = opp.history[:] # prevents weird copy issues - print(orig_hist) - print(oppo_hist) + oppo_hist = opp.history[:] # prevents weird copy issues + # P1 = getattr(axelrod, player.name[5:]).clone() P1 = strategyclass.clone() - P2 = opponentclass.clone() + P2 = opp.clone() new_orig_hist, new_oppo_hist = simulate_play(P1, P2, h1=orig_hist, h2=oppo_hist) - print(new_orig_hist) - print(new_oppo_hist) P1.history = new_orig_hist P2.history = new_oppo_hist - print(P1.history[-1], P2.history[-1]) P1.play(P2) - print(P1.history) - print(P2.history) - print(opp.history) - print("------") return flip_action(P1.history[-1]) From 7ea47bfea8cf433e6687305d84449e61db2bfe82 Mon Sep 17 00:00:00 2001 From: James Campbell Date: Sat, 29 Oct 2016 18:32:18 +0100 Subject: [PATCH 05/25] Change method of implementing dual from Transformer to function --- axelrod/strategy_transformers.py | 47 ++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/axelrod/strategy_transformers.py b/axelrod/strategy_transformers.py index 2aa6c3ba0..8251bf1c9 100644 --- a/axelrod/strategy_transformers.py +++ b/axelrod/strategy_transformers.py @@ -13,7 +13,7 @@ from .actions import Actions, flip_action from .random_ import random_choice -from axelrod import simulate_play, strategies +from axelrod import simulate_play, strategies, Player C, D = Actions.C, Actions.D @@ -110,7 +110,8 @@ def strategy(self, opponent): { "name": name, "strategy": strategy, - "__module__": PlayerClass.__module__ + "__module__": PlayerClass.__module__, + "original_class": PlayerClass }) return new_class return Decorator @@ -165,25 +166,7 @@ def flip_wrapper(player, opponent, action): flip_wrapper, name_prefix="Flipped") -def dual_wrapper(player, opp, proposed_action, strategyclass): - """Applies flip_action at the class level.""" - if len(player.history) == 0: - return flip_action(proposed_action) - - orig_hist = [flip_action(a) for a in player.history] - oppo_hist = opp.history[:] # prevents weird copy issues - # P1 = getattr(axelrod, player.name[5:]).clone() - P1 = strategyclass.clone() - P2 = opp.clone() - new_orig_hist, new_oppo_hist = simulate_play(P1, P2, h1=orig_hist, h2=oppo_hist) - P1.history = new_orig_hist - P2.history = new_oppo_hist - P1.play(P2) - return flip_action(P1.history[-1]) - -DualTransformer = StrategyTransformerFactory( - dual_wrapper, name_prefix="Dual") def noisy_wrapper(player, opponent, action, noise=0.05): @@ -379,3 +362,27 @@ def __call__(self, player, opponent, action): RetaliateUntilApologyTransformer = StrategyTransformerFactory( RetaliationUntilApologyWrapper(), name_prefix="RUA") + + +def dual_strategy(dual_player, opponent): + from copy import deepcopy + fresh_opponent = deepcopy(opponent) + dual_player.original.play(fresh_opponent) + return flip_action(dual_player.original.history[-1]) + + +def dual(player): + """Magic""" + from types import MethodType + class Dual(Player): + __name__ = "Dual " + player.__class__.__name__ + name = "Dual " + player.name + classifier = player.__class__.classifier + + dual_player = player.clone() + dual_player.original = player.clone() + dual_player.__class__ = Dual + + dual_player.strategy = MethodType(dual_strategy, dual_player) + return dual_player + From 8db32eb40004392e5853a033127f10adf7644373 Mon Sep 17 00:00:00 2001 From: James Campbell Date: Sat, 29 Oct 2016 19:14:10 +0100 Subject: [PATCH 06/25] Remove the need for Dual class --- axelrod/strategy_transformers.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/axelrod/strategy_transformers.py b/axelrod/strategy_transformers.py index 8251bf1c9..e939d52c4 100644 --- a/axelrod/strategy_transformers.py +++ b/axelrod/strategy_transformers.py @@ -374,14 +374,11 @@ def dual_strategy(dual_player, opponent): def dual(player): """Magic""" from types import MethodType - class Dual(Player): - __name__ = "Dual " + player.__class__.__name__ - name = "Dual " + player.name - classifier = player.__class__.classifier + player.__class__.__name__ = "Dual " + player.__class__.__name__ + player.name = "Dual " + player.name dual_player = player.clone() dual_player.original = player.clone() - dual_player.__class__ = Dual dual_player.strategy = MethodType(dual_strategy, dual_player) return dual_player From a07f1ae0942bebce2e52d1d8cb7465afbf77889a Mon Sep 17 00:00:00 2001 From: James Campbell Date: Sun, 30 Oct 2016 10:43:09 +0000 Subject: [PATCH 07/25] Move dual_strategy to be a Player method --- axelrod/player.py | 6 ++++++ axelrod/strategy_transformers.py | 11 +---------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/axelrod/player.py b/axelrod/player.py index f8ab83065..732663a14 100644 --- a/axelrod/player.py +++ b/axelrod/player.py @@ -134,6 +134,12 @@ def strategy(self, opponent): """This is a placeholder strategy.""" raise NotImplementedError() + def dual_strategy(self, opponent): + from copy import deepcopy + fresh_opponent = deepcopy(opponent) + self.original.play(fresh_opponent) + return flip_action(self.original.history[-1]) + def play(self, opponent, noise=0): """This pits two players against each other.""" s1, s2 = self.strategy(opponent), opponent.strategy(self) diff --git a/axelrod/strategy_transformers.py b/axelrod/strategy_transformers.py index e939d52c4..1c7fc54fe 100644 --- a/axelrod/strategy_transformers.py +++ b/axelrod/strategy_transformers.py @@ -364,22 +364,13 @@ def __call__(self, player, opponent, action): RetaliationUntilApologyWrapper(), name_prefix="RUA") -def dual_strategy(dual_player, opponent): - from copy import deepcopy - fresh_opponent = deepcopy(opponent) - dual_player.original.play(fresh_opponent) - return flip_action(dual_player.original.history[-1]) - - def dual(player): """Magic""" - from types import MethodType player.__class__.__name__ = "Dual " + player.__class__.__name__ player.name = "Dual " + player.name dual_player = player.clone() dual_player.original = player.clone() - dual_player.strategy = MethodType(dual_strategy, dual_player) + dual_player.strategy = dual_player.dual_strategy return dual_player - From f31e08343520b4fecf69d5aeeefc4f63021e49c1 Mon Sep 17 00:00:00 2001 From: Vince Knight Date: Mon, 31 Oct 2016 14:55:28 +0000 Subject: [PATCH 08/25] Adding a dual? --- axelrod/strategy_transformers.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/axelrod/strategy_transformers.py b/axelrod/strategy_transformers.py index 9ea5be8fb..36ab4d7be 100644 --- a/axelrod/strategy_transformers.py +++ b/axelrod/strategy_transformers.py @@ -9,6 +9,7 @@ import inspect import random import collections +import copy from numpy.random import choice from .actions import Actions, flip_action @@ -107,6 +108,7 @@ def strategy(self, opponent): new_class_name, (PlayerClass,), { "name": name, + "original_class": PlayerClass, "strategy": strategy, "__module__": PlayerClass.__module__ }) @@ -354,3 +356,19 @@ def __call__(self, player, opponent, action): RetaliateUntilApologyTransformer = StrategyTransformerFactory( RetaliationUntilApologyWrapper(), name_prefix="RUA") + + +def dual_wrapper(player, opponent, proposed_action): + """ + Dual + """ + if len(player.history) == 0: + player.original_player = player.original_class(*player.init_args) + + action = player.original_player.strategy(opponent) + player.original_player.history.append(action) + + return flip_action(action) + +DualTransformer = StrategyTransformerFactory( + dual_wrapper, name_prefix="Dual") From 3bab487c694e20cfc3a3005c70e41a1a4bd9a4c0 Mon Sep 17 00:00:00 2001 From: Vince Knight Date: Thu, 3 Nov 2016 08:15:06 +0000 Subject: [PATCH 09/25] Fix very minor rst syntax in Moran process docs. --- docs/tutorials/getting_started/moran.rst | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/tutorials/getting_started/moran.rst b/docs/tutorials/getting_started/moran.rst index 74536719f..36e44ccd5 100644 --- a/docs/tutorials/getting_started/moran.rst +++ b/docs/tutorials/getting_started/moran.rst @@ -56,9 +56,10 @@ The scores in each round:: [3.04, 3.04, 3.04, 2.97]] -The `MoranProcess` class also accepts an argument for a mutation rate. Nonzero mutation changes the Markov process so -that it no longer has absorbing states, and will iterate forever. To prevent this, iterate with a loop (or function -like `takewhile` from `itertools`): +The :code:`MoranProcess` class also accepts an argument for a mutation rate. +Nonzero mutation changes the Markov process so that it no longer has absorbing +states, and will iterate forever. To prevent this, iterate with a loop (or +function like :code:`takewhile` from :code:`itertools`): >>> import axelrod as axl >>> axl.seed(4) # for reproducible example From 0f5b0aff092d1634cd91d1d2a60d3e2ea74622d6 Mon Sep 17 00:00:00 2001 From: Marc Harper Date: Mon, 31 Oct 2016 22:00:30 -0700 Subject: [PATCH 10/25] Meta Winner Ensemble strategy --- axelrod/strategies/__init__.py | 5 ++-- axelrod/strategies/meta.py | 28 ++++++++++++++++++- axelrod/tests/unit/test_meta.py | 21 ++++++++++++++ .../advanced/classification_of_strategies.rst | 2 +- 4 files changed, 52 insertions(+), 4 deletions(-) diff --git a/axelrod/strategies/__init__.py b/axelrod/strategies/__init__.py index 831692172..bdd48c470 100644 --- a/axelrod/strategies/__init__.py +++ b/axelrod/strategies/__init__.py @@ -10,13 +10,14 @@ MetaPlayer, MetaMajority, MetaMinority, MetaWinner, MetaHunter, MetaMajorityMemoryOne, MetaWinnerMemoryOne, MetaMajorityFiniteMemory, MetaWinnerFiniteMemory, MetaMajorityLongMemory, MetaWinnerLongMemory, - MetaMixer + MetaMixer, MetaWinnerEnsemble ) all_strategies.extend([MetaHunter, MetaMajority, MetaMinority, MetaWinner, MetaMajorityMemoryOne, MetaWinnerMemoryOne, MetaMajorityFiniteMemory, MetaWinnerFiniteMemory, - MetaMajorityLongMemory, MetaWinnerLongMemory, MetaMixer]) + MetaMajorityLongMemory, MetaWinnerLongMemory, MetaMixer, + MetaWinnerEnsemble]) # Distinguished strategy collections in addition to diff --git a/axelrod/strategies/meta.py b/axelrod/strategies/meta.py index 7c7150a53..5a36b2965 100644 --- a/axelrod/strategies/meta.py +++ b/axelrod/strategies/meta.py @@ -1,4 +1,4 @@ -from axelrod import Actions, Player, obey_axelrod +from axelrod import Actions, Player, obey_axelrod, random_choice from ._strategies import all_strategies from .hunter import ( DefectorHunter, AlternatorHunter, RandomHunter, MathConstantHunter, @@ -165,6 +165,32 @@ def meta_strategy(self, results, opponent): return bestresult +class MetaWinnerEnsemble(MetaWinner): + """A variant of MetaWinner that chooses one of the top scoring strategies at random against each opponent.""" + + name = "Meta Winner Ensemble" + + def meta_strategy(self, results, opponent): + # Sort by score + scores = [(pl.score, i) for (i, pl) in enumerate(self.team)] + # Choose one of the best scorers at random + scores.sort(reverse=True) + prop = max(1, int(len(scores) * 0.08)) + index = choice([i for (s, i) in scores[:prop]]) + + # Update each player's proposed history with his proposed result, but + # always after the new result has been settled based on scores + # accumulated until now. + for r, t in zip(results, self.team): + t.proposed_history.append(r) + + if opponent.defections == 0: + # Don't poke the bear + return C + + # return result + return results[index] + class MetaHunter(MetaPlayer): """A player who uses a selection of hunters.""" diff --git a/axelrod/tests/unit/test_meta.py b/axelrod/tests/unit/test_meta.py index 2067a4083..fccbf0dcd 100644 --- a/axelrod/tests/unit/test_meta.py +++ b/axelrod/tests/unit/test_meta.py @@ -185,6 +185,27 @@ def test_strategy(self): self.assertEqual(player.history[-1], D) +class TestMetaWinnerEnsemble(TestMetaPlayer): + name = "Meta Winner Ensemble" + player = axelrod.MetaWinnerEnsemble + expected_classifier = { + 'memory_depth': float('inf'), # Long memory + 'stochastic': True, + 'makes_use_of': set(['game']), + 'long_run_time': True, + 'inspects_source': False, + 'manipulates_source': False, + 'manipulates_state': False + } + + expected_class_classifier = copy.copy(expected_classifier) + expected_class_classifier['stochastic'] = False + expected_class_classifier['makes_use_of'] = set([]) + + def test_strategy(self): + self.first_play_test(C) + + class TestMetaHunter(TestMetaPlayer): name = "Meta Hunter" diff --git a/docs/tutorials/advanced/classification_of_strategies.rst b/docs/tutorials/advanced/classification_of_strategies.rst index de2628b32..cad2ebe73 100644 --- a/docs/tutorials/advanced/classification_of_strategies.rst +++ b/docs/tutorials/advanced/classification_of_strategies.rst @@ -101,7 +101,7 @@ Some strategies have been classified as having a particularly long run time:: ... } >>> strategies = axl.filtered_strategies(filterset) >>> len(strategies) - 10 + 11 Strategies that :code:`manipulate_source`, :code:`manipulate_state` and/or :code:`inspect_source` return :code:`False` for the :code:`obey_axelrod` From c04bdb39f7a91bb0018209356304a5e71de2352d Mon Sep 17 00:00:00 2001 From: James Campbell Date: Wed, 2 Nov 2016 10:09:48 +0000 Subject: [PATCH 11/25] Create Joss-Ann Transformer --- axelrod/strategy_transformers.py | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/axelrod/strategy_transformers.py b/axelrod/strategy_transformers.py index 1a5c4d2f3..ab7347fac 100644 --- a/axelrod/strategy_transformers.py +++ b/axelrod/strategy_transformers.py @@ -13,7 +13,6 @@ from .actions import Actions, flip_action from .random_ import random_choice -from axelrod import simulate_play, strategies, C, D = Actions.C, Actions.D @@ -342,6 +341,33 @@ def mixed_wrapper(player, opponent, action, probability, m_player): MixedTransformer = StrategyTransformerFactory( mixed_wrapper, name_prefix="Mutated") + +def joss_ann_wrapper(player, opponent, proposed_action, probability): + """ + The Joss-Ann of a strategy is a new strategy which has a probability of + choosing the move C, a probability of choosing the move D, and otherwise + uses the response appropriate to the original strategy. + + Parameters + ---------- + + probability: a tuple or list representing a probability distribution of + playing move C or D (doesn't have to be complete) ie. (0, 1) or + (0.2, 0.3) + """ + if sum(probability) > 1: + probability[:] = [i / sum(probability) for i in probability] + + remaining_probability = max(0, 1 - probability[0] - probability[1]) + probability += (remaining_probability,) + options = [C, D, proposed_action] + action = choice(options, p=probability) + return action + + +JossAnnTransformer = StrategyTransformerFactory(joss_ann_wrapper, + name_prefix="Joss-Ann") + # Strategy wrappers as classes From fdd7e3fa7eb040fa0b729582b817d67ccc5eb11a Mon Sep 17 00:00:00 2001 From: James Campbell Date: Sat, 5 Nov 2016 10:48:37 +0000 Subject: [PATCH 12/25] Add full documentation to Dual and Joss-Ann transformers --- axelrod/strategy_transformers.py | 34 ++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/axelrod/strategy_transformers.py b/axelrod/strategy_transformers.py index ab7347fac..f72cd6321 100644 --- a/axelrod/strategy_transformers.py +++ b/axelrod/strategy_transformers.py @@ -167,8 +167,21 @@ def flip_wrapper(player, opponent, action): def dual_wrapper(player, opponent, proposed_action): - """ - Dual + """Wraps the players strategy function to produce the Dual. + + The Dual of a strategy will return the exact opposite set of moves to the + original strategy when both are faced with the same history. + + Parameters + ---------- + player: Player object or subclass (self) + opponent: Player object or subclass + proposed_action: axelrod.Action, C or D + The proposed action by the wrapped strategy + + Returns + ------- + action: an axelrod.Action, C or D """ if not player.history: player.original_player = player.original_class(*player.init_args) @@ -343,7 +356,8 @@ def mixed_wrapper(player, opponent, action, probability, m_player): def joss_ann_wrapper(player, opponent, proposed_action, probability): - """ + """Wraps the players strategy function to produce the Joss-Ann. + The Joss-Ann of a strategy is a new strategy which has a probability of choosing the move C, a probability of choosing the move D, and otherwise uses the response appropriate to the original strategy. @@ -351,9 +365,17 @@ def joss_ann_wrapper(player, opponent, proposed_action, probability): Parameters ---------- - probability: a tuple or list representing a probability distribution of - playing move C or D (doesn't have to be complete) ie. (0, 1) or - (0.2, 0.3) + player: Player object or subclass (self) + opponent: Player object or subclass + proposed_action: axelrod.Action, C or D + The proposed action by the wrapped strategy + probability: tuple + a tuple or list representing a probability distribution of playing move + C or D (doesn't have to be complete) ie. (0, 1) or (0.2, 0.3) + + Returns + ------- + action: an axelrod.Action, C or D """ if sum(probability) > 1: probability[:] = [i / sum(probability) for i in probability] From 9db6388b9be48f1450938cda126cb76857c57b6d Mon Sep 17 00:00:00 2001 From: Marc Harper Date: Sat, 5 Nov 2016 09:25:56 -0700 Subject: [PATCH 13/25] MWE tests and naming --- axelrod/strategies/meta.py | 17 +++++++++++++---- axelrod/tests/unit/test_meta.py | 16 +++++++++++++--- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/axelrod/strategies/meta.py b/axelrod/strategies/meta.py index 5a36b2965..de80888ef 100644 --- a/axelrod/strategies/meta.py +++ b/axelrod/strategies/meta.py @@ -54,7 +54,8 @@ def strategy(self, opponent): # Get the results of all our players. results = [player.strategy(opponent) for player in self.team] - # A subclass should just define a way to choose the result based on team results. + # A subclass should just define a way to choose the result based on + # team results. return self.meta_strategy(results, opponent) def meta_strategy(self, results, opponent): @@ -137,7 +138,8 @@ def __init__(self, team=None): t.score = 0 def strategy(self, opponent): - # Update the running score for each player, before determining the next move. + # Update the running score for each player, before determining the + # next move. if len(self.history): for player in self.team: game = self.match_attributes["game"] @@ -149,7 +151,8 @@ def strategy(self, opponent): def meta_strategy(self, results, opponent): scores = [pl.score for pl in self.team] bestscore = max(scores) - beststrategies = [i for i, pl in enumerate(self.team) if pl.score == bestscore] + beststrategies = [i for (i, pl) in enumerate(self.team) + if pl.score == bestscore] bestproposals = [results[i] for i in beststrategies] bestresult = C if C in bestproposals else D @@ -166,7 +169,13 @@ def meta_strategy(self, results, opponent): return bestresult class MetaWinnerEnsemble(MetaWinner): - """A variant of MetaWinner that chooses one of the top scoring strategies at random against each opponent.""" + """A variant of MetaWinner that chooses one of the top scoring strategies + at random against each opponent. + + Names: + + Meta Winner Ensemble: Original name by Marc Harper + """ name = "Meta Winner Ensemble" diff --git a/axelrod/tests/unit/test_meta.py b/axelrod/tests/unit/test_meta.py index fccbf0dcd..f1c0cec22 100644 --- a/axelrod/tests/unit/test_meta.py +++ b/axelrod/tests/unit/test_meta.py @@ -5,7 +5,7 @@ import axelrod import copy -from .test_player import TestPlayer +from .test_player import TestPlayer, test_responses C, D = axelrod.Actions.C, axelrod.Actions.D @@ -153,7 +153,8 @@ def test_strategy(self): P1 = axelrod.MetaWinner(team=[axelrod.Cooperator, axelrod.Defector]) P2 = axelrod.Player() - # This meta player will simply choose the strategy with the highest current score. + # This meta player will simply choose the strategy with the highest + # current score. P1.team[0].score = 0 P1.team[1].score = 1 self.assertEqual(P1.strategy(P2), C) @@ -205,6 +206,14 @@ class TestMetaWinnerEnsemble(TestMetaPlayer): def test_strategy(self): self.first_play_test(C) + P1 = axelrod.MetaWinner(team=[axelrod.Cooperator, axelrod.Defector]) + P2 = axelrod.Cooperator() + test_responses(self, P1, P2, [C] * 4, [C] * 4, [C] * 4) + + P1 = axelrod.MetaWinner(team=[axelrod.Cooperator, axelrod.Defector]) + P2 = axelrod.Defector() + test_responses(self, P1, P2, [C] * 4, [D] * 4, [D] * 4) + class TestMetaHunter(TestMetaPlayer): @@ -222,7 +231,8 @@ class TestMetaHunter(TestMetaPlayer): def test_strategy(self): self.first_play_test(C) - # We are not using the Cooperator Hunter here, so this should lead to cooperation. + # We are not using the Cooperator Hunter here, so this should lead to + # cooperation. self.responses_test([C, C, C, C], [C, C, C, C], [C]) # After long histories tit-for-tat should come into play. From 2dd113b50b7f7d68b14353bf9af67b2b0095d1da Mon Sep 17 00:00:00 2001 From: Marc Harper Date: Sat, 5 Nov 2016 10:04:05 -0700 Subject: [PATCH 14/25] Fix classification tests --- axelrod/tests/unit/test_classification.py | 30 ++++++++++++++++------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/axelrod/tests/unit/test_classification.py b/axelrod/tests/unit/test_classification.py index 110fbaa57..88d508226 100644 --- a/axelrod/tests/unit/test_classification.py +++ b/axelrod/tests/unit/test_classification.py @@ -129,6 +129,11 @@ def test_is_basic(self): self.assertFalse(axelrod.is_basic(strategy()), msg=strategy) +def str_reps(xs): + """Maps a collection of player classes to their string representations.""" + return set(map(str, [x() for x in xs])) + + class TestStrategies(unittest.TestCase): def test_strategy_list(self): @@ -159,18 +164,21 @@ def test_inclusion_of_strategy_lists(self): axelrod.strategies, axelrod.ordinary_strategies, axelrod.cheating_strategies]: - self.assertTrue(set(strategy_list).issubset(all_strategies_set)) + self.assertTrue(str_reps(strategy_list).issubset( + str_reps(all_strategies_set))) strategies_set = set(axelrod.strategies) for strategy_list in [axelrod.demo_strategies, axelrod.basic_strategies, axelrod.long_run_time_strategies]: - self.assertTrue(set(strategy_list).issubset(strategies_set)) + self.assertTrue(str_reps(strategy_list).issubset( + str_reps(strategies_set))) def test_long_run_strategies(self): long_run_time_strategies = [axelrod.MetaMajority, axelrod.MetaMinority, axelrod.MetaWinner, + axelrod.MetaWinnerEnsemble, axelrod.MetaMajorityMemoryOne, axelrod.MetaWinnerMemoryOne, axelrod.MetaMajorityFiniteMemory, @@ -178,15 +186,18 @@ def test_long_run_strategies(self): axelrod.MetaMajorityLongMemory, axelrod.MetaWinnerLongMemory, axelrod.MetaMixer] - self.assertTrue(long_run_time_strategies, - axelrod.long_run_time_strategies) + + self.assertEqual(str_reps(long_run_time_strategies), + str_reps(axelrod.long_run_time_strategies)) def test_meta_inclusion(self): - self.assertTrue(axelrod.MetaMajority in axelrod.strategies) + self.assertTrue(str(axelrod.MetaMajority()) in + str_reps(axelrod.strategies)) - self.assertTrue(axelrod.MetaHunter in axelrod.strategies) - self.assertFalse( - axelrod.MetaHunter in axelrod.long_run_time_strategies) + self.assertTrue(str(axelrod.MetaHunter()) in + str_reps(axelrod.strategies)) + self.assertFalse(str(axelrod.MetaHunter()) in + str_reps(axelrod.long_run_time_strategies)) def test_demo_strategies(self): demo_strategies = [axelrod.Cooperator, @@ -194,4 +205,5 @@ def test_demo_strategies(self): axelrod.TitForTat, axelrod.Grudger, axelrod.Random] - self.assertTrue(demo_strategies, axelrod.demo_strategies) + self.assertTrue(str_reps(demo_strategies), + str_reps(axelrod.demo_strategies)) From 0774642465e3d325ed91bd2967f124900343bd32 Mon Sep 17 00:00:00 2001 From: James Campbell Date: Sun, 6 Nov 2016 15:07:28 +0000 Subject: [PATCH 15/25] Add tests for DualTransformer --- axelrod/strategy_transformers.py | 7 ++- .../tests/unit/test_strategy_transformers.py | 60 ++++++++++++++++++- 2 files changed, 62 insertions(+), 5 deletions(-) diff --git a/axelrod/strategy_transformers.py b/axelrod/strategy_transformers.py index f72cd6321..d3335df54 100644 --- a/axelrod/strategy_transformers.py +++ b/axelrod/strategy_transformers.py @@ -11,6 +11,7 @@ import collections from numpy.random import choice + from .actions import Actions, flip_action from .random_ import random_choice @@ -111,7 +112,6 @@ def strategy(self, opponent): "original_class": PlayerClass, "strategy": strategy, "__module__": PlayerClass.__module__, - "original_class": PlayerClass }) return new_class return Decorator @@ -387,8 +387,9 @@ def joss_ann_wrapper(player, opponent, proposed_action, probability): return action -JossAnnTransformer = StrategyTransformerFactory(joss_ann_wrapper, - name_prefix="Joss-Ann") +JossAnnTransformer = StrategyTransformerFactory( + joss_ann_wrapper, name_prefix="Joss-Ann") + # Strategy wrappers as classes diff --git a/axelrod/tests/unit/test_strategy_transformers.py b/axelrod/tests/unit/test_strategy_transformers.py index d82a95057..10a1020ef 100644 --- a/axelrod/tests/unit/test_strategy_transformers.py +++ b/axelrod/tests/unit/test_strategy_transformers.py @@ -4,11 +4,13 @@ import axelrod from axelrod import simulate_play from axelrod.strategy_transformers import * +from axelrod.actions import flip_action from .test_titfortat import TestTitForTat from .test_cooperator import TestCooperator C, D = axelrod.Actions.C, axelrod.Actions.D + @IdentityTransformer class TestClass(object): name = 'Test Class' @@ -45,7 +47,7 @@ def test_cloning(self): """Tests that Player.clone preserves the application of transformations. """ p1 = axelrod.Cooperator() - p2 = FlipTransformer()(axelrod.Cooperator)() # Defector + p2 = FlipTransformer()(axelrod.Cooperator)() # Defector p3 = p2.clone() self.assertEqual(simulate_play(p1, p3), (C, D)) self.assertEqual(simulate_play(p1, p3), (C, D)) @@ -63,11 +65,65 @@ def test_generic(self): def test_flip_transformer(self): """Tests that FlipTransformer(Cooperator) == Defector.""" p1 = axelrod.Cooperator() - p2 = FlipTransformer()(axelrod.Cooperator)() # Defector + p2 = FlipTransformer()(axelrod.Cooperator)() # Defector self.assertEqual(simulate_play(p1, p2), (C, D)) self.assertEqual(simulate_play(p1, p2), (C, D)) self.assertEqual(simulate_play(p1, p2), (C, D)) + def test_dual_wsls_transformer(self): + """Tests that DualTransformer produces the opposite results when faced + with the same opponent history. + """ + p1 = axelrod.WinStayLoseShift() + p2 = DualTransformer()(axelrod.WinStayLoseShift)() + p3 = axelrod.CyclerCCD() # Cycles 'CCD' + + for _ in range(10): + p1.play(p3) + + p3.reset() + for _ in range(10): + p2.play(p3) + + self.assertEqual(p1.history, [flip_action(x) for x in p2.history]) + + def test_dual_tft_transformer(self): + """Tests that DualTransformer produces the opposite results when faced + with the same opponent history. + """ + p1 = axelrod.TitForTat() + p2 = DualTransformer()(axelrod.TitForTat)() + p3 = axelrod.CyclerCCD() # Cycles 'CCD' + + for _ in range(10): + p1.play(p3) + + p3.reset() + for _ in range(10): + p2.play(p3) + + self.assertEqual(p1.history, [flip_action(x) for x in p2.history]) + + def test_dual_majority_transformer(self): + """Tests that DualTransformer produces the opposite results when faced + with the same opponent history. + """ + p1 = axelrod.GoByMajority() + p2 = DualTransformer()(axelrod.GoByMajority)() + p3 = axelrod.Cycler('CDD') # Cycles 'CDD' + + for _ in range(10): + p1.play(p3) + + p3.reset() + for _ in range(10): + p2.play(p3) + + self.assertEqual(p1.history, [flip_action(x) for x in p2.history]) + + def test_jossann_transformer(self): + pass + def test_noisy_transformer(self): """Tests that the noisy transformed does flip some moves.""" random.seed(5) From 0b5b23757b46ee28182ca082461e1f7d3d8f2701 Mon Sep 17 00:00:00 2001 From: James Campbell Date: Sun, 6 Nov 2016 15:13:09 +0000 Subject: [PATCH 16/25] Remove dual strategy function --- axelrod/player.py | 6 ------ axelrod/strategy_transformers.py | 2 -- 2 files changed, 8 deletions(-) diff --git a/axelrod/player.py b/axelrod/player.py index 732663a14..f8ab83065 100644 --- a/axelrod/player.py +++ b/axelrod/player.py @@ -134,12 +134,6 @@ def strategy(self, opponent): """This is a placeholder strategy.""" raise NotImplementedError() - def dual_strategy(self, opponent): - from copy import deepcopy - fresh_opponent = deepcopy(opponent) - self.original.play(fresh_opponent) - return flip_action(self.original.history[-1]) - def play(self, opponent, noise=0): """This pits two players against each other.""" s1, s2 = self.strategy(opponent), opponent.strategy(self) diff --git a/axelrod/strategy_transformers.py b/axelrod/strategy_transformers.py index d3335df54..e66ab1c22 100644 --- a/axelrod/strategy_transformers.py +++ b/axelrod/strategy_transformers.py @@ -10,8 +10,6 @@ import random import collections from numpy.random import choice - - from .actions import Actions, flip_action from .random_ import random_choice From b3f0306898f35c61c9d811c6a053be7628cd56e1 Mon Sep 17 00:00:00 2001 From: James Campbell Date: Sun, 6 Nov 2016 15:18:34 +0000 Subject: [PATCH 17/25] Begin tests for JossAnnTransformer --- axelrod/tests/unit/test_strategy_transformers.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/axelrod/tests/unit/test_strategy_transformers.py b/axelrod/tests/unit/test_strategy_transformers.py index 10a1020ef..b36e74c56 100644 --- a/axelrod/tests/unit/test_strategy_transformers.py +++ b/axelrod/tests/unit/test_strategy_transformers.py @@ -122,7 +122,19 @@ def test_dual_majority_transformer(self): self.assertEqual(p1.history, [flip_action(x) for x in p2.history]) def test_jossann_transformer(self): - pass + probability = (1, 0) + p1 = JossAnnTransformer(probability)(axelrod.Defector)() + p2 = axelrod.Cooperator() + for _ in range(5): + p1.play(p2) + self.assertEqual(p1.history, [C, C, C, C, C]) + + probability = (0, 1) + p1 = JossAnnTransformer(probability)(axelrod.Cooperator)() + p2 = axelrod.Cooperator() + for _ in range(5): + p1.play(p2) + self.assertEqual(p1.history, [D, D, D, D, D]) def test_noisy_transformer(self): """Tests that the noisy transformed does flip some moves.""" From e897b66071cf1897a82c661c7fff321571b440a2 Mon Sep 17 00:00:00 2001 From: James Campbell Date: Sun, 6 Nov 2016 15:24:11 +0000 Subject: [PATCH 18/25] Add a random tests for JossAnnTransformer --- axelrod/tests/unit/test_strategy_transformers.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/axelrod/tests/unit/test_strategy_transformers.py b/axelrod/tests/unit/test_strategy_transformers.py index b36e74c56..ea9550911 100644 --- a/axelrod/tests/unit/test_strategy_transformers.py +++ b/axelrod/tests/unit/test_strategy_transformers.py @@ -122,6 +122,8 @@ def test_dual_majority_transformer(self): self.assertEqual(p1.history, [flip_action(x) for x in p2.history]) def test_jossann_transformer(self): + """Tests the JossAnn transformer. + """ probability = (1, 0) p1 = JossAnnTransformer(probability)(axelrod.Defector)() p2 = axelrod.Cooperator() @@ -136,6 +138,14 @@ def test_jossann_transformer(self): p1.play(p2) self.assertEqual(p1.history, [D, D, D, D, D]) + probability = (0.3, 0.3) + p1 = JossAnnTransformer(probability)(axelrod.TitForTat)() + p2 = axelrod.Cycler() + axelrod.seed(0) + for _ in range(5): + p1.play(p2) + self.assertEqual(p1.history, [D, C, C, D, D]) + def test_noisy_transformer(self): """Tests that the noisy transformed does flip some moves.""" random.seed(5) From 59da7494593f7cfbe0a8e49e732898941255eb1d Mon Sep 17 00:00:00 2001 From: James Campbell Date: Sun, 6 Nov 2016 15:46:42 +0000 Subject: [PATCH 19/25] Include reference for Dual and JossAnn Transformer --- axelrod/strategy_transformers.py | 4 ++++ docs/reference/bibliography.rst | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/axelrod/strategy_transformers.py b/axelrod/strategy_transformers.py index e66ab1c22..433664d39 100644 --- a/axelrod/strategy_transformers.py +++ b/axelrod/strategy_transformers.py @@ -170,6 +170,8 @@ def dual_wrapper(player, opponent, proposed_action): The Dual of a strategy will return the exact opposite set of moves to the original strategy when both are faced with the same history. + A formal definition can be found in [Ashlock2010]_. + Parameters ---------- player: Player object or subclass (self) @@ -360,6 +362,8 @@ def joss_ann_wrapper(player, opponent, proposed_action, probability): choosing the move C, a probability of choosing the move D, and otherwise uses the response appropriate to the original strategy. + A formal definition can be found in [Ashlock2010]_. + Parameters ---------- diff --git a/docs/reference/bibliography.rst b/docs/reference/bibliography.rst index 55eb55cff..d77adcc13 100644 --- a/docs/reference/bibliography.rst +++ b/docs/reference/bibliography.rst @@ -26,4 +26,4 @@ documentation. .. [Stewart2012] Stewart, a. J., & Plotkin, J. B. (2012). Extortion and cooperation in the Prisoner’s Dilemma. Proceedings of the National Academy of Sciences, 109(26), 10134–10135. http://doi.org/10.1073/pnas.1208087109 .. [Szabó1992] Szabó, G., & Fáth, G. (2007). Evolutionary games on graphs. Physics Reports, 446(4-6), 97–216. http://doi.org/10.1016/j.physrep.2007.04.004 .. [Tzafestas2000] Tzafestas, E. (2000). Toward adaptive cooperative behavior. From Animals to Animals: Proceedings of the 6th International Conference on the Simulation of Adaptive Behavior {(SAB-2000)}, 2, 334–340. - +.. [Ashlock2010] Ashlock, D., Kim, E. Y., & Ashlock, W. (2010). A fingerprint comparison of different Prisoner’s Dilemma payoff matrices. Proceedings of the 2010 IEEE Conference on Computational Intelligence and Games, CIG2010, (2009), 219–226. http://doi.org/10.1109/ITW.2010.5593352 From 854a5104e1dfbddc0b87c951f09642d4585750c4 Mon Sep 17 00:00:00 2001 From: James Campbell Date: Sun, 6 Nov 2016 18:03:25 +0000 Subject: [PATCH 20/25] Add url to paper for Dual and JossAnn Transformers --- axelrod/strategy_transformers.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/axelrod/strategy_transformers.py b/axelrod/strategy_transformers.py index 433664d39..5a3848609 100644 --- a/axelrod/strategy_transformers.py +++ b/axelrod/strategy_transformers.py @@ -171,6 +171,7 @@ def dual_wrapper(player, opponent, proposed_action): original strategy when both are faced with the same history. A formal definition can be found in [Ashlock2010]_. + http://doi.org/10.1109/ITW.2010.5593352 Parameters ---------- @@ -363,6 +364,7 @@ def joss_ann_wrapper(player, opponent, proposed_action, probability): uses the response appropriate to the original strategy. A formal definition can be found in [Ashlock2010]_. + http://doi.org/10.1109/ITW.2010.5593352 Parameters ---------- From 8c854a5b35429295fd3929801216ca869cca22a6 Mon Sep 17 00:00:00 2001 From: James Campbell Date: Sun, 6 Nov 2016 18:13:43 +0000 Subject: [PATCH 21/25] Include Dual and JossAnn in tutorials/advanced/strategy_transformers.rst --- docs/tutorials/advanced/strategy_transformers.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/tutorials/advanced/strategy_transformers.rst b/docs/tutorials/advanced/strategy_transformers.rst index ac2563134..6208c3cdc 100644 --- a/docs/tutorials/advanced/strategy_transformers.rst +++ b/docs/tutorials/advanced/strategy_transformers.rst @@ -60,6 +60,16 @@ The library includes the following transformers: >>> NoisyCooperator = NoisyTransformer(0.5)(axl.Cooperator) >>> player = NoisyCooperator() +* :code:`DualTransformer`: The Dual of a strategy will return the exact opposite set of moves to the original strategy when both are faced with the same history. [Ashlock2010]_:: + + >>> DualWSLS = DualTransformer()(axl.WinStayLoseShift) + >>> player = DualWSLS() + +* :code:`JossAnnTransformer(probability)`: Where :code:`probability = (x, y)`, the Joss-Ann of a strategy is a new strategy which has a probability :code:`x` of choosing the move C, a probability :code:`y` of choosing the move D, and otherwise uses the response appropriate to the original strategy. [Ashlock2010]_:: + + >>> JossAnnTFT = JossAnnTransformer((0.2, 0.3))(axl.TitForTat) + >>> player = JossAnnTFT() + * :code:`ForgiverTransformer(p)`: Flips defections with probability :code:`p`:: >>> ForgivinDefector = ForgiverTransformer(0.1)(axl.Defector) From 2d81a8cc1c89ff20f63d7163a9f3c8bbf2619f0d Mon Sep 17 00:00:00 2001 From: Vince Knight Date: Thu, 10 Nov 2016 19:18:51 +0000 Subject: [PATCH 22/25] Stop random_choice sampling when used with 0 or 1 Closes #760 - Test included to check that random seed is not offset - Adjusted some remaining tests (surprisingly, not that many!) --- axelrod/random_.py | 18 +++++++++++++++++- axelrod/tests/unit/test_axelrod_second.py | 2 +- axelrod/tests/unit/test_headsup.py | 2 +- axelrod/tests/unit/test_memoryone.py | 2 +- axelrod/tests/unit/test_random_.py | 16 ++++++++++++++-- 5 files changed, 34 insertions(+), 6 deletions(-) diff --git a/axelrod/random_.py b/axelrod/random_.py index 9636882e3..24913cc97 100644 --- a/axelrod/random_.py +++ b/axelrod/random_.py @@ -8,7 +8,23 @@ def random_choice(p=0.5): Return 'C' with probability `p`, else return 'D' Emulates Python's random.choice(['C', 'D']) since it is not consistent - across Python 2.7 to Python 3.4""" + across Python 2.7 to Python 3.4 + + Parameters + ---------- + + p : float + The probability of picking 'C' + + Returns + ------- + axelrod.Actions.C or axelrod.Actions.D + """ + if p == 0: + return Actions.D + + if p == 1: + return Actions.C r = random.random() if r < p: diff --git a/axelrod/tests/unit/test_axelrod_second.py b/axelrod/tests/unit/test_axelrod_second.py index 3257dff7f..b962eed6b 100644 --- a/axelrod/tests/unit/test_axelrod_second.py +++ b/axelrod/tests/unit/test_axelrod_second.py @@ -80,7 +80,7 @@ def test_strategy(self): # Test defection after opponent defection self.responses_test([D], [D], [D]) self.responses_test([D, D], [D, D], [D]) - self.responses_test([D, C, C, D], [D, C, C, D], [D, C], random_seed=8) + self.responses_test([D, C, C, D], [D, C, C, D], [C, C], random_seed=8) class TestTester(TestPlayer): diff --git a/axelrod/tests/unit/test_headsup.py b/axelrod/tests/unit/test_headsup.py index 86de2ccfb..98fd3beb5 100644 --- a/axelrod/tests/unit/test_headsup.py +++ b/axelrod/tests/unit/test_headsup.py @@ -38,7 +38,7 @@ def test_rounds(self): class TestZDGTFT2vsBully(TestHeadsUp): """Test ZDGTFT2 vs Bully""" def test_rounds(self): - self.versus_test(axelrod.ZDGTFT2(), axelrod.Bully(), [C, D, D, C, C, D], + self.versus_test(axelrod.ZDGTFT2(), axelrod.Bully(), [C, D, D, C, C, C], [D, D, C, C, D, D], random_seed=2) diff --git a/axelrod/tests/unit/test_memoryone.py b/axelrod/tests/unit/test_memoryone.py index d3b57a34f..828280971 100644 --- a/axelrod/tests/unit/test_memoryone.py +++ b/axelrod/tests/unit/test_memoryone.py @@ -229,7 +229,7 @@ def test_effect_of_strategy(self): self.responses_test([C], [C], [D, D, C, C], random_seed=2) self.responses_test([C], [D], [D, D, C, C], random_seed=2) self.responses_test([D], [C], [D, D, C, C], random_seed=2) - self.responses_test([D], [D], [D, D, C, C], random_seed=2) + self.responses_test([C], [D], [D, D, C, C], random_seed=2) class TestZDExtort2v2(TestPlayer): diff --git a/axelrod/tests/unit/test_random_.py b/axelrod/tests/unit/test_random_.py index 3303aee53..c5cfee843 100644 --- a/axelrod/tests/unit/test_random_.py +++ b/axelrod/tests/unit/test_random_.py @@ -8,14 +8,15 @@ C, D = Actions.C, Actions.D + class TestRandom_(unittest.TestCase): def test_return_values(self): self.assertEqual(random_choice(1), C) self.assertEqual(random_choice(0), D) - random.seed(1) + seed(1) self.assertEqual(random_choice(), C) - random.seed(2) + seed(2) self.assertEqual(random_choice(), D) def test_set_seed(self): @@ -30,3 +31,14 @@ def test_set_seed(self): self.assertEqual(numpy_random_numbers[0], numpy_random_numbers[1]) self.assertEqual(stdlib_random_numbers[0], stdlib_random_numbers[1]) + + def test_seed_not_offset_by_deterministic_call(self): + """Test that when called with p = 0 or 1, the random seed is not + affected""" + for p in [0, 1]: + seed(0) + r = random.random() + + random.seed(0) + random_choice(p) + self.assertEqual(r, random.random()) From 62f6934d8a77a76e505e1e9daaca275213cb850a Mon Sep 17 00:00:00 2001 From: Vince Knight Date: Fri, 11 Nov 2016 14:18:27 +0000 Subject: [PATCH 23/25] Change to avoid if statements. --- axelrod/random_.py | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/axelrod/random_.py b/axelrod/random_.py index 24913cc97..bc1c32d0a 100644 --- a/axelrod/random_.py +++ b/axelrod/random_.py @@ -2,6 +2,9 @@ import numpy from axelrod import Actions +# A dictionary mapping 0 or 1 probability of cooperating to an action. +choices = {0: Actions.D, 1: Actions.C} + def random_choice(p=0.5): """ @@ -20,16 +23,11 @@ def random_choice(p=0.5): ------- axelrod.Actions.C or axelrod.Actions.D """ - if p == 0: - return Actions.D - - if p == 1: - return Actions.C - - r = random.random() - if r < p: - return Actions.C - return Actions.D + try: + return choices[p] + except KeyError: + r = random.random() + return choices[r < p] def randrange(a, b): From 13969dd0d50af163d5485c15d1026a4686c4ac95 Mon Sep 17 00:00:00 2001 From: Vince Knight Date: Fri, 11 Nov 2016 17:05:16 +0000 Subject: [PATCH 24/25] Revert "Change to avoid if statements." This reverts commit 62f6934d8a77a76e505e1e9daaca275213cb850a. --- axelrod/random_.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/axelrod/random_.py b/axelrod/random_.py index bc1c32d0a..24913cc97 100644 --- a/axelrod/random_.py +++ b/axelrod/random_.py @@ -2,9 +2,6 @@ import numpy from axelrod import Actions -# A dictionary mapping 0 or 1 probability of cooperating to an action. -choices = {0: Actions.D, 1: Actions.C} - def random_choice(p=0.5): """ @@ -23,11 +20,16 @@ def random_choice(p=0.5): ------- axelrod.Actions.C or axelrod.Actions.D """ - try: - return choices[p] - except KeyError: - r = random.random() - return choices[r < p] + if p == 0: + return Actions.D + + if p == 1: + return Actions.C + + r = random.random() + if r < p: + return Actions.C + return Actions.D def randrange(a, b): From 93c5b8cb930d3209ca40f4196e959632210d3e4c Mon Sep 17 00:00:00 2001 From: Vince Knight Date: Sun, 13 Nov 2016 18:17:32 +0000 Subject: [PATCH 25/25] Release v1.16.0 --- CHANGES.md | 14 ++++++++++++++ axelrod/version.py | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index a8e9d06a3..3ffdfd92b 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,17 @@ +# v1.16.0, 2016-11-13 + +Minor internal change, new strategy and new strategy transformers + +- Random_choice method does not sample if not necessary + https://github.com/Axelrod-Python/Axelrod/pull/761 +- New strategy: Meta Winner Ensemble + https://github.com/Axelrod-Python/Axelrod/pull/757 +- New strategy transformers: Dual transformer and Joss-Ann transformer + https://github.com/Axelrod-Python/Axelrod/pull/758 + +Here are all the commits for this PR: +https://github.com/Axelrod-Python/Axelrod/compare/v1.15.0...v1.16.0 + # v1.15.0, 2016-11-03 Mutation in Moran process, players track state pairs, save all plots method, diff --git a/axelrod/version.py b/axelrod/version.py index 6b0872cb2..638c1217d 100644 --- a/axelrod/version.py +++ b/axelrod/version.py @@ -1 +1 @@ -__version__ = "1.15.0" +__version__ = "1.16.0"