Skip to content

add implementation of a generic memory two + 2 strategies #1171

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Apr 6, 2018
Merged
4 changes: 3 additions & 1 deletion axelrod/strategies/_strategies.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
MemoryOnePlayer, ALLCorALLD, FirmButFair, GTFT, SoftJoss,
StochasticCooperator, StochasticWSLS, WinStayLoseShift, WinShiftLoseStay,
ReactivePlayer)
from .memorytwo import MEM2
from .memorytwo import MemoryTwoPlayer, AON2, DelayedAON1, MEM2
Copy link
Member

Choose a reason for hiding this comment

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

AON2 and DelayedAON1 need to be added to the all_players list.

from .mindcontrol import MindController, MindWarper, MindBender
from .mindreader import MindReader, ProtectedMindReader, MirrorMindReader
from .mutual import Desperate, Hopeless, Willing
Expand Down Expand Up @@ -103,6 +103,7 @@
AlternatorHunter,
AntiCycler,
AntiTitForTat,
AON2,
APavlov2006,
APavlov2011,
Appeaser,
Expand Down Expand Up @@ -136,6 +137,7 @@
Defector,
DefectorHunter,
Desperate,
DelayedAON1,
DoubleCrosser,
Doubler,
DoubleResurrection,
Expand Down
193 changes: 193 additions & 0 deletions axelrod/strategies/memorytwo.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,204 @@
"""Memory Two strategies."""

import itertools
import warnings
from typing import Tuple, Dict

from axelrod.action import Action
from axelrod.player import Player
from axelrod.random_ import random_choice
from .titfortat import TitForTat, TitFor2Tats
from .defector import Defector


C, D = Action.C, Action.D


class MemoryTwoPlayer(Player):
"""
Uses a sixteen-vector for strategies based on the 16 conditional probabilities
P(X | I,J,K,L) where X, I, J, K, L in [C, D] and I, J are the players last
two moves and K, L are the opponents last two moves. These conditional
probabilities are the following:
1. P(C|CC, CC)
2. P(C|CC, CD)
3. P(C|CC, DC)
4. P(C|CC, DD)
5. P(C|CD, CC)
6. P(C|CD, CD)
7. P(C|CD, DC)
8. P(C|CD, DD)
9. P(C|DC, CC)
10. P(C|DC, CD)
11. P(C|DC, DC)
12. P(C|DC, DD)
13. P(C|DD, CC)
14. P(C|DD, CD)
15. P(C|DD, DC)
16. P(C|DD, DD))
Cooperator is set as the default player if sixteen_vector is not given.

Names

- Memory Two: [Hilbe2017]_
"""

name = 'Generic Memory Two Player'
classifier = {
'memory_depth': 2,
'stochastic': False,
'makes_use_of': set(),
'long_run_time': False,
'inspects_source': False,
'manipulates_source': False,
'manipulates_state': False
}

def __init__(self, sixteen_vector: Tuple[float, ...] = None,
initial: Action = C) -> None:
"""
Parameters
----------

sixteen_vector: list or tuple of floats of length 16
The response probabilities to the preceding round of play
initial: C or D
The initial 2 moves
"""
super().__init__()
self._initial = initial
self.set_initial_sixteen_vector(sixteen_vector)

def set_initial_sixteen_vector(self, sixteen_vector):
if sixteen_vector is None:
sixteen_vector = tuple([1] * 16)
warnings.warn("Memory two player is set to default, Cooperator.")

self.set_sixteen_vector(sixteen_vector)
if self.name == 'Generic Memory Two Player':
self.name = "%s: %s" % (self.name, sixteen_vector)

def set_sixteen_vector(self, sixteen_vector: Tuple):
if not all(0 <= p <= 1 for p in sixteen_vector):
raise ValueError("An element in the probability vector, {}, is not "
"between 0 and 1.".format(str(sixteen_vector)))

states = [(hist[:2], hist[2:])
for hist in list(itertools.product((C, D), repeat=4))]

self._sixteen_vector = dict(zip(states, sixteen_vector)) # type: Dict[tuple, float]
Copy link
Member

@drvinceknight drvinceknight Apr 5, 2018

Choose a reason for hiding this comment

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

@marcharper this no longer maps float on to states, it's a change I suggested to @Nikoleta-v3 when trying to get the type hints to work https://github.com/Nikoleta-v3/Axelrod/pull/8

I couldn't see why we needed to map(float but we do do it for memory_one so just highlighting in case this is something I've forgotten/missed (if indeed it's not needed, let's get rid of it from memory_one too).

self.classifier['stochastic'] = any(0 < x < 1 for x in set(sixteen_vector))

def strategy(self, opponent: Player) -> Action:
if len(opponent.history) <= 1:
return self._initial
# Determine which probability to use
p = self._sixteen_vector[(tuple(self.history[-2:]),
tuple(opponent.history[-2:]))]
# Draw a random number in [0, 1] to decide
return random_choice(p)


class AON2(MemoryTwoPlayer):
"""
AON2 a memory two strategy introduced in [Hilbe2017]_. It belongs to the
AONk (all-or-none) family of strategies. These strategies were designed to
satisfy the three following properties:

1. Mutually Cooperative. A strategy is mutually cooperative if there are
histories for which the strategy prescribes to cooperate, and if it continues
to cooperate after rounds with mutual cooperation (provided the last k actions
of the focal player were actually consistent).

2. Error correcting. A strategy is error correcting after at most k rounds if,
after any history, it generally takes a group of players at most k + 1 rounds
to re-establish mutual cooperation.

3. Retaliating. A strategy is retaliating for at least k rounds if, after
rounds in which the focal player cooperated while the coplayer defected,
the strategy responds by defecting the following k rounds.

In [Hilbe2017]_ the following vectors are reported as "equivalent" to AON2
with their respective self-cooperation rate (note that these are not the same):

1. [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], self-cooperation
rate: 0.952
2. [1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], self-cooperation
rate: 0.951
3. [1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], self-cooperation
rate: 0.951
4. [1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1], self-cooperation
rate: 0.952

AON2 is implemented using vector 1 due its self-cooperation rate.

In essence it is a strategy that starts off by cooperating and will cooperate
again only after the states (CC, CC), (CD, CD), (DC, DC), (DD, DD).

Names:

- AON2: [Hilbe2017]_
"""

name = 'AON2'
classifier = {
'memory_depth': 2,
'stochastic': False,
'makes_use_of': set(),
'long_run_time': False,
'inspects_source': False,
'manipulates_source': False,
'manipulates_state': False
}

def __init__(self) -> None:
sixteen_vector = (1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)
super().__init__(sixteen_vector)


class DelayedAON1(MemoryTwoPlayer):
"""
Delayed AON1 a memory two strategy also introduced in [Hilbe2017]_ and belongs
to the AONk family. Note that AON1 is equivalent to Win Stay Lose Shift.

In [Hilbe2017]_ the following vectors are reported as "equivalent" to Delayed
AON1 with their respective self-cooperation rate (note that these are not the
same):

1. [1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1], self-cooperation
rate: 0.952
2. [1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1], self-cooperation
rate: 0.970
3. [1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1], self-cooperation
rate: 0.971

Delayed AON1 is implemented using vector 3 due its self-cooperation rate.

In essence it is a strategy that starts off by cooperating and will cooperate
again only after the states (CC, CC), (CD, CD), (CD, DD), (DD, CD),
(DC, DC) and (DD, DD).

Names:

- Delayed AON1: [Hilbe2017]_
"""

name = 'Delayed AON1'
classifier = {
'memory_depth': 2,
'stochastic': False,
'makes_use_of': set(),
'long_run_time': False,
'inspects_source': False,
'manipulates_source': False,
'manipulates_state': False
}

def __init__(self) -> None:
sixteen_vector = (1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1)
super().__init__(sixteen_vector)


class MEM2(Player):
"""A memory-two player that switches between TFT, TFTT, and ALLD.

Expand Down
Loading