@@ -566,3 +566,155 @@ def strategy(self, opponent: Player) -> Action:
566
566
return one_move_ago
567
567
else :
568
568
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 ])
0 commit comments