From 09d13ebfb5fa5a9dc8af490d25cfdef1d200c511 Mon Sep 17 00:00:00 2001 From: eric-s-s Date: Thu, 6 Apr 2017 10:28:00 +0800 Subject: [PATCH] just finite_state_machines (#956) * 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 --- axelrod/strategies/finite_state_machines.py | 105 +-- .../strategies/test_finite_state_machines.py | 614 ++++++++++++++---- 2 files changed, 536 insertions(+), 183 deletions(-) diff --git a/axelrod/strategies/finite_state_machines.py b/axelrod/strategies/finite_state_machines.py index cb74f4308..c94586699 100644 --- a/axelrod/strategies/finite_state_machines.py +++ b/axelrod/strategies/finite_state_machines.py @@ -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): @@ -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) @@ -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): @@ -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): @@ -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): @@ -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): @@ -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): @@ -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): @@ -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): @@ -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): @@ -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): @@ -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): @@ -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): @@ -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), @@ -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), @@ -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): @@ -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), @@ -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) diff --git a/axelrod/tests/strategies/test_finite_state_machines.py b/axelrod/tests/strategies/test_finite_state_machines.py index b56cf4c14..54ea89e18 100644 --- a/axelrod/tests/strategies/test_finite_state_machines.py +++ b/axelrod/tests/strategies/test_finite_state_machines.py @@ -2,102 +2,125 @@ import unittest import axelrod -from .test_player import TestMatch, TestPlayer +from .test_player import TestPlayer +from axelrod.strategies.finite_state_machines import SimpleFSM C, D = axelrod.Actions.C, axelrod.Actions.D -def check_state_transitions(state_transitions): - """Checks that the supplied transitions for a finite state machine are - well-formed.""" - keys = state_transitions.keys() - values = state_transitions.values() - # Check that the set of source states contains the set of sink states - sources = [k[0] for k in keys] - sinks = [v[0] for v in values] - if not set(sinks).issubset(set(sources)): - return False - # Check that there are two outgoing edges for every source state - for state in sources: - for action in [C, D]: - if not ((state, action) in keys): - return False - return True - - -class TestFSMPlayers(unittest.TestCase): +class TestSimpleFSM(unittest.TestCase): + def setUp(self): + self.two_state_transition = ((1, C, 0, C), (1, D, 0, D), (0, C, 1, D), (0, D, 1, C)) + + self.two_state = SimpleFSM(transitions=self.two_state_transition, initial_state=1) + + def test__eq__true(self): + new_two_state = SimpleFSM(transitions=self.two_state_transition, initial_state=1) + self.assertTrue(new_two_state.__eq__(self.two_state)) + new_two_state.move(C) + self.two_state.move(D) + self.assertTrue(new_two_state.__eq__(self.two_state)) + + def test__eq__false_by_state(self): + new_two_state = SimpleFSM(transitions=self.two_state_transition, initial_state=0) + self.assertFalse(new_two_state.__eq__(self.two_state)) + + def test__eq__false_by_transition(self): + different_transitions = ((1, C, 0, D), (1, D, 0, D), (0, C, 1, D), (0, D, 1, C)) + new_two_state = SimpleFSM(transitions=different_transitions, initial_state=1) + + self.assertFalse(new_two_state.__eq__(self.two_state)) + + def test__eq__false_by_not_SimpleFSM(self): + self.assertFalse(self.two_state.__eq__(3)) + + def test__ne__(self): + new_two_state = SimpleFSM(transitions=self.two_state_transition, initial_state=1) + self.assertFalse(new_two_state.__ne__(self.two_state)) + new_two_state.move(C) + self.assertTrue(new_two_state.__ne__(self.two_state)) + + def test_move(self): + self.assertEqual(self.two_state.move(C), C) + self.assertEqual(self.two_state.state, 0) + self.assertEqual(self.two_state.move(C), D) + self.assertEqual(self.two_state.state, 1) + + self.assertEqual(self.two_state.move(D), D) + self.assertEqual(self.two_state.state, 0) + self.assertEqual(self.two_state.move(D), C) + self.assertEqual(self.two_state.state, 1) + + def test_bad_transitions_raise_error(self): + bad_transitions = [(1, C, 0, D), (1, D, 0, D), (0, C, 1, D)] + self.assertRaises(ValueError, SimpleFSM, transitions=bad_transitions, initial_state=1) + + def test_bad_initial_state_raises_error(self): + self.assertRaises(ValueError, SimpleFSM, transitions=self.two_state_transition, initial_state=5) + + def test_state_setter_raises_error_for_bad_input(self): + with self.assertRaises(ValueError) as cm: + self.two_state.state = 5 + error_msg = cm.exception.args[0] + self.assertEqual(error_msg, 'state: 5 does not have values for both C and D') + + +class TestFSMPlayer(TestPlayer): """Test a few sample tables to make sure that the finite state machines are working as intended.""" + name = "FSM Player: ((1, 'C', 1, 'C'), (1, 'D', 1, 'D')), 1, C" + player = axelrod.FSMPlayer + + expected_classifier = { + 'memory_depth': 1, + 'stochastic': False, + 'makes_use_of': set(), + 'long_run_time': False, + 'inspects_source': False, + 'manipulates_source': False, + 'manipulates_state': False + } + def test_cooperator(self): """Tests that the player defined by the table for Cooperator is in fact Cooperator.""" - transitions = [(1, C, 1, C), (1, D, 1, C)] - player = axelrod.FSMPlayer( - transitions=transitions, initial_state=1, initial_action=C) - opponent = axelrod.Alternator() - for i in range(6): - player.play(opponent) - self.assertEqual(opponent.history, [C, D] * 3) - self.assertEqual(player.history, [C] * 6) + cooperator_init_kwargs = {'transitions': [(1, C, 1, C), (1, D, 1, C)], + 'initial_state': 1, + 'initial_action': C} + self.versus_test(axelrod.Alternator(), expected_actions=[(C, C), (C, D)] * 5, + init_kwargs=cooperator_init_kwargs) def test_defector(self): """Tests that the player defined by the table for Defector is in fact Defector.""" - transitions = [(1, C, 1, D), (1, D, 1, D)] - player = axelrod.FSMPlayer( - transitions=transitions, initial_state=1, initial_action=D) - opponent = axelrod.Alternator() - for i in range(6): - player.play(opponent) - self.assertEqual(opponent.history, [C, D] * 3) - self.assertEqual(player.history, [D] * 6) + defector_init_kwargs = {'transitions': [(1, C, 1, D), (1, D, 1, D)], + 'initial_state': 1, + 'initial_action': D} + self.versus_test(axelrod.Alternator(), expected_actions=[(D, C), (D, D)] * 5, init_kwargs=defector_init_kwargs) def test_tft(self): """Tests that the player defined by the table for TFT is in fact TFT.""" - transitions = [(1, C, 1, C), (1, D, 1, D)] - player = axelrod.FSMPlayer( - transitions=transitions, initial_state=1, initial_action=C) - opponent = axelrod.Alternator() - for i in range(6): - player.play(opponent) - self.assertEqual(opponent.history, [C, D] * 3) - self.assertEqual(player.history, [C, C, D, C, D, C]) + tft_init_kwargs = {'transitions': [(1, C, 1, C), (1, D, 1, D)], + 'initial_state': 1, + 'initial_action': C} + self.versus_test(axelrod.Alternator(), expected_actions=[(C, C)] + [(C, D), (D, C)] * 5, + init_kwargs=tft_init_kwargs) def test_wsls(self): """Tests that the player defined by the table for TFT is in fact WSLS (also known as Pavlov.""" - transitions = [(1, C, 1, C), (1, D, 2, D), (2, C, 2, D), (2, D, 1, C)] - player = axelrod.FSMPlayer( - transitions=transitions, initial_state=1, initial_action=C) - opponent = axelrod.Alternator() - for i in range(6): - player.play(opponent) - self.assertEqual(opponent.history, [C, D] * 3) - self.assertEqual(player.history, [C, C, D, D, C, C]) - - def test_malformed_tables(self): - # Test a malformed table - transitions = ((1, D, 2, D), - (1, C, 1, D), - (2, C, 1, D), - (2, D, 3, C), - (3, C, 3, C)) - player = axelrod.FSMPlayer(transitions=transitions, initial_state=1, - initial_action=C) - self.assertFalse(check_state_transitions(player.fsm.state_transitions)) - - transitions = [(1, D, 2, D)] - player = axelrod.FSMPlayer(transitions=transitions, initial_state=1, - initial_action=C) - self.assertFalse(check_state_transitions(player.fsm.state_transitions)) + wsls_init_kwargs = {'transitions': [(1, C, 1, C), (1, D, 2, D), (2, C, 2, D), (2, D, 1, C)], + 'initial_state': 1, + 'initial_action': C} + expected = [(C, C), (C, D), (D, C), (D, D)] * 3 + self.versus_test(axelrod.Alternator(), expected_actions=expected, init_kwargs=wsls_init_kwargs) -class TestFSMPlayer(TestPlayer): - - name = "FSM Player" +class TestFsmTransitions(TestPlayer): + name = "FSM Player: ((1, 'C', 1, 'C'), (1, 'D', 1, 'D')), 1, C" player = axelrod.FSMPlayer expected_classifier = { @@ -110,20 +133,62 @@ class TestFSMPlayer(TestPlayer): 'manipulates_state': False } - def test_transitions(self): - # Test that the finite state machine is well-formed - player = self.player() - fsm = player.fsm - self.assertTrue(check_state_transitions(fsm.state_transitions)) + def transitions_test(self, state_and_action): + """ + takes a list of [(initial_state, first_opponent_action), (next_state, next_opponent_action), ...] + and creates a list of opponent moves, and a list of expected_actions based on the FiniteStateMachine. + Then creates a versus_test of those two lists. + """ + fsm_player = self.player() + transitions = fsm_player.fsm.state_transitions + first_opponent_move = state_and_action[0][1] + + expected_actions = [(fsm_player.initial_action, first_opponent_move)] + opponent_actions = [first_opponent_move] + + for index in range(1, len(state_and_action)): + current_state, last_opponent_move = state_and_action[index - 1] + fsm_move = transitions[(current_state, last_opponent_move)][1] - def test_reset_initial_state(self): + new_state, current_opponent_move = state_and_action[index] + + expected_actions.append((fsm_move, current_opponent_move)) + opponent_actions.append(current_opponent_move) + + self.verify_against_finite_state_machine(current_state=current_state, + expected_state=new_state, + last_opponent_move=last_opponent_move, + expected_move=fsm_move) + + self.versus_test(axelrod.MockPlayer(actions=opponent_actions), expected_actions=expected_actions) + + def verify_against_finite_state_machine(self, current_state, expected_state, last_opponent_move, expected_move): + test_fsm = self.player().fsm + test_fsm.state = current_state + self.assertEqual(test_fsm.move(last_opponent_move), expected_move) + self.assertEqual(test_fsm.state, expected_state) + + def test_transitions_with_default_fsm(self): + if self.player is axelrod.FSMPlayer: + state_action = [(1, C), (1, D)] + self.transitions_test(state_action) + + def test_all_states_reachable(self): player = self.player() - player.fsm.state = -1 - player.reset() - self.assertFalse(player.fsm.state == -1) + initial_state = player.initial_state + transitions = player.fsm.state_transitions + + called_states = set(pair[0] for pair in transitions.values()) + called_states.add(initial_state) + + owned_states = set(pair[0] for pair in transitions.keys()) + un_callable_states = owned_states.difference(called_states) + extra_info = 'The following states are un-reachable: {}'.format(un_callable_states) + self.assertEqual(un_callable_states, set(), msg=extra_info) -class TestFortress3(TestFSMPlayer): + +class TestFortress3(TestFsmTransitions): name = "Fortress3" player = axelrod.Fortress3 @@ -136,13 +201,33 @@ class TestFortress3(TestFSMPlayer): 'manipulates_source': False, 'manipulates_state': False } + """ + transitions = ( + (1, C, 1, D), + (1, D, 2, D), + (2, C, 1, D), + (2, D, 3, C), + (3, C, 3, C), + (3, D, 1, D) + ) + """ def test_strategy(self): - # Test initial play sequence self.first_play_test(D) + state_and_actions = [(1, C), (1, D), (2, C), (1, C)] + self.transitions_test(state_and_actions) + + state_and_actions = [(1, D), (2, D), (3, C), (3, C), (3, C), (3, D), (1, C)] * 2 + self.transitions_test(state_and_actions) -class TestFortress4(TestFSMPlayer): + @unittest.expectedFailure + def test_incorrect_transitions(self): + state_and_actions = [(1, C), (1, D), (1, D)] + self.transitions_test(state_and_actions) + + +class TestFortress4(TestFsmTransitions): name = "Fortress4" player = axelrod.Fortress4 @@ -155,13 +240,33 @@ class TestFortress4(TestFSMPlayer): 'manipulates_source': False, 'manipulates_state': False } + """ + transitions = ( + (1, C, 1, D), + (1, D, 2, D), + (2, C, 1, D), + (2, D, 3, D), + (3, C, 1, D), + (3, D, 4, C), + (4, C, 4, C), + (4, D, 1, D) + ) + """ def test_strategy(self): - # Test initial play sequence self.first_play_test(D) + state_and_actions = [(1, C), (1, D), (2, C)] * 3 + self.transitions_test(state_and_actions) + + state_and_actions = [(1, D), (2, D), (3, C), (1, C)] * 3 + self.transitions_test(state_and_actions) -class TestPredator(TestFSMPlayer): + state_and_actions = [(1, D), (2, D), (3, D), (4, C), (4, C), (4, C), (4, C), (4, D)] * 3 + self.transitions_test(state_and_actions) + + +class TestPredator(TestFsmTransitions): name = "Predator" player = axelrod.Predator @@ -174,13 +279,44 @@ class TestPredator(TestFSMPlayer): 'manipulates_source': False, 'manipulates_state': False } + """ + transitions = ( + (0, C, 0, D), + (0, D, 1, D), + (1, C, 2, D), + (1, D, 3, D), + (2, C, 4, C), + (2, D, 3, D), + (3, C, 5, D), + (3, D, 4, C), + (4, C, 2, C), + (4, D, 6, D), + (5, C, 7, D), + (5, D, 3, D), + (6, C, 7, C), + (6, D, 7, D), + (7, C, 8, D), + (7, D, 7, D), + (8, C, 8, D), + (8, D, 6, D) + ) + """ def test_strategy(self): - # Test initial play sequence self.first_play_test(C) + state_and_actions = ([(0, D), (1, C), (2, C), (4, C), (2, D), (3, D), (4, D), (6, C)] + + [(7, D), (7, C), (8, C), (8, D), (6, D)] * 3) + self.transitions_test(state_and_actions) + + state_and_actions = [(0, D), (1, C), (2, D), (3, C), (5, D), (3, C), (5, C)] + [(7, C), (8, D), (6, C)] * 5 + self.transitions_test(state_and_actions) -class TestPun1(TestFSMPlayer): + state_and_actions = [(0, C), (0, D)] + [(1, D), (3, D), (4, D), (6, D)] + [(7, D)] * 10 + self.transitions_test(state_and_actions) + + +class TestPun1(TestFsmTransitions): name = "Pun1" player = axelrod.Pun1 @@ -194,16 +330,23 @@ class TestPun1(TestFSMPlayer): 'manipulates_state': False } + """ + transitions = ( + (1, C, 2, C), + (1, D, 2, C), + (2, C, 1, C), + (2, D, 1, D) + ) + """ + def test_strategy(self): - # Test initial play sequence self.first_play_test(D) - self.responses_test([C], [D, C], [C, C]) - self.responses_test([C], [D, C], [D, C]) - self.responses_test([C], [D, C, C], [C, C, C]) - self.responses_test([D], [D, C, C, C], [C, C, C, D]) + state_and_actions = [(1, C), (2, D), (1, D), (2, D)] * 3 + self.transitions_test(state_and_actions) -class TestRaider(TestFSMPlayer): + +class TestRaider(TestFsmTransitions): name = "Raider" player = axelrod.Raider @@ -217,12 +360,33 @@ class TestRaider(TestFSMPlayer): 'manipulates_state': False } + """ + transitions = ( + (0, C, 2, D), + (0, D, 2, D), + (1, C, 1, C), + (1, D, 1, D), + (2, C, 0, D), + (2, D, 3, C), + (3, C, 0, D), + (3, D, 1, C) + ) + """ + def test_strategy(self): - # Test initial play sequence self.first_play_test(D) + state_and_actions = [(0, C), (2, C), (0, D), (2, C)] * 3 + self.transitions_test(state_and_actions) -class TestRipoff(TestFSMPlayer): + state_and_actions = [(0, C), (2, D), (3, C)] * 3 + self.transitions_test(state_and_actions) + + state_and_actions = [(0, C), (2, D), (3, D)] + [(1, C), (1, D)] * 5 + self.transitions_test(state_and_actions) + + +class TestRipoff(TestFsmTransitions): name = "Ripoff" player = axelrod.Ripoff @@ -236,12 +400,28 @@ class TestRipoff(TestFSMPlayer): 'manipulates_state': False } + """ + transitions = ( + (1, C, 2, C), + (1, D, 3, C), + (2, C, 1, D), + (2, D, 3, C), + (3, C, 3, C), # Note that it's TFT in state 3 + (3, D, 3, D) + ) + """ + def test_strategy(self): - # Test initial play sequence self.first_play_test(D) + state_and_actions = [(1, C), (2, C)] * 3 + [(1, D)] + [(3, C), (3, D)] * 5 + self.transitions_test(state_and_actions) + + state_and_actions = [(1, C), (2, D)] + [(3, D)] * 5 + self.transitions_test(state_and_actions) -class TestSolutionB1(TestFSMPlayer): + +class TestSolutionB1(TestFsmTransitions): name = "SolutionB1" player = axelrod.SolutionB1 @@ -255,12 +435,25 @@ class TestSolutionB1(TestFSMPlayer): 'manipulates_state': False } + """ + transitions = ( + (1, C, 2, D), + (1, D, 1, D), + (2, C, 2, C), + (2, D, 3, C), + (3, C, 3, C), + (3, D, 3, C) + ) + """ + def test_strategy(self): - # Test initial play sequence self.first_play_test(D) + state_and_actions = [(1, D)] * 3 + [(1, C)] + [(2, C)] * 3 + [(2, D)] + [(3, C), (3, D)] * 3 + self.transitions_test(state_and_actions) + -class TestSolutionB5(TestFSMPlayer): +class TestSolutionB5(TestFsmTransitions): name = "SolutionB5" player = axelrod.SolutionB5 @@ -274,12 +467,37 @@ class TestSolutionB5(TestFSMPlayer): 'manipulates_state': False } + """ + transitions = ( + (1, C, 2, C), + (1, D, 6, D), + (2, C, 2, C), + (2, D, 3, D), + (3, C, 6, C), + (3, D, 1, D), + (4, C, 3, C), + (4, D, 6, D), + (5, C, 5, D), + (5, D, 4, D), + (6, C, 3, C), + (6, D, 5, D) + ) + """ + def test_strategy(self): - # Test initial play sequence self.first_play_test(D) + state_and_actions = ([(1, C)] + [(2, C)] * 3 + [(2, D), (3, D)]) * 2 + self.transitions_test(state_and_actions) + + state_and_actions = [(1, C), (2, D)] + [(3, C), (6, D), (5, C), (5, D), (4, C), (3, C), (6, C)] * 3 + self.transitions_test(state_and_actions) -class TestThumper(TestFSMPlayer): + state_and_actions = [(1, D)] + [(6, D), (5, D), (4, D)] * 3 + self.transitions_test(state_and_actions) + + +class TestThumper(TestFsmTransitions): name = "Thumper" player = axelrod.Thumper @@ -293,12 +511,23 @@ class TestThumper(TestFSMPlayer): 'manipulates_state': False } + """ + transitions = ( + (1, C, 1, C), + (1, D, 2, D), + (2, C, 1, D), + (2, D, 1, D) + ) + """ + def test_strategy(self): - # Test initial play sequence self.first_play_test(C) + state_and_actions = [(1, C)] * 3 + [(1, D), (2, C), (1, D), (2, D)] * 3 + self.transitions_test(state_and_actions) -class TestEvolvedFSM4(TestFSMPlayer): + +class TestEvolvedFSM4(TestFsmTransitions): name = "Evolved FSM 4" player = axelrod.EvolvedFSM4 @@ -312,12 +541,30 @@ class TestEvolvedFSM4(TestFSMPlayer): 'manipulates_state': False } + """ + transitions = ( + (0, C, 0, C), + (0, D, 2, D), + (1, C, 3, D), + (1, D, 0, C), + (2, C, 2, D), + (2, D, 1, C), + (3, C, 3, D), + (3, D, 1, D) + ) + """ + def test_strategy(self): - # Test initial play sequence self.first_play_test(C) + state_and_actions = [(0, C)] * 3 + [(0, D), (2, C), (2, D), (1, D)] * 3 + self.transitions_test(state_and_actions) -class TestEvolvedFSM16(TestFSMPlayer): + state_and_actions = [(0, D), (2, D), (1, C), (3, C), (3, C), (3, D), (1, C), (3, D), (1, D)] * 3 + self.transitions_test(state_and_actions) + + +class TestEvolvedFSM16(TestFsmTransitions): name = "Evolved FSM 16" player = axelrod.EvolvedFSM16 @@ -331,12 +578,78 @@ class TestEvolvedFSM16(TestFSMPlayer): 'manipulates_state': False } + """ + FSM created by ML algorithm never called states 4 or 9, so they were deleted. + transitions = ( + (0, C, 0, C), + (0, D, 12, D), + (1, C, 3, D), + (1, D, 6, C), + (2, C, 2, D), + (2, D, 14, D), + (3, C, 3, D), + (3, D, 3, D), + + (5, C, 12, D), + (5, D, 10, D), + (6, C, 5, C), + (6, D, 12, D), + (7, C, 3, D), + (7, D, 1, C), + (8, C, 5, C), + (8, D, 5, C), + + (10, C, 11, D), + (10, D, 8, C), + (11, C, 15, D), + (11, D, 5, D), + (12, C, 8, C), + (12, D, 11, D), + (13, C, 13, D), + (13, D, 7, D), + (14, C, 13, D), + (14, D, 13, D), + (15, C, 15, D), + (15, D, 2, C) + ) + """ + def test_strategy(self): - # Test initial play sequence self.first_play_test(C) + # finished: 0, + state_and_actions = [(0, C)] * 3 + [(0, D)] + [(12, D), (11, D), (5, C)] * 3 + self.transitions_test(state_and_actions) + + # finished: 0, 5, 10 + state_and_actions = [(0, D), (12, D), (11, D)] + [(5, D), (10, C), (11, D), (5, D), (10, D), (8, C)] * 3 + self.transitions_test(state_and_actions) + + # finished: 0, 2, 5, 10, 11, 12, 15 + state_and_actions = ([(0, D), (12, C), (8, D), (5, D), (10, C), (11, C), (15, C), (15, C), (15, D)] + + [(2, C)] * 3 + [(2, D), (14, C), (13, C)]) + self.transitions_test(state_and_actions) -class TestEvolvedFSM16Noise05(TestFSMPlayer): + # finished: 0, 2, 3, 5, 10, 11, 12, 13, 14, 15 + to_state_fourteen = [(0, D), (12, D), (11, C), (15, D), (2, D)] + state_and_actions = to_state_fourteen + [(14, D), (13, C), (13, C), (13, D), (7, C)] + [(3, D), (3, C)] * 3 + self.transitions_test(state_and_actions) + + # finished: 0, 2, 3, 5, 7, 10, 11, 12, 13, 14, 15 + to_state_seven = to_state_fourteen + [(14, D), (13, D)] + state_and_actions = to_state_seven + [(7, D), (1, C)] + [(3, C)] * 5 + self.transitions_test(state_and_actions) + + # finished: 0, 1, 2, 3, 5, 10, 11, 12, 13, 14, 15 + state_and_actions = to_state_seven + [(7, D), (1, D), (6, C), (5, D), (10, C)] + self.transitions_test(state_and_actions) + + # finished: 0, 1, 2, 3, 5, 6, 7, 8, 10, 11, 12, 13, 14, 15 + state_and_actions = to_state_seven + [(7, D), (1, D), (6, D), (12, C), (8, D), (5, D)] + self.transitions_test(state_and_actions) + + +class TestEvolvedFSM16Noise05(TestFsmTransitions): name = "Evolved FSM 16 Noise 05" player = axelrod.EvolvedFSM16Noise05 @@ -350,42 +663,71 @@ class TestEvolvedFSM16Noise05(TestFSMPlayer): 'manipulates_state': False } + """ + FSM created by ML algorithm never called states 7 or 9, so they were deleted. + transitions = ( + (0, C, 8, C), + (0, D, 3, D), + (1, C, 13, C), + (1, D, 15, D), + (2, C, 12, C), + (2, D, 3, D), + (3, C, 10, C), + (3, D, 3, D), + (4, C, 5, D), + (4, D, 4, D), + (5, C, 4, D), + (5, D, 10, D), + (6, C, 8, C), + (6, D, 6, D), + + (8, C, 2, C), + (8, D, 4, D), + + (10, C, 4, D), + (10, D, 1, D), + (11, C, 14, D), + (11, D, 13, C), + (12, C, 13, C), + (12, D, 2, C), + (13, C, 13, C), + (13, D, 6, C), + (14, C, 3, D), + (14, D, 13, D), + (15, C, 5, D), + (15, D, 11, C) + ) + """ + def test_strategy(self): - # Test initial play sequence self.first_play_test(C) + # finished: 12, 13 + state_and_actions = [(0, C), (8, C), (2, C), (12, D), (2, C), (12, C), (13, C), (13, C), (13, D)] + [(6, D)] * 3 + self.transitions_test(state_and_actions) -class TestFortress3vsFortress3(TestMatch): - def test_rounds(self): - self.versus_test(axelrod.Fortress3(), axelrod.Fortress3(), - [D, D, C, C, C], [D, D, C, C, C]) - - -class TestFortress3vsTitForTat(TestMatch): - def test_rounds(self): - self.versus_test(axelrod.Fortress3(), axelrod.TitForTat(), - [D, D, D, C], [C, D, D, D]) - - -class TestFortress3vsCooperator(TestMatch): - def test_rounds(self): - self.versus_test(axelrod.Fortress3(), axelrod.Cooperator(), - [D, D, D, D, D, D], [C] * 6) + # finished 2, 3, 4, 12, 13 + state_and_actions = [(0, C), (8, C), (2, D), (3, D), (3, D), (3, C), (10, C), (4, D), (4, D), (4, C), (5, D)] + self.transitions_test(state_and_actions) + # finished 0, 2, 3, 4, 6, 8, 10, 12, 13 + state_and_actions = [(0, D), (3, C), (10, D), (1, C), (13, D), (6, C), (8, D), (4, C), (5, C), (4, C), (5, D)] + self.transitions_test(state_and_actions) -class TestFortress4vsFortress4(TestMatch): - def test_rounds(self): - self.versus_test(axelrod.Fortress4(), axelrod.Fortress4(), - [D, D, D, C, C, C], [D, D, D, C, C, C]) + # finished 0, 1, 2, 3, 4, 5, 6, 8, 10, 12, 13, 15 + state_and_actions = [(0, D), (3, C), (10, D), (1, D), (15, C), (5, D), (10, D), (1, D), (15, D), (11, D)] + self.transitions_test(state_and_actions) + # finished 0, 1, 2, 3, 4, 5, 6, 8, 10, 12, 13, 15 + to_state_eleven = [(0, D), (3, C), (10, D), (1, D), (15, D)] -class TestFortress4vsTitForTat(TestMatch): - def test_rounds(self): - self.versus_test(axelrod.Fortress4(), axelrod.TitForTat(), - [D, D, D, D, C, D], [C, D, D, D, D, C]) + state_and_actions = to_state_eleven + [(11, C), (14, C), (3, C), (10, D)] + self.transitions_test(state_and_actions) + # finished 0, 1, 2, 3, 4, 5, 6, 8, 10, 11, 12, 13, 15 + state_and_actions = to_state_eleven + [(11, D)] + [(13, C)] * 3 + self.transitions_test(state_and_actions) -class TestFortress4vsCooperator(TestMatch): - def test_rounds(self): - self.versus_test(axelrod.Fortress4(), axelrod.Cooperator(), - [D, D, D, D, D, D], [C] * 6) + # finished 0, 1, 2, 3, 4, 5, 6, 8, 10, 11, 12, 13, 14, 15 + state_and_actions = to_state_eleven + [(11, C), (14, D)] + [(13, C)] * 3 + self.transitions_test(state_and_actions)