-
-
Save alexhroom/bea19c0bec62a08838bb6c8c4c949a6a to your computer and use it in GitHub Desktop.
Rock-Paper-Scissors in Axelrod
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"""Implementation of Rock-Paper-Scissors into Axelrod.""" | |
from enum import Enum | |
import random | |
from statistics import mode | |
import numpy as np | |
import axelrod as axl | |
from axelrod.player import Player | |
class RPSAction(Enum): | |
"""Actions for Rock-Paper-Scissors.""" | |
R = 0 | |
P = 1 | |
S = 2 | |
def __repr__(self): | |
return self.name | |
def __str__(self): | |
return self.name | |
def rotate(self): | |
""" | |
Cycles one step through the actions. | |
Maps: | |
R -> P | |
P -> S | |
S -> R | |
""" | |
rotations = { | |
RPSAction.R: RPSAction.P, | |
RPSAction.P: RPSAction.S, | |
RPSAction.S: RPSAction.R | |
} | |
return rotations[self] | |
def beats(self, coplay): | |
""" | |
Returns a Bool for whether this action beats a given action. | |
Parameters | |
---------- | |
coplay: RPSAction | |
The move being played against this one. | |
Returns | |
------- | |
True if this action beats the coplay action, False otherwise. | |
""" | |
if coplay.rotate() == self: | |
return True | |
return False | |
# define game itself | |
A = np.array([[0, -1, 1], [1, 0, -1], [-1, 1, 0]]) | |
rock_paper_scissors = axl.AsymmetricGame(A, -A) | |
R = RPSAction.R | |
P = RPSAction.P | |
S = RPSAction.S | |
# define strategies | |
class Random(Player): | |
""" | |
Plays Rock, Paper or Scissors at random. | |
""" | |
name = "Random" | |
classifier = { | |
"memory_depth": 1, | |
"stochastic": True, | |
"long_run_time": False, | |
"inspects_source": False, | |
"manipulates_source": False, | |
"manipulates_state": False, | |
} | |
def strategy(self, opponent: Player) -> RPSAction: | |
"""Actual strategy definition that determines player's action.""" | |
# currently, Axelrod only supports reproducible randomness for Prisoner's Dilemma | |
# so use base Python random | |
return random.choice([R, P, S]) | |
class Copycat(Player): | |
""" | |
Starts with a chosen move, | |
and then copies their opponent's previous move. | |
Parameters | |
---------- | |
starting_move: RPSAction, default S | |
What move to play on the first round. | |
""" | |
name = "Copycat" | |
classifier = { | |
"memory_depth": 1, | |
"stochastic": False, | |
"long_run_time": False, | |
"inspects_source": False, | |
"manipulates_source": False, | |
"manipulates_state": False, | |
} | |
def __init__(self, starting_move=S): | |
self.starting_move = starting_move | |
super().__init__() | |
def strategy(self, opponent: Player) -> RPSAction: | |
"""Actual strategy definition that determines player's action.""" | |
try: | |
return opponent.history[-1] | |
except IndexError: | |
return self.starting_move | |
class Copycounter(Player): | |
""" | |
Starts with a chosen move, | |
and then plays what beats their opponents' previous move. | |
Parameters | |
---------- | |
starting_move: RPSAction, default S | |
What move to play on the first round. | |
""" | |
name = "Copycounter" | |
classifier = { | |
"memory_depth": 1, | |
"stochastic": False, | |
"long_run_time": False, | |
"inspects_source": False, | |
"manipulates_source": False, | |
"manipulates_state": False, | |
} | |
def __init__(self, starting_move=S): | |
self.starting_move = starting_move | |
super().__init__() | |
def strategy(self, opponent: Player) -> RPSAction: | |
"""Actual strategy definition that determines player's action.""" | |
if not self.history: | |
return self.starting_move | |
return opponent.history[-1].rotate() | |
class Rotator(Player): | |
""" | |
Cycles through the moves from a chosen starting move. | |
Parameters | |
---------- | |
starting_move: RPSAction, default S | |
What move to play on the first round. | |
""" | |
name = "Rotator" | |
classifier = { | |
"memory_depth": 1, | |
"stochastic": False, | |
"long_run_time": False, | |
"inspects_source": False, | |
"manipulates_source": False, | |
"manipulates_state": False, | |
} | |
def __init__(self, starting_move=S): | |
self.starting_move = starting_move | |
super().__init__() | |
def strategy(self, opponent: Player) -> RPSAction: | |
"""Actual strategy definition that determines player's action.""" | |
if not self.history: | |
return self.starting_move | |
return self.history[-1].rotate() | |
class ReverseRotator(Player): | |
""" | |
Cycles through the moves from a chosen starting move. | |
Parameters | |
---------- | |
starting_move: RPSAction, default S | |
What move to play on the first round. | |
""" | |
name = "ReverseRotator" | |
classifier = { | |
"memory_depth": 1, | |
"stochastic": False, | |
"long_run_time": False, | |
"inspects_source": False, | |
"manipulates_source": False, | |
"manipulates_state": False, | |
} | |
def __init__(self, starting_move=S): | |
self.starting_move = starting_move | |
super().__init__() | |
def strategy(self, opponent: Player) -> RPSAction: | |
"""Actual strategy definition that determines player's action.""" | |
if not self.history: | |
return self.starting_move | |
# two clockwise rotations is equivalent to one anti-clockwise rotation | |
return self.history[-1].rotate().rotate() | |
class Static(Player): | |
""" | |
Plays the same action every round. | |
Parameters | |
---------- | |
move: RPSAction, default R | |
What move to play. | |
""" | |
name = "Static" | |
classifier = { | |
"memory_depth": 1, | |
"stochastic": False, | |
"long_run_time": False, | |
"inspects_source": False, | |
"manipulates_source": False, | |
"manipulates_state": False, | |
} | |
def __init__(self, move=R): | |
self.move = move | |
super().__init__() | |
def strategy(self, opponent: Player) -> RPSAction: | |
"""Actual strategy definition that determines player's action.""" | |
return self.move | |
class Countertactic(Player): | |
""" | |
Plays a countertactic: | |
If the player wins, stick with the same move. | |
If the player loses, switch to the move which beats the opponents' last move. | |
Parameters | |
---------- | |
starting_move: RPSAction, default S | |
What move to play on the first round. | |
""" | |
name = "Countertactic" | |
classifier = { | |
"memory_depth": 1, | |
"stochastic": False, | |
"long_run_time": False, | |
"inspects_source": False, | |
"manipulates_source": False, | |
"manipulates_state": False, | |
} | |
def __init__(self, starting_move=S): | |
self.starting_move = starting_move | |
super().__init__() | |
def strategy(self, opponent: Player) -> RPSAction: | |
"""Actual strategy definition that determines player's action.""" | |
if not self.history: | |
return self.starting_move | |
if opponent.history[-1].beats(self.history[-1]): | |
return opponent.history[-1].rotate() | |
return self.history[-1] | |
class CounterMostCommon(Player): | |
""" | |
Counts the opponents' previous moves and plays the counter to the | |
most popular one. | |
""" | |
name = "CounterMostCommon" | |
classifier = { | |
"memory_depth": float("inf"), | |
"stochastic": True, | |
"long_run_time": False, | |
"inspects_source": False, | |
"manipulates_source": False, | |
"manipulates_state": False, | |
} | |
def __init__(self, starting_move=S): | |
self.starting_move = starting_move | |
super().__init__() | |
def strategy(self, opponent: Player) -> RPSAction: | |
"""Actual strategy definition that determines player's action.""" | |
if not self.history: | |
return self.starting_move | |
return mode(opponent.history).rotate() | |
rps_strategies = [Copycat(), Copycounter(), Rotator(), ReverseRotator(), Static(), Countertactic(), CounterMostCommon(), Random()] |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment