From 47d8a067bcc415b22ed7005c098e76bdab3ab656 Mon Sep 17 00:00:00 2001 From: AI-MoonDongWon Date: Wed, 22 Jan 2025 17:08:09 +0900 Subject: [PATCH 01/12] Add Momentum strategy --- axelrod/strategies/momentum.py | 64 +++++++++++++++ axelrod/tests/strategies/test_momentum.py | 99 +++++++++++++++++++++++ 2 files changed, 163 insertions(+) create mode 100644 axelrod/strategies/momentum.py create mode 100644 axelrod/tests/strategies/test_momentum.py diff --git a/axelrod/strategies/momentum.py b/axelrod/strategies/momentum.py new file mode 100644 index 000000000..15f2b0a46 --- /dev/null +++ b/axelrod/strategies/momentum.py @@ -0,0 +1,64 @@ +from axelrod.action import Action +from axelrod.player import Player + +C, D = Action.C, Action.D + + +class Momentum(Player): + """ + This strategy is inspired by the concept of Gradual and the mathematical foundation of + the Momentum optimizer used in deep learning. + + The idea is that trust (or cooperation) evolves dynamically. A shift in trust can + create significant and rapid changes in the player's behavior, much like how momentum + responds to gradients in optimization. + + Names: + - Momentum: Original name by Dong Won Moon + + Notes: + - While I am an undergraduate student with limited experience in game theory, I + believe this strategy has potential in various scenarios. + - I encourage experts to explore and extend this idea in other contexts, such as + environments with noise, one-hot vectorization approaches at multiple actions. + + """ + + name = "Momentum" + classifier = { + "memory_depth": float("inf"), + "stochastic": False, + "long_run_time": False, + "inspects_source": False, + "manipulates_source": False, + "manipulates_state": False, + } + + def __init__( + self, + alpha=0.9914655399877477, # Optimized by Genetic Algorithm. You can try to adapt it to any Env. + threshold=0.9676595613724907, # This one too + ) -> None: + super().__init__() + self.alpha = alpha + self.threshold = threshold + self.momentum = 1.0 + + def __repr__(self): + return f"Momentum: {self.alpha}, {self.threshold}" + + def update_momentum(self, opponent_action): + action_value = 1 if opponent_action == C else 0 + # If the opponent defects, the momentum decreases, reflecting a loss of trust. + self.momentum = ( + self.alpha * self.momentum + (1 - self.alpha) * action_value + ) + + def strategy(self, opponent: Player) -> Action: + if len(self.history) == 0: + self.momentum = 1.0 + return C + + else: + self.update_momentum(opponent.history[-1]) + return C if self.momentum >= self.threshold else D diff --git a/axelrod/tests/strategies/test_momentum.py b/axelrod/tests/strategies/test_momentum.py new file mode 100644 index 000000000..f1207dd8f --- /dev/null +++ b/axelrod/tests/strategies/test_momentum.py @@ -0,0 +1,99 @@ +import axelrod as axl +from axelrod import Action +from axelrod.tests.strategies.test_player import TestPlayer +from axelrod.strategies.momentum import Momentum + +C, D = Action.C, Action.D + +class TestMomentum(TestPlayer): + name = "Momentum" + player = Momentum + expected_classifier = { + "memory_depth": float("inf"), + "stochastic": False, + "long_run_time": False, + "inspects_source": False, + "manipulates_source": False, + "manipulates_state": False, + } + + def test_initialisation(self): + player = self.player(alpha=0.9, threshold=0.8) + self.assertEqual(player.alpha, 0.9) + self.assertEqual(player.threshold, 0.8) + self.assertEqual(player.momentum, 1.0) + + def test_repr(self): + player = self.player(alpha=0.9, threshold=0.8) + self.assertEqual(repr(player), "Momentum: 0.9, 0.8") + + def test_strategy(self): + actions = [(C, C)] + self.versus_test( + axl.MockPlayer(actions=[C]), + expected_actions=actions, + init_kwargs={"alpha": 0.5, + "threshold": 0.5 + }, + attrs={"momentum": 1.0} + ) + + actions = [(C, D), (C, D), (D, D)] + self.versus_test( + axl.MockPlayer(actions=[D]), + expected_actions=actions, + init_kwargs={"alpha": 0.5, + "threshold": 0.5 + }, + attrs={"momentum": 0.25} + ) + + def test_vs_alternator(self): + actions = [(C, C), (C, D), (C, C), (C, D), (D, C)] + self.versus_test(axl.Alternator(), + expected_actions=actions, + init_kwargs={"alpha": 0.5, + "threshold": 0.5 + }, + ) + + def test_vs_cooperator(self): + actions = [(C, C), (C, C), (C, C), (C, C), (C, C)] + self.versus_test(axl.Cooperator(), + expected_actions=actions, + init_kwargs={"alpha": 0.5, + "threshold": 0.5 + }, + ) + + def test_vs_defector(self): + actions = [(C, D), (C, D), (D, D), (D, D), (D, D)] + self.versus_test(axl.Defector(), + expected_actions=actions, + init_kwargs={"alpha": 0.5, + "threshold": 0.5 + }, + ) + + def test_vs_random(self): + # We can also test against random strategies + actions = [(C, D), (C, C), (C, C), (C, D), (D, D)] + self.versus_test(axl.Random(), + expected_actions=actions, + seed=17, + init_kwargs={"alpha": 0.5, + "threshold": 0.5 + }, + ) + + def test_vs_random2(self): + actions = [(C, C), (C, C), (C, C), (C, C)] + self.versus_test(axl.Random(), + expected_actions=actions, + seed=3, + init_kwargs={"alpha": 0.5, + "threshold": 0.5 + }, + ) + + From 6c20f54f766cc782808dca356d5f910c83920361 Mon Sep 17 00:00:00 2001 From: AI-MoonDongWon Date: Thu, 23 Jan 2025 15:11:41 +0900 Subject: [PATCH 02/12] Format code with black and isort --- axelrod/strategies/momentum.py | 2 +- axelrod/tests/strategies/test_momentum.py | 71 ++++++++++------------- 2 files changed, 31 insertions(+), 42 deletions(-) diff --git a/axelrod/strategies/momentum.py b/axelrod/strategies/momentum.py index 15f2b0a46..36405c13f 100644 --- a/axelrod/strategies/momentum.py +++ b/axelrod/strategies/momentum.py @@ -37,7 +37,7 @@ class Momentum(Player): def __init__( self, alpha=0.9914655399877477, # Optimized by Genetic Algorithm. You can try to adapt it to any Env. - threshold=0.9676595613724907, # This one too + threshold=0.9676595613724907, # This one too ) -> None: super().__init__() self.alpha = alpha diff --git a/axelrod/tests/strategies/test_momentum.py b/axelrod/tests/strategies/test_momentum.py index f1207dd8f..4dddc867b 100644 --- a/axelrod/tests/strategies/test_momentum.py +++ b/axelrod/tests/strategies/test_momentum.py @@ -1,10 +1,11 @@ import axelrod as axl from axelrod import Action -from axelrod.tests.strategies.test_player import TestPlayer from axelrod.strategies.momentum import Momentum +from axelrod.tests.strategies.test_player import TestPlayer C, D = Action.C, Action.D + class TestMomentum(TestPlayer): name = "Momentum" player = Momentum @@ -32,68 +33,56 @@ def test_strategy(self): self.versus_test( axl.MockPlayer(actions=[C]), expected_actions=actions, - init_kwargs={"alpha": 0.5, - "threshold": 0.5 - }, - attrs={"momentum": 1.0} + init_kwargs={"alpha": 0.5, "threshold": 0.5}, + attrs={"momentum": 1.0}, ) - + actions = [(C, D), (C, D), (D, D)] self.versus_test( axl.MockPlayer(actions=[D]), expected_actions=actions, - init_kwargs={"alpha": 0.5, - "threshold": 0.5 - }, - attrs={"momentum": 0.25} + init_kwargs={"alpha": 0.5, "threshold": 0.5}, + attrs={"momentum": 0.25}, ) - + def test_vs_alternator(self): actions = [(C, C), (C, D), (C, C), (C, D), (D, C)] - self.versus_test(axl.Alternator(), - expected_actions=actions, - init_kwargs={"alpha": 0.5, - "threshold": 0.5 - }, + self.versus_test( + axl.Alternator(), + expected_actions=actions, + init_kwargs={"alpha": 0.5, "threshold": 0.5}, ) def test_vs_cooperator(self): actions = [(C, C), (C, C), (C, C), (C, C), (C, C)] - self.versus_test(axl.Cooperator(), - expected_actions=actions, - init_kwargs={"alpha": 0.5, - "threshold": 0.5 - }, + self.versus_test( + axl.Cooperator(), + expected_actions=actions, + init_kwargs={"alpha": 0.5, "threshold": 0.5}, ) def test_vs_defector(self): actions = [(C, D), (C, D), (D, D), (D, D), (D, D)] - self.versus_test(axl.Defector(), - expected_actions=actions, - init_kwargs={"alpha": 0.5, - "threshold": 0.5 - }, + self.versus_test( + axl.Defector(), + expected_actions=actions, + init_kwargs={"alpha": 0.5, "threshold": 0.5}, ) def test_vs_random(self): - # We can also test against random strategies actions = [(C, D), (C, C), (C, C), (C, D), (D, D)] - self.versus_test(axl.Random(), - expected_actions=actions, - seed=17, - init_kwargs={"alpha": 0.5, - "threshold": 0.5 - }, + self.versus_test( + axl.Random(), + expected_actions=actions, + seed=17, + init_kwargs={"alpha": 0.5, "threshold": 0.5}, ) def test_vs_random2(self): actions = [(C, C), (C, C), (C, C), (C, C)] - self.versus_test(axl.Random(), - expected_actions=actions, - seed=3, - init_kwargs={"alpha": 0.5, - "threshold": 0.5 - }, + self.versus_test( + axl.Random(), + expected_actions=actions, + seed=3, + init_kwargs={"alpha": 0.5, "threshold": 0.5}, ) - - From 9e8092c672455d03004cd746c4a3ae4e3d1e855c Mon Sep 17 00:00:00 2001 From: AI-MoonDongWon Date: Thu, 23 Jan 2025 15:14:53 +0900 Subject: [PATCH 03/12] Remove unnecessary DataFrame import --- axelrod/tests/unit/test_resultset.py | 1 - 1 file changed, 1 deletion(-) diff --git a/axelrod/tests/unit/test_resultset.py b/axelrod/tests/unit/test_resultset.py index 586d38603..011f34f23 100644 --- a/axelrod/tests/unit/test_resultset.py +++ b/axelrod/tests/unit/test_resultset.py @@ -4,7 +4,6 @@ from collections import Counter import pandas as pd -from dask.dataframe.core import DataFrame from hypothesis import given, settings from numpy import mean, nanmedian, std From a6733f4e3af8f73b806651874409976540d3240c Mon Sep 17 00:00:00 2001 From: AI-MoonDongWon Date: Fri, 24 Jan 2025 17:50:41 +0900 Subject: [PATCH 04/12] Add type annotation for frequency table variable --- axelrod/strategies/frequency_analyzer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/axelrod/strategies/frequency_analyzer.py b/axelrod/strategies/frequency_analyzer.py index 9545bdc83..492e7d464 100644 --- a/axelrod/strategies/frequency_analyzer.py +++ b/axelrod/strategies/frequency_analyzer.py @@ -62,7 +62,7 @@ def __init__(self) -> None: """ super().__init__() self.minimum_cooperation_ratio = 0.25 - self.frequency_table = dict() + self.frequency_table: dict = dict() self.last_sequence = "" self.current_sequence = "" From bb000348e1b895fca2a3c2e08c6e2e4d0858b80e Mon Sep 17 00:00:00 2001 From: AI-MoonDongWon Date: Sun, 26 Jan 2025 00:51:17 +0900 Subject: [PATCH 05/12] add Momentum strategy --- axelrod/strategies/_strategies.py | 2 ++ docs/index.rst | 2 +- docs/reference/strategy_index.rst | 2 ++ run_mypy.py | 1 + 4 files changed, 6 insertions(+), 1 deletion(-) diff --git a/axelrod/strategies/_strategies.py b/axelrod/strategies/_strategies.py index 284cca1ee..a209664c2 100644 --- a/axelrod/strategies/_strategies.py +++ b/axelrod/strategies/_strategies.py @@ -193,6 +193,7 @@ from .memorytwo import AON2, MEM2, DelayedAON1 from .memorytwo import MemoryTwoPlayer # pylint: disable=unused-import +from .momentum import Momentum from .mutual import Desperate, Hopeless, Willing from .negation import Negation from .oncebitten import FoolMeOnce, ForgetfulFoolMeOnce, OnceBitten @@ -402,6 +403,7 @@ MEM2, MathConstantHunter, Michaelos, + Momentum, NTitsForMTats, NaiveProber, Negation, diff --git a/docs/index.rst b/docs/index.rst index b8a865f4c..0fc7c8ff3 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -53,7 +53,7 @@ Count the number of available players:: >>> import axelrod as axl >>> len(axl.strategies) - 241 + 242 Create matches between two players:: diff --git a/docs/reference/strategy_index.rst b/docs/reference/strategy_index.rst index 2bb232506..1e570fac6 100644 --- a/docs/reference/strategy_index.rst +++ b/docs/reference/strategy_index.rst @@ -76,6 +76,8 @@ Here are the docstrings of all the strategies in the library. :members: .. automodule:: axelrod.strategies.memoryone :members: +.. automodule:: axelrod.strategies.momentum + :members: .. automodule:: axelrod.strategies.meta :members: .. automodule:: axelrod.strategies.mutual diff --git a/run_mypy.py b/run_mypy.py index b3c586c9b..9286bca9a 100755 --- a/run_mypy.py +++ b/run_mypy.py @@ -41,6 +41,7 @@ "axelrod/strategies/mathematicalconstants.py", "axelrod/strategies/memoryone.py", "axelrod/strategies/memorytwo.py", + "axelrod/strategies/momentum.py", "axelrod/strategies/mutual.py", "axelrod/strategies/negation.py", "axelrod/strategies/oncebitten.py", From 37a52715bdb84e4d54ee86d58fc6dfbc8809633a Mon Sep 17 00:00:00 2001 From: AI-MoonDongWon Date: Sun, 26 Jan 2025 00:51:57 +0900 Subject: [PATCH 06/12] changed annotation --- axelrod/strategies/momentum.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/axelrod/strategies/momentum.py b/axelrod/strategies/momentum.py index 36405c13f..92d43967f 100644 --- a/axelrod/strategies/momentum.py +++ b/axelrod/strategies/momentum.py @@ -16,12 +16,6 @@ class Momentum(Player): Names: - Momentum: Original name by Dong Won Moon - Notes: - - While I am an undergraduate student with limited experience in game theory, I - believe this strategy has potential in various scenarios. - - I encourage experts to explore and extend this idea in other contexts, such as - environments with noise, one-hot vectorization approaches at multiple actions. - """ name = "Momentum" From e236312df8babe204e555948b0a7e7482769699d Mon Sep 17 00:00:00 2001 From: AI-MoonDongWon Date: Wed, 29 Jan 2025 19:34:46 +0900 Subject: [PATCH 07/12] add description --- axelrod/strategies/momentum.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/axelrod/strategies/momentum.py b/axelrod/strategies/momentum.py index 92d43967f..4b8c5daef 100644 --- a/axelrod/strategies/momentum.py +++ b/axelrod/strategies/momentum.py @@ -13,6 +13,11 @@ class Momentum(Player): create significant and rapid changes in the player's behavior, much like how momentum responds to gradients in optimization. + Parameters: + - alpha: + - threshold: + - momentum: + Names: - Momentum: Original name by Dong Won Moon @@ -42,8 +47,8 @@ def __repr__(self): return f"Momentum: {self.alpha}, {self.threshold}" def update_momentum(self, opponent_action): - action_value = 1 if opponent_action == C else 0 # If the opponent defects, the momentum decreases, reflecting a loss of trust. + action_value = 1 if opponent_action == C else 0 self.momentum = ( self.alpha * self.momentum + (1 - self.alpha) * action_value ) From ccf81caada3ebf2bdb453d160a62ac731ea2a4cc Mon Sep 17 00:00:00 2001 From: AI-MoonDongWon Date: Wed, 29 Jan 2025 19:40:57 +0900 Subject: [PATCH 08/12] add description --- axelrod/strategies/momentum.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/axelrod/strategies/momentum.py b/axelrod/strategies/momentum.py index 4b8c5daef..141f8a76a 100644 --- a/axelrod/strategies/momentum.py +++ b/axelrod/strategies/momentum.py @@ -14,9 +14,9 @@ class Momentum(Player): responds to gradients in optimization. Parameters: - - alpha: - - threshold: - - momentum: + - alpha: Momentum decay factor that determines the rate of trust reduction. A higher value leads to slower decay, and the opponent's Defect acts as a trigger. (Optimized by Genetic Algorithm) + - threshold: The minimum momentum required to continue cooperation. If momentum falls below this value, the strategy switches to Defect as punishment. (Optimized by Genetic Algorithm) + - momentum: Represents the inertia of trust, dynamically changing based on past cooperation. Names: - Momentum: Original name by Dong Won Moon @@ -35,8 +35,8 @@ class Momentum(Player): def __init__( self, - alpha=0.9914655399877477, # Optimized by Genetic Algorithm. You can try to adapt it to any Env. - threshold=0.9676595613724907, # This one too + alpha=0.9914655399877477, + threshold=0.9676595613724907, ) -> None: super().__init__() self.alpha = alpha From 5348663082e9489398d9ca04ab745df081f406e2 Mon Sep 17 00:00:00 2001 From: AI-MoonDongWon Date: Wed, 29 Jan 2025 20:55:04 +0900 Subject: [PATCH 09/12] run black --- axelrod/classifier.py | 2 +- axelrod/makes_use_of.py | 2 +- axelrod/strategies/memorytwo.py | 2 +- axelrod/tests/strategies/test_gambler.py | 3 +-- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/axelrod/classifier.py b/axelrod/classifier.py index b9386099c..cc56f6000 100644 --- a/axelrod/classifier.py +++ b/axelrod/classifier.py @@ -185,7 +185,7 @@ def __getitem__( raise KeyError("Unknown classifier") def classify_player_for_this_classifier( - player: Union[Player, Type[Player]] + player: Union[Player, Type[Player]], ) -> Any: def try_lookup() -> Any: try: diff --git a/axelrod/makes_use_of.py b/axelrod/makes_use_of.py index cd6bc16d9..b397fbddd 100644 --- a/axelrod/makes_use_of.py +++ b/axelrod/makes_use_of.py @@ -36,7 +36,7 @@ def makes_use_of(player: Type[Player]) -> Set[Text]: def makes_use_of_variant( - player_or_method: Union[Callable, Type[Player]] + player_or_method: Union[Callable, Type[Player]], ) -> Set[Text]: """A version of makes_use_of that works on functions or player classes.""" try: diff --git a/axelrod/strategies/memorytwo.py b/axelrod/strategies/memorytwo.py index b5edcf3c3..b351bd6d0 100644 --- a/axelrod/strategies/memorytwo.py +++ b/axelrod/strategies/memorytwo.py @@ -98,7 +98,7 @@ def set_sixteen_vector(self, sixteen_vector: Tuple[float, ...]): @staticmethod def compute_memory_depth( - sixteen_vector: Dict[Tuple[Action, Action], float] + sixteen_vector: Dict[Tuple[Action, Action], float], ) -> int: values = set(list(sixteen_vector.values())) diff --git a/axelrod/tests/strategies/test_gambler.py b/axelrod/tests/strategies/test_gambler.py index 62810b7a1..fae570990 100755 --- a/axelrod/tests/strategies/test_gambler.py +++ b/axelrod/tests/strategies/test_gambler.py @@ -1,5 +1,4 @@ -"""Test for the Gambler strategy. Most tests come from the LookerUp test suite. -""" +"""Test for the Gambler strategy. Most tests come from the LookerUp test suite.""" import copy import unittest From bcdf81787eaca7b775f386d14fe0ca9f38a07fc9 Mon Sep 17 00:00:00 2001 From: AI-MoonDongWon Date: Fri, 31 Jan 2025 14:43:49 +0900 Subject: [PATCH 10/12] add name --- axelrod/strategies/momentum.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/axelrod/strategies/momentum.py b/axelrod/strategies/momentum.py index 141f8a76a..7515d583e 100644 --- a/axelrod/strategies/momentum.py +++ b/axelrod/strategies/momentum.py @@ -44,7 +44,7 @@ def __init__( self.momentum = 1.0 def __repr__(self): - return f"Momentum: {self.alpha}, {self.threshold}" + return f"Momentum: {self.momentum}, Alpha: {self.alpha}, Threshold: {self.threshold}" def update_momentum(self, opponent_action): # If the opponent defects, the momentum decreases, reflecting a loss of trust. From 020dba4219631cf83f1df4958ae69e9dce7324a7 Mon Sep 17 00:00:00 2001 From: AI-MoonDongWon Date: Fri, 31 Jan 2025 17:13:57 +0900 Subject: [PATCH 11/12] add name --- axelrod/tests/strategies/test_momentum.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/axelrod/tests/strategies/test_momentum.py b/axelrod/tests/strategies/test_momentum.py index 4dddc867b..a97d1e7e7 100644 --- a/axelrod/tests/strategies/test_momentum.py +++ b/axelrod/tests/strategies/test_momentum.py @@ -26,7 +26,7 @@ def test_initialisation(self): def test_repr(self): player = self.player(alpha=0.9, threshold=0.8) - self.assertEqual(repr(player), "Momentum: 0.9, 0.8") + self.assertEqual(repr(player), "Momentum: 1.0, Alpha: 0.9, Threshold: 0.8") def test_strategy(self): actions = [(C, C)] From a349ee00ee5c00424f3575070fa7a5801578588f Mon Sep 17 00:00:00 2001 From: AI-MoonDongWon Date: Fri, 31 Jan 2025 17:15:30 +0900 Subject: [PATCH 12/12] run black --- axelrod/tests/strategies/test_momentum.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/axelrod/tests/strategies/test_momentum.py b/axelrod/tests/strategies/test_momentum.py index a97d1e7e7..8ff56a215 100644 --- a/axelrod/tests/strategies/test_momentum.py +++ b/axelrod/tests/strategies/test_momentum.py @@ -26,7 +26,9 @@ def test_initialisation(self): def test_repr(self): player = self.player(alpha=0.9, threshold=0.8) - self.assertEqual(repr(player), "Momentum: 1.0, Alpha: 0.9, Threshold: 0.8") + self.assertEqual( + repr(player), "Momentum: 1.0, Alpha: 0.9, Threshold: 0.8" + ) def test_strategy(self): actions = [(C, C)]