Skip to content
Open
2 changes: 1 addition & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ The simplest way to install is::

To install from source::

$ git clone https://github.com/Axelrod-Python/Axelrod.git
$ git clone https://github.com/benjjo/Axelrod.git
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'll want to keep this as is for others.

$ cd Axelrod
$ python setup.py install

Expand Down
5 changes: 4 additions & 1 deletion axelrod/strategies/_strategies.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@
from .cycler import Cycler, EvolvableCycler # pylint: disable=unused-import
from .darwin import Darwin
from .dbs import DBS
from .defector import Defector, TrickyDefector
from .defector import Defector, TrickyDefector, ModalDefector
from .doubler import Doubler
from .finite_state_machines import (
TF1,
Expand Down Expand Up @@ -260,6 +260,7 @@
HardTitFor2Tats,
HardTitForTat,
Michaelos,
ModalTFT,
NTitsForMTats,
OmegaTFT,
OriginalGradual,
Expand Down Expand Up @@ -409,6 +410,8 @@
MEM2,
MathConstantHunter,
Michaelos,
ModalDefector,
ModalTFT,
NTitsForMTats,
NaiveProber,
Negation,
Expand Down
33 changes: 33 additions & 0 deletions axelrod/strategies/defector.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from axelrod.action import Action
from axelrod.player import Player
import statistics

C, D = Action.C, Action.D

Expand Down Expand Up @@ -61,3 +62,35 @@ def strategy(self, opponent: Player) -> Action:
):
return C
return D


class ModalDefector(Player):
"""
A player starts by Defecting and then analyses the history of the opponent. If the opponent Cooperated in the
last round, they are returned with a Defection. If the opponent chose to Defect in the previous round,
then this strategy will return with the mode of the previous opponent responses.
"""

# These are various properties for the strategy
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this comment/line is from our contributing guidelines. You can remove it.

name = "Modal Defector"
classifier = {
"memory_depth": float("inf"),
"stochastic": False,
"long_run_time": False,
"inspects_source": False,
"manipulates_source": False,
"manipulates_state": False,
}

def strategy(self, opponent: Player) -> Action:
"""This is the actual strategy"""
# First move
if not self.history:
return D
# React to the opponent's historical moves
if opponent.history[-1] == C:
return D
else:
# returns with the mode of the opponent's history.
return statistics.mode(opponent.history)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than use the statistics library, the history class already keeps a running count of C and D, so you can do something like if opponent.history.cooperations > opponent.history.defections return C else return D. That saves a library import and is more efficient (mode is going to be O(n^2) over the entire game, this way is linear).


37 changes: 35 additions & 2 deletions axelrod/strategies/titfortat.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from axelrod.action import Action, actions_to_str
from axelrod.player import Player
import statistics
from axelrod.strategy_transformers import (
FinalTransformer,
TrackHistoryTransformer,
Expand Down Expand Up @@ -872,7 +873,7 @@ def strategy(self, opponent: Player) -> Action:
class RandomTitForTat(Player):
"""
A player starts by cooperating and then follows by copying its
opponent (tit for tat style). From then on the player
opponent (tit-for-tat style). From then on the player
will switch between copying its opponent and randomly
responding every other iteration.

Expand Down Expand Up @@ -945,9 +946,41 @@ def strategy(self, opponent):
if not opponent.history:
# Make sure we cooperate first turn
return C
# BBE modification
# BBE modification
if opponent.history[-1] == C:
# Cooperate with 0.9
return self._random.random_choice(0.9)
# Else TFT. Opponent played D, so play D in return.
return D


class ModalTFT(Player):
"""
A player starts by cooperating and then analyses the history of the opponent. If the opponent Cooperated in the
last round, they are returned with a Cooperation. If the opponent chose to Defect in the previous round,
then this strategy will return with the mode of the previous opponent responses.
"""

# These are various properties for the strategy
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here.

name = "Modal TFT"
classifier = {
"memory_depth": float("inf"),
"stochastic": False,
"long_run_time": False,
"inspects_source": False,
"manipulates_source": False,
"manipulates_state": False,
}

def strategy(self, opponent: Player) -> Action:
"""This is the actual strategy"""
# First move
if not self.history:
return C
# React to the opponent's historical moves
if opponent.history[-1] == C:
return C
else:
# returns with the mode of the opponent's history.
return statistics.mode(opponent.history)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment re: statistics.mode


18 changes: 18 additions & 0 deletions axelrod/tests/strategies/test_defector.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,21 @@ def test_defects_if_opponent_last_three_are_not_D(self):
self.versus_test(
axl.MockPlayer(actions=opponent_actions), expected_actions=actions
)


class TestModalDefector(TestPlayer):
name = "Modal Defector"
player = axl.ModalDefector
expected_classifier = {
"memory_depth": float("inf"),
"stochastic": False,
"makes_use_of": set(),
"inspects_source": False,
"manipulates_source": False,
"manipulates_state": False,
}

def test_strategy(self):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Try to add a few more tests that single out the specific behaviors like the first move and that the mode works for both C and D. If possible try to use a real strategy like Alternator rather than mock players, but mocks are ok if it's hard to target a particular case.

opponent = axl.MockPlayer(actions=[C, C, D, D, D, D, C, D])
actions = [(D, C), (D, C), (D, D), (C, D), (C, D), (D, D), (D, C), (D, D), (D, C)]
self.versus_test(opponent, expected_actions=actions)
20 changes: 20 additions & 0 deletions axelrod/tests/strategies/test_titfortat.py
Original file line number Diff line number Diff line change
Expand Up @@ -1341,3 +1341,23 @@ def test_vs_cooperator2(self):
def test_vs_defector(self):
actions = [(C, D), (D, D), (D, D), (D, D), (D, D)]
self.versus_test(axl.Defector(), expected_actions=actions)


class TestModalTFT(TestPlayer):
name = "Modal TFT"
player = axl.ModalTFT
expected_classifier = {
"memory_depth": float("inf"),
"stochastic": False,
"makes_use_of": set(),
"inspects_source": False,
"manipulates_source": False,
"manipulates_state": False,
}

def test_strategy(self):
opponent = axl.MockPlayer(actions=[C, C, D, D, D, D, C, D])
actions = [(C, C), (C, C), (C, D), (C, D), (C, D), (D, D), (D, C), (C, D), (D, C)]
self.versus_test(opponent, expected_actions=actions)