Skip to content

Implemented Yamachi, k64r from Axelrod's Second. #1163

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
Jan 29, 2018
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
4 changes: 3 additions & 1 deletion axelrod/strategies/_strategies.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
from .axelrod_second import (
Champion, Eatherley, Tester, Gladstein, Tranquilizer, MoreGrofman,
Kluepfel, Borufsen, Cave, WmAdams, GraaskampKatzen, Weiner, Harrington,
MoreTidemanAndChieruzzi, Getzler, Leyvraz, White, Black, RichardHufford)
MoreTidemanAndChieruzzi, Getzler, Leyvraz, White, Black, RichardHufford,
Yamachi)
from .backstabber import BackStabber, DoubleCrosser
from .better_and_better import BetterAndBetter
from .bush_mosteller import BushMosteller
Expand Down Expand Up @@ -288,6 +289,7 @@
WorseAndWorse,
WorseAndWorse2,
WorseAndWorse3,
Yamachi,
ZDExtortion,
ZDExtort2,
ZDExtort3,
Expand Down
94 changes: 94 additions & 0 deletions axelrod/strategies/axelrod_second.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import random
import numpy as np
from typing import List

from axelrod.action import Action
from axelrod.player import Player
Expand Down Expand Up @@ -1709,3 +1710,96 @@ def strategy(self, opponent: Player) -> Action:
elif proportion_agree >= 0.625 and last_four_num >= 2:
return opponent.history[-1]
return D


class Yamachi(Player):
"""
Strategy submitted to Axelrod's second tournament by Brian Yamachi (K64R)
and came in seventeenth in that tournament.

The strategy keeps track of play history through a variable called
`count_them_us_them`, which is a dict indexed by (X, Y, Z), where X is an
opponent's move and Y and Z are the following moves by this player and the
opponent, respectively. Each turn, we look at our opponent's move two
turns ago, call X, and our move last turn, call Y. If (X, Y, C) has
occurred more often (or as often) as (X, Y, D), then Cooperate. Otherwise
Defect. [Note that this reflects likelihood of Cooperations or Defections
in opponent's previous move; we don't update `count_them_us_them` with
previous move until next turn.]

Starting with the 41st turn, there's a possibility to override this
behavior. If `portion_defect` is between 45% and 55% (exclusive), then
Defect, where `portion_defect` equals number of opponent defects plus 0.5
divided by the turn number (indexed by 1). When overriding this way, still
record `count_them_us_them` as though the strategy didn't override.

Names:

- Yamachi: [Axelrod1980b]_
"""

name = 'Yamachi'
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) -> None:
super().__init__()
self.count_them_us_them = {(C, C, C): 0,
(C, C, D): 0,
(C, D, C): 0,
(C, D, D): 0,
(D, C, C): 0,
(D, C, D): 0,
(D, D, C): 0,
(D, D, D): 0}
self.mod_history = list() # type: List[Action]

def try_return(self, to_return, opp_def):
"""
Return `to_return`, unless the turn is greater than 40 AND
`portion_defect` is between 45% and 55%.

In this case, still record the history as `to_return` so that the
modified behavior doesn't affect the calculation of `count_us_them_us`.
"""
turn = len(self.history) + 1

self.mod_history.append(to_return)

# In later turns, check if the opponent is close to 50/50
# If so, then override
if turn > 40:
portion_defect = (opp_def + 0.5) / turn
if 0.45 < portion_defect and portion_defect < 0.55:
return D

return to_return

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

us_last = self.mod_history[-1]
them_two_ago, us_two_ago, them_three_ago = C, C, C
if turn >= 3:
them_two_ago = opponent.history[-2]
us_two_ago = self.mod_history[-2]
if turn >= 4:
them_three_ago = opponent.history[-3]

# Update history
if turn >= 3:
self.count_them_us_them[(them_three_ago, us_two_ago, them_two_ago)] += 1

if self.count_them_us_them[(them_two_ago, us_last, C)] >= \
self.count_them_us_them[(them_two_ago, us_last, D)]:
return self.try_return(C, opponent.defections)
return self.try_return(D, opponent.defections)
106 changes: 106 additions & 0 deletions axelrod/tests/strategies/test_axelrod_second.py
Original file line number Diff line number Diff line change
Expand Up @@ -1055,3 +1055,109 @@ def test_strategy(self):
actions += [(D, D)] # Three of last four are disagreements.
actions += [(D, D)] # Now there are 5/9 disagreements, so Defect.
self.versus_test(axelrod.WinShiftLoseStay(), expected_actions=actions, attrs={"num_agreements": 5})


class TestYamachi(TestPlayer):
name = 'Yamachi'
player = axelrod.Yamachi
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
self.versus_test(axelrod.Cooperator(), expected_actions=actions)

actions = [(C, D)] * 2 # Also Cooperate in first two moves (until we update `count_them_us_them`.)
actions += [(C, D)] # them_three_ago defaults to C, so that (C, C, *) gets updated, then (D, C, *) get checked.
# It's actually impossible to Defect on the third move.
actions += [(D, D)] # (D, C, *) gets updated, then checked.
actions += [(C, D)] # (D, C, *) gets updated, but (D, D, *) checked.
actions += [(D, D)] * 30 # (D, D, *) gets updated and checked from here on.
self.versus_test(axelrod.Defector(), expected_actions=actions)

actions = [(C, C), (C, D)]
actions += [(C, C)] # Increment (C, C, C). Check (C, C, *). Cooperate.
# Reminder that first C is default value and last C is opponent's first move.
actions += [(C, D)] # Increment (C, C, D). Check (D, C, *) = 0. Cooperate.
actions += [(C, C)] # Increment (D, C, C). Check (C, C, *) = 0. Cooperate.
# There is one Defection and one Cooperation in this scenario,
# but the Cooperation was due to a default value only. We can see where this is going.
actions += [(C, D)] # Increment (C, C, D). Check (D, C, *) = 1. Cooperate.
actions += [(D, C)] # Increment (D, C, C). Check (C, C, *) = -1. Defect.
actions += [(C, D)] # Increment (C, C, D). Check (D, D, *) = 0 (New). Cooperate.
actions += [(D, C)] # Increment (D, D, C). Check (C, C, *) < 0. Defect.
actions += [(C, D)] # Increment (C, C, D). Check (D, D, *) > 0. Cooperate.
actions += [(D, C), (C, D)] * 15 # This pattern continues for a while.
actions += [(D, C), (D, D)] * 30 # Defect from turn 41 on, since near 50% Defections.
self.versus_test(axelrod.Alternator(), expected_actions=actions)

# Rip-off is the most interesting interaction.
actions = [(C, D),
(C, C),
(C, D),
(D, C),
(C, C),
(D, C),
(C, D),
(D, C),
(C, D),
(D, C),
(C, D),
(D, C),
(C, D),
(D, C),
(C, D),
(D, C),
(C, D),
(D, C),
(C, D),
(D, C),
(C, D),
(D, C),
(C, D),
(D, C),
(C, D),
(D, C),
(C, D),
(D, C),
(C, D),
(D, C),
(C, D),
(D, C),
(C, D),
(D, C),
(C, D),
(D, C),
(C, D),
(D, C),
(C, D),
(D, C)]
my_dict = {(C, C, C): 1,
(C, C, D): 18,
(C, D, C): 1,
(C, D, D): 0,
(D, C, C): 1,
(D, C, D): 0,
(D, D, C): 17,
(D, D, D): 0}
RipoffPlayer = axelrod.Ripoff()
self.versus_test(RipoffPlayer, expected_actions=actions, attrs={"count_them_us_them": my_dict})
self.assertEqual(RipoffPlayer.defections, 19) # Next turn, `portion_defect` = 0.4756

# The pattern (C, D), (D, C) will continue indefintely unless overriden.
actions += [(D, D)] # Next turn, `portion_defect` = 0.4881
actions += [(D, D)] # Next turn, `portion_defect` = 0.5
actions += [(D, D)] # Next turn, `portion_defect` = 0.5114
actions += [(D, D)] # Next turn, `portion_defect` = 0.5222
actions += [(D, D)] # Next turn, `portion_defect` = 0.5326
actions += [(D, D)] # Next turn, `portion_defect` = 0.5426
actions += [(D, D)] # Next turn, `portion_defect` = 0.5521
actions += [(D, D), (C, D), (D, C), (C, D)] # Takes a turn to fall back into the cycle.
self.versus_test(axelrod.Ripoff(), expected_actions=actions)
2 changes: 1 addition & 1 deletion docs/reference/overview_of_strategies.rst
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ repository.
"K61R_", "Danny C Champion", ":class:`Champion <axelrod.strategies.axelrod_second.Champion>`"
"K62R_", "Howard R Hollander", "Not Implemented"
"K63R_", "George Duisman", "Not Implemented"
"K64R_", "Brian Yamachi", "Not Implemented"
"K64R_", "Brian Yamachi", ":class:`Yamachi <axelrod.strategies.axelrod_second.Yamachi>`"
"K65R_", "Mark F Batell", "Not Implemented"
"K66R_", "Ray Mikkelson", "Not Implemented"
"K67R_", "Craig Feathers", ":class:`Tranquilizer <axelrod.strategies.axelrod_second.Tranquilizer>`"
Expand Down