From 526913327c832c729ddc4b534a8418297fded9f6 Mon Sep 17 00:00:00 2001 From: Marc Harper Date: Tue, 12 Apr 2016 23:08:15 -0700 Subject: [PATCH 1/8] Moran process and tests --- axelrod/__init__.py | 1 + axelrod/moran.py | 111 +++++++++++++++++++++++++++++++ axelrod/tests/unit/test_moran.py | 71 ++++++++++++++++++++ 3 files changed, 183 insertions(+) create mode 100644 axelrod/moran.py create mode 100644 axelrod/tests/unit/test_moran.py diff --git a/axelrod/__init__.py b/axelrod/__init__.py index be8b67f9b..db33e4c34 100644 --- a/axelrod/__init__.py +++ b/axelrod/__init__.py @@ -8,6 +8,7 @@ from .player import init_args, is_basic, obey_axelrod, update_history, Player from .mock_player import MockPlayer, simulate_play from .match import Match +from .moran import MoranProcess from .strategies import * from .deterministic_cache import DeterministicCache from .match_generator import * diff --git a/axelrod/moran.py b/axelrod/moran.py new file mode 100644 index 000000000..e94b3ef23 --- /dev/null +++ b/axelrod/moran.py @@ -0,0 +1,111 @@ +# -*- coding: utf-8 -*- +from collections import Counter +import random + +import numpy as np + +from .deterministic_cache import DeterministicCache +from .match import Match +from .player import Player + + +def fitness_proportionate_selection(scores): + """Randomly selects an individual proportionally to score.""" + csums = np.cumsum(scores) + total = csums[-1] + r = random.random() * total + + for i, x in enumerate(csums): + if x >= r: + return i + +class MoranProcess(object): + def __init__(self, players, turns=100, noise=0): + self.turns = turns + self.noise = noise + self.players = list(players) # initial population + self.winner = None + self.populations = [] + self.populations.append(self.population_distribution()) + self.score_history = [] + + @property + def _stochastic(self): + """ + A boolean to show whether a match between two players would be + stochastic + """ + if self.noise: + return True + else: + return any(p.classifier['stochastic'] for p in self.players) + + def __next__(self): + """Iterate the population: + - play the round's matches + - chooses a player proportionally to fitness (total score) to reproduce + - choose a player at random to be replaced + - update the population + """ + # Check the exit condition, that all players are of the same type. + population = self.populations[-1] + classes = set(p.__class__ for p in self.players) + if len(classes) == 1: + self.winner = str(self.players[0]) + raise StopIteration + scores = self._play_next_round() + # Update the population + # Fitness proportionate selection + j = fitness_proportionate_selection(scores) + # Randomly remove a strategy + i = random.randrange(0, len(self.players)) + # Replace player i with clone of player j + self.players[i] = self.players[j].clone() + self.populations.append(self.population_distribution()) + + def _play_next_round(self): + """Plays the next round of the process. Every player is paired up + against every other player and the total scores are recorded.""" + N = len(self.players) + scores = [0] * N + for i in range(N): + for j in range(i + 1, N): + player1 = self.players[i] + player2 = self.players[j] + player1.reset() + player2.reset() + match = Match((player1, player2), self.turns, noise=self.noise) + match.play() + match_scores = np.sum(match.scores(), axis=0) / float(self.turns) + scores[i] += match_scores[0] + scores[j] += match_scores[1] + self.score_history.append(scores) + return scores + + def population_distribution(self): + """Returns the population distribution of the last iteration.""" + player_names = [str(player) for player in self.players] + counter = Counter(player_names) + return counter + + next = __next__ # Python 2 + + def __iter__(self): + return self + + def reset(self): + """Reset the process to replay.""" + self.winner = None + self.populations = [self.populations[0]] + self.score_history = [] + + def play(self): + """Play the process out to completion.""" + while True: # O_o + try: + self.__next__() + except StopIteration: + break + + def __len__(self): + return len(self.populations) diff --git a/axelrod/tests/unit/test_moran.py b/axelrod/tests/unit/test_moran.py new file mode 100644 index 000000000..0f818bf58 --- /dev/null +++ b/axelrod/tests/unit/test_moran.py @@ -0,0 +1,71 @@ +# -*- coding: utf-8 -*- +import random +import unittest + +import axelrod +from axelrod import MoranProcess +from axelrod.moran import fitness_proportionate_selection + + +class TestMoranProcess(unittest.TestCase): + + def test_fps(self): + self.assertEqual(fitness_proportionate_selection([0, 0, 1]), 2) + random.seed(1) + self.assertEqual(fitness_proportionate_selection([1, 1, 1]), 0) + self.assertEqual(fitness_proportionate_selection([1, 1, 1]), 2) + + def test_stochastic(self): + p1, p2 = axelrod.Cooperator(), axelrod.Cooperator() + mp = MoranProcess((p1, p2)) + self.assertFalse(mp._stochastic) + p1, p2 = axelrod.Cooperator(), axelrod.Cooperator() + mp = MoranProcess((p1, p2), noise=0.05) + self.assertTrue(mp._stochastic) + p1, p2 = axelrod.Cooperator(), axelrod.Random() + mp = MoranProcess((p1, p2)) + self.assertTrue(mp._stochastic) + + def test_exit_condition(self): + p1, p2 = axelrod.Cooperator(), axelrod.Cooperator() + mp = MoranProcess((p1, p2)) + mp.play() + self.assertEqual(len(mp), 1) + + def test_two_players(self): + p1, p2 = axelrod.Cooperator(), axelrod.Defector() + random.seed(5) + mp = MoranProcess((p1, p2)) + mp.play() + self.assertEqual(len(mp), 3) + self.assertEqual(mp.winner, str(p2)) + + def test_three_players(self): + players = [axelrod.Cooperator(), axelrod.Cooperator(), + axelrod.Defector()] + random.seed(5) + mp = MoranProcess(players) + mp.play() + self.assertEqual(len(mp), 3) + self.assertEqual(mp.winner, str(axelrod.Cooperator())) + + def test_four_players(self): + players = [axelrod.Cooperator() for _ in range(3)] + players.append(axelrod.Defector()) + random.seed(10) + mp = MoranProcess(players) + mp.play() + self.assertEqual(len(mp), 4) + self.assertEqual(mp.winner, str(axelrod.Cooperator())) + + def test_reset(self): + p1, p2 = axelrod.Cooperator(), axelrod.Defector() + random.seed(5) + mp = MoranProcess((p1, p2)) + mp.play() + self.assertEqual(len(mp), 3) + self.assertEqual(len(mp.score_history), 2) + mp.reset() + self.assertEqual(len(mp), 1) + self.assertEqual(mp.winner, None) + self.assertEqual(mp.score_history, []) From 9a3494df943ac259bd9d0ddff2a4381141cfd8ea Mon Sep 17 00:00:00 2001 From: Marc Harper Date: Tue, 12 Apr 2016 23:20:25 -0700 Subject: [PATCH 2/8] Docs for Moran Process Doc tests to skip Doctest Add Moran doc to toc tree --- docs/tutorials/getting_started/index.rst | 1 + docs/tutorials/getting_started/moran.rst | 49 ++++++++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 docs/tutorials/getting_started/moran.rst diff --git a/docs/tutorials/getting_started/index.rst b/docs/tutorials/getting_started/index.rst index 77a686d8e..8f3b6d793 100644 --- a/docs/tutorials/getting_started/index.rst +++ b/docs/tutorials/getting_started/index.rst @@ -16,3 +16,4 @@ Contents: noisy_tournaments.rst ecological_variant.rst command_line.rst + moran.rst diff --git a/docs/tutorials/getting_started/moran.rst b/docs/tutorials/getting_started/moran.rst new file mode 100644 index 000000000..37dc08d22 --- /dev/null +++ b/docs/tutorials/getting_started/moran.rst @@ -0,0 +1,49 @@ +Moran Process +============= + +The strategies in the library can be pitted against one another in the +[Moran process](https://en.wikipedia.org/wiki/Moran_process), a population +process simulating natural selection. The process works as follows. Given an +initial population of players, the population is iterated in rounds consisting +of: +- matched played between each pair of players, with the cumulative total +scores recored +- a player is chosen to reproduce proportional to the player's score in the +round +- a player is chosen at random to be replaced + +The process proceeds in rounds until the population consists of a single player +type. That type is declared the winner. To run an instance of the process with +the library, proceed as follows:: + + >>> import axelrod as axl + >>> players = [axl.Cooperator(), axl.Defector(), + ... axl.TitForTat(), axl.Grudger()] + >>> mp = axl.MoranProcess(players) + >>> mp.play() + >>> mp.winner # doctest: +SKIP + + Defector + +You can access some attributes of the process, such as the number of rounds:: + >>> len(mp) # doctest: +SKIP + 6 + +The sequence of populations:: + >>> import pprint + >>> pprint.pprint(mp.populations) # doctest: +SKIP + [Counter({'Defector': 1, 'Cooperator': 1, 'Grudger': 1, 'Tit For Tat': 1}), + Counter({'Defector': 1, 'Cooperator': 1, 'Grudger': 1, 'Tit For Tat': 1}), + Counter({'Defector': 2, 'Cooperator': 1, 'Grudger': 1}), + Counter({'Defector': 3, 'Grudger': 1}), + Counter({'Defector': 3, 'Grudger': 1}), + Counter({'Defector': 4})] + +The scores in each round:: + >>> import pprint + >>> pprint.pprint(mp.score_history) # doctest: +SKIP + [[6.0, 7.0800000000000001, 6.9900000000000002, 6.9900000000000002], + [6.0, 7.0800000000000001, 6.9900000000000002, 6.9900000000000002], + [3.0, 7.04, 7.04, 4.9800000000000004], + [3.04, 3.04, 3.04, 2.9699999999999998], + [3.04, 3.04, 3.04, 2.9699999999999998]] From 9de867e9c0447f84fe6a6144324d01f136828b16 Mon Sep 17 00:00:00 2001 From: Marc Harper Date: Wed, 13 Apr 2016 00:07:27 -0700 Subject: [PATCH 3/8] Fix random differences between python 2 and 3 Fix tests --- axelrod/moran.py | 3 ++- axelrod/random_.py | 6 ++++++ axelrod/tests/unit/test_moran.py | 16 ++++++++-------- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/axelrod/moran.py b/axelrod/moran.py index e94b3ef23..534e6906f 100644 --- a/axelrod/moran.py +++ b/axelrod/moran.py @@ -7,6 +7,7 @@ from .deterministic_cache import DeterministicCache from .match import Match from .player import Player +from .random_ import randrange def fitness_proportionate_selection(scores): @@ -58,7 +59,7 @@ def __next__(self): # Fitness proportionate selection j = fitness_proportionate_selection(scores) # Randomly remove a strategy - i = random.randrange(0, len(self.players)) + i = randrange(0, len(self.players)) # Replace player i with clone of player j self.players[i] = self.players[j].clone() self.populations.append(self.population_distribution()) diff --git a/axelrod/random_.py b/axelrod/random_.py index 3c614ff50..1d5decfa6 100644 --- a/axelrod/random_.py +++ b/axelrod/random_.py @@ -12,3 +12,9 @@ def random_choice(p=0.5): if r < p: return Actions.C return Actions.D + +def randrange(a, b): + """Python 2 / 3 compatible randrange.""" + c = b - a + r = c * random.random() + return a + int(r) \ No newline at end of file diff --git a/axelrod/tests/unit/test_moran.py b/axelrod/tests/unit/test_moran.py index 0f818bf58..ac23dcadc 100644 --- a/axelrod/tests/unit/test_moran.py +++ b/axelrod/tests/unit/test_moran.py @@ -37,7 +37,7 @@ def test_two_players(self): random.seed(5) mp = MoranProcess((p1, p2)) mp.play() - self.assertEqual(len(mp), 3) + self.assertEqual(len(mp), 5) self.assertEqual(mp.winner, str(p2)) def test_three_players(self): @@ -46,8 +46,8 @@ def test_three_players(self): random.seed(5) mp = MoranProcess(players) mp.play() - self.assertEqual(len(mp), 3) - self.assertEqual(mp.winner, str(axelrod.Cooperator())) + self.assertEqual(len(mp), 7) + self.assertEqual(mp.winner, str(axelrod.Defector())) def test_four_players(self): players = [axelrod.Cooperator() for _ in range(3)] @@ -55,16 +55,16 @@ def test_four_players(self): random.seed(10) mp = MoranProcess(players) mp.play() - self.assertEqual(len(mp), 4) - self.assertEqual(mp.winner, str(axelrod.Cooperator())) + self.assertEqual(len(mp), 9) + self.assertEqual(mp.winner, str(axelrod.Defector())) def test_reset(self): p1, p2 = axelrod.Cooperator(), axelrod.Defector() - random.seed(5) + random.seed(8) mp = MoranProcess((p1, p2)) mp.play() - self.assertEqual(len(mp), 3) - self.assertEqual(len(mp.score_history), 2) + self.assertEqual(len(mp), 4) + self.assertEqual(len(mp.score_history), 3) mp.reset() self.assertEqual(len(mp), 1) self.assertEqual(mp.winner, None) From fc831f3535480f35be83a74bdbeb02f0c214bfac Mon Sep 17 00:00:00 2001 From: Marc Harper Date: Wed, 13 Apr 2016 00:35:05 -0700 Subject: [PATCH 4/8] Review fixes Docstring improvement More review updates --- axelrod/moran.py | 17 ++++++++++++++--- axelrod/random_.py | 5 +++-- docs/tutorials/getting_started/index.rst | 2 +- docs/tutorials/getting_started/moran.rst | 12 ++++++++++-- 4 files changed, 28 insertions(+), 8 deletions(-) diff --git a/axelrod/moran.py b/axelrod/moran.py index 534e6906f..4b628b2f2 100644 --- a/axelrod/moran.py +++ b/axelrod/moran.py @@ -11,7 +11,17 @@ def fitness_proportionate_selection(scores): - """Randomly selects an individual proportionally to score.""" + """Randomly selects an individual proportionally to score. + + Parameters + ---------- + scores: Any sequence of real numbers + + Returns + ------- + An index of the above list selected at random proportionally to the list + element divided by the total. + """ csums = np.cumsum(scores) total = csums[-1] r = random.random() * total @@ -29,6 +39,7 @@ def __init__(self, players, turns=100, noise=0): self.populations = [] self.populations.append(self.population_distribution()) self.score_history = [] + self.num_players = len(self.players) @property def _stochastic(self): @@ -67,7 +78,7 @@ def __next__(self): def _play_next_round(self): """Plays the next round of the process. Every player is paired up against every other player and the total scores are recorded.""" - N = len(self.players) + N = self.num_players scores = [0] * N for i in range(N): for j in range(i + 1, N): @@ -102,7 +113,7 @@ def reset(self): def play(self): """Play the process out to completion.""" - while True: # O_o + while True: try: self.__next__() except StopIteration: diff --git a/axelrod/random_.py b/axelrod/random_.py index 1d5decfa6..dba272687 100644 --- a/axelrod/random_.py +++ b/axelrod/random_.py @@ -14,7 +14,8 @@ def random_choice(p=0.5): return Actions.D def randrange(a, b): - """Python 2 / 3 compatible randrange.""" + """Python 2 / 3 compatible randrange. Returns a random integer uniformly + between a and b (inclusive)""" c = b - a r = c * random.random() - return a + int(r) \ No newline at end of file + return a + int(r) diff --git a/docs/tutorials/getting_started/index.rst b/docs/tutorials/getting_started/index.rst index 8f3b6d793..4d9d47df3 100644 --- a/docs/tutorials/getting_started/index.rst +++ b/docs/tutorials/getting_started/index.rst @@ -15,5 +15,5 @@ Contents: visualising_results.rst noisy_tournaments.rst ecological_variant.rst - command_line.rst moran.rst + command_line.rst diff --git a/docs/tutorials/getting_started/moran.rst b/docs/tutorials/getting_started/moran.rst index 37dc08d22..15e004b39 100644 --- a/docs/tutorials/getting_started/moran.rst +++ b/docs/tutorials/getting_started/moran.rst @@ -3,10 +3,15 @@ Moran Process The strategies in the library can be pitted against one another in the [Moran process](https://en.wikipedia.org/wiki/Moran_process), a population -process simulating natural selection. The process works as follows. Given an +process simulating natural selection. Given the evolutionary basis of the Moran +process it can be compared to the [ecological variant](../ecological_variant.rst). +While that variant was used by Axelrod in his original works, the Moran process +is now much more widely studied in the literature. + +The process works as follows. Given an initial population of players, the population is iterated in rounds consisting of: -- matched played between each pair of players, with the cumulative total +- matches played between each pair of players, with the cumulative total scores recored - a player is chosen to reproduce proportional to the player's score in the round @@ -26,10 +31,12 @@ the library, proceed as follows:: Defector You can access some attributes of the process, such as the number of rounds:: + >>> len(mp) # doctest: +SKIP 6 The sequence of populations:: + >>> import pprint >>> pprint.pprint(mp.populations) # doctest: +SKIP [Counter({'Defector': 1, 'Cooperator': 1, 'Grudger': 1, 'Tit For Tat': 1}), @@ -40,6 +47,7 @@ The sequence of populations:: Counter({'Defector': 4})] The scores in each round:: + >>> import pprint >>> pprint.pprint(mp.score_history) # doctest: +SKIP [[6.0, 7.0800000000000001, 6.9900000000000002, 6.9900000000000002], From 1146f3ab609217e996d4275948d66d7a9cb854c0 Mon Sep 17 00:00:00 2001 From: Vince Knight Date: Wed, 13 Apr 2016 10:45:27 +0100 Subject: [PATCH 5/8] Adding hypothesis test and specific examples. --- axelrod/tests/unit/test_moran.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/axelrod/tests/unit/test_moran.py b/axelrod/tests/unit/test_moran.py index ac23dcadc..ed400fb48 100644 --- a/axelrod/tests/unit/test_moran.py +++ b/axelrod/tests/unit/test_moran.py @@ -6,6 +6,9 @@ from axelrod import MoranProcess from axelrod.moran import fitness_proportionate_selection +from hypothesis import given, example, settings +from hypothesis.strategies import integers, lists, sampled_from, random_module, floats + class TestMoranProcess(unittest.TestCase): @@ -58,6 +61,24 @@ def test_four_players(self): self.assertEqual(len(mp), 9) self.assertEqual(mp.winner, str(axelrod.Defector())) + @given(strategies=lists(sampled_from(axelrod.strategies), + min_size=2, # Errors are returned if less than 2 strategies + max_size=5, unique=True), + rm=random_module()) + @settings(max_examples=5, timeout=0) # Very low number of examples + + # Two specific examples relating to cloning of strategies + @example(strategies=[axelrod.BackStabber, axelrod.MindReader], + rm=random.seed(0)) + @example(strategies=[axelrod.ThueMorse, axelrod.MindReader], + rm=random.seed(0)) + def test_property_players(self, strategies, rm): + """Hypothesis test that randomly checks players""" + players = [s() for s in strategies] + mp = MoranProcess(players) + mp.play() + self.assertIn(mp.winner, [str(p) for p in players]) + def test_reset(self): p1, p2 = axelrod.Cooperator(), axelrod.Defector() random.seed(8) From 5b28eec0cab5d961820857c8bc119ba42790ca48 Mon Sep 17 00:00:00 2001 From: Vince Knight Date: Wed, 13 Apr 2016 10:48:09 +0100 Subject: [PATCH 6/8] Correcting rst syntax. --- docs/tutorials/getting_started/moran.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorials/getting_started/moran.rst b/docs/tutorials/getting_started/moran.rst index 15e004b39..40b7e5f1b 100644 --- a/docs/tutorials/getting_started/moran.rst +++ b/docs/tutorials/getting_started/moran.rst @@ -4,7 +4,7 @@ Moran Process The strategies in the library can be pitted against one another in the [Moran process](https://en.wikipedia.org/wiki/Moran_process), a population process simulating natural selection. Given the evolutionary basis of the Moran -process it can be compared to the [ecological variant](../ecological_variant.rst). +process it can be compared to the :ref:`ecological-variant`. While that variant was used by Axelrod in his original works, the Moran process is now much more widely studied in the literature. From b487bdef6fa3d8e62020b491db4675f34a34546e Mon Sep 17 00:00:00 2001 From: Marc Harper Date: Wed, 13 Apr 2016 07:54:30 -0700 Subject: [PATCH 7/8] Abstract is_stochastic from Match and Moran More review updates more review --- axelrod/match.py | 10 ++++++---- axelrod/moran.py | 13 +++++-------- docs/tutorials/getting_started/moran.rst | 7 +++---- 3 files changed, 14 insertions(+), 16 deletions(-) diff --git a/axelrod/match.py b/axelrod/match.py index b5be6af3c..623e54ee2 100644 --- a/axelrod/match.py +++ b/axelrod/match.py @@ -7,6 +7,11 @@ C, D = Actions.C, Actions.D +def is_stochastic(players, noise): + """Determines if a match is stochastic -- true if there is noise or if any + of the players involved is stochastic.""" + return (noise or any(p.classifier['stochastic'] for p in players)) + class Match(object): def __init__(self, players, turns, deterministic_cache=None, noise=0): @@ -38,10 +43,7 @@ def _stochastic(self): A boolean to show whether a match between two players would be stochastic """ - return ( - self._noise or - any(p.classifier['stochastic'] for p in self.players) - ) + return is_stochastic(self.players, self._noise) @property def _cache_update_required(self): diff --git a/axelrod/moran.py b/axelrod/moran.py index 4b628b2f2..bd56df55d 100644 --- a/axelrod/moran.py +++ b/axelrod/moran.py @@ -5,7 +5,7 @@ import numpy as np from .deterministic_cache import DeterministicCache -from .match import Match +from .match import Match, is_stochastic from .player import Player from .random_ import randrange @@ -35,7 +35,7 @@ def __init__(self, players, turns=100, noise=0): self.turns = turns self.noise = noise self.players = list(players) # initial population - self.winner = None + self.winning_strategy_name = None self.populations = [] self.populations.append(self.population_distribution()) self.score_history = [] @@ -47,10 +47,7 @@ def _stochastic(self): A boolean to show whether a match between two players would be stochastic """ - if self.noise: - return True - else: - return any(p.classifier['stochastic'] for p in self.players) + is_stochastic(self.players, self.noise) def __next__(self): """Iterate the population: @@ -63,7 +60,7 @@ def __next__(self): population = self.populations[-1] classes = set(p.__class__ for p in self.players) if len(classes) == 1: - self.winner = str(self.players[0]) + self.winning_strategy_name = str(self.players[0]) raise StopIteration scores = self._play_next_round() # Update the population @@ -107,7 +104,7 @@ def __iter__(self): def reset(self): """Reset the process to replay.""" - self.winner = None + self.winning_strategy_name = None self.populations = [self.populations[0]] self.score_history = [] diff --git a/docs/tutorials/getting_started/moran.rst b/docs/tutorials/getting_started/moran.rst index 40b7e5f1b..a95b22ab5 100644 --- a/docs/tutorials/getting_started/moran.rst +++ b/docs/tutorials/getting_started/moran.rst @@ -25,9 +25,8 @@ the library, proceed as follows:: >>> players = [axl.Cooperator(), axl.Defector(), ... axl.TitForTat(), axl.Grudger()] >>> mp = axl.MoranProcess(players) + >>> mp.winning_strategy_name # doctest: +SKIP >>> mp.play() - >>> mp.winner # doctest: +SKIP - Defector You can access some attributes of the process, such as the number of rounds:: @@ -48,8 +47,8 @@ The sequence of populations:: The scores in each round:: - >>> import pprint - >>> pprint.pprint(mp.score_history) # doctest: +SKIP + >>> for row in mp.score_history: # doctest: +SKIP + ... print([round(element, 1) for element in row]) [[6.0, 7.0800000000000001, 6.9900000000000002, 6.9900000000000002], [6.0, 7.0800000000000001, 6.9900000000000002, 6.9900000000000002], [3.0, 7.04, 7.04, 4.9800000000000004], From 9a950d845e9ec3446cabf801a172cdc7aac218c6 Mon Sep 17 00:00:00 2001 From: Marc Harper Date: Wed, 13 Apr 2016 12:27:39 -0700 Subject: [PATCH 8/8] Fix Moran Tests Fix doc test --- axelrod/moran.py | 2 +- axelrod/tests/unit/test_moran.py | 10 +++++----- docs/tutorials/further_topics/index.rst | 1 + .../{getting_started => further_topics}/moran.rst | 2 +- docs/tutorials/getting_started/index.rst | 1 - 5 files changed, 8 insertions(+), 8 deletions(-) rename docs/tutorials/{getting_started => further_topics}/moran.rst (100%) diff --git a/axelrod/moran.py b/axelrod/moran.py index bd56df55d..7da4e8528 100644 --- a/axelrod/moran.py +++ b/axelrod/moran.py @@ -47,7 +47,7 @@ def _stochastic(self): A boolean to show whether a match between two players would be stochastic """ - is_stochastic(self.players, self.noise) + return is_stochastic(self.players, self.noise) def __next__(self): """Iterate the population: diff --git a/axelrod/tests/unit/test_moran.py b/axelrod/tests/unit/test_moran.py index ed400fb48..58fcf00e3 100644 --- a/axelrod/tests/unit/test_moran.py +++ b/axelrod/tests/unit/test_moran.py @@ -41,7 +41,7 @@ def test_two_players(self): mp = MoranProcess((p1, p2)) mp.play() self.assertEqual(len(mp), 5) - self.assertEqual(mp.winner, str(p2)) + self.assertEqual(mp.winning_strategy_name, str(p2)) def test_three_players(self): players = [axelrod.Cooperator(), axelrod.Cooperator(), @@ -50,7 +50,7 @@ def test_three_players(self): mp = MoranProcess(players) mp.play() self.assertEqual(len(mp), 7) - self.assertEqual(mp.winner, str(axelrod.Defector())) + self.assertEqual(mp.winning_strategy_name, str(axelrod.Defector())) def test_four_players(self): players = [axelrod.Cooperator() for _ in range(3)] @@ -59,7 +59,7 @@ def test_four_players(self): mp = MoranProcess(players) mp.play() self.assertEqual(len(mp), 9) - self.assertEqual(mp.winner, str(axelrod.Defector())) + self.assertEqual(mp.winning_strategy_name, str(axelrod.Defector())) @given(strategies=lists(sampled_from(axelrod.strategies), min_size=2, # Errors are returned if less than 2 strategies @@ -77,7 +77,7 @@ def test_property_players(self, strategies, rm): players = [s() for s in strategies] mp = MoranProcess(players) mp.play() - self.assertIn(mp.winner, [str(p) for p in players]) + self.assertIn(mp.winning_strategy_name, [str(p) for p in players]) def test_reset(self): p1, p2 = axelrod.Cooperator(), axelrod.Defector() @@ -88,5 +88,5 @@ def test_reset(self): self.assertEqual(len(mp.score_history), 3) mp.reset() self.assertEqual(len(mp), 1) - self.assertEqual(mp.winner, None) + self.assertEqual(mp.winning_strategy_name, None) self.assertEqual(mp.score_history, []) diff --git a/docs/tutorials/further_topics/index.rst b/docs/tutorials/further_topics/index.rst index bae96c213..2a365235d 100644 --- a/docs/tutorials/further_topics/index.rst +++ b/docs/tutorials/further_topics/index.rst @@ -11,6 +11,7 @@ Contents: classification_of_strategies.rst creating_matches.rst + moran.rst morality_metrics.rst probabilistict_end_tournaments.rst reading_and_writing_interactions.rst diff --git a/docs/tutorials/getting_started/moran.rst b/docs/tutorials/further_topics/moran.rst similarity index 100% rename from docs/tutorials/getting_started/moran.rst rename to docs/tutorials/further_topics/moran.rst index a95b22ab5..4802a608a 100644 --- a/docs/tutorials/getting_started/moran.rst +++ b/docs/tutorials/further_topics/moran.rst @@ -25,8 +25,8 @@ the library, proceed as follows:: >>> players = [axl.Cooperator(), axl.Defector(), ... axl.TitForTat(), axl.Grudger()] >>> mp = axl.MoranProcess(players) - >>> mp.winning_strategy_name # doctest: +SKIP >>> mp.play() + >>> mp.winning_strategy_name # doctest: +SKIP Defector You can access some attributes of the process, such as the number of rounds:: diff --git a/docs/tutorials/getting_started/index.rst b/docs/tutorials/getting_started/index.rst index 4d9d47df3..77a686d8e 100644 --- a/docs/tutorials/getting_started/index.rst +++ b/docs/tutorials/getting_started/index.rst @@ -15,5 +15,4 @@ Contents: visualising_results.rst noisy_tournaments.rst ecological_variant.rst - moran.rst command_line.rst