Skip to content
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

Adding MemoryDecay strategy. Check if ready to merge, please. #1137

Merged
merged 27 commits into from
Nov 14, 2017
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
bcd3e59
added MemoryDecay draft (no test)
Nov 2, 2017
5602605
added no. of turns param
Nov 2, 2017
3f5426d
fixed list in __init__
Nov 2, 2017
0ce447f
Update writing_test_for_the_new_strategy.rst
vdeni Nov 2, 2017
43a8a0d
fixed typo
vdeni Nov 3, 2017
404148e
updated memdecay code
Nov 3, 2017
211bf0f
Merge branch 'decay' of github.com:vdeni/Axelrod into decay
Nov 3, 2017
e318b9a
switching MemoryDecay to MetaPlayer class
Nov 3, 2017
119187a
fixing strategy code
Nov 7, 2017
e05efaf
added mem_decay to meta.py, expanded tests
Nov 9, 2017
0f249a6
finishing unit tests
Nov 9, 2017
d03bfad
added MemoryDecay draft (no test)
Nov 2, 2017
b13051d
removed stray swapfiles
Nov 9, 2017
3de5a04
Merge branch 'decay' of github.com:vdeni/Axelrod into decay
Nov 9, 2017
e65018c
removing swp files
Nov 9, 2017
ebf0f9c
removing swp files
Nov 9, 2017
0147cd9
changes made according to pull request suggestions
Nov 10, 2017
63a3922
Merge branch 'decay' of github.com:vdeni/Axelrod into decay
Nov 10, 2017
b7e4d8c
changed strategy search implementation
Nov 10, 2017
fdc4741
removing swp files
Nov 10, 2017
91202c7
fixing unittest errors
Nov 13, 2017
ea68662
fixing unittest errors
Nov 13, 2017
3de65d8
removing swapfiles
Nov 13, 2017
4e51db7
changed Test class name
Nov 13, 2017
46ffad9
added lines to player.py to display name instead of class in __repr__…
Nov 13, 2017
67dcac4
making requested changes: cleaning and simplifying MemoryDecay class …
Nov 14, 2017
f1a75c9
switched gain_loss_translate() to list comprehension
Nov 14, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added axelrod/strategies/.meta.py.swp
Binary file not shown.
79 changes: 79 additions & 0 deletions axelrod/strategies/meta.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from numpy.random import choice
import random

from axelrod.strategies import TitForTat
from axelrod.action import Action
from axelrod.player import Player, obey_axelrod
from axelrod.strategy_transformers import NiceTransformer
Expand Down Expand Up @@ -533,3 +535,80 @@ def __init__(self):
<= 1]
super().__init__(team=team)
self.classifier["long_run_time"] = False

class MemoryDecay(MetaPlayer):
"""
A player utilizes the (default) Tit for Tat strategy for the first (default) 15 turns,
at the same time memorizing the opponent's decisions. After the 15 turns have
passed, the player calculates a 'net cooperation score' (NCS) for his opponent,
Copy link
Member

Choose a reason for hiding this comment

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

Please use 'its' or 'their' instead of 'his'

weighing decisions to Cooperate as (default) 1, and to Defect as (default)
-2. If the opponent's NCS is below 0, the play defects; otherwise,
Copy link
Member

Choose a reason for hiding this comment

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

typo: 'play' should be 'player'

they cooperate.

The player's memories of the opponent's decisions have a random chance to be
altered (i.e., a C decision becomes D or vice versa; default probability
is 0.03) or deleted (default probability is 0.1).

It's necessary to specify EXACT name of the starting strategy if changing
Copy link
Member

Choose a reason for hiding this comment

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

no need for the capitals and should instead read '... specify the exact name ...'

the default. Possible strategies can be accessed with the .team attribute.
Copy link
Member

Choose a reason for hiding this comment

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

Also: this is no longer correct. This argument is now an player class. Perhaps change this whole paragraph to just say something like:

It is possible to pass a different axelrod player class to change the initial player behaviour.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've changed this according to your recommendation :)


Name: Memory Decay
"""
name = 'Memory Decay'
classifier = {
'memory_depth' : float('inf'),
'long_run_time' : False,
'stochastic' : True,
'makes_use_of' : set(),
'inspects_source' : False,
'manipulates_source' : False,
'manipulates_state' : False
}

def __init__(self, p_memory_delete: float = 0.1, p_memory_alter: float = 0.03,
loss_value: float = -2, gain_value: float = 1,
memory: list = None, start_strategy: Player = TitForTat,
start_strategy_duration: int = 15):
super().__init__()
self.p_memory_delete = p_memory_delete
self.p_memory_alter = p_memory_alter
self.loss_value = loss_value
self.gain_value = gain_value
self.memory = [] if memory == None else memory
self.start_strategy_duration = start_strategy_duration
self.team = [start_strategy()]

# translates the actions (D and C) to numeric values (loss_value and
# gain_value)
def gain_loss_tr(self):
Copy link
Member

Choose a reason for hiding this comment

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

could you rename this to gain_loss_translate and use a docstring for this method instead of the inline comment?

self.gloss_values = [*map(lambda x: self.loss_value if x == D else
self.gain_value, self.memory)]
Copy link
Member

Choose a reason for hiding this comment

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

This is tricky to read - could you expand it to use a few much simpler lines of code rather than fitting all into one line?


# alters memory entry, i.e. puts C if there's a D and vice versa
def mem_alter(self):
Copy link
Member

Choose a reason for hiding this comment

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

please use a full name for the method memory_alter, alter_memory or flip_memory and a docstring instead of a comment

alter = choice(range(0, len(self.memory)))
self.memory[alter] = self.memory[alter].flip()

# deletes memory entry
def mem_delete(self):
Copy link
Member

Choose a reason for hiding this comment

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

Could you use a fuller name e.g. memory_delete or delete_memory and a docstring instead of a comment?

self.memory.pop(choice(range(0, len(self.memory))))

def strategy(self, opponent):
try:
self.memory.append(opponent.history[-1])
except IndexError:
pass
if len(self.history) < self.start_strategy_duration:
play = self.team[0].strategy(opponent)
self.team[0].history.append(play)
return play
else:
if random.random() <= self.p_memory_alter:
self.mem_alter()
if random.random() <= self.p_memory_delete:
self.mem_delete()
self.gain_loss_tr()
if sum(self.gloss_values) < 0:
return D
else:
return C
74 changes: 74 additions & 0 deletions axelrod/tests/strategies/test_meta.py
Original file line number Diff line number Diff line change
Expand Up @@ -585,3 +585,77 @@ def test_strategy(self):
actions = [(C, C), (C, D), (D, C), (D, D), (D, C)]
self.versus_test(opponent=axelrod.Alternator(),
expected_actions=actions)

"""Tests for the Memory Decay strategy"""

class TestMemoryDecay(TestPlayer):

name = 'MemoryDecay'
player = axelrod.meta.MemoryDecay
Copy link
Member

Choose a reason for hiding this comment

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

This strategy will need to be added to the axelrod/strategies/__init__.py file (where all meta strategies get imported), when you've done that you should be able to go with player = axelrod.MemoryDecay.

expected_classifier = {
'memory_depth': float('inf'),
'stochastic': True,
'makes_use_of': set([]),
'long_run_time': False,
'inspects_source': False,
'manipulates_source': False,
'manipulates_state': False
}

def test_strategy(self):
#Test TitForTat behavior in first 15 turns
opponent = axelrod.Cooperator()
actions = list([(C, C)]) * 15
self.versus_test(opponent, expected_actions=actions)

opponent = axelrod.Defector()
actions = [(C, D)] + list([(D, D)]) * 14
self.versus_test(opponent, expected_actions=actions)

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

opponent_actions = [C, D, D, C, D, C, C, D, C, D, D, C, C, D, D]
opponent = axelrod.MockPlayer(actions =opponent_actions)
mem_actions = [C, C, D, D, C, D, C, C, D, C, D, D, C, C, D]
actions = list(zip(mem_actions, opponent_actions))
self.versus_test(opponent, expected_actions=actions)

opponent = axelrod.Random()
actions = [(C, D), (D, D), (D, C), (C, C), (C, D), (D, C)]
self.versus_test(opponent, expected_actions=actions, seed=0)

#Test alternative starting strategies
opponent = axelrod.Cooperator()
actions = list([(D, C)]) * 15
self.versus_test(opponent, expected_actions=actions,
init_kwargs = {'start_strategy': 'Defector'})

opponent = axelrod.Cooperator()
actions = list([(C, C)]) * 15
self.versus_test(opponent, expected_actions=actions,
init_kwargs = {'start_strategy': 'Cooperator'})

opponent = axelrod.Cooperator()
actions = [(C, C)] + list([(D, C), (C, C)]) * 7
self.versus_test(opponent, expected_actions=actions,
init_kwargs = {'start_strategy': 'Alternator'})

#Test net-cooperation-score (NCS) based decisions in subsequent turns
opponent = axelrod.Cooperator()
actions = [(C, C)] * 15 + [(C, C)]
self.versus_test(opponent, expected_actions=actions, seed=1,
init_kwargs = {'memory': [D] * 5 + [C] * 10})

opponent = axelrod.Cooperator()
actions = [(C, C)] * 15 + [(C, C)]
self.versus_test(opponent, expected_actions=actions, seed=1,
init_kwargs = {'memory': [D] * 4 + [C] * 11})

opponent = axelrod.Defector()
actions = [(C, D)] * 7 + [((D, D))]
self.versus_test(opponent, expected_actions=actions, seed=4,
init_kwargs = {'memory': [C] * 12,
'start_strategy': 'Defector',
'start_strategy_duration': 0})
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ argument :code:`seed` (useful and necessary for stochastic strategies,
expected_actions=[(D, C), (C, D), (C, C)], seed=None)

In this case the player is tested against an opponent that will cycle through
:code:`C, D`. The :code:`expected_actions` are the actions player by both
:code:`C, D`. The :code:`expected_actions` are the actions played by both
the tested player and the opponent in the match. In this case we see that the
player is expected to play :code:`D, C, C` against :code:`C, D, C`.

Expand Down Expand Up @@ -52,7 +52,7 @@ assumes that players do not know the length of the match::

The function :code:`versus_test` also accepts a dictionary parameter of
keyword arguments that dictate how the player is initiated. For example this
test how the player plays when initialised with :code:`p=1`::
tests how the player plays when initialised with :code:`p=1`::

actions = [(C, C), (C, D), (C, C), (C, D)]
self.versus_test(axelrod.Alternator(), expected_actions=actions,
Expand Down