Skip to content

Commit 92b9ee2

Browse files
authored
Merge pull request #1139 from gaffney2010/master
Added strategy Borufsen/k42r from Axelrod's second tourn
2 parents 1ac45e5 + e6f80bb commit 92b9ee2

File tree

5 files changed

+197
-4
lines changed

5 files changed

+197
-4
lines changed

axelrod/strategies/_strategies.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
from .axelrod_first import (
88
Davis, RevisedDowning, Feld, Grofman, Nydegger, Joss, Shubik, Tullock,
99
UnnamedStrategy, SteinAndRapoport, TidemanAndChieruzzi)
10-
from .axelrod_second import Champion, Eatherley, Tester, Gladstein, Tranquilizer, MoreGrofman, Kluepfel
10+
from .axelrod_second import (
11+
Champion, Eatherley, Tester, Gladstein, Tranquilizer, MoreGrofman,
12+
Kluepfel, Borufsen)
1113
from .backstabber import BackStabber, DoubleCrosser
1214
from .better_and_better import BetterAndBetter
1315
from .bush_mosteller import BushMosteller
@@ -106,6 +108,7 @@
106108
AverageCopier,
107109
BetterAndBetter,
108110
BackStabber,
111+
Borufsen,
109112
Bully,
110113
BushMosteller,
111114
Calculator,

axelrod/strategies/axelrod_second.py

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -566,3 +566,155 @@ def strategy(self, opponent: Player) -> Action:
566566
return one_move_ago
567567
else:
568568
return one_move_ago.flip()
569+
570+
class Borufsen(Player):
571+
"""
572+
Strategy submitted to Axelrod's second tournament by Otto Borufsen
573+
(K32R), and came in third in that tournament.
574+
575+
This player keeps track of the the opponent's responses to own behavior:
576+
577+
- `cd_count` counts: Opponent cooperates as response to player defecting.
578+
- `cc_count` counts: Opponent cooperates as response to player cooperating.
579+
580+
The player has a defect mode and a normal mode. In defect mode, the
581+
player will always defect. In normal mode, the player obeys the following
582+
ranked rules:
583+
584+
1. If in the last three turns, both the player/opponent defected, then
585+
cooperate for a single turn.
586+
2. If in the last three turns, the player/opponent acted differently from
587+
each other and they're alternating, then change next defect to
588+
cooperate. (Doesn't block third rule.)
589+
3. Otherwise, do tit-for-tat.
590+
591+
Start in normal mode, but every 25 turns starting with the 27th turn,
592+
re-evaluate the mode. Enter defect mode if any of the following
593+
conditions hold:
594+
595+
- Detected random: Opponent cooperated 7-18 times since last mode
596+
evaluation (or start) AND less than 70% of opponent cooperation was in
597+
response to player's cooperation, i.e.
598+
cc_count / (cc_count+cd_count) < 0.7
599+
- Detect defective: Opponent cooperated fewer than 3 times since last mode
600+
evaluation.
601+
602+
When switching to defect mode, defect immediately. The first two rules for
603+
normal mode require that last three turns were in normal mode. When starting
604+
normal mode from defect mode, defect on first move.
605+
606+
Names:
607+
608+
- Borufsen: [Axelrod1980b]_
609+
"""
610+
611+
name = "Borufsen"
612+
classifier = {
613+
'memory_depth': float('inf'),
614+
'stochastic': False,
615+
'makes_use_of': set(),
616+
'long_run_time': False,
617+
'inspects_source': False,
618+
'manipulates_source': False,
619+
'manipulates_state': False
620+
}
621+
622+
def __init__(self):
623+
super().__init__()
624+
self.cd_counts, self.cc_counts = 0, 0
625+
self.mutual_defect_streak = 0
626+
self.echo_streak = 0
627+
self.flip_next_defect = False
628+
self.mode = "Normal"
629+
630+
def try_return(self, to_return):
631+
"""
632+
We put the logic here to check for the `flip_next_defect` bit here,
633+
and proceed like normal otherwise.
634+
"""
635+
636+
if to_return == C:
637+
return C
638+
# Otherwise look for flip bit.
639+
if self.flip_next_defect:
640+
self.flip_next_defect = False
641+
return C
642+
return D
643+
644+
def strategy(self, opponent: Player) -> Action:
645+
turn = len(self.history) + 1
646+
647+
if turn == 1:
648+
return C
649+
650+
# Update the response history.
651+
if turn >= 3:
652+
if opponent.history[-1] == C:
653+
if self.history[-2] == C:
654+
self.cc_counts += 1
655+
else:
656+
self.cd_counts += 1
657+
658+
# Check if it's time for a mode change.
659+
if turn > 2 and turn % 25 == 2:
660+
coming_from_defect = False
661+
if self.mode == "Defect":
662+
coming_from_defect = True
663+
664+
self.mode = "Normal"
665+
coops = self.cd_counts + self.cc_counts
666+
667+
# Check for a defective strategy
668+
if coops < 3:
669+
self.mode = "Defect"
670+
671+
# Check for a random strategy
672+
if (coops >= 8 and coops <= 17) and self.cc_counts/coops < 0.7:
673+
self.mode = "Defect"
674+
675+
self.cd_counts, self.cc_counts = 0, 0
676+
677+
# If defect mode, clear flags
678+
if self.mode == "Defect":
679+
self.mutual_defect_streak = 0
680+
self.echo_streak = 0
681+
self.flip_next_defect = False
682+
683+
# Check this special case
684+
if self.mode == "Normal" and coming_from_defect:
685+
return D
686+
687+
# Proceed
688+
if self.mode == "Defect":
689+
return D
690+
else:
691+
assert self.mode == "Normal"
692+
693+
# Look for mutual defects
694+
if self.history[-1] == D and opponent.history[-1] == D:
695+
self.mutual_defect_streak += 1
696+
else:
697+
self.mutual_defect_streak = 0
698+
if self.mutual_defect_streak >= 3:
699+
self.mutual_defect_streak = 0
700+
self.echo_streak = 0 # Reset both streaks.
701+
return self.try_return(C)
702+
703+
# Look for echoes
704+
# Fortran code defaults two turns back to C if only second turn
705+
my_two_back, opp_two_back = C, C
706+
if turn >= 3:
707+
my_two_back = self.history[-2]
708+
opp_two_back = opponent.history[-2]
709+
if self.history[-1] != opponent.history[-1] and \
710+
self.history[-1] == opp_two_back and opponent.history[-1] == my_two_back:
711+
self.echo_streak += 1
712+
else:
713+
self.echo_streak = 0
714+
if self.echo_streak >= 3:
715+
self.mutual_defect_streak = 0 # Reset both streaks.
716+
self.echo_streak = 0
717+
self.flip_next_defect = True
718+
719+
# Tit-for-tat
720+
return self.try_return(opponent.history[-1])

axelrod/tests/strategies/test_axelrod_second.py

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -451,4 +451,42 @@ def test_strategy(self):
451451
(D, C),(D, C),(D, D),(D, D),(D, C),(D, C),(D, C),(D, C),(D, D),
452452
(D, C),(D, C),(D, C),(D, C),(D, D)]
453453
self.versus_test(axelrod.Random(0.5), expected_actions=actions, seed=10)
454-
454+
455+
class TestBorufsen(TestPlayer):
456+
name = "Borufsen"
457+
player = axelrod.Borufsen
458+
expected_classifier = {
459+
'memory_depth': float('inf'),
460+
'stochastic': False,
461+
'makes_use_of': set(),
462+
'long_run_time': False,
463+
'inspects_source': False,
464+
'manipulates_source': False,
465+
'manipulates_state': False
466+
}
467+
468+
def test_strategy(self):
469+
actions = [(C, C)] * 100 # Cooperate forever
470+
self.versus_test(axelrod.Cooperator(), expected_actions=actions)
471+
472+
# Tries to cooperate every third time until detecting defective
473+
actions = [(C, D), (D, D), (D, D), (D, D)] * 6 + \
474+
[(C, D), (D, D)] + [(D, D)] * 100
475+
self.versus_test(axelrod.Defector(), expected_actions=actions)
476+
477+
# Alternates with additional coop, every sixth turn
478+
# Won't be labeled as random, since 2/3 of opponent's C follow player's C
479+
# `flip_next_defect` will get set on the sixth turn, which changes the seventh action
480+
# Note that the first two turns of each period of six aren't
481+
# marked as echoes, and the third isn't marked that way until the fourth turn.
482+
actions = [(C, C), (C, D), (D, C), (C, D), (D, C), (C, D)] * 20
483+
self.versus_test(axelrod.Alternator(), expected_actions=actions)
484+
485+
# Basically does tit-for-tat against Win-Shift, Lose-Stay D
486+
# After 26 turns, will detect random since half of opponent's C follow Cs
487+
# Coming out of it, there will be new pattern. Then random is detected again.
488+
actions = [(C, D), (D, C), (C, C)] * 8 + \
489+
[(C, D), (D, C)] + [(D, C)] * 25 + \
490+
[(D, C)] + [(C, C), (C, D), (D, C)] * 8 + \
491+
[(D, C)] * 25
492+
self.versus_test(axelrod.WinShiftLoseStay(D), expected_actions=actions)

axelrod/tests/strategies/test_meta.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -335,7 +335,7 @@ def test_strategy(self):
335335
self.versus_test(opponent=axelrod.Alternator(),
336336
expected_actions=actions, seed=0)
337337

338-
actions = [(C, C), (C, D), (C, C), (C, D), (D, C)]
338+
actions = [(C, C), (C, D), (D, C), (C, D), (D, C)]
339339
self.versus_test(opponent=axelrod.Alternator(),
340340
expected_actions=actions, seed=1)
341341

docs/reference/overview_of_strategies.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ repository.
112112
"K39R_", "Tom Almy", "Not Implemented"
113113
"K40R_", "Robert Adams", "Not Implemented"
114114
"K41R_", "Herb Weiner", "Not Implemented"
115-
"K42R_", "Otto Borufsen", "Not Implemented"
115+
"K42R_", "Otto Borufsen", ":class:`Borufsen <axelrod.strategies.axelrod_second.Borufsen>`"
116116
"K43R_", "R D Anderson", "Not Implemented"
117117
"K44R_", "William Adams", "Not Implemented"
118118
"K45R_", "Michael F McGurrin", "Not Implemented"

0 commit comments

Comments
 (0)