diff --git a/axelrod/compute_finite_state_machine_memory.py b/axelrod/compute_finite_state_machine_memory.py index fc928c54b..0f83d3494 100644 --- a/axelrod/compute_finite_state_machine_memory.py +++ b/axelrod/compute_finite_state_machine_memory.py @@ -231,17 +231,6 @@ def get_memory_from_transitions( ordered_memit_tuple(x_successor, y_successor) ) - if len(pair_nodes) == 0: - # If there are no pair of tied memits, then either no memits are needed - # to break a tie (i.e. all next_actions are the same) or the first memit - # breaks a tie (i.e. memory 1) - next_action_set = set() - for trans in transition_iterator(transitions): - next_action_set.add(trans.next_action) - if len(next_action_set) == 1: - return 0 - return 1 - # Get next_action for each memit. Used to decide if they are in conflict, # because we only have undecidability if next_action doesn't match. next_action_by_memit = dict() @@ -261,5 +250,17 @@ def get_memory_from_transitions( path_length = longest_path(pair_edges, pair) + 1 if record < path_length: record = path_length - return record + + if record > 0: + return record + + # If there are no pair of tied memits (for which the next action are + # distinct), then either no memits are needed to break a tie (i.e. all + # next_actions are the same) or the first memit breaks a tie (i.e. memory 1) + next_action_set = set() + for trans in transition_iterator(transitions): + next_action_set.add(trans.next_action) + if len(next_action_set) == 1: + return 0 + return 1 diff --git a/axelrod/tests/unit/test_compute_finite_state_machine_memory.py b/axelrod/tests/unit/test_compute_finite_state_machine_memory.py index 7610af486..f027d1be2 100644 --- a/axelrod/tests/unit/test_compute_finite_state_machine_memory.py +++ b/axelrod/tests/unit/test_compute_finite_state_machine_memory.py @@ -69,7 +69,6 @@ def test_two_state_memory_two(self): """If all D lead to state 0 and all C lead to state 1. We make it so that all paths out of state 0 plays Cooperator and state 1 plays Defector. - In this case, we must know what state we're in to know how to respond to the opponent's previou action, but we cannot determine from our own previous action; we must look at opponent's action from two turns ago. @@ -88,11 +87,26 @@ def test_two_state_tft(self): trans_dict = self.transitions_to_dict(transitions) self.assertEqual(get_memory_from_transitions(trans_dict), 1) + def test_three_state_tft(self): + """Tit-for-tat again, but using three states, and a complex web of + transitions between them. + """ + transitions = ( + (0, C, 1, C), + (0, D, 1, D), + (1, C, 2, C), + (1, D, 0, D), + (2, C, 0, C), + (2, D, 2, D) + ) + + trans_dict = self.transitions_to_dict(transitions) + self.assertEqual(get_memory_from_transitions(trans_dict), 1) + def test_two_state_inf_memory(self): """A C will cause the FSM to stay in the same state, and D causes to change states. Will always respond to a C with a C. Will respond to a D with a C in state 0, but with a D in state 1. - So we need to know the state to know how to respond to a D. But since an arbitarily long sequence of C/C may occur, we need infinite memory. """ @@ -125,7 +139,6 @@ def test_tit_for_two_tat(self): let states 1 and 2 be the cooperating states, with state 2 being the state after one opponent defection. And states 3 and 4 are the defecting states, with state 4 after 1 opponent cooperation. - The memory should be two, because if the last two moves don't match, then we can look to see what we did in the last move. If the do match, then we can respond in kind. @@ -176,12 +189,10 @@ def test_tit_for_five_tat(self): def test_fortress_3(self): """Tests Fortress-3, which Defects unless the opponent D twice in a row. In that case C, and continue to C for as long as the opponent does. - We know we're in state 3 if our own previous move was a C. Otherwise, C if and only if the opponent's previous two moves were D. [Unless we were in state 3 last turn, in which case we would have C'd two turns ago.] - So the memory should be 2. """ transitions = ( @@ -267,7 +278,6 @@ def test_transient_state(self): """Test a setup where we a transient state (no incoming transitions) goes into a Fortress3 (and D) if the opponent D, and goes into a Cooperator if the opponent C. - The transient state is state 0. Fortress3 starts at state 1. And the Cooperator is state 4. """ @@ -336,3 +346,4 @@ def test_evolved_fsm_4(self): trans_dict = self.transitions_to_dict(transitions) self.assertEqual(get_memory_from_transitions(trans_dict), float("inf")) +