From 8f9e0e05d31d6d6914fd15d753197abc7b9fb5d8 Mon Sep 17 00:00:00 2001 From: dashiellfryer <59979761+dashiellfryer@users.noreply.github.com> Date: Mon, 3 Feb 2020 22:18:11 -0800 Subject: [PATCH 01/13] Add files via upload --- axelrod/strategies/Adaptivezerodet.py | 120 ++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 axelrod/strategies/Adaptivezerodet.py diff --git a/axelrod/strategies/Adaptivezerodet.py b/axelrod/strategies/Adaptivezerodet.py new file mode 100644 index 000000000..c870d0051 --- /dev/null +++ b/axelrod/strategies/Adaptivezerodet.py @@ -0,0 +1,120 @@ +from axelrod.action import Action +from axelrod.player import Player +from axelrod.random_ import random_choice +from typing import Tuple +from typing import List + +C, D = Action.C, Action.D + +class AdaptiveZeroDet(Player): + name = 'AdaptiveZeroDet' + classifier = { + 'memory_depth': float('inf'), # Long memory + 'stochastic': True, + 'makes_use_of': set(["game"]), + 'long_run_time': False, + 'inspects_source': False, + 'manipulates_source': False, + 'manipulates_state': False + } + def __init__(self, phi: float = 0.125, s: float = 0.5, l: float = 3, four_vector: Tuple[float, float, float, float] = None, initial: Action = C) -> None: + # This Keeps track of the parameter values (phi,s,l) as well as the four vector which makes final decisions. + self.scores = {C: 0, D: 0} + self.phi = phi + self.s = s + self.l = l + self._initial = initial + super().__init__() + + def set_four_vector(self, four_vector: Tuple[float, float, float, float]): + # This checks the four vector is usable and allows previous matches' output to be input for next four vector + if not all(0 <= p <= 1 for p in four_vector): + raise ValueError("An element in the probability vector, {}, is not between 0 and 1.".format(str(four_vector))) + self._four_vector = dict(zip([(C, C), (C, D), (D, C), (D, D)], map(float, four_vector))) + self.classifier['stochastic'] = any(0 < x < 1 for x in set(four_vector)) + + def score_last_round(self, opponent: Player): + # This gives the strategy the game attributes and allows the strategy to score itself properly + game = self.match_attributes["game"] + if len(self.history): + last_round = (self.history[-1], opponent.history[-1]) + scores = game.score(last_round) + self.scores[last_round[0]] += scores[0] + + def strategy(self, opponent: Player) -> Action: + s = self.s + phi = self.phi + l = self.l + d = randint(0, 9)/1000 # Selects random value to adjust s and l + if self.scores[C] > self.scores[D] & len(self.history): + # This checks scores to determine how to adjust s and l either up or down by d + # This also checks if the length of the game is long enough to start adjusting + self.l = l+d + l = self.l + # adjust l up + self.s = s-d + s = self.s + # adjust s down + R, P, S, T = self.match_attributes["game"].RPST() + phi = self.phi + s_min = - min((T - l) / (l - S), (l - S) / (T - l)) # Sets minimum for s + if (l > R) or (s < s_min): + # This checks that neither s nor l is leaving its range + if (l > R): + l = l-d + self.l = (l+R)/2 + l = self.l + # If l would leave its range instead its distance from its max is halved + if (s < s_min): + s = s+d + self.s = (s+s_min)/2 + s = self.s + # If s would leave its range instead its distance from its min is halved + p1 = 1 - phi * (1 - s) * (R - l) + p2 = 1 - phi * (s * (l - S) + (T - l)) + p3 = phi * ((l - S) + s * (T - l)) + p4 = phi * (1 - s) * (l - P) + four_vector = [p1, p2, p3, p4] + # Four vector is calculated with new parameters + self.set_four_vector(four_vector) + if not hasattr(self, "_four_vector"): + raise ValueError("_four_vector not yet set") + if len(opponent.history) == 0: + return self._initial + p = self._four_vector[(self.history[-1], opponent.history[-1])] + return random_choice(p) + else: + # This adjusts s and l in the opposite direction + self.l = l-d + l = self.l + # adjust l down + self.s = s+d + s = self.s + # adjust s up + R, P, S, T = self.match_attributes["game"].RPST() + phi = self.phi + if (l < P) or (s > 1): + # This checks that neither s nor l is leaving its range + if (l < P): + l = l+d + self.l = (l+P)/2 + l = self.l + # If l would leave its range instead its distance from its min is halved + if (s > 1): + s = s-d + self.s = (s+1)/2 + s = self.s + # If s would leave its range instead its distance from its max is halved + p1 = 1 - phi * (1 - s) * (R - l) + p2 = 1 - phi * (s * (l - S) + (T - l)) + p3 = phi * ((l - S) + s * (T - l)) + p4 = phi * (1 - s) * (l - P) + four_vector = [p1, p2, p3, p4] + # Four vector is calculated with new parameters + self.set_four_vector(four_vector) + if not hasattr(self, "_four_vector"): + raise ValueError("_four_vector not yet set") + if len(opponent.history) == 0: + return self._initial + p = self._four_vector[(self.history[-1], opponent.history[-1])] + return random_choice(p) From 3627b9a5afe84de0eb59457123923160c300f663 Mon Sep 17 00:00:00 2001 From: dashiellfryer <59979761+dashiellfryer@users.noreply.github.com> Date: Mon, 3 Feb 2020 22:22:39 -0800 Subject: [PATCH 02/13] Update _strategies.py --- axelrod/strategies/_strategies.py | 1 + 1 file changed, 1 insertion(+) diff --git a/axelrod/strategies/_strategies.py b/axelrod/strategies/_strategies.py index 5c618d875..a79727a58 100644 --- a/axelrod/strategies/_strategies.py +++ b/axelrod/strategies/_strategies.py @@ -1,5 +1,6 @@ from .adaptive import Adaptive from .adaptor import AdaptorBrief, AdaptorLong +from .Adaptivezerodet import AdaptiveZeroDet from .alternator import Alternator from .ann import ANN, EvolvableANN, EvolvedANN, EvolvedANN5, EvolvedANNNoise05 from .apavlov import APavlov2006, APavlov2011 From 863fb8acfd519efd6718ac75062057cadd7df77d Mon Sep 17 00:00:00 2001 From: dashiellfryer <59979761+dashiellfryer@users.noreply.github.com> Date: Mon, 3 Feb 2020 22:23:25 -0800 Subject: [PATCH 03/13] Rename Adaptivezerodet.py to adaptivezerodet.py --- axelrod/strategies/{Adaptivezerodet.py => adaptivezerodet.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename axelrod/strategies/{Adaptivezerodet.py => adaptivezerodet.py} (100%) diff --git a/axelrod/strategies/Adaptivezerodet.py b/axelrod/strategies/adaptivezerodet.py similarity index 100% rename from axelrod/strategies/Adaptivezerodet.py rename to axelrod/strategies/adaptivezerodet.py From f678fff409ed7936df396e7bca73b450e8235b14 Mon Sep 17 00:00:00 2001 From: dashiellfryer <59979761+dashiellfryer@users.noreply.github.com> Date: Mon, 3 Feb 2020 22:24:04 -0800 Subject: [PATCH 04/13] Update _strategies.py --- axelrod/strategies/_strategies.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/axelrod/strategies/_strategies.py b/axelrod/strategies/_strategies.py index a79727a58..fbdfe369e 100644 --- a/axelrod/strategies/_strategies.py +++ b/axelrod/strategies/_strategies.py @@ -1,6 +1,6 @@ from .adaptive import Adaptive from .adaptor import AdaptorBrief, AdaptorLong -from .Adaptivezerodet import AdaptiveZeroDet +from .adaptivezerodet import AdaptiveZeroDet from .alternator import Alternator from .ann import ANN, EvolvableANN, EvolvedANN, EvolvedANN5, EvolvedANNNoise05 from .apavlov import APavlov2006, APavlov2011 From 318e37ebf130b388f6e3b372f45aef3b455ccd55 Mon Sep 17 00:00:00 2001 From: dashiellfryer <59979761+dashiellfryer@users.noreply.github.com> Date: Mon, 3 Feb 2020 22:26:06 -0800 Subject: [PATCH 05/13] Update all_strategies.rst --- docs/reference/all_strategies.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/reference/all_strategies.rst b/docs/reference/all_strategies.rst index f933dd43e..ba4292f8e 100644 --- a/docs/reference/all_strategies.rst +++ b/docs/reference/all_strategies.rst @@ -8,6 +8,8 @@ Here are the docstrings of all the strategies in the library. .. automodule:: axelrod.strategies.adaptive :members: +.. automodule:: axelrod.strategies.adaptivezerodet + :members: .. automodule:: axelrod.strategies.adaptor :members: .. automodule:: axelrod.strategies.alternator From 9c9f3a7e911817dc10960f11897490dab5f39afd Mon Sep 17 00:00:00 2001 From: Marc Harper Date: Sun, 1 Mar 2020 20:48:57 -0800 Subject: [PATCH 06/13] Rework AdaptiveZeroDet to be a subclass of LRPlayer --- axelrod/strategies/adaptivezerodet.py | 245 +++++++++++++------------ axelrod/strategies/zero_determinant.py | 79 ++++++++ 2 files changed, 204 insertions(+), 120 deletions(-) diff --git a/axelrod/strategies/adaptivezerodet.py b/axelrod/strategies/adaptivezerodet.py index c870d0051..080b69588 100644 --- a/axelrod/strategies/adaptivezerodet.py +++ b/axelrod/strategies/adaptivezerodet.py @@ -1,120 +1,125 @@ -from axelrod.action import Action -from axelrod.player import Player -from axelrod.random_ import random_choice -from typing import Tuple -from typing import List - -C, D = Action.C, Action.D - -class AdaptiveZeroDet(Player): - name = 'AdaptiveZeroDet' - classifier = { - 'memory_depth': float('inf'), # Long memory - 'stochastic': True, - 'makes_use_of': set(["game"]), - 'long_run_time': False, - 'inspects_source': False, - 'manipulates_source': False, - 'manipulates_state': False - } - def __init__(self, phi: float = 0.125, s: float = 0.5, l: float = 3, four_vector: Tuple[float, float, float, float] = None, initial: Action = C) -> None: - # This Keeps track of the parameter values (phi,s,l) as well as the four vector which makes final decisions. - self.scores = {C: 0, D: 0} - self.phi = phi - self.s = s - self.l = l - self._initial = initial - super().__init__() - - def set_four_vector(self, four_vector: Tuple[float, float, float, float]): - # This checks the four vector is usable and allows previous matches' output to be input for next four vector - if not all(0 <= p <= 1 for p in four_vector): - raise ValueError("An element in the probability vector, {}, is not between 0 and 1.".format(str(four_vector))) - self._four_vector = dict(zip([(C, C), (C, D), (D, C), (D, D)], map(float, four_vector))) - self.classifier['stochastic'] = any(0 < x < 1 for x in set(four_vector)) - - def score_last_round(self, opponent: Player): - # This gives the strategy the game attributes and allows the strategy to score itself properly - game = self.match_attributes["game"] - if len(self.history): - last_round = (self.history[-1], opponent.history[-1]) - scores = game.score(last_round) - self.scores[last_round[0]] += scores[0] - - def strategy(self, opponent: Player) -> Action: - s = self.s - phi = self.phi - l = self.l - d = randint(0, 9)/1000 # Selects random value to adjust s and l - if self.scores[C] > self.scores[D] & len(self.history): - # This checks scores to determine how to adjust s and l either up or down by d - # This also checks if the length of the game is long enough to start adjusting - self.l = l+d - l = self.l - # adjust l up - self.s = s-d - s = self.s - # adjust s down - R, P, S, T = self.match_attributes["game"].RPST() - phi = self.phi - s_min = - min((T - l) / (l - S), (l - S) / (T - l)) # Sets minimum for s - if (l > R) or (s < s_min): - # This checks that neither s nor l is leaving its range - if (l > R): - l = l-d - self.l = (l+R)/2 - l = self.l - # If l would leave its range instead its distance from its max is halved - if (s < s_min): - s = s+d - self.s = (s+s_min)/2 - s = self.s - # If s would leave its range instead its distance from its min is halved - p1 = 1 - phi * (1 - s) * (R - l) - p2 = 1 - phi * (s * (l - S) + (T - l)) - p3 = phi * ((l - S) + s * (T - l)) - p4 = phi * (1 - s) * (l - P) - four_vector = [p1, p2, p3, p4] - # Four vector is calculated with new parameters - self.set_four_vector(four_vector) - if not hasattr(self, "_four_vector"): - raise ValueError("_four_vector not yet set") - if len(opponent.history) == 0: - return self._initial - p = self._four_vector[(self.history[-1], opponent.history[-1])] - return random_choice(p) - else: - # This adjusts s and l in the opposite direction - self.l = l-d - l = self.l - # adjust l down - self.s = s+d - s = self.s - # adjust s up - R, P, S, T = self.match_attributes["game"].RPST() - phi = self.phi - if (l < P) or (s > 1): - # This checks that neither s nor l is leaving its range - if (l < P): - l = l+d - self.l = (l+P)/2 - l = self.l - # If l would leave its range instead its distance from its min is halved - if (s > 1): - s = s-d - self.s = (s+1)/2 - s = self.s - # If s would leave its range instead its distance from its max is halved - p1 = 1 - phi * (1 - s) * (R - l) - p2 = 1 - phi * (s * (l - S) + (T - l)) - p3 = phi * ((l - S) + s * (T - l)) - p4 = phi * (1 - s) * (l - P) - four_vector = [p1, p2, p3, p4] - # Four vector is calculated with new parameters - self.set_four_vector(four_vector) - if not hasattr(self, "_four_vector"): - raise ValueError("_four_vector not yet set") - if len(opponent.history) == 0: - return self._initial - p = self._four_vector[(self.history[-1], opponent.history[-1])] - return random_choice(p) +from typing import Tuple + +from axelrod.action import Action +from axelrod.player import Player +from axelrod.random_ import random_choice + +C, D = Action.C, Action.D + + +class AdaptiveZeroDet(Player): + name = 'AdaptiveZeroDet' + classifier = { + 'memory_depth': float('inf'), # Long memory + 'stochastic': True, + 'makes_use_of': set(["game"]), + 'long_run_time': False, + 'inspects_source': False, + 'manipulates_source': False, + 'manipulates_state': False + } + + def __init__(self, phi: float = 0.125, s: float = 0.5, l: float = 3, + initial: Action = C) -> None: + # This Keeps track of the parameter values (phi,s,l) as well as the + # four vector which makes final decisions. + super().__init__() + self.scores = {C: 0, D: 0} + self.phi = phi + self.s = s + self.l = l + self._initial = initial + + def set_four_vector(self, four_vector: Tuple[float, float, float, float]): + # This checks the four vector is usable and allows previous matches' output to be input for next four vector + if not all(0 <= p <= 1 for p in four_vector): + raise ValueError( + "An element in the probability vector, {}, is not between 0 and 1.".format(str(four_vector))) + self._four_vector = dict(zip([(C, C), (C, D), (D, C), (D, D)], map(float, four_vector))) + self.classifier['stochastic'] = any(0 < x < 1 for x in set(four_vector)) + + def score_last_round(self, opponent: Player): + # This gives the strategy the game attributes and allows the strategy to score itself properly + game = self.match_attributes["game"] + if len(self.history): + last_round = (self.history[-1], opponent.history[-1]) + scores = game.score(last_round) + self.scores[last_round[0]] += scores[0] + + def strategy(self, opponent: Player) -> Action: + s = self.s + phi = self.phi + l = self.l + d = randint(0, 9) / 1000 # Selects random value to adjust s and l + if self.scores[C] > self.scores[D] & len(self.history): + # This checks scores to determine how to adjust s and l either up or down by d + # This also checks if the length of the game is long enough to start adjusting + self.l = l + d + l = self.l + # adjust l up + self.s = s - d + s = self.s + # adjust s down + R, P, S, T = self.match_attributes["game"].RPST() + phi = self.phi + s_min = - min((T - l) / (l - S), (l - S) / (T - l)) # Sets minimum for s + if (l > R) or (s < s_min): + # This checks that neither s nor l is leaving its range + if (l > R): + l = l - d + self.l = (l + R) / 2 + l = self.l + # If l would leave its range instead its distance from its max is halved + if (s < s_min): + s = s + d + self.s = (s + s_min) / 2 + s = self.s + # If s would leave its range instead its distance from its min is halved + p1 = 1 - phi * (1 - s) * (R - l) + p2 = 1 - phi * (s * (l - S) + (T - l)) + p3 = phi * ((l - S) + s * (T - l)) + p4 = phi * (1 - s) * (l - P) + four_vector = [p1, p2, p3, p4] + # Four vector is calculated with new parameters + self.set_four_vector(four_vector) + if not hasattr(self, "_four_vector"): + raise ValueError("_four_vector not yet set") + if len(opponent.history) == 0: + return self._initial + p = self._four_vector[(self.history[-1], opponent.history[-1])] + return random_choice(p) + else: + # This adjusts s and l in the opposite direction + self.l = l - d + l = self.l + # adjust l down + self.s = s + d + s = self.s + # adjust s up + R, P, S, T = self.match_attributes["game"].RPST() + phi = self.phi + if (l < P) or (s > 1): + # This checks that neither s nor l is leaving its range + if (l < P): + l = l + d + self.l = (l + P) / 2 + l = self.l + # If l would leave its range instead its distance from its min is halved + if (s > 1): + s = s - d + self.s = (s + 1) / 2 + s = self.s + # If s would leave its range instead its distance from its max is halved + p1 = 1 - phi * (1 - s) * (R - l) + p2 = 1 - phi * (s * (l - S) + (T - l)) + p3 = phi * ((l - S) + s * (T - l)) + p4 = phi * (1 - s) * (l - P) + four_vector = [p1, p2, p3, p4] + # Four vector is calculated with new parameters + self.set_four_vector(four_vector) + if not hasattr(self, "_four_vector"): + raise ValueError("_four_vector not yet set") + if len(opponent.history) == 0: + return self._initial + p = self._four_vector[(self.history[-1], opponent.history[-1])] + return random_choice(p) diff --git a/axelrod/strategies/zero_determinant.py b/axelrod/strategies/zero_determinant.py index ea5a16c80..43c33ae19 100644 --- a/axelrod/strategies/zero_determinant.py +++ b/axelrod/strategies/zero_determinant.py @@ -1,4 +1,6 @@ +from random import randint from axelrod.action import Action +from axelrod.player import Player from .memoryone import MemoryOnePlayer @@ -225,3 +227,80 @@ class ZDSet2(LRPlayer): def __init__(self, phi: float = 1 / 4, s: float = 0.0, l: float = 2) -> None: super().__init__(phi, s, l) + + +class AdaptiveZeroDet(LRPlayer): + name = 'AdaptiveZeroDet' + classifier = { + 'memory_depth': float('inf'), # Long memory + 'stochastic': True, + 'makes_use_of': set(["game"]), + 'long_run_time': False, + 'inspects_source': False, + 'manipulates_source': False, + 'manipulates_state': False + } + + def __init__(self, phi: float = 0.125, s: float = 0.5, l: float = 3, + initial: Action = C) -> None: + # This Keeps track of the parameter values (phi,s,l) as well as the + # four vector which makes final decisions. + super().__init__(phi=phi, s=s, l=l) + self._scores = {C: 0, D: 0} + self._initial = initial + + def score_last_round(self, opponent: Player): + """This gives the strategy the game attributes and allows the strategy + to score itself properly.""" + game = self.match_attributes["game"] + if len(self.history): + last_round = (self.history[-1], opponent.history[-1]) + scores = game.score(last_round) + self._scores[last_round[0]] += scores[0] + + def _adjust_parameters(self): + d = randint(0, 9) / 1000 # Selects random value to adjust s and l + + if self._scores[C] > self._scores[D]: + # This checks scores to determine how to adjust s and l either + # up or down by d + self.l = self.l + d + self.s = self.s - d + R, P, S, T = self.match_attributes["game"].RPST() + l = self.l + s = self.s + s_min = - min((T - l) / (l - S), (l - S) / (T - l)) # Sets minimum for s + if (l > R) or (s < s_min): + # This checks that neither s nor l is leaving its range + # If l would leave its range instead its distance from its max is halved + if l > R: + l = l - d + self.l = (l + R) / 2 + # If s would leave its range instead its distance from its min is halved + if s < s_min: + s = s + d + self.s = (s + s_min) / 2 + else: + # This adjusts s and l in the opposite direction + self.l = self.l - d + self.s = self.s + d + R, P, S, T = self.match_attributes["game"].RPST() + l = self.l + s = self.s + if (l < P) or (s > 1): + # This checks that neither s nor l is leaving its range + if l < P: + l = l + d + self.l = (l + P) / 2 + # If l would leave its range instead its distance from its min is halved + if s > 1: + s = s - d + self.s = (s + 1) / 2 + # Update the four vector for the new l and s values + self.receive_match_attributes() + + def strategy(self, opponent: Player) -> Action: + if len(self.history) > 0: + self.score_last_round(opponent) + self._adjust_parameters() + return super().strategy(opponent) From 60788b7be21c186d7cae7dfec25767c40ae95316 Mon Sep 17 00:00:00 2001 From: Marc Harper Date: Sun, 1 Mar 2020 21:01:11 -0800 Subject: [PATCH 07/13] Remove earlier version of AdaptiveZeroDet and add a basic test --- axelrod/strategies/_strategies.py | 2 +- axelrod/strategies/adaptivezerodet.py | 125 ------------------ axelrod/strategies/zero_determinant.py | 4 +- .../tests/strategies/test_zero_determinant.py | 24 ++++ 4 files changed, 27 insertions(+), 128 deletions(-) delete mode 100644 axelrod/strategies/adaptivezerodet.py diff --git a/axelrod/strategies/_strategies.py b/axelrod/strategies/_strategies.py index fbdfe369e..959fff9a0 100644 --- a/axelrod/strategies/_strategies.py +++ b/axelrod/strategies/_strategies.py @@ -1,6 +1,5 @@ from .adaptive import Adaptive from .adaptor import AdaptorBrief, AdaptorLong -from .adaptivezerodet import AdaptiveZeroDet from .alternator import Alternator from .ann import ANN, EvolvableANN, EvolvedANN, EvolvedANN5, EvolvedANNNoise05 from .apavlov import APavlov2006, APavlov2011 @@ -235,6 +234,7 @@ ZDGen2, ZDMischief, ZDSet2, + AdaptiveZeroDet, ) # Note: Meta* strategies are handled in .__init__.py diff --git a/axelrod/strategies/adaptivezerodet.py b/axelrod/strategies/adaptivezerodet.py deleted file mode 100644 index 080b69588..000000000 --- a/axelrod/strategies/adaptivezerodet.py +++ /dev/null @@ -1,125 +0,0 @@ -from typing import Tuple - -from axelrod.action import Action -from axelrod.player import Player -from axelrod.random_ import random_choice - -C, D = Action.C, Action.D - - -class AdaptiveZeroDet(Player): - name = 'AdaptiveZeroDet' - classifier = { - 'memory_depth': float('inf'), # Long memory - 'stochastic': True, - 'makes_use_of': set(["game"]), - 'long_run_time': False, - 'inspects_source': False, - 'manipulates_source': False, - 'manipulates_state': False - } - - def __init__(self, phi: float = 0.125, s: float = 0.5, l: float = 3, - initial: Action = C) -> None: - # This Keeps track of the parameter values (phi,s,l) as well as the - # four vector which makes final decisions. - super().__init__() - self.scores = {C: 0, D: 0} - self.phi = phi - self.s = s - self.l = l - self._initial = initial - - def set_four_vector(self, four_vector: Tuple[float, float, float, float]): - # This checks the four vector is usable and allows previous matches' output to be input for next four vector - if not all(0 <= p <= 1 for p in four_vector): - raise ValueError( - "An element in the probability vector, {}, is not between 0 and 1.".format(str(four_vector))) - self._four_vector = dict(zip([(C, C), (C, D), (D, C), (D, D)], map(float, four_vector))) - self.classifier['stochastic'] = any(0 < x < 1 for x in set(four_vector)) - - def score_last_round(self, opponent: Player): - # This gives the strategy the game attributes and allows the strategy to score itself properly - game = self.match_attributes["game"] - if len(self.history): - last_round = (self.history[-1], opponent.history[-1]) - scores = game.score(last_round) - self.scores[last_round[0]] += scores[0] - - def strategy(self, opponent: Player) -> Action: - s = self.s - phi = self.phi - l = self.l - d = randint(0, 9) / 1000 # Selects random value to adjust s and l - if self.scores[C] > self.scores[D] & len(self.history): - # This checks scores to determine how to adjust s and l either up or down by d - # This also checks if the length of the game is long enough to start adjusting - self.l = l + d - l = self.l - # adjust l up - self.s = s - d - s = self.s - # adjust s down - R, P, S, T = self.match_attributes["game"].RPST() - phi = self.phi - s_min = - min((T - l) / (l - S), (l - S) / (T - l)) # Sets minimum for s - if (l > R) or (s < s_min): - # This checks that neither s nor l is leaving its range - if (l > R): - l = l - d - self.l = (l + R) / 2 - l = self.l - # If l would leave its range instead its distance from its max is halved - if (s < s_min): - s = s + d - self.s = (s + s_min) / 2 - s = self.s - # If s would leave its range instead its distance from its min is halved - p1 = 1 - phi * (1 - s) * (R - l) - p2 = 1 - phi * (s * (l - S) + (T - l)) - p3 = phi * ((l - S) + s * (T - l)) - p4 = phi * (1 - s) * (l - P) - four_vector = [p1, p2, p3, p4] - # Four vector is calculated with new parameters - self.set_four_vector(four_vector) - if not hasattr(self, "_four_vector"): - raise ValueError("_four_vector not yet set") - if len(opponent.history) == 0: - return self._initial - p = self._four_vector[(self.history[-1], opponent.history[-1])] - return random_choice(p) - else: - # This adjusts s and l in the opposite direction - self.l = l - d - l = self.l - # adjust l down - self.s = s + d - s = self.s - # adjust s up - R, P, S, T = self.match_attributes["game"].RPST() - phi = self.phi - if (l < P) or (s > 1): - # This checks that neither s nor l is leaving its range - if (l < P): - l = l + d - self.l = (l + P) / 2 - l = self.l - # If l would leave its range instead its distance from its min is halved - if (s > 1): - s = s - d - self.s = (s + 1) / 2 - s = self.s - # If s would leave its range instead its distance from its max is halved - p1 = 1 - phi * (1 - s) * (R - l) - p2 = 1 - phi * (s * (l - S) + (T - l)) - p3 = phi * ((l - S) + s * (T - l)) - p4 = phi * (1 - s) * (l - P) - four_vector = [p1, p2, p3, p4] - # Four vector is calculated with new parameters - self.set_four_vector(four_vector) - if not hasattr(self, "_four_vector"): - raise ValueError("_four_vector not yet set") - if len(opponent.history) == 0: - return self._initial - p = self._four_vector[(self.history[-1], opponent.history[-1])] - return random_choice(p) diff --git a/axelrod/strategies/zero_determinant.py b/axelrod/strategies/zero_determinant.py index 43c33ae19..f5d63cf03 100644 --- a/axelrod/strategies/zero_determinant.py +++ b/axelrod/strategies/zero_determinant.py @@ -1,4 +1,4 @@ -from random import randint +import random from axelrod.action import Action from axelrod.player import Player @@ -259,7 +259,7 @@ def score_last_round(self, opponent: Player): self._scores[last_round[0]] += scores[0] def _adjust_parameters(self): - d = randint(0, 9) / 1000 # Selects random value to adjust s and l + d = random.randint(0, 9) / 1000 # Selects random value to adjust s and l if self._scores[C] > self._scores[D]: # This checks scores to determine how to adjust s and l either diff --git a/axelrod/tests/strategies/test_zero_determinant.py b/axelrod/tests/strategies/test_zero_determinant.py index a6f479124..277f34d3b 100644 --- a/axelrod/tests/strategies/test_zero_determinant.py +++ b/axelrod/tests/strategies/test_zero_determinant.py @@ -317,3 +317,27 @@ def test_strategy(self): actions = [(C, D), (D, C), (D, D), (D, C), (D, D), (D, C)] self.versus_test(opponent=axelrod.CyclerDC(), expected_actions=actions, seed=5) + + +class TestAdaptiveZeroDet(TestPlayer): + """ + Tests for the N Tit(s) For M Tat(s) strategy + """ + + name = "AdaptiveZeroDet: 0.125, 0.5, 3, C" + player = axelrod.AdaptiveZeroDet + expected_classifier = { + "memory_depth": float('inf'), + "stochastic": True, + "makes_use_of": set(['game']), + "long_run_time": False, + "inspects_source": False, + "manipulates_source": False, + "manipulates_state": False, + } + + def test_strategy(self): + actions = [(C, C), (C, D), (C, C), (C, D), (C, C)] + self.versus_test( + axelrod.Alternator(), expected_actions=actions + ) From f2c41e295b98e45d332fd2f570d4faaf62e6d24b Mon Sep 17 00:00:00 2001 From: Marc Harper Date: Tue, 10 Mar 2020 09:23:48 -0700 Subject: [PATCH 08/13] Update doc strings --- axelrod/strategies/grudger.py | 2 +- axelrod/strategies/zero_determinant.py | 6 ++++++ axelrod/tests/strategies/test_zero_determinant.py | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/axelrod/strategies/grudger.py b/axelrod/strategies/grudger.py index 61215bb9a..af844bc3e 100644 --- a/axelrod/strategies/grudger.py +++ b/axelrod/strategies/grudger.py @@ -64,8 +64,8 @@ class ForgetfulGrudger(Player): def __init__(self) -> None: """Initialised the player.""" - super().__init__() self.mem_length = 10 + super().__init__() self.grudged = False self.grudge_memory = 0 diff --git a/axelrod/strategies/zero_determinant.py b/axelrod/strategies/zero_determinant.py index f5d63cf03..31e1bfca4 100644 --- a/axelrod/strategies/zero_determinant.py +++ b/axelrod/strategies/zero_determinant.py @@ -230,6 +230,12 @@ def __init__(self, phi: float = 1 / 4, s: float = 0.0, l: float = 2) -> None: class AdaptiveZeroDet(LRPlayer): + """A Strategy that uses a zero determinant structure that updates + its parameters after each round of play. + + Names: + - AdaptiveZeroDet by Emmanuel Estrada and Dashiell Fryer + """ name = 'AdaptiveZeroDet' classifier = { 'memory_depth': float('inf'), # Long memory diff --git a/axelrod/tests/strategies/test_zero_determinant.py b/axelrod/tests/strategies/test_zero_determinant.py index 277f34d3b..296ca01a9 100644 --- a/axelrod/tests/strategies/test_zero_determinant.py +++ b/axelrod/tests/strategies/test_zero_determinant.py @@ -321,7 +321,7 @@ def test_strategy(self): class TestAdaptiveZeroDet(TestPlayer): """ - Tests for the N Tit(s) For M Tat(s) strategy + Test the AdaptiveZeroDet strategy. """ name = "AdaptiveZeroDet: 0.125, 0.5, 3, C" From 6982c058cb63db0b9b49b63e5bd90540edf57d8c Mon Sep 17 00:00:00 2001 From: Marc Harper Date: Tue, 10 Mar 2020 09:53:29 -0700 Subject: [PATCH 09/13] Refactor strategy slightly and update a test --- axelrod/strategies/zero_determinant.py | 59 ++++++++----------- .../tests/strategies/test_zero_determinant.py | 9 +-- 2 files changed, 30 insertions(+), 38 deletions(-) diff --git a/axelrod/strategies/zero_determinant.py b/axelrod/strategies/zero_determinant.py index 31e1bfca4..11e8ed9c3 100644 --- a/axelrod/strategies/zero_determinant.py +++ b/axelrod/strategies/zero_determinant.py @@ -230,7 +230,7 @@ def __init__(self, phi: float = 1 / 4, s: float = 0.0, l: float = 2) -> None: class AdaptiveZeroDet(LRPlayer): - """A Strategy that uses a zero determinant structure that updates + """A strategy that uses a zero determinant structure that updates its parameters after each round of play. Names: @@ -266,43 +266,34 @@ def score_last_round(self, opponent: Player): def _adjust_parameters(self): d = random.randint(0, 9) / 1000 # Selects random value to adjust s and l - + R, P, S, T = self.match_attributes["game"].RPST() + l = self.l + s = self.s if self._scores[C] > self._scores[D]: # This checks scores to determine how to adjust s and l either - # up or down by d - self.l = self.l + d - self.s = self.s - d - R, P, S, T = self.match_attributes["game"].RPST() - l = self.l - s = self.s - s_min = - min((T - l) / (l - S), (l - S) / (T - l)) # Sets minimum for s - if (l > R) or (s < s_min): - # This checks that neither s nor l is leaving its range - # If l would leave its range instead its distance from its max is halved - if l > R: - l = l - d - self.l = (l + R) / 2 - # If s would leave its range instead its distance from its min is halved - if s < s_min: - s = s + d - self.s = (s + s_min) / 2 + # up or down by d, making sure not to exceed bounds. + if l + d > R: + l = (l + R) / 2 + else: + l += d + s_min = - min((T - l) / (l - S), (l - S) / (T - l)) + if s - d < s_min: + s = (s + s_min) / 2 + else: + s = s - d else: - # This adjusts s and l in the opposite direction - self.l = self.l - d - self.s = self.s + d - R, P, S, T = self.match_attributes["game"].RPST() - l = self.l - s = self.s - if (l < P) or (s > 1): - # This checks that neither s nor l is leaving its range - if l < P: - l = l + d - self.l = (l + P) / 2 - # If l would leave its range instead its distance from its min is halved - if s > 1: - s = s - d - self.s = (s + 1) / 2 + # This adjusts s and l in the opposite direction, also checking distance + if l - d < P: + l = (l + P) / 2 + else: + l -= d + if s + d > 1: + s = (s + 1) / 2 + else: + s += d # Update the four vector for the new l and s values + self.l = l + self.s = s self.receive_match_attributes() def strategy(self, opponent: Player) -> Action: diff --git a/axelrod/tests/strategies/test_zero_determinant.py b/axelrod/tests/strategies/test_zero_determinant.py index 296ca01a9..c3ce06dac 100644 --- a/axelrod/tests/strategies/test_zero_determinant.py +++ b/axelrod/tests/strategies/test_zero_determinant.py @@ -337,7 +337,8 @@ class TestAdaptiveZeroDet(TestPlayer): } def test_strategy(self): - actions = [(C, C), (C, D), (C, C), (C, D), (C, C)] - self.versus_test( - axelrod.Alternator(), expected_actions=actions - ) + # Play some matches to trigger all the if-then branches for coverage + for seed in range(20): + axelrod.seed(seed) + match = axelrod.Match((self.player(), self.Alternator()), turns=20) + match.play() From 9848f38047b6b6e394a8bb80ddda3cca216cdaa3 Mon Sep 17 00:00:00 2001 From: Marc Harper Date: Tue, 10 Mar 2020 10:08:05 -0700 Subject: [PATCH 10/13] Update all_strategies.rst for the strategy move --- axelrod/strategies/_strategies.py | 1 + docs/reference/all_strategies.rst | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/axelrod/strategies/_strategies.py b/axelrod/strategies/_strategies.py index 959fff9a0..6f3b94d4e 100644 --- a/axelrod/strategies/_strategies.py +++ b/axelrod/strategies/_strategies.py @@ -243,6 +243,7 @@ all_strategies = [ Adaptive, AdaptiveTitForTat, + AdaptiveZeroDet, AdaptorBrief, AdaptorLong, Aggravater, diff --git a/docs/reference/all_strategies.rst b/docs/reference/all_strategies.rst index ba4292f8e..f933dd43e 100644 --- a/docs/reference/all_strategies.rst +++ b/docs/reference/all_strategies.rst @@ -8,8 +8,6 @@ Here are the docstrings of all the strategies in the library. .. automodule:: axelrod.strategies.adaptive :members: -.. automodule:: axelrod.strategies.adaptivezerodet - :members: .. automodule:: axelrod.strategies.adaptor :members: .. automodule:: axelrod.strategies.alternator From 73e871fbb45300451949bb734e66a40923ccbad0 Mon Sep 17 00:00:00 2001 From: Marc Harper Date: Tue, 10 Mar 2020 10:19:30 -0700 Subject: [PATCH 11/13] Update doctest --- docs/tutorials/advanced/classification_of_strategies.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorials/advanced/classification_of_strategies.rst b/docs/tutorials/advanced/classification_of_strategies.rst index 9e7db52b4..36eae01a1 100644 --- a/docs/tutorials/advanced/classification_of_strategies.rst +++ b/docs/tutorials/advanced/classification_of_strategies.rst @@ -47,7 +47,7 @@ strategies:: ... } >>> strategies = axl.filtered_strategies(filterset) >>> len(strategies) - 88 + 89 Or, to find out how many strategies only use 1 turn worth of memory to make a decision:: From 35de1e40b09a6ddfa147481f519a2af5085720ed Mon Sep 17 00:00:00 2001 From: Marc Harper Date: Tue, 10 Mar 2020 10:54:25 -0700 Subject: [PATCH 12/13] Fix test --- axelrod/tests/strategies/test_zero_determinant.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/axelrod/tests/strategies/test_zero_determinant.py b/axelrod/tests/strategies/test_zero_determinant.py index c3ce06dac..3160737b7 100644 --- a/axelrod/tests/strategies/test_zero_determinant.py +++ b/axelrod/tests/strategies/test_zero_determinant.py @@ -340,5 +340,5 @@ def test_strategy(self): # Play some matches to trigger all the if-then branches for coverage for seed in range(20): axelrod.seed(seed) - match = axelrod.Match((self.player(), self.Alternator()), turns=20) + match = axelrod.Match((self.player(), axelrod.Alternator()), turns=20) match.play() From 0d684b3273d15e3e0ecf70be8e893fffc5277c84 Mon Sep 17 00:00:00 2001 From: Marc Harper Date: Tue, 10 Mar 2020 20:48:01 -0700 Subject: [PATCH 13/13] Update test for coverage --- .../tests/strategies/test_zero_determinant.py | 28 +++++++++++++++---- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/axelrod/tests/strategies/test_zero_determinant.py b/axelrod/tests/strategies/test_zero_determinant.py index 3160737b7..7a7f2a39e 100644 --- a/axelrod/tests/strategies/test_zero_determinant.py +++ b/axelrod/tests/strategies/test_zero_determinant.py @@ -337,8 +337,26 @@ class TestAdaptiveZeroDet(TestPlayer): } def test_strategy(self): - # Play some matches to trigger all the if-then branches for coverage - for seed in range(20): - axelrod.seed(seed) - match = axelrod.Match((self.player(), axelrod.Alternator()), turns=20) - match.play() + R, P, S, T = axelrod.Game().RPST() + + player = self.player(l=R) + axelrod.seed(0) + match = axelrod.Match((player, axelrod.Alternator()), turns=200) + match.play() + + player = self.player(l=P) + axelrod.seed(0) + match = axelrod.Match((player, axelrod.Alternator()), turns=200) + match.play() + + player = self.player(s=1) + axelrod.seed(0) + match = axelrod.Match((player, axelrod.Alternator()), turns=200) + match.play() + + l = 2 + s_min = - min((T - l) / (l - S), (l - S) / (T - l)) + player = self.player(s=s_min, l=2) + axelrod.seed(0) + match = axelrod.Match((player, axelrod.Alternator()), turns=200) + match.play()