Skip to content

Implemented Weiner, k41r from Axelrod's Second #1145

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

Merged
merged 2 commits into from
Dec 1, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 2 additions & 1 deletion axelrod/strategies/_strategies.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
UnnamedStrategy, SteinAndRapoport, TidemanAndChieruzzi)
from .axelrod_second import (
Champion, Eatherley, Tester, Gladstein, Tranquilizer, MoreGrofman,
Kluepfel, Borufsen, Cave, WmAdams, GraaskampKatzen)
Kluepfel, Borufsen, Cave, WmAdams, GraaskampKatzen, Weiner)
from .backstabber import BackStabber, DoubleCrosser
from .better_and_better import BetterAndBetter
from .bush_mosteller import BushMosteller
Expand Down Expand Up @@ -270,6 +270,7 @@
Tullock,
TwoTitsForTat,
VeryBad,
Weiner,
Willing,
Winner12,
Winner21,
Expand Down
97 changes: 96 additions & 1 deletion axelrod/strategies/axelrod_second.py
Original file line number Diff line number Diff line change
Expand Up @@ -762,6 +762,7 @@ def strategy(self, opponent: Player) -> Action:

number_defects = opponent.defections
perc_defects = number_defects / turn

# Defect if the opponent has defected often or appears random.
if turn > 39 and perc_defects > 0.39:
return D
Expand Down Expand Up @@ -812,11 +813,13 @@ def strategy(self, opponent: Player) -> Action:
if opponent.history[0] == D:
number_defects -= 1

if number_defects in [4, 7, 9]: return D
if number_defects in [4, 7, 9]:
Copy link
Member

Choose a reason for hiding this comment

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

Hey @gaffney2010, it looks like you've copied this manually from #1143?

No big deal at all, I suspect you've done this to try and avoid merge conflicts going forward but for info, that's not considered best practice in terms of etiquette. A better way to do this would be to "go get" the actual git commits that make this change, there are various ways to do this (for example a command called git cherry-pick. One of the main reasons for this is that it ends up causing problems on other people's branches (as well as potentially erasing their contributions in terms of git commit logs).

Not a big problem at all, and not suggesting you change anything (although once #1143 goes in you might end up with a merge conflict here), just letting you know 👍 :)

Copy link
Member

Choose a reason for hiding this comment

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

@gaffney2010 now that #1143 has gone in there are a couple of merge conflicts. Let me know if you need a hand 👍

Copy link
Member Author

Choose a reason for hiding this comment

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

Okay. I can see why it would be better to go get the changes directly from the commit. How can you tell that I did it manually. Did I miss something or does it show up differently?

Copy link
Member

Choose a reason for hiding this comment

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

No it shows up the same but the commit that made the code on #1143 isn't in the history here so the conclusion is that you did it manually.

Not a big deal :)

return D
if number_defects > 9 and opponent.history[-1] == D:
return random_choice((0.5) ** (number_defects - 9))
return C


class GraaskampKatzen(Player):
"""
Strategy submitted to Axelrod's second tournament by Jim Graaskamp and Ken
Expand Down Expand Up @@ -880,3 +883,95 @@ def strategy(self, opponent: Player) -> Action:
return D

return opponent.history[-1] # Tit-for-Tat


class Weiner(Player):
"""
Strategy submitted to Axelrod's second tournament by Herb Weiner (K41R),
and came in seventh in that tournament.

Play Tit-for-Tat with a chance for forgiveness and a defective override.

The chance for forgiveness happens only if `forgive_flag` is raised
(flag discussed below). If raised and `turn` is greater than `grudge`,
then override Tit-for-Tat with Cooperation. `grudge` is a variable that
starts at 0 and increments 20 with each forgiven Defect (a Defect that is
overriden through the forgiveness logic). `forgive_flag` is lower whether
logic is overriden or not.

The variable `defect_padding` increments with each opponent Defect, but
resets to zero with each opponent Cooperate (or `forgive_flag` lowering) so
that it roughly counts Defects between Cooperates. Whenever the opponent
Cooperates, if `defect_padding` (before reseting) is odd, then we raise
`forgive_flag` for next turn.

Finally a defective override is assessed after forgiveness. If five or
more of the opponent's last twelve actions are Defects, then Defect. This
will overrule a forgiveness, but doesn't undo the lowering of
`forgiveness_flag`. Note that "last twelve actions" doesn't count the most
recent action. Actually the original code updates history after checking
for defect override.

Names:

- Weiner: [Axelrod1980b]_
"""

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

def __init__(self):
super().__init__()
self.forgive_flag = False
self.grudge = 0
self.defect_padding = 0
self.last_twelve = [0] * 12
self.lt_index = 0 # Circles around last_twelve

def try_return(self, to_return):
"""
We put the logic here to check for the defective override.
"""

if np.sum(self.last_twelve) >= 5:
return D
return to_return

def strategy(self, opponent: Player) -> Action:
if len(opponent.history) == 0:
return C

# Update history, lag 1.
if len(opponent.history) >= 2:
self.last_twelve[self.lt_index] = 0
if opponent.history[-2] == D:
self.last_twelve[self.lt_index] = 1
self.lt_index = (self.lt_index + 1) % 12

if self.forgive_flag:
self.forgive_flag = False
self.defect_padding = 0
if self.grudge < len(self.history) + 1 and opponent.history[-1] == D:
# Then override
self.grudge += 20
return self.try_return(C)
else:
return self.try_return(opponent.history[-1])
else:
# See if forgive_flag should be raised
if opponent.history[-1] == D:
self.defect_padding += 1
else:
if self.defect_padding % 2 == 1:
self.forgive_flag = True
self.defect_padding = 0

return self.try_return(opponent.history[-1])
73 changes: 73 additions & 0 deletions axelrod/tests/strategies/test_axelrod_second.py
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,7 @@ def test_strategy(self):
(C, D), (C, D), (C, D), (D, D), (D, C)]
self.versus_test(opponent, expected_actions=actions)


class TestKluepfel(TestPlayer):
name = "Kluepfel"
player = axelrod.Kluepfel
Expand Down Expand Up @@ -453,6 +454,7 @@ def test_strategy(self):
(D, C), (D, D)]
self.versus_test(axelrod.Random(0.5), expected_actions=actions, seed=10)


class TestBorufsen(TestPlayer):
name = "Borufsen"
player = axelrod.Borufsen
Expand Down Expand Up @@ -497,6 +499,7 @@ def test_strategy(self):
[(D, C)] * 25
self.versus_test(axelrod.WinShiftLoseStay(D), expected_actions=actions)


class TestCave(TestPlayer):
name = "Cave"
player = axelrod.Cave
Expand Down Expand Up @@ -546,6 +549,7 @@ def test_strategy(self):
actions += [(D, D), (D, C)] * 100 # Random finally detected
self.versus_test(axelrod.Alternator(), expected_actions=actions, seed=2)


class TestWmAdams(TestPlayer):
name = "WmAdams"
player = axelrod.WmAdams
Expand Down Expand Up @@ -586,6 +590,7 @@ def test_strategy(self):
actions += [(C, C)] * 99
self.versus_test(changed_man, expected_actions=actions, seed=1)


class TestGraaskampKatzen(TestPlayer):
name = "GraaskampKatzen"
player = axelrod.GraaskampKatzen
Expand Down Expand Up @@ -621,3 +626,71 @@ def test_strategy(self):
actions += [(D, C)]
actions += [(D, D), (D, D), (D, C)] * 20 # Defect here on
self.versus_test(Delayed_GK_Foil, expected_actions=actions)


class TestWeiner(TestPlayer):
name = "Weiner"
player = axelrod.Weiner
expected_classifier = {
'memory_depth': float('inf'),
'stochastic': False,
'makes_use_of': set(),
'long_run_time': False,
'inspects_source': False,
'manipulates_source': False,
'manipulates_state': False
}

def test_strategy(self):
actions = [(C, C)] * 100 # Cooperate forever
self.versus_test(axelrod.Cooperator(), expected_actions=actions)

actions = [(C, C)]
actions += [(C, D), (D, C)] # Tit-for-Tat
# Opponent's last move was a C with 1 D between
actions += [(C, D)] # Tit-for-Tat. Raise forgiveness flag.
actions += [(C, C)] # Tit-for-Tat. Use forgiveness flag.
# Opponent's last move was a C, but defect_padding counted as 0.
actions += [(C, D), (D, C)] # Tit-for-Tat
# Opponent's last move was a C with 1 D between
actions += [(C, D)] # Tit-for-Tat. Raise forgiveness flag.
actions += [(D, C)] # Tit-for-Tat. Try forgiveness flag.
# This time grudge=20, so the forgiveness flag doesn't work.
actions += [(C, D)] # Tit-for-Tat.
# This is the 5th opponent defect, won't be counted for 2 turns
actions += [(D, C)] # Tit-for-Tat.
actions += [(D, D), (D, C)] * 100 # Defect now on.
self.versus_test(axelrod.Alternator(), expected_actions=actions)

# Build an opponent that will cause a wasted flag.
opponent_actions = [C, D, C, C, C, C, D, D]
Flag_Waster_1 = axelrod.MockPlayer(actions=opponent_actions)
actions = [(C, C), (C, D), (D, C)]
actions += [(C, C)] # Raise flag, like in Alternator
actions += [(C, C)] # Use flag, but don't change outcome
actions += [(C, C)]
actions += [(C, D)] # Don't raise flag
actions += [(D, D)] # Don't use flag
self.versus_test(Flag_Waster_1, expected_actions=actions)

# Demonstrate that grudge is not incremented on wasted flag.
opponent_actions = [C, D, C, C, C, C, D, C, D, C]
Flag_Waster_2 = axelrod.MockPlayer(actions=opponent_actions)
actions = [(C, C), (C, D), (D, C)]
actions += [(C, C)] # Raise flag, like in Alternator
actions += [(C, C)] # Use flag, but don't change outcome
actions += [(C, C), (C, D), (D, C)]
actions += [(C, D)] # Raise flag
actions += [(C, C)] # Use flag to change outcome
self.versus_test(Flag_Waster_2, expected_actions=actions)

# Show grudge passing over time
opponent_actions = [C, D, C, D, C] + [C] * 11 + [C, D, C, D, C]
Time_Passer = axelrod.MockPlayer(actions=opponent_actions)
actions = [(C, C), (C, D), (D, C)]
actions += [(C, D)] # Raise flag
actions += [(C, C)] # Use flag to change outcome
actions += [(C, C)] * 11
actions += [(C, C), (C, D), (D, C)]
actions += [(C, D)] # Raise flag
actions += [(C, C)] # Use flag to change outcome
2 changes: 1 addition & 1 deletion axelrod/tests/strategies/test_meta.py
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,7 @@ def test_strategy(self):
self.versus_test(opponent=axelrod.Alternator(),
expected_actions=actions, seed=0)

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

Expand Down
2 changes: 1 addition & 1 deletion docs/reference/overview_of_strategies.rst
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ repository.
"K38R_", "Nelson Weiderman", "Not Implemented"
"K39R_", "Tom Almy", "Not Implemented"
"K40R_", "Robert Adams", "Not Implemented"
"K41R_", "Herb Weiner", "Not Implemented"
"K41R_", "Herb Weiner", ":class:`Weiner <axelrod.strategies.axelrod_second.Weiner>`"
"K42R_", "Otto Borufsen", ":class:`Borufsen <axelrod.strategies.axelrod_second.Borufsen>`"
"K43R_", "R D Anderson", "Not Implemented"
"K44R_", "William Adams", ":class:`WmAdams <axelrod.strategies.axelrod_second.WmAdams>`"
Expand Down