Skip to content

Instantly share code, notes, and snippets.

@alexhroom
Last active April 21, 2023 16:55
Show Gist options
  • Save alexhroom/bea19c0bec62a08838bb6c8c4c949a6a to your computer and use it in GitHub Desktop.
Save alexhroom/bea19c0bec62a08838bb6c8c4c949a6a to your computer and use it in GitHub Desktop.
Rock-Paper-Scissors in Axelrod
"""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