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/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/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..de80888ef 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, @@ -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 @@ -165,6 +168,38 @@ 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. + + Names: + + Meta Winner Ensemble: Original name by Marc Harper + """ + + 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/strategy_transformers.py b/axelrod/strategy_transformers.py index 9ea5be8fb..5a3848609 100644 --- a/axelrod/strategy_transformers.py +++ b/axelrod/strategy_transformers.py @@ -10,10 +10,10 @@ import random import collections from numpy.random import choice - from .actions import Actions, flip_action from .random_ import random_choice + C, D = Actions.C, Actions.D # Note: After a transformation is applied, @@ -107,8 +107,9 @@ def strategy(self, opponent): new_class_name, (PlayerClass,), { "name": name, + "original_class": PlayerClass, "strategy": strategy, - "__module__": PlayerClass.__module__ + "__module__": PlayerClass.__module__, }) return new_class return Decorator @@ -150,6 +151,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) @@ -157,10 +159,42 @@ 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, opponent, proposed_action): + """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. + + A formal definition can be found in [Ashlock2010]_. + http://doi.org/10.1109/ITW.2010.5593352 + + 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) + + action = player.original_player.strategy(opponent) + player.original_player.history.append(action) + 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() @@ -168,6 +202,7 @@ def noisy_wrapper(player, opponent, action, noise=0.05): return flip_action(action) return action + NoisyTransformer = StrategyTransformerFactory( noisy_wrapper, name_prefix="Noisy") @@ -179,6 +214,7 @@ def forgiver_wrapper(player, opponent, action, p): return random_choice(p) return C + ForgiverTransformer = StrategyTransformerFactory( forgiver_wrapper, name_prefix="Forgiving") @@ -192,6 +228,7 @@ def initial_sequence(player, opponent, action, initial_seq): return initial_seq[index] return action + InitialTransformer = StrategyTransformerFactory(initial_sequence, name_prefix="Initial") @@ -217,6 +254,7 @@ def final_sequence(player, opponent, action, seq): return seq[-index] return action + FinalTransformer = StrategyTransformerFactory(final_sequence, name_prefix="Final") @@ -229,6 +267,7 @@ def history_track_wrapper(player, opponent, action): player._recorded_history = [action] return action + TrackHistoryTransformer = StrategyTransformerFactory( history_track_wrapper, name_prefix="HistoryTracking") @@ -245,6 +284,7 @@ def deadlock_break_wrapper(player, opponent, action): return C return action + DeadlockBreakingTransformer = StrategyTransformerFactory( deadlock_break_wrapper, name_prefix="DeadlockBreaking") @@ -255,6 +295,7 @@ def grudge_wrapper(player, opponent, action, grudges): return D return action + GrudgeTransformer = StrategyTransformerFactory( grudge_wrapper, name_prefix="Grudging") @@ -268,6 +309,7 @@ def apology_wrapper(player, opponent, action, myseq, opseq): return C return action + ApologyTransformer = StrategyTransformerFactory( apology_wrapper, name_prefix="Apologizing") @@ -309,9 +351,50 @@ def mixed_wrapper(player, opponent, action, probability, m_player): return action + MixedTransformer = StrategyTransformerFactory( mixed_wrapper, name_prefix="Mutated") + +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. + + A formal definition can be found in [Ashlock2010]_. + http://doi.org/10.1109/ITW.2010.5593352 + + 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 + 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] + + 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 @@ -331,6 +414,7 @@ def __call__(self, player, opponent, action, retaliations): self.retaliation_count -= 1 return D + RetaliationTransformer = StrategyTransformerFactory( RetaliationWrapper(), name_prefix="Retaliating") @@ -352,5 +436,6 @@ def __call__(self, player, opponent, action): return D return action + RetaliateUntilApologyTransformer = StrategyTransformerFactory( RetaliationUntilApologyWrapper(), name_prefix="RUA") 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_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)) 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_meta.py b/axelrod/tests/unit/test_meta.py index 2067a4083..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) @@ -185,6 +186,35 @@ 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) + + 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): name = "Meta Hunter" @@ -201,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. 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()) diff --git a/axelrod/tests/unit/test_strategy_transformers.py b/axelrod/tests/unit/test_strategy_transformers.py index d82a95057..ea9550911 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,87 @@ 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): + """Tests the JossAnn transformer. + """ + 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]) + + 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) 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" 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 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` 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) 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