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

Add EugineNier strategy from LessWrong #1016

Merged
merged 14 commits into from
May 25, 2017
3 changes: 2 additions & 1 deletion axelrod/strategies/_strategies.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@
TitForTat, TitFor2Tats, TwoTitsForTat, Bully, SneakyTitForTat,
SuspiciousTitForTat, AntiTitForTat, HardTitForTat, HardTitFor2Tats,
OmegaTFT, Gradual, ContriteTitForTat, SlowTitForTwoTats, AdaptiveTitForTat,
SpitefulTitForTat, SlowTitForTwoTats2, Alexei)
SpitefulTitForTat, SlowTitForTwoTats2, Alexei, EugineNier)
from .verybad import VeryBad
from .worse_and_worse import (WorseAndWorse, KnowledgeableWorseAndWorse,
WorseAndWorse2, WorseAndWorse3)
Expand Down Expand Up @@ -126,6 +126,7 @@
DoubleResurrection,
EasyGo,
Eatherley,
EugineNier,
EventualCycleHunter,
EvolvedANN,
EvolvedANN5,
Expand Down
43 changes: 41 additions & 2 deletions axelrod/strategies/titfortat.py
Original file line number Diff line number Diff line change
Expand Up @@ -642,10 +642,10 @@ def strategy(self, opponent: Player) -> Action:
class Alexei(Player):
"""
Plays similar to Tit-for-Tat, but always defect on last turn.

Names:

- Alexei's Strategy: [LessWrong2011]_
- Alexei's Strategy: [LessWrong2011]_
Copy link
Member

Choose a reason for hiding this comment

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

Is this correct? There's already a strategy called Alexei's strategy, and the name of this one is EugineNier.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

My bad. I will fix it.

Copy link
Contributor Author

@souravsingh souravsingh May 24, 2017

Choose a reason for hiding this comment

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

I believe it is correct. I checked the source and I didn't find anything wrong with naming.

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 correct, it's the name for Alexei, it's just appearing in the diff because some white space is being cleared up on line 648.

Copy link
Member

Choose a reason for hiding this comment

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

However could you change this to just say:

- Alexei: [LessWrong2011]_

"""

name = 'Alexei'
Expand All @@ -665,3 +665,42 @@ def strategy(self, opponent: Player) -> Action:
if opponent.history[-1] == D:
return D
return C

@FinalTransformer((D,), name_prefix=None)
class EugineNier(Player):
"""
Plays similar to Tit-for-Tat, but with two conditions:
1) Always Defect on Last Move
2) If other player defects five times, switch to all defects.

Names:

- EugineNier Strategy: [LessWrong2011]_
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 change this to say:

- Eugine Nier: [LessWrong2011]_

"""

name = 'EugineNier'
classifier = {
'memory_depth': float('inf'),
'stochastic': False,
'makes_use_of': {'length'},
'long_run_time': False,
'inspects_source': False,
'manipulates_source': False,
'manipulates_state': False
}

def __init__(self):
super().__init__()
self.is_defector = False

def strategy(self, opponent: Player) -> Action:
if not self.history:
return C
if self.is_defector or opponent.history[-5:] == [D] * 5:
Copy link
Member

Choose a reason for hiding this comment

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

I prefer this split into:

if opponent.history[-5:] == [D] * 5:
    self.is_defector = True
if self.is_defector:
    return D
return opponent.history[-1]

Also, is it five defections ever? Or five consecutively?

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 believe it is five consecutive defections

Copy link
Member

Choose a reason for hiding this comment

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

I don't think it is actually, here's the source from http://lesswrong.com/lw/7f2/prisoners_dilemma_tournament_results/:

Once the other player defects 5 times, switch to all defect.

So you can change this to be:

699         if (not self.is_defector) and opponent.history.count(D) >= 5:

(Checking not self.is_defector and ensures we only count if it's not a Defector.)

Could you adjust the tests so that we check five defections (not necessarily consecutively):

  643         # Becomes defector after 5 defections                                                                                                   
  644         opponent = axelrod.MockPlayer(actions=[D, C, D, D, D, D, C, C])                                                                         
  645         actions = [(C, D), (D, C), (C, D), (D, D),                                                                                              
  646                    (D, D), (D, D), (D, C), (D, C)]                                                                                              
  647         self.versus_test(opponent, expected_actions=actions)  

If you can please run those tests locally to make sure they work (I have just checked on my machine).

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 opponent.defections instead of opponent.history.count(D), it's more efficientPlease use opponent.defections instead of opponent.history.count(D), it's more efficient

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 opponent.defections instead of opponent.history.count(D), it's more efficient

Copy link
Member

Choose a reason for hiding this comment

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

Good call!

Copy link
Member

Choose a reason for hiding this comment

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

Slipped my mind :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Give me a few minutes.

self.is_defector = True
return D
return opponent.history[-1]

def reset(self):
super().reset()
self.is_defector = False
49 changes: 48 additions & 1 deletion axelrod/tests/strategies/test_titfortat.py
Original file line number Diff line number Diff line change
Expand Up @@ -593,10 +593,57 @@ def test_strategy(self):
actions = [(C, C), (C, D), (D, C), (C, D), (D, C), (C, D)]
self.versus_test(axelrod.Alternator(), expected_actions=actions,
match_attributes={"length": -1})

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

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

class TestEugineNier(TestPlayer):
"""
Tests for the EugineNier strategy
"""

name = "EugineNier: ('D',)"
player = axelrod.EugineNier
expected_classifier = {
'memory_depth': float('inf'),
'stochastic': False,
'makes_use_of': {'length'},
'inspects_source': False,
'manipulates_source': False,
'manipulates_state': False
}

def test_strategy(self):
self.first_play_test(C)
self.second_play_test(rCC=C, rCD=D, rDC=C, rDD=D)

actions = [(C, C), (C, C), (C, C), (D, C)]
self.versus_test(axelrod.Cooperator(), expected_actions=actions,
attrs={"is_defector": False})

actions = [(C, C), (C, C), (C, C), (C, C)]
self.versus_test(axelrod.Cooperator(), expected_actions=actions,
Copy link
Member

Choose a reason for hiding this comment

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

This would become:

self.versus_test(axelrod.Cooperator(), expected_actions=actions, 
                 attrs={"is_defector": False},
                 match_attributes={"length": -1})

attrs={"is_defector": False},
match_attributes={"length": -1})


# Plays TfT and defects in last round
actions = [(C, C), (C, D), (D, C), (C, D), (D, C), (D, D)]
self.versus_test(axelrod.Alternator(), expected_actions=actions,
attrs={"is_defector": False})

actions = [(C, C), (C, D), (D, C), (C, D), (D, C), (C, D)]
self.versus_test(axelrod.Alternator(), expected_actions=actions,
Copy link
Member

Choose a reason for hiding this comment

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

self.versus_test(axelrod.Alternator(), expected_actions=actions,
                 match_attributes={"length": -1},
                 attrs={"is_defector": False})

attrs={"is_defector": False},
match_attributes={"length": -1})

# Becomes defector after 5 defections
opponent = axelrod.MockPlayer(actions=[C, D, D, D, D, D, C, C])
actions = [(C, C), (C, D), (D, D), (D, D),
(D, D), (D, D), (D, C), (D, C)]
self.versus_test(opponent, expected_actions=actions,
attrs={"is_defector": True})
2 changes: 1 addition & 1 deletion docs/tutorials/advanced/classification_of_strategies.rst
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ length of each match of the tournament::
... }
>>> strategies = axl.filtered_strategies(filterset)
>>> len(strategies)
27
28

Note that in the filterset dictionary, the value for the 'makes_use_of' key
must be a list. Here is how we might identify the number of strategies that use
Expand Down