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

just finite_state_machines #956

Merged
merged 16 commits into from
Apr 6, 2017
105 changes: 58 additions & 47 deletions axelrod/strategies/finite_state_machines.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,33 +11,56 @@ class SimpleFSM(object):
https://en.wikipedia.org/wiki/Finite-state_machine
"""

def __init__(self, transitions, initial_state):
def __init__(self, transitions: tuple, initial_state: int) -> None:
"""
transitions is a list of the form
[(state, last_opponent_action, next_state, next_action), ...]
((state, last_opponent_action, next_state, next_action), ...)

TitForTat would be represented with the following table:
[(1, C, 1, C), (1, D, 1, D)]
((1, C, 1, C), (1, D, 1, D))
with initial play C and initial state 1.

"""
self.state = initial_state
self.state_transitions = dict()
for (state, opp_action, next_state, next_action) in transitions:
self.state_transitions[(state, opp_action)] = (next_state, next_action)
self._state = initial_state
self._state_transitions = {(current_state, input_action): (next_state, output_action) for
current_state, input_action, next_state, output_action in transitions} # type: dict

self._raise_error_for_bad_input()

def _raise_error_for_bad_input(self):
callable_states = set(pair[0] for pair in self._state_transitions.values())
callable_states.add(self._state)
for state in callable_states:
self._raise_error_for_bad_state(state)

def _raise_error_for_bad_state(self, state: int):
if (state, C) not in self._state_transitions or (state, D) not in self._state_transitions:
raise ValueError('state: {} does not have values for both C and D'.format(state))

@property
def state(self) -> int:
return self._state

@state.setter
def state(self, new_state: int):
self._raise_error_for_bad_state(new_state)
self._state = new_state

@property
def state_transitions(self) -> dict:
return self._state_transitions.copy()

def move(self, opponent_action: Action) -> Action:
"""Computes the response move and changes state."""
next_state, next_action = self.state_transitions[(self.state, opponent_action)]
self.state = next_state
next_state, next_action = self._state_transitions[(self._state, opponent_action)]
self._state = next_state
return next_action

def __eq__(self, other: Player) -> bool:
def __eq__(self, other) -> bool:
"""Equality of two FSMs"""
check = True
for attr in ["state", "state_transitions"]:
check = check and getattr(self, attr) == getattr(other, attr)
return check
if not isinstance(other, SimpleFSM):
return False
return (self._state, self._state_transitions) == (other.state, other.state_transitions)


class FSMPlayer(Player):
Expand All @@ -55,16 +78,12 @@ class FSMPlayer(Player):
'manipulates_state': False
}

def __init__(self, transitions=None, initial_state=None,
initial_action=None) -> None:
if not transitions:
# Tit For Tat
transitions = [(1, C, 1, C), (1, D, 1, D)]
initial_state = 1
initial_action = C
def __init__(self, transitions: tuple = ((1, C, 1, C), (1, D, 1, D)),
initial_state: int = 1,
initial_action: Action = C) -> None:

super().__init__()
self.initial_state = initial_state
self.state = initial_state
self.initial_action = initial_action
self.fsm = SimpleFSM(transitions, initial_state)

Expand All @@ -73,15 +92,11 @@ def strategy(self, opponent: Player) -> Action:
return self.initial_action
else:
action = self.fsm.move(opponent.history[-1])
# Record the state for testing purposes, this isn't necessary
# for the strategy to function
self.state = self.fsm.state
return action

def reset(self) -> None:
super().reset()
self.fsm.state = self.initial_state
self.state = self.initial_state


class Fortress3(FSMPlayer):
Expand All @@ -102,15 +117,15 @@ class Fortress3(FSMPlayer):

def __init__(self) -> None:
transitions = (
(1, D, 2, D),
(1, C, 1, D),
(1, D, 2, D),
(2, C, 1, D),
(2, D, 3, C),
(3, C, 3, C),
(3, D, 1, D)
)

super().__init__(transitions, initial_state=1, initial_action=D)
super().__init__(transitions=transitions, initial_state=1, initial_action=D)


class Fortress4(FSMPlayer):
Expand Down Expand Up @@ -141,7 +156,7 @@ def __init__(self) -> None:
(4, D, 1, D)
)

super().__init__(transitions, initial_state=1, initial_action=D)
super().__init__(transitions=transitions, initial_state=1, initial_action=D)


class Predator(FSMPlayer):
Expand Down Expand Up @@ -180,7 +195,7 @@ def __init__(self) -> None:
(8, D, 6, D)
)

super().__init__(transitions, initial_state=1, initial_action=C)
super().__init__(transitions=transitions, initial_state=0, initial_action=C)


class Pun1(FSMPlayer):
Expand Down Expand Up @@ -209,7 +224,7 @@ def __init__(self) -> None:
(2, D, 1, D)
)

super().__init__(transitions, initial_state=1, initial_action=D)
super().__init__(transitions=transitions, initial_state=1, initial_action=D)


class Raider(FSMPlayer):
Expand Down Expand Up @@ -238,7 +253,7 @@ def __init__(self) -> None:
(3, D, 1, C)
)

super().__init__(transitions, initial_state=0, initial_action=D)
super().__init__(transitions=transitions, initial_state=0, initial_action=D)


class Ripoff(FSMPlayer):
Expand All @@ -265,7 +280,7 @@ def __init__(self) -> None:
(3, D, 3, D)
)

super().__init__(transitions, initial_state=1, initial_action=D)
super().__init__(transitions=transitions, initial_state=1, initial_action=D)


class SolutionB1(FSMPlayer):
Expand All @@ -292,7 +307,7 @@ def __init__(self) -> None:
(3, D, 3, C)
)

super().__init__(transitions, initial_state=1, initial_action=D)
super().__init__(transitions=transitions, initial_state=1, initial_action=D)


class SolutionB5(FSMPlayer):
Expand Down Expand Up @@ -325,7 +340,7 @@ def __init__(self) -> None:
(6, D, 5, D)
)

super().__init__(transitions, initial_state=1, initial_action=D)
super().__init__(transitions=transitions, initial_state=1, initial_action=D)


class Thumper(FSMPlayer):
Expand All @@ -350,7 +365,7 @@ def __init__(self) -> None:
(2, D, 1, D)
)

super().__init__(transitions, initial_state=1, initial_action=C)
super().__init__(transitions=transitions, initial_state=1, initial_action=C)


class EvolvedFSM4(FSMPlayer):
Expand Down Expand Up @@ -385,7 +400,7 @@ def __init__(self) -> None:
(3, D, 1, D)
)

super().__init__(transitions, initial_state=0, initial_action=C)
super().__init__(transitions=transitions, initial_state=0, initial_action=C)


class EvolvedFSM16(FSMPlayer):
Expand Down Expand Up @@ -419,8 +434,7 @@ def __init__(self) -> None:
(2, D, 14, D),
(3, C, 3, D),
(3, D, 3, D),
(4, C, 11, D),
(4, D, 7, D),

(5, C, 12, D),
(5, D, 10, D),
(6, C, 5, C),
Expand All @@ -429,8 +443,7 @@ def __init__(self) -> None:
(7, D, 1, C),
(8, C, 5, C),
(8, D, 5, C),
(9, C, 10, D),
(9, D, 13, D),

(10, C, 11, D),
(10, D, 8, C),
(11, C, 15, D),
Expand All @@ -445,7 +458,7 @@ def __init__(self) -> None:
(15, D, 2, C)
)

super().__init__(transitions, initial_state=0, initial_action=C)
super().__init__(transitions=transitions, initial_state=0, initial_action=C)


class EvolvedFSM16Noise05(FSMPlayer):
Expand Down Expand Up @@ -485,12 +498,10 @@ def __init__(self) -> None:
(5, D, 10, D),
(6, C, 8, C),
(6, D, 6, D),
(7, C, 5, D),
(7, D, 15, C),

(8, C, 2, C),
(8, D, 4, D),
(9, C, 15, D),
(9, D, 6, D),

(10, C, 4, D),
(10, D, 1, D),
(11, C, 14, D),
Expand All @@ -505,4 +516,4 @@ def __init__(self) -> None:
(15, D, 11, C)
)

super().__init__(transitions, initial_state=0, initial_action=C)
super().__init__(transitions=transitions, initial_state=0, initial_action=C)
Loading