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

issue#844 backstabber.py #924

Merged
merged 8 commits into from
Mar 23, 2017

Conversation

eric-s-s
Copy link
Contributor

@eric-s-s eric-s-s commented Mar 22, 2017

added tests.

also refactored backstabber and fixed an off-by-one error.
#884

Copy link
Member

@drvinceknight drvinceknight left a comment

Choose a reason for hiding this comment

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

Thanks for this @eric-s-s it looks good.

  • Requests for docstrings (even if just a line)
  • I'd like you to tweak the tests a bit. Moving the sub calls to their own test_... method so that they get recognised as individual tests by unittest. This just helps in the case of test failures.

Could you point out and expand on the off by 1 error that you've fixed? Just to make it clear please.

return _backstabber_strategy(opponent)


def _backstabber_strategy(opponent):
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 add a docstring here please.

return C


def _alt_strategy(opponent):
Copy link
Member

Choose a reason for hiding this comment

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

Docstring please.

return C


def _opponent_triggers_alt_strategy(opponent):
Copy link
Member

Choose a reason for hiding this comment

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

Docstring please.

return before_alt_strategy < rounds_opponent_played < after_alt_strategy


def _opponent_defected_in_first_n_rounds(opponent, first_n_rounds):
Copy link
Member

Choose a reason for hiding this comment

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

Docstring please.


# Defects on rounds 199, and 200 no matter what
self.responses_test([C, D, D], [C] * 197, [C] * 197, length=200)
self._when_alt_strategy_is_triggered()
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 put these as their own test methods.

So remove them from here and rename them to start with the word test:

_when_alt_stragegy_is_triggered -> test_when_alt_strategy_is_triggered
...

This would make the test failures easier to read (by having them as specific tests instances so to speak).

@@ -24,58 +24,100 @@ def test_strategy(self):
Forgives the first 3 defections but on the fourth
will defect forever. Defects after the 198th round unconditionally.
"""
self._defects_after_four_defections()
Copy link
Member

Choose a reason for hiding this comment

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

Remove these and change the name of the methods to be test_defects_after_four_defections.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

is ok that there will be no "test_strategy" method? it will get replaced by the more specifically named ones.

Copy link
Member

Choose a reason for hiding this comment

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

Yup that's fine, you'll still run test_strategy from the super TestBackStabber class so it's kind of like what you had before :)

alternator_actions = [(C, C), (C, D)] * 4 + [(D, C), (D, D)] * 2
self.versus_test(axelrod.Alternator(), expected_actions=alternator_actions, match_attributes={"length": 200})

def _defects_on_last_two_rounds_by_match_len(self):
Copy link
Member

Choose a reason for hiding this comment

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

change to test_defects...

self._alt_strategy_stops_at_round_180()
super(TestDoubleCrosser, self).test_strategy()

def _when_alt_strategy_is_triggered(self):
Copy link
Member

Choose a reason for hiding this comment

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

test_when_...

self.versus_test(axelrod.MockPlayer(opponent_actions), expected_actions=expected_actions,
match_attributes={"length": 200})

def _starting_defect_keeps_alt_strategy_from_triggering(self):
Copy link
Member

Choose a reason for hiding this comment

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

test_starting_...

expected_actions=defects_on_last_actions + expected_actions_suffix,
match_attributes={"length": 200})

def _alt_strategy_stops_at_round_180(self):
Copy link
Member

Choose a reason for hiding this comment

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

test_alt_

return C
if len(opponent.history) < 180:
if len(opponent.history) > cutoff:
if D not in opponent.history[:cutoff + 1]:
Copy link
Contributor Author

Choose a reason for hiding this comment

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

here's the off-by-one. It should only check for defection in the first 6, but opponent.history[:cutoff +1] is the first 7.

Copy link
Member

Choose a reason for hiding this comment

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

Thanks, looks like you are right to me. Would be good to have @uglyfruitcake's thoughts on this PR too :)

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes the code doesn't match the documentation, however, It's the documentation that's wrong. I spent ages optimises the numbers in Doublecrosser to get it to perform optimally and its current state will be it's optimal performance. When I wrote the description I will have put the wrong number of turns and so it should say "the first 7 rounds". It is a mistake but its wrong in the documentation not the code. Sorry!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

rewriting that now. in a moment, comments and tests should follow the specs.

@eric-s-s
Copy link
Contributor Author

@drvinceknight thank you for all the comments. i'll get on it ASAP.

"""
before_alt_strategy = 6
after_alt_strategy = 180
if _opponent_defected_in_first_n_rounds(opponent, before_alt_strategy):
Copy link
Member

Choose a reason for hiding this comment

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

Can you explicitly write first_n_rounds=before_alt_strategy (for clarity).

return C


def _opponent_triggers_alt_strategy(opponent: Player) -> bool:
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@uglyfruitcake is this correct?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes that looks good to me.

return C


def _alt_strategy(opponent: Player) -> Action:
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 write specific tests for these module functions please.

The don't need to be extensive but just enough so that they'd fail if something got refactored in the strategy methods removing the need for them.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

actually. this brings up a problem i had, to which module methods was the most expedient solution.

as i see it, these should all be private methods of their classes and DoubleCrosser should inherit from BackStabber. The tests, as they are, should fully cover all those methods (i don't have a coverage tool, but i think i got it).

I originally wrote class DoubleCrosser(BackStabber): , but strange things would happen with @FinalTransformer((D, D), name_prefix=None) If only BackStabber had that decorator, DoubleCrosser would not inherit that behavior. If both had that decorator I got a maximum recursion depth error.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

that aside, will write tests for module as is.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

errrr. i hope complete tests is ok. trying to write non-complete tests is, for me, like not completing a

Copy link
Member

Choose a reason for hiding this comment

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

Haha, this is way more than I was expecting but it looks good to me :)

@drvinceknight
Copy link
Member

I've labelled this as ready to go but we have a 2 core reviewer process, so either @marcharper or @meatballs will be along when they have a moment and might make further requests (or indeed disagree with my requests :)) 👍

"""
If opponent's last two plays were defect, then defects on next round. Otherwise, cooperates.
"""
final_two_plays = opponent.history[-2:]
Copy link
Member

Choose a reason for hiding this comment

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

Could we rename this variable? Given that a major feature of these strategies is to defect unconditionally for the final two rounds of the match, it's confusing to call this variable final_two_plays

How about previous_two_plays instead?

@drvinceknight drvinceknight merged commit d6b171d into Axelrod-Python:master Mar 23, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants