Skip to content

Commit 47d8a06

Browse files
committed
Add Momentum strategy
1 parent 6d2d465 commit 47d8a06

File tree

2 files changed

+163
-0
lines changed

2 files changed

+163
-0
lines changed

axelrod/strategies/momentum.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
from axelrod.action import Action
2+
from axelrod.player import Player
3+
4+
C, D = Action.C, Action.D
5+
6+
7+
class Momentum(Player):
8+
"""
9+
This strategy is inspired by the concept of Gradual and the mathematical foundation of
10+
the Momentum optimizer used in deep learning.
11+
12+
The idea is that trust (or cooperation) evolves dynamically. A shift in trust can
13+
create significant and rapid changes in the player's behavior, much like how momentum
14+
responds to gradients in optimization.
15+
16+
Names:
17+
- Momentum: Original name by Dong Won Moon
18+
19+
Notes:
20+
- While I am an undergraduate student with limited experience in game theory, I
21+
believe this strategy has potential in various scenarios.
22+
- I encourage experts to explore and extend this idea in other contexts, such as
23+
environments with noise, one-hot vectorization approaches at multiple actions.
24+
25+
"""
26+
27+
name = "Momentum"
28+
classifier = {
29+
"memory_depth": float("inf"),
30+
"stochastic": False,
31+
"long_run_time": False,
32+
"inspects_source": False,
33+
"manipulates_source": False,
34+
"manipulates_state": False,
35+
}
36+
37+
def __init__(
38+
self,
39+
alpha=0.9914655399877477, # Optimized by Genetic Algorithm. You can try to adapt it to any Env.
40+
threshold=0.9676595613724907, # This one too
41+
) -> None:
42+
super().__init__()
43+
self.alpha = alpha
44+
self.threshold = threshold
45+
self.momentum = 1.0
46+
47+
def __repr__(self):
48+
return f"Momentum: {self.alpha}, {self.threshold}"
49+
50+
def update_momentum(self, opponent_action):
51+
action_value = 1 if opponent_action == C else 0
52+
# If the opponent defects, the momentum decreases, reflecting a loss of trust.
53+
self.momentum = (
54+
self.alpha * self.momentum + (1 - self.alpha) * action_value
55+
)
56+
57+
def strategy(self, opponent: Player) -> Action:
58+
if len(self.history) == 0:
59+
self.momentum = 1.0
60+
return C
61+
62+
else:
63+
self.update_momentum(opponent.history[-1])
64+
return C if self.momentum >= self.threshold else D
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import axelrod as axl
2+
from axelrod import Action
3+
from axelrod.tests.strategies.test_player import TestPlayer
4+
from axelrod.strategies.momentum import Momentum
5+
6+
C, D = Action.C, Action.D
7+
8+
class TestMomentum(TestPlayer):
9+
name = "Momentum"
10+
player = Momentum
11+
expected_classifier = {
12+
"memory_depth": float("inf"),
13+
"stochastic": False,
14+
"long_run_time": False,
15+
"inspects_source": False,
16+
"manipulates_source": False,
17+
"manipulates_state": False,
18+
}
19+
20+
def test_initialisation(self):
21+
player = self.player(alpha=0.9, threshold=0.8)
22+
self.assertEqual(player.alpha, 0.9)
23+
self.assertEqual(player.threshold, 0.8)
24+
self.assertEqual(player.momentum, 1.0)
25+
26+
def test_repr(self):
27+
player = self.player(alpha=0.9, threshold=0.8)
28+
self.assertEqual(repr(player), "Momentum: 0.9, 0.8")
29+
30+
def test_strategy(self):
31+
actions = [(C, C)]
32+
self.versus_test(
33+
axl.MockPlayer(actions=[C]),
34+
expected_actions=actions,
35+
init_kwargs={"alpha": 0.5,
36+
"threshold": 0.5
37+
},
38+
attrs={"momentum": 1.0}
39+
)
40+
41+
actions = [(C, D), (C, D), (D, D)]
42+
self.versus_test(
43+
axl.MockPlayer(actions=[D]),
44+
expected_actions=actions,
45+
init_kwargs={"alpha": 0.5,
46+
"threshold": 0.5
47+
},
48+
attrs={"momentum": 0.25}
49+
)
50+
51+
def test_vs_alternator(self):
52+
actions = [(C, C), (C, D), (C, C), (C, D), (D, C)]
53+
self.versus_test(axl.Alternator(),
54+
expected_actions=actions,
55+
init_kwargs={"alpha": 0.5,
56+
"threshold": 0.5
57+
},
58+
)
59+
60+
def test_vs_cooperator(self):
61+
actions = [(C, C), (C, C), (C, C), (C, C), (C, C)]
62+
self.versus_test(axl.Cooperator(),
63+
expected_actions=actions,
64+
init_kwargs={"alpha": 0.5,
65+
"threshold": 0.5
66+
},
67+
)
68+
69+
def test_vs_defector(self):
70+
actions = [(C, D), (C, D), (D, D), (D, D), (D, D)]
71+
self.versus_test(axl.Defector(),
72+
expected_actions=actions,
73+
init_kwargs={"alpha": 0.5,
74+
"threshold": 0.5
75+
},
76+
)
77+
78+
def test_vs_random(self):
79+
# We can also test against random strategies
80+
actions = [(C, D), (C, C), (C, C), (C, D), (D, D)]
81+
self.versus_test(axl.Random(),
82+
expected_actions=actions,
83+
seed=17,
84+
init_kwargs={"alpha": 0.5,
85+
"threshold": 0.5
86+
},
87+
)
88+
89+
def test_vs_random2(self):
90+
actions = [(C, C), (C, C), (C, C), (C, C)]
91+
self.versus_test(axl.Random(),
92+
expected_actions=actions,
93+
seed=3,
94+
init_kwargs={"alpha": 0.5,
95+
"threshold": 0.5
96+
},
97+
)
98+
99+

0 commit comments

Comments
 (0)