-
Notifications
You must be signed in to change notification settings - Fork 263
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
Conversation
added no. of turns param fixed list in __init__ updated memdecay code Update writing_test_for_the_new_strategy.rst fixed typo switching MemoryDecay to MetaPlayer class fixing strategy code added mem_decay to meta.py, expanded tests finishing unit tests
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've picked up a few things that are causing errors and test failures.
A couple of other things (that might be worth trying to pick up):
- The doctests are failing because of the classification (this is normal and this file http://axelrod.readthedocs.io/en/stable/tutorials/advanced/classification_of_strategies.html just needs to be adjusted). Info on running doctests: http://axelrod.readthedocs.io/en/stable/tutorials/contributing/running_tests.html#testing-the-documentation
- The coverage report is saying that not all lines are tested. It might be easier to find these once you've made my suggested changes :)
👍 :)
axelrod/strategies/_strategies.py
Outdated
@@ -193,7 +193,6 @@ | |||
MathConstantHunter, | |||
NaiveProber, | |||
MEM2, | |||
Michaelos, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do not delete this line.
@@ -0,0 +1,78 @@ | |||
"""Tests for the Memory Decay strategy""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the strategy is going to stay in the meta.py
module, have the tests in the test_meta.py
module please.
@@ -0,0 +1,78 @@ | |||
"""Tests for the Memory Decay strategy""" | |||
|
|||
import axelrod as axe |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The convention throughout the library is to import axelrod as axl
#Test TitForTat behavior in first 15 turns | ||
opponent = axe.Cooperator() | ||
actions = list([(C, C)]) * 15 | ||
self.versus_test(opponent, expected_actions = actions) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PEP8 require no spaces around the =
in function calls:
self.versus_test(opponent, expected_actions=actions)
(Throughout this file.)
docs/reference/all_strategies.rst
Outdated
@@ -102,6 +102,9 @@ Here are the docstrings of all the strategies in the library. | |||
.. automodule:: axelrod.strategies.mathematicalconstants | |||
:members: | |||
:undoc-members: | |||
.. automodule:: axelrod.strategies.memorydecay |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is no need for this as the strategy is not in this module but in axelrod.strategies.meta
(which is already included).
axelrod/strategies/meta.py
Outdated
|
||
class MemoryDecay(MetaPlayer): | ||
""" | ||
A player utilizes the (default) Tit for Tat stretegy for the first (default) 15 turns, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
strategy
axelrod/strategies/meta.py
Outdated
passed, the player calculates a 'net cooperation score' (NCS) for his opponent, | ||
weighing decisions to Cooperate as (default) 1, and to Defect as (default) | ||
-2. If the opponent's NCS is below 0, the player Defects; otherwise, | ||
he Cooperates. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the play defects
they cooperate
.
axelrod/strategies/meta.py
Outdated
-2. If the opponent's NCS is below 0, the player Defects; otherwise, | ||
he Cooperates. | ||
|
||
The player's memories of his opponent's decisions have a random chance to be |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
of the opponent's
axelrod/strategies/meta.py
Outdated
|
||
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: str = 'Tit For Tat', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's just pass the actual strategy class: start_strategy: axl.Player = axl.TitForTat
, then the complicated search code can be simplified.
Something like:
self.team = [start_strategy()]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey, so, if i change the __init__
to
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: axelrod.Player = axelrod.TitForTat,
start_strategy_duration: int = 15):
I get this when trying to import the library:
>>> import axelrod
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/denis/Documents/Axelrod/axelrod/__init__.py", line 17, in <module>
from .strategies import *
File "/home/denis/Documents/Axelrod/axelrod/strategies/__init__.py", line 9, in <module>
from .meta import (
File "/home/denis/Documents/Axelrod/axelrod/strategies/meta.py", line 538, in <module>
class MemoryDecay(MetaPlayer):
File "/home/denis/Documents/Axelrod/axelrod/strategies/meta.py", line 569, in MemoryDecay
memory: list = None, start_strategy: axelrod.Player = axelrod.TitForTat,
NameError: name 'axelrod' is not defined
If I add import axelrod
to the beginning of meta.py
(which, I suppose, shouldn't be necessary?), I get this:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/denis/Documents/Axelrod/axelrod/__init__.py", line 17, in <module>
from .strategies import *
File "/home/denis/Documents/Axelrod/axelrod/strategies/__init__.py", line 9, in <module>
from .meta import (
File "/home/denis/Documents/Axelrod/axelrod/strategies/meta.py", line 539, in <module>
class MemoryDecay(MetaPlayer):
File "/home/denis/Documents/Axelrod/axelrod/strategies/meta.py", line 570, in MemoryDecay
memory: list = None, start_strategy: axelrod.Player = axelrod.TitForTat,
AttributeError: module 'axelrod' has no attribute 'TitForTat'
Any ideas?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Go with:
from axelrod.strategies import TitForTat
...
..., axelrod.Player = TitForTat
(This is due to the order of imports when loading the library itself.)
axelrod/strategies/meta.py
Outdated
@@ -1,4 +1,5 @@ | |||
from numpy.random import choice | |||
from random import random,choice |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is overwriting the choice
imported in numpy.random
and causing numerous failures of other tests.
I suggest importing import random
and using random.random
but using numpy's choice
(which has similar behaviour) - alternatively you could use random.choice
.
|
I think I've applied all the suggested changes. I ran the doctest and it says:
|
axelrod/strategies/meta.py
Outdated
self.gain_value = gain_value | ||
self.memory = [] if memory == None else memory | ||
self.start_strategy_duration = start_strategy_duration | ||
self.team = start_strategy() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are numerous unit tests that are failing. This is because self.team
needs to be a list (even if it only contains 1 strategy):
self.team = [start_strategy()]
There's information on running tests here: http://axelrod.readthedocs.io/en/stable/tutorials/contributing/running_tests.html#running-tests
but let me know if you'd like a hand :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've changed self.team
to a list and ran unittest.
There are a few errors I don't know how to handle, so I'd appreciate it if you'd take a look.
1 > python -m unittest axelrod.tests.strategies.test_meta
.F.F..E..........................................................................................................................................................
======================================================================
ERROR: test_strategy (axelrod.tests.strategies.test_meta.TestMemoryDecay)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/denis/Documents/Axelrod/axelrod/tests/strategies/test_meta.py", line 633, in test_strategy
init_kwargs = {'start_strategy': 'Defector'})
File "/home/denis/Documents/Axelrod/axelrod/tests/strategies/test_player.py", line 490, in versus_test
player = self.player(**init_kwargs)
File "/home/denis/Documents/Axelrod/axelrod/strategies/meta.py", line 579, in __init__
self.team = [start_strategy()]
TypeError: 'str' object is not callable
======================================================================
FAIL: test_initialisation (axelrod.tests.strategies.test_meta.TestMemoryDecay)
Test that the player initiates correctly.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/denis/Documents/Axelrod/axelrod/tests/strategies/test_player.py", line 372, in test_initialisation
self.classifier_test(self.expected_class_classifier)
File "/home/denis/Documents/Axelrod/axelrod/tests/strategies/test_player.py", line 512, in classifier_test
self.assertEqual(expected_class_classifier, self.player.classifier)
AssertionError: {'mem[72 chars]of': {'length', 'game'}, 'inspects_source': Fa[56 chars]alse} != {'mem[72 chars]of': set(), 'inspects_source': False, 'manipul[43 chars]alse}
{'inspects_source': False,
'long_run_time': False,
- 'makes_use_of': {'length', 'game'},
+ 'makes_use_of': set(),
'manipulates_source': False,
'manipulates_state': False,
'memory_depth': inf,
'stochastic': True}
======================================================================
FAIL: test_repr (axelrod.tests.strategies.test_meta.TestMemoryDecay)
Test that the representation is correct.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/denis/Documents/Axelrod/axelrod/tests/strategies/test_player.py", line 377, in test_repr
self.assertEqual(str(self.player()), self.name)
AssertionError: 'Memory Decay: 1 player' != 'MemoryDecay'
- Memory Decay: 1 player
+ MemoryDecay
----------------------------------------------------------------------
Ran 161 tests in 56.801s
FAILED (failures=2, errors=1)
Also, thank you for being so patient and helpful. Really means a lot to me, being new to all this :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, thank you for being so patient and helpful. Really means a lot to me, being new to all this :)
No problem at all, appreciate the contribution. We're not far now, hopefully below I've pointed out everything that needs fixing. Not a problem if it's still not quite there though and we need to figure something else out :) 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few tests are still failing:
======================================================================
ERROR: test_strategy (axelrod.tests.strategies.test_meta.TestMemoryDecay)
----------------------------------------------------------------------
Traceback (most recent call last):
File "C:\projects\axelrod\axelrod\tests\strategies\test_meta.py", line 633, in test_strategy
init_kwargs = {'start_strategy': 'Defector'})
File "C:\projects\axelrod\axelrod\tests\strategies\test_player.py", line 490, in versus_test
player = self.player(**init_kwargs)
File "C:\projects\axelrod\axelrod\strategies\meta.py", line 579, in __init__
self.team = [start_strategy()]
TypeError: 'str' object is not callable
======================================================================
FAIL: test_initialisation (axelrod.tests.strategies.test_meta.TestMemoryDecay)
Test that the player initiates correctly.
----------------------------------------------------------------------
Traceback (most recent call last):
File "C:\projects\axelrod\axelrod\tests\strategies\test_player.py", line 372, in test_initialisation
self.classifier_test(self.expected_class_classifier)
File "C:\projects\axelrod\axelrod\tests\strategies\test_player.py", line 512, in classifier_test
self.assertEqual(expected_class_classifier, self.player.classifier)
AssertionError: {'mem[13 chars]nf, 'manipulates_source': False, 'stochastic':[115 chars]alse} != {'mem[13 chars]nf, 'inspects_source': False, 'manipulates_sou[102 chars]alse}
{'inspects_source': False,
'long_run_time': False,
- 'makes_use_of': {'length', 'game'},
+ 'makes_use_of': set(),
'manipulates_source': False,
'manipulates_state': False,
'memory_depth': inf,
'stochastic': True}
======================================================================
FAIL: test_repr (axelrod.tests.strategies.test_meta.TestMemoryDecay)
Test that the representation is correct.
----------------------------------------------------------------------
Traceback (most recent call last):
File "C:\projects\axelrod\axelrod\tests\strategies\test_player.py", line 377, in test_repr
self.assertEqual(str(self.player()), self.name)
AssertionError: 'Memory Decay: 1 player' != 'MemoryDecay'
- Memory Decay: 1 player
+ MemoryDecay
-
Looks like there's a test that's still passing
Defector
(and others below) as a string (need to passaxl.Defector
) -
The classifier test isn't passing. It's because the classification is being updated by the parent class's init. So let's remove
self.team = [starting_strategy()]
and modify line 572 to be:572 super().__init__(team=[start_strategy])
This ensures that the classification of the strategy gets updated properly.
-
The repr is still using the parent classe's
__repr__
can you write a new__repr__
along the lines of:def __repr__(self): return axl.Player.__repr__(self)
This way the string representation of the strategy will use the default
repr
method which includes all init keywords.
👍
class TestMemoryDecay(TestPlayer): | ||
|
||
name = 'MemoryDecay' | ||
player = axelrod.meta.MemoryDecay |
There was a problem hiding this comment.
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
.
I ran
The report says everything except
Besides the changes you've suggested, I had to change the I thought the strategy was supposed to be stochastic since it has a random component (memory deletion and alteration). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the change to stochastic: True
causes other errors we'll need to understand them :)
axelrod/strategies/meta.py
Outdated
classifier = { | ||
'memory_depth' : float('inf'), | ||
'long_run_time' : False, | ||
'stochastic' : False, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The strategy is certainly stochastic. This should be True
expected_classifier = { | ||
'memory_depth': float('inf'), | ||
'long_run_time': False, | ||
'stochastic': False, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will be need to be changed to True
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've changed it to True
in both test_meta.py
and meta.py
, and I get this error:
======================================================================
FAIL: test_initialisation (axelrod.tests.strategies.test_meta.TestMemoryDecay)
Test that the player initiates correctly.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/denis/Documents/Axelrod/axelrod/tests/strategies/test_player.py", line 372, in test_initialisation
self.classifier_test(self.expected_class_classifier)
File "/home/denis/Documents/Axelrod/axelrod/tests/strategies/test_player.py", line 512, in classifier_test
self.assertEqual(expected_class_classifier, self.player.classifier)
AssertionError: {'mem[50 chars]ic': False, 'makes_use_of': set(), 'inspects_s[66 chars]alse} != {'mem[50 chars]ic': True, 'makes_use_of': set(), 'inspects_so[65 chars]alse}
{'inspects_source': False,
'long_run_time': False,
'makes_use_of': set(),
'manipulates_source': False,
'manipulates_state': False,
'memory_depth': inf,
- 'stochastic': False}
? ^^^^
+ 'stochastic': True}
? ^^^
----------------------------------------------------------------------
Ran 2771 tests in 441.809s
FAILED (failures=1, expected failures=1)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, yes I think I know why that is (the init
of the parent class is overwriting it).
Add (to the init):
572 super().__init__(team = [start_strategy])
573 self.classifier["stochastic"] = True
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll try it now. Another thing I've noticed is that I used the TestPlayer
as the parent class, not the TestMetaPlayer
. Could that be the cause of the issue?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Your solution solves the error. Should I stick with it or try changing the parent class to TestMetaPlayer
? Or both?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's stick with TestPlayer
, this player is a bit different so I think it makes sense :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay. I've added the code suggested for player.py
and am currently running the tests on my machine. I'll report if there are any errors.
|
||
class TestMemoryDecay(TestPlayer): | ||
|
||
name = "Memory Decay: 0.1, 0.03, -2, 1, <class 'axelrod.strategies.titfortat.TitForTat'>, 15" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a bit of a mouthful but I think it's fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we look at what happens if we modify the axl.Player
__repr__
method to be:
196 def __repr__(self):
197 """The string method for the strategy.
198 Appends the `__init__` parameters to the strategy's name."""
199 name = self.name
200 prefix = ': '
201 gen = (value for value in self.init_kwargs.values() if value is not None)
202 for value in gen:
+ 203 if issubclass(value, Player):
+ 204 value = value.name
205 name = ''.join([name, prefix, str(value)])
206 prefix = ', '
207 return name
(Lines 203, 204 of https://github.com/Axelrod-Python/Axelrod/blob/master/axelrod/player.py, will need a from axelrod.player import Player
at the top.)
This way it ensures the default player repr displays the strategy name and not the class...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry that won't work. This does:
195 def __repr__(self):
196 """The string method for the strategy.
197 Appends the `__init__` parameters to the strategy's name."""
198 name = self.name
199 prefix = ': '
200 gen = (value for value in self.init_kwargs.values() if value is not None)
201 for value in gen:
+ 202 try:
+ 203 if issubclass(value, Player):
+ 204 value = value.name
+ 205 except TypeError:
+ 206 pass
207 name = ''.join([name, prefix, str(value)])
208 prefix = ', '
209 return name
…, fixed stochastic classifier for memdecay
I ran the tests locally on the latest commit and everything seems fine! |
Yup, this looks good to me! Thanks! Let's see what the other core developers have to say (there might be a nicer modification of the |
Great! Thank you for the help! |
axelrod/strategies/meta.py
Outdated
""" | ||
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, |
There was a problem hiding this comment.
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'
axelrod/strategies/meta.py
Outdated
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, | ||
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, |
There was a problem hiding this comment.
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'
axelrod/strategies/meta.py
Outdated
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 |
There was a problem hiding this comment.
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 ...'
axelrod/strategies/meta.py
Outdated
|
||
# translates the actions (D and C) to numeric values (loss_value and | ||
# gain_value) | ||
def gain_loss_tr(self): |
There was a problem hiding this comment.
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?
axelrod/strategies/meta.py
Outdated
self.gain_value, self.memory)] | ||
|
||
# alters memory entry, i.e. puts C if there's a D and vice versa | ||
def mem_alter(self): |
There was a problem hiding this comment.
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
axelrod/strategies/meta.py
Outdated
# gain_value) | ||
def gain_loss_tr(self): | ||
self.gloss_values = [*map(lambda x: self.loss_value if x == D else | ||
self.gain_value, self.memory)] |
There was a problem hiding this comment.
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?
axelrod/strategies/meta.py
Outdated
self.memory[alter] = self.memory[alter].flip() | ||
|
||
# deletes memory entry | ||
def mem_delete(self): |
There was a problem hiding this comment.
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?
axelrod/strategies/meta.py
Outdated
is 0.03) or deleted (default probability is 0.1). | ||
|
||
It's necessary to specify EXACT name of the starting strategy if changing | ||
the default. Possible strategies can be accessed with the .team attribute. |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 :)
axelrod/strategies/meta.py
Outdated
if action == D: | ||
self.gloss_values.append(self.loss_value) | ||
else: | ||
self.gloss_values.append(self.gain_value) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a suggestion rather than a request but Github doesn't really cope with that idea!
def gain_loss_translate(self):
"""
Translates the actions (D and C) to numeric values (loss_value and
gain_value).
"""
values = {
C: self.gain_value,
D: self.loss_value
}
self.gloss_values = [values[action] for action in self.memory]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll go with this, it's more elegant. Thanks!
Awesome! Thanks for the contribution @vdeni! 👍 |
Thank you for the help! :) |
Hi! I think I've managed to implement the strategy I wanted. Following a short discussion with dr Knight on Gitter, I've decided to make the strategy a child class of the MetaPlayer, and have appended it to the
meta.py
file (MemoryDecay
class) in the strategies.The test file is
test_memory_decay.py
.Also, I've fixed a few typos in the docs.
I'd appreciate it if you'd take a look at the code and see if it's ready for merging. If there's anything wrong, please inform me, and I'll do my best to fix it.
Regards,
Denis Vlašiček