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

Added Appold (k88r) strategy from Axelrod's second. #1263

Merged
merged 3 commits into from
Oct 7, 2019
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
2 changes: 2 additions & 0 deletions axelrod/strategies/_strategies.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
UnnamedStrategy,
)
from .axelrod_second import (
Appold,
Black,
Borufsen,
Cave,
Expand Down Expand Up @@ -248,6 +249,7 @@
APavlov2006,
APavlov2011,
Appeaser,
Appold,
ArrogantQLearner,
AverageCopier,
BackStabber,
Expand Down
75 changes: 72 additions & 3 deletions axelrod/strategies/axelrod_second.py
Original file line number Diff line number Diff line change
Expand Up @@ -536,15 +536,15 @@ def strategy(self, opponent: Player) -> Action:
self.dd_counts += 1
else:
if opponent.history[-1] == C:
self.dc_counts += 1
else:
self.cc_counts += 1
else:
self.dc_counts += 1

# Check for randomness
if len(self.history) > 26:
if self.cd_counts >= (self.cd_counts + self.dd_counts) / 2 - 0.75 * np.sqrt(
self.cd_counts + self.dd_counts
) and self.cc_counts >= (
) and self.dc_counts >= (
self.dc_counts + self.cc_counts
) / 2 - 0.75 * np.sqrt(
self.dc_counts + self.cc_counts
Expand Down Expand Up @@ -2059,3 +2059,72 @@ def strategy(self, opponent: Player) -> Action:
self.mode = "Coop Def Cycle 1"
return D


class Appold(Player):
"""
Strategy submitted to Axelrod's second tournament by Scott Appold (K88R) and
came in 22nd in that tournament.

Cooperates for first four turns.

After four turns, will cooperate immediately following the first time the
opponent cooperates (starting with the opponent's fourth move). Otherwise
will cooperate with probability equal to:

- If this strategy defected two turns ago, the portion of the time
(historically) that the opponent followed a defection with a cooperation.
- If this strategy cooperated two turns ago, the portion of the time
(historically) that the opponent followed a cooperation with a cooperation.
The opponent's first move is counted as a response to a cooperation.


Names:

- Appold: [Axelrod1980b]_
"""

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

def __init__(self) -> None:
super().__init__()

# Probability of a cooperation after an x is:
# opp_c_after_x / total_num_of_x.
self.opp_c_after_x = {C: 0, D: 1}
# This is the total counted, so it doesn't include the most recent.
self.total_num_of_x = {C: 0, D: 1}

self.first_opp_def = False

def strategy(self, opponent: Player) -> Action:
turn = len(self.history) + 1

us_two_turns_ago = C if turn <= 2 else self.history[-2]

# Update trackers
if turn > 1:
self.total_num_of_x[us_two_turns_ago] += 1
if turn > 1 and opponent.history[-1] == C:
self.opp_c_after_x[us_two_turns_ago] += 1

if turn <= 4:
return C

if opponent.history[-1] == D and not self.first_opp_def:
self.first_opp_def = True
return C

# Calculate the probability that the opponent cooperated last turn given
# what we know two turns ago.
prob_coop = self.opp_c_after_x[us_two_turns_ago] / self.total_num_of_x[
us_two_turns_ago]
return random_choice(prob_coop)
122 changes: 122 additions & 0 deletions axelrod/tests/strategies/test_axelrod_second.py
Original file line number Diff line number Diff line change
Expand Up @@ -1908,3 +1908,125 @@ def test_strategy(self):
self.versus_test(custom_opponent, expected_actions=actions, attrs={
"distrust_points": 2}) # But no more


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

def test_strategy(self):
# Should cooperate 100% of the time with the cooperator
actions = [(C, C)] * 100
self.versus_test(axelrod.Cooperator(), expected_actions=actions)

opponent = axelrod.Defector()
# Cooperate always the first 4 turns
actions = [(C, D)] * 4
# Should cooperate because we forgive the first_opp_def after the fourth
# turn.
actions += [(C, D)]
# Own move two turns ago is C, so D.
actions += [(D, D)]
# Then defect most of the time, depending on the random number. We
# don't defect 100% of the time, because of the way that initialize
# opp_c_after_x.
actions += [(D, D),
(C, D),
(D, D),
(D, D), # C can never be two moves after a C.
(D, D),
(D, D),
(D, D),
(D, D),
(D, D),
(D, D),
(C, D),
(C, D),
(D, D),
(D, D),
(D, D),
(D, D),
(D, D),
(C, D),
(D, D),
(D, D),
(D, D),
(D, D),
(D, D),
(D, D),
(C, D),
(C, D),
(D, D),
(D, D)]
self.versus_test(opponent, expected_actions=actions, seed=1,
attrs={"first_opp_def": True})

# An opponent who defects for a long time, then tries cooperating
opponent_actions = [C] * 30 + [D] + [C] * 10
MostlyCooperates = axelrod.MockPlayer(actions=opponent_actions)
# Cooperate always at first
actions = [(C, C)] * 30
# The opponent defects once
actions += [(C, D)]
# But we forgive it.
actions += [(C, C)] * 10
self.versus_test(MostlyCooperates, expected_actions=actions)

opponent = axelrod.CyclerDC()
# First three opponent actions get counted as reactions to C. Fourth
# action will get counted on next turn.
actions = [(C, D), (C, C), (C, D), (C, C)]
self.versus_test(opponent, expected_actions=actions,
attrs={"opp_c_after_x": {C: 1, D: 1},
"total_num_of_x": {C: 3, D: 1}})
# Will cooperate 50% of the time
actions += [(C, D)]
self.versus_test(opponent, expected_actions=actions,
attrs={"opp_c_after_x": {C: 2, D: 1},
"total_num_of_x": {C: 4, D: 1},
"first_opp_def": False}, seed=100)
# Always cooperate, because we forgive the first defect
actions += [(C, C)]
self.versus_test(opponent, expected_actions=actions,
attrs={"first_opp_def": True}, seed=100)

# Against a random opponent, will respond mostly randomly too.
actions = [(C, C),
(C, C),
(C, D),
(C, C),
(C, C),
(C, D),
(C, C),
(C, C),
(C, C),
(D, C),
(C, D),
(D, D),
(C, D),
(C, D),
(C, C),
(C, C),
(D, C),
(C, D),
(D, D),
(C, C),
(C, D),
(C, C),
(C, C),
(C, D),
(D, C),
(C, D),
(D, D),
(C, D),
(C, C),
(D, C)]
self.versus_test(axelrod.Random(0.5), expected_actions=actions, seed=7)
2 changes: 1 addition & 1 deletion docs/reference/overview_of_strategies.rst
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ repository.
"K85R_", "Robert B Falk and James M Langsted", "Not Implemented"
"K86R_", "Bernard Grofman", "Not Implemented"
"K87R_", "E E H Schurmann", "Not Implemented"
"K88R_", "Scott Appold", "Not Implemented"
"K88R_", "Scott Appold", ":class:`Appold <axelrod.strategies.axelrod_second.Appold>`"
"K89R_", "Gene Snodgrass", "Not Implemented"
"K90R_", "John Maynard Smith", "Not Implemented"
"K91R_", "Jonathan Pinkley", "Not Implemented"
Expand Down
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 @@ -47,7 +47,7 @@ strategies::
... }
>>> strategies = axl.filtered_strategies(filterset)
>>> len(strategies)
86
87

Or, to find out how many strategies only use 1 turn worth of memory to
make a decision::
Expand Down