Skip to content

Commit

Permalink
just finite_state_machines (#956)
Browse files Browse the repository at this point in the history
* mypy vs FSM grrrrrr

* before bed

* switch branch

* set up fsm tests.  branch switch

* i now despise FSM

* PR only for FSM

* need to fix merge issue with master

* possibly resolved all issues. switching branches

* keeping current with *args to **kwargs

* made required changes. mypy pickiness forced simplified type hints

* i think that works

* did not see that coming. fixed coverage

* sigh. brain not working

* small change to test comment strings
  • Loading branch information
eric-s-s authored and marcharper committed Apr 6, 2017
1 parent be79688 commit 09d13eb
Show file tree
Hide file tree
Showing 2 changed files with 536 additions and 183 deletions.
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

0 comments on commit 09d13eb

Please sign in to comment.