Skip to content

Commit 32f9c31

Browse files
committed
Implemented RichardHufford strat, K47R from Axelrod's Second.
1 parent ea46854 commit 32f9c31

File tree

4 files changed

+155
-3
lines changed

4 files changed

+155
-3
lines changed

axelrod/strategies/_strategies.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from .axelrod_second import (
1111
Champion, Eatherley, Tester, Gladstein, Tranquilizer, MoreGrofman,
1212
Kluepfel, Borufsen, Cave, WmAdams, GraaskampKatzen, Weiner, Harrington,
13-
MoreTidemanAndChieruzzi, Getzler, Leyvraz, White, Black)
13+
MoreTidemanAndChieruzzi, Getzler, Leyvraz, White, Black, RichardHufford)
1414
from .backstabber import BackStabber, DoubleCrosser
1515
from .better_and_better import BetterAndBetter
1616
from .bush_mosteller import BushMosteller
@@ -243,6 +243,7 @@
243243
Retaliate2,
244244
Retaliate3,
245245
RevisedDowning,
246+
RichardHufford,
246247
Ripoff,
247248
RiskyQLearner,
248249
SelfSteem,

axelrod/strategies/axelrod_second.py

Lines changed: 114 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1550,7 +1550,7 @@ def strategy(self, opponent: Player) -> Action:
15501550
class Black(Player):
15511551
"""
15521552
Strategy submitted to Axelrod's second tournament by Paul E Black (K83R)
1553-
and came in fourteenth in that tournament.
1553+
and came in fifteenth in that tournament.
15541554
15551555
The strategy Cooperates for the first five turns. Then it calculates the
15561556
number of opponent defects in the last five moves and Cooperates with
@@ -1597,3 +1597,116 @@ def strategy(self, opponent: Player) -> Action:
15971597
number_defects = np.sum(did_d(recent_history))
15981598

15991599
return random_choice(self.prob_coop[number_defects])
1600+
1601+
1602+
class RichardHufford(Player):
1603+
"""
1604+
Strategy submitted to Axelrod's second tournament by Richard Hufford (K47R)
1605+
and came in sixteenth in that tournament.
1606+
1607+
The strategy tracks opponent "agreements," that is whenever the opponent's
1608+
previous move is the some as this player's move two turns ago. If the
1609+
opponent's first move is a Defection, this is counted as a disagreement,
1610+
and otherwise an agreement. From the agreement counts, two measures are
1611+
calculated:
1612+
1613+
- `proportion_agree`: This is the number of agreements (through opponent's
1614+
last turn) + 2 divided by the current turn number.
1615+
- `last_four_num`: The number of agreements in the last four turns. If
1616+
there have been fewer than four previous turns, then this is number of
1617+
agreement + (4 - number of past turns).
1618+
1619+
We then use these measures to decide how to play, using these rules:
1620+
1621+
1. If `proportion_agree` > 0.9 and `last_four_num` >= 4, then Cooperate.
1622+
2. Otherwise if `proportion_agree` >= 0.625 and `last_four_num` >= 2, then
1623+
Tit-for-Tat.
1624+
3. Otherwise, Defect.
1625+
1626+
However, if the opponent has Cooperated the last `streak_needed` turns,
1627+
then the strategy deviates from the usual strategy, and instead Defects.
1628+
(We call such deviation an "aberration.") In the turn immediately after an
1629+
aberration, the strategy doesn't override, even if there's a streak of
1630+
Cooperations. Two turns after an aberration, the strategy: Restarts the
1631+
Cooperation streak (never looking before this turn); Cooperates; and
1632+
changes `streak_needed` to:
1633+
1634+
floor(20.0 * `num_abb_def` / `num_abb_coop`) + 1
1635+
1636+
Here `num_abb_def` is 2 + the number of times that the opponent Defected in
1637+
the turn after an aberration, and `num_abb_coop` is 2 + the number of times
1638+
that the opponent Cooperated in response to an aberration.
1639+
1640+
Names:
1641+
1642+
- RichardHufford: [Axelrod1980b]_
1643+
"""
1644+
1645+
name = 'RichardHufford'
1646+
classifier = {
1647+
'memory_depth': float("inf"),
1648+
'stochastic': False,
1649+
'makes_use_of': set(),
1650+
'long_run_time': False,
1651+
'inspects_source': False,
1652+
'manipulates_source': False,
1653+
'manipulates_state': False
1654+
}
1655+
1656+
def __init__(self) -> None:
1657+
super().__init__()
1658+
self.num_agreements = 2
1659+
self.last_four_agreements = [1] * 4
1660+
self.last_four_index = 0
1661+
1662+
self.streak_needed = 21
1663+
self.current_streak = 2
1664+
self.last_aberration = float("inf")
1665+
self.num_abb_coop = 2
1666+
self.num_abb_def = 2
1667+
1668+
def strategy(self, opponent: Player) -> Action:
1669+
turn = len(self.history) + 1
1670+
if turn == 1:
1671+
return C
1672+
1673+
# Check if opponent agreed with us.
1674+
self.last_four_index = (self.last_four_index + 1) % 4
1675+
me_two_moves_ago = C
1676+
if turn > 2:
1677+
me_two_moves_ago = self.history[-2]
1678+
if me_two_moves_ago == opponent.history[-1]:
1679+
self.num_agreements += 1
1680+
self.last_four_agreements[self.last_four_index] = 1
1681+
else:
1682+
self.last_four_agreements[self.last_four_index] = 0
1683+
1684+
# Check is last_aberration is infinite.
1685+
# i.e Not an aberration in last two turns.
1686+
if turn < self.last_aberration:
1687+
if opponent.history[-1] == C:
1688+
self.current_streak += 1
1689+
else:
1690+
self.current_streak = 0
1691+
if self.current_streak >= self.streak_needed:
1692+
self.last_aberration = turn
1693+
if self.current_streak == self.streak_needed:
1694+
return D
1695+
elif turn == self.last_aberration + 2:
1696+
self.last_aberration = float("inf")
1697+
if opponent.history[-1] == C:
1698+
self.num_abb_coop += 1
1699+
else:
1700+
self.num_abb_def += 1
1701+
self.streak_needed = np.floor(20.0 * self.num_abb_def / self.num_abb_coop) + 1
1702+
self.current_streak = 0
1703+
return C
1704+
1705+
proportion_agree = self.num_agreements / turn
1706+
last_four_num = np.sum(self.last_four_agreements)
1707+
if proportion_agree > 0.9 and last_four_num >= 4:
1708+
return C
1709+
elif proportion_agree >= 0.625 and last_four_num >= 2:
1710+
return opponent.history[-1]
1711+
else:
1712+
return D

axelrod/tests/strategies/test_axelrod_second.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1017,3 +1017,41 @@ def test_strategy(self):
10171017
actions += [(D, D), (C, D), (D, D), (D, D), (D, D), (C, D), (D, D), (D, D), (D, D), (D, D)]
10181018
self.versus_test(axelrod.Defector(), expected_actions=actions, seed=15)
10191019

1020+
1021+
class TestRichardHufford(TestPlayer):
1022+
name = 'RichardHufford'
1023+
player = axelrod.RichardHufford
1024+
expected_classifier = {
1025+
'memory_depth': float('inf'),
1026+
'stochastic': False,
1027+
'makes_use_of': set(),
1028+
'long_run_time': False,
1029+
'inspects_source': False,
1030+
'manipulates_source': False,
1031+
'manipulates_state': False
1032+
}
1033+
1034+
def test_strategy(self):
1035+
actions = [(C, C)] * 19 + [(D, C), (C, C), (C, C)]
1036+
self.versus_test(axelrod.Cooperator(), expected_actions=actions, attrs={"streak_needed": 14})
1037+
1038+
actions = [(C, C)] * 19 + [(D, C), (C, C)]
1039+
actions += [(C, C)] # This is the first Cooperation that gets counted on the new streak
1040+
actions += [(C, C)] * 13 + [(D, C), (C, C), (C, C)]
1041+
self.versus_test(axelrod.Cooperator(), expected_actions=actions, attrs={"streak_needed": 11})
1042+
1043+
opponent_actions = [C] * 20 + [D]
1044+
BoredCooperator = axelrod.MockPlayer(actions=opponent_actions)
1045+
actions = [(C, C)] * 19 + [(D, C), (C, D), (C, C)]
1046+
self.versus_test(BoredCooperator, expected_actions=actions, attrs={"streak_needed": 31})
1047+
1048+
actions = [(C, D)] # "Disagreement"
1049+
actions += [(D, C)] # TFT. Disagreement
1050+
actions += [(C, C)] # TFT.
1051+
actions += [(C, D)] # TFT. Disagreement
1052+
actions += [(D, C)] # Three of last four are disagreements.
1053+
actions += [(C, C)] # TFT. Disagreement
1054+
actions += [(D, D)] # Three of last four are disagreements. Disagreement
1055+
actions += [(D, D)] # Three of last four are disagreements.
1056+
actions += [(D, D)] # Now there are 5/9 disagreements, so Defect.
1057+
self.versus_test(axelrod.WinShiftLoseStay(), expected_actions=actions, attrs={"num_agreements": 5})

docs/reference/overview_of_strategies.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ repository.
115115
"K44R_", "William Adams", ":class:`WmAdams <axelrod.strategies.axelrod_second.WmAdams>`"
116116
"K45R_", "Michael F McGurrin", "Not Implemented"
117117
"K46R_", "Graham J Eatherley", ":class:`Eatherley <axelrod.strategies.axelrod_second.Eatherley>`"
118-
"K47R_", "Richard Hufford", "Not Implemented"
118+
"K47R_", "Richard Hufford", ":class:`RichardHufford <axelrod.strategies.axelrod_second.RichardHufford>`"
119119
"K48R_", "George Hufford", "Not Implemented"
120120
"K49R_", "Rob Cave", ":class:`Cave <axelrod.strategies.axelrod_second.Cave>`"
121121
"K50R_", "Rik Smoody", "Not Implemented"

0 commit comments

Comments
 (0)