Skip to content

Commit

Permalink
add freqAnalyzer strat
Browse files Browse the repository at this point in the history
  • Loading branch information
miller-ian committed Jul 5, 2024
1 parent 9838f84 commit ee4c454
Show file tree
Hide file tree
Showing 4 changed files with 212 additions and 0 deletions.
6 changes: 6 additions & 0 deletions axelrod/strategies/_strategies.py
Original file line number Diff line number Diff line change
Expand Up @@ -238,12 +238,15 @@
)
from .shortmem import ShortMem
from .stalker import Stalker
from .frequency_analyzer import FreqAnalyzer
from .titfortat import (
AdaptiveTitForTat,
Alexei,
AntiTitForTat,
Bully,
BurnBothEnds,
SofteningTitForTat,
HardeningTitForTat,
ContriteTitForTat,
DynamicTwoTitsForTat,
EugineNier,
Expand Down Expand Up @@ -366,6 +369,7 @@
ForgivingTitForTat,
Fortress3,
Fortress4,
FreqAnalyzer,
GTFT,
GeneralSoftGrudger,
GoByMajority,
Expand All @@ -392,6 +396,8 @@
Hopeless,
Inverse,
InversePunisher,
SofteningTitForTat,
HardeningTitForTat,
KnowledgeableWorseAndWorse,
LevelPunisher,
LimitedRetaliate,
Expand Down
112 changes: 112 additions & 0 deletions axelrod/strategies/frequency_analyzer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
from axelrod.action import Action, actions_to_str
from axelrod.player import Player
from axelrod.strategy_transformers import (
FinalTransformer,
TrackHistoryTransformer,
)

C, D = Action.C, Action.D

class FreqAnalyzer(Player):
"""
A player starts by playing TitForTat for the first 30 turns (dataset generation phase).
Take the matrix of last 2 moves by both Player and Opponent.
While in dataset generation phase, construct a dictionary d, where keys are each 4 move sequence
and the corresponding value for each key is a list of the subsequent Opponent move. The 4 move sequence
starts with the Opponent move.
For example, if a game at turn 5 looks like this:
Opp: C, C, D, C, D
Player: C, C, C, D, C
d should look like this:
{ [CCCC]: [D],
[CCDC]: [C],
[DCCD]: [D] }
During dataset generation phase, Player will play TitForTat. After end of dataset generation phase,
Player will switch strategies. Upon encountering a particular 4-move sequence in the game, Player will look up history
of subsequent Opponent move. If ratio of defections to total moves exceeds p, Player will defect. Otherwise,
Player will cooperate.
Could fall under "Hunter" class of strategies.
More likely falls under LookerUp class of strategies.
Names:
- FreqAnalyzer (FREQ): Original by Ian Miller
"""

# These are various properties for the strategy
name = "FreqAnalyzer"
classifier = {
"memory_depth": float("inf"),
"stochastic": False,
"long_run_time": False,
"inspects_source": False,
"manipulates_source": False,
"manipulates_state": False,
}
def __init__(self) -> None:
"""
Parameters
----------
p, float
The probability to cooperate
"""
super().__init__()
self.minimum_cooperation_ratio = 0.5
self.frequency_table = dict()
self.last_sequence = ''
self.current_sequence = ''

def strategy(self, opponent: Player) -> Action:
"""This is the actual strategy"""
if len(self.history) > 5:
self.last_sequence = str(opponent.history[-3]) + str(self.history[-3]) + str(opponent.history[-2]) + str(self.history[-2])
self.current_sequence = str(opponent.history[-2]) + str(self.history[-2]) + str(opponent.history[-1]) + str(self.history[-1])

self.update_table(opponent)

if len(self.history) < 30:
# Play TitForTat
# First move
if not self.history:
return C
# React to the opponent's last move
if opponent.history[-1] == D:
return D
return C
else:
try:
results = self.frequency_table[self.current_sequence]
cooperates = results.count('C')
if (cooperates / len(self.history)) > self.minimum_cooperation_ratio:
return C
return D
except:
if not self.history:
return C
# React to the opponent's last move
if opponent.history[-1] == D:
return D
return C

def update_table(self, opponent: Player):
print(self.frequency_table)
print("___________________")
print("current sequence is {}", self.last_sequence)
if self.last_sequence in self.frequency_table.keys():
print("seen this key before")
print("freq table keys = {}", self.frequency_table.keys())
results = self.frequency_table[self.last_sequence]
results.append(opponent.history[-1])
self.frequency_table[self.last_sequence] = results
else:
print("not seen this key ever")
self.frequency_table[self.last_sequence] = [opponent.history[-1]]

93 changes: 93 additions & 0 deletions axelrod/tests/strategies/test_freqanalyzer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
"""Tests for the Stalker strategy."""

import axelrod as axl

from .test_player import TestPlayer

C, D = axl.Action.C, axl.Action.D


class TestStalker(TestPlayer):

name = "Stalker: (D,)"
player = axl.Stalker
expected_classifier = {
"memory_depth": float("inf"),
"stochastic": True,
"makes_use_of": {"game", "length"},
"long_run_time": False,
"inspects_source": False,
"manipulates_source": False,
"manipulates_state": False,
}

def test_strategy(self):
actions = [(C, C)] * 3 + [(D, C)]
self.versus_test(opponent=axl.Cooperator(), expected_actions=actions)

# wish_score < current_average_score < very_good_score
actions = [(C, C)] * 7 + [(C, D), (C, D), (C, C), (C, C), (D, C)]
self.versus_test(
opponent=axl.MockPlayer(actions=[C] * 7 + [D] * 2),
expected_actions=actions,
)

actions = [(C, C)] * 7 + [(C, D), (C, C), (D, C)]
self.versus_test(
opponent=axl.MockPlayer(actions=[C] * 7 + [D]),
expected_actions=actions,
)

# current_average_score > 2
actions = [(C, C)] * 9 + [(D, C)]
self.versus_test(axl.Cooperator(), expected_actions=actions)

# 1 < current_average_score < 2
actions = [(C, C)] * 7 + [(C, D)] * 4 + [(D, D)]
self.versus_test(
opponent=axl.MockPlayer(actions=[C] * 7 + [D] * 5),
expected_actions=actions,
)

def test_strategy2(self):
# current_average_score < 1
actions = (
[(C, D)]
+ [(D, D)] * 2
+ [(C, D)] * 3
+ [(D, D), (C, D), (D, D), (C, D), (D, D), (C, D), (D, D)]
)
self.versus_test(axl.Defector(), expected_actions=actions, seed=3222)

def test_strategy3(self):
actions = [(C, D)] * 3 + [
(D, D),
(C, D),
(D, D),
(C, D),
(C, D),
(D, D),
(C, D),
(C, D),
(C, D),
(D, D),
]
self.versus_test(axl.Defector(), expected_actions=actions, seed=649)

def test_strategy4(self):
# defect in last round
actions = [(C, C)] * 199 + [(D, C)]
self.versus_test(
axl.Cooperator(),
expected_actions=actions,
match_attributes={"length": 200},
)

# Force a defection before the end of the actual match which ensures
# that current_average_score > very_good_score
actions = [(C, C)] * 3 + [(D, C)] * 3
self.versus_test(
opponent=axl.Cooperator(),
expected_actions=actions,
match_attributes={"length": 4},
)
1 change: 1 addition & 0 deletions run_mypy.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"axelrod/strategies/darwin.py",
"axelrod/strategies/defector.py",
"axelrod/strategies/forgiver.py",
"axelrod/strategies/frequency_analyzer.py",
"axelrod/strategies/gradualkiller.py",
"axelrod/strategies/grudger.py",
"axelrod/strategies/grumpy.py",
Expand Down

0 comments on commit ee4c454

Please sign in to comment.