-
Notifications
You must be signed in to change notification settings - Fork 268
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
Rework basic tests and update a number of explicit tests #820
Conversation
There's one doc build test failing that I'm not sure how to fix. |
@marcharper, I've just pushed 81853d6 to this branch (I hope that's ok). I believe that should fix your rest syntax errors. |
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 all looks great @marcharper, I have a couple of requests about the documentation where I feel things could be tightened up a bit as well as possibly changing history1, history2
as variables in responses_test
to be player_history, opponent_history
(or perhaps the other way around: I'm not entirely sure).
When this goes it it will close #726
# Need to retain history for opponents that examine opponents history | ||
# Do a deep copy just to be safe | ||
super().__init__() | ||
self.history = copy.deepcopy(player.history) |
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.
Why do we not need 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.
The new code below it copies the list element by element using update_history
.
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.
Cool. Thanks.
|
||
|
||
def simulate_play(P1, P2, h1=None, h2=None): | ||
def simulate_play(player1, player2, h1=None, h2=None): |
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.
history1, history2
perhaps?
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 changed h1
and h2
to action1
and action2
.
@@ -43,13 +39,11 @@ class TestTrickyCooperator(TestPlayer): | |||
} | |||
|
|||
def test_strategy(self): | |||
"""Starts by cooperating.""" | |||
# Starts by cooperating. |
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.
Why an inline comment instead of a docstring?
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.
Because the docstring doesn't fully describe the function and a lot of others were this way, so I standardized them. We can write better doc strings for these if you want but it's essentially always the same thing. The inline comments are better since they explain what each group of assertions is testing.
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'm indifferent, although in terms of PEP8 standards is a docstring not what is usual here?
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.
Sure but the docstring is either generic for all instances of test_strategy
, i.e. """Tests that the strategy returns the expected actions."""
or it's essentially a repeat of the strategy's docstring. Is there a preference?
in the four possible second rounds of play, depending on the move the the | ||
strategy and the opponent in the first round. | ||
* `responses_test(responses, history1=None, history2=None, ...)` is a powerful | ||
test that can handle a variety of situations, testing the first X actions, the |
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.
n actions? (not overly insistant but personally prefer n for discrete variables...)
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.
ok
:code:`strategy` method in the :code:`TitForTat` class works as expected: | ||
|
||
1. If the opponent's last strategy was :code:`C`: then :code:`TitForTat` should | ||
cooperate:: | ||
|
||
self.responses_test([C] * 4, [C, C, C, C], [C]) | ||
self.responses_test([C], [C] * 4, [C, C, C, C]) |
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 still not immediately clear to me, perhaps for clarity write this (and the class above) with:
self.responses_test(responses=[C], history1=[C, C, C, C], history2=[C, C, C, C]),
I also wonder if the variable names (here and in the class) should be opponent_history
, player_history
?
In your explanation here you have not made it clear why you have a list of 4 C
, perhaps a sentence like here we also are able to test the response :code:C
is obtained after a history of 4 consecutive C
for each player. (I also feel that the mix of [C] *4
and [C, C, C, C]
is not helpful: perhaps we just stick with one of them?).
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 just used the test that was already there; I'll improve the language.
self.assertEqual(P1.strategy(P2), 'C') | ||
This is equivalent to choosing an opponent will play C or D as needed and | ||
checking the next move. This function can also take an optional random seed | ||
argument `seed`. | ||
|
||
3. The member function :code:`responses_test` takes arbitrary histories for each |
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 I'm not mistaken you have already mentioned responses_test
on line 77. Could this be brought together?
|
||
2. If the opponent's last strategy was :code:`D`: then :code:`TitForTat` should | ||
defect:: | ||
|
||
self.responses_test([C] * 5, [C, C, C, C, D], [D]) | ||
self.responses_test([D], [C] * 5, [C, C, C, C, D]) | ||
|
||
We have added some convenience member functions to the :code:`TestPlayer` 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.
This has now been said above right?
and so will not actually test the strategy correctly. The test suite will | ||
warn you if it detects a mismatch in simulated history and actual history. | ||
|
||
Note also that in general it is not a good idea to manually set the history |
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.
Is this sentence necessary?
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.
About setting the history? Indeed it is, setting the history directly will often not set the member instances correctly leading to an erroneous test.
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.
Yes I agree with what you say in the sentence (because I know why which the reader might not) but with this framework there's not much need for the sentence (as people are unlikely to do it right?) or at least you could add the explanation?
@@ -113,7 +113,7 @@ opponent has not played yet) will cooperate:: | |||
except IndexError: |
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 file should be deleted.
self.first_play_test(C, seed=11) | ||
self.first_play_test(D, seed=23) | ||
|
||
* `second_play_test(rCC, rCD, rDC, rDD, seed=None)` tests the strategies 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.
You discuss these 3 functions lower down in terms of the member functions, I think you could remove this paragraph and just start by illustrating with an example (TitForTat). Or perhaps something else but it's not ideal to have these twice.
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.
Maybe the extra examples are useful?
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.
Yes the extra example (that was not the point I was making). You introduce these functions twice, from a newcomer/beginner's point of view that can be confusing.
I'll write a modification and send you a PR and you can see what I'm trying to say.
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 also fix your rest syntax (you can build the docs locally by running make html
).
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.
Thanks!
Any thoughts on:
|
Have opened #825 to this branch with what I had in mind. |
Also: fix rst syntax.
I'm fine with changing the argument names for response test. Sorry I had to get on a plane and didn't get to finish and push the updates. |
No problem at all 👍 :) |
Let me know if you'd like me to do this or we could have this as a further PR :) |
I can do it -- pycharm can globally find-replace |
Winning :) |
@drvinceknight Let me know if I've missed anything, and if you'd like docstrings for |
No: you got everything for me. Thanks! This is an awesome contribution and I think really tidies up our testing framework! :D
Yeah: we can open up an issue to clean up the warnings we're now getting once this is in. 👍 |
I'm simplifying #807 by porting out some of the changes. This PR mostly contains updates to the testing suite including:
TestHeadsUp
is nowTestMatch
. We should expand and encourage testing actual matches and their outcomes.markov_test([C, D, D, C])
is nowsecond_play_test(C, D, D, C)
(name is debatable)responses_test
got some upgrades: the argument order is nowresponses_test(responses, history1=None, history2=None, ...)
so you can test the first X actions with e.g.responses_test([C,D,D,...])
without any other argumentsYou may note some additional warnings now in the test suite -- if the requested history of play is impossible then a warning will be raised (but the test can still pass). This is to encourage the writing of better tests. We can suppress these.