def test_stonehenge_is_valid_move_false(self, input):
        """
        Test is_valid_move() to make sure an invalid move (e.g. '8') is not
        valid.
        """
        game = StonehengeGame(True)
        move = game.str_to_move("D")

        self.assertFalse(
            game.current_state.is_valid_move(move),
            "is_valid_move() should return False for a move " +
            "that is invalid (e.g. 'D' in a game with " + "side-length 1.")
    def test_stonehenge_str_to_move(self, input):
        """
        Test str_to_move() to make sure it returns moves that are in the
        moves from get_possible_moves().
        """
        game = StonehengeGame(True)
        moves = game.current_state.get_possible_moves()
        move = game.str_to_move("A")

        self.assertTrue(
            move in moves, "str_to_move('A') should produce a " +
            "move that's in get_possible_moves() in a game with " +
            "a side-length of 1.")
 def test_is_over_true(self, input):
     """
     Test is_over to ensure that it returns True when the game is over.
     """
     game = StonehengeGame(True)
     current_state = game.current_state
     current_state = current_state.make_move(game.str_to_move("A"))
     is_over = game.is_over(current_state)
     sample_board = ("      1   @\n     /   /\n1 - 1 - B\n     \\ / \\\n" +
                     "  @ - C   @\n       \\\n        1")
     self.assertTrue(
         is_over, "is_over() should return True when given " +
         "a state that is over such as the following board:" +
         "\n{}".format(sample_board))
    def test_get_possible_moves_game_over(self, input):
        """
        Test get_possible_moves() to make sure it returns an empty list for
        a game that is over.
        """
        game = StonehengeGame(True)
        game.current_state = game.current_state.make_move(
            game.str_to_move("A"))
        moves = game.current_state.get_possible_moves()

        self.assertEqual(len(moves), 0,
                         ("get_possible_moves() on a game of " +
                          "Stonehenge that is over should " +
                          "return 0 moves, but instead {} " +
                          "moves were returned.").format(len(moves)))
    def test_stonehenge_str(self, input):
        """
        Test the str output of Stonehenge's state to make sure the number
        is extracted correctly.
        """
        game = StonehengeGame(True)
        ley_lines, cells = self.extract_stonehenge_values(game.current_state)
        self.assertTrue(
            len(ley_lines) == 6 and len(cells) == 3,
            ("A newly initialized game of Stonehenge should " +
             "consist of 6 @s (empty ley-lines) and 3 cells " +
             "labelled A, B, and C. The board:\n{}\nWas returned " +
             "while the board should look like:\n{}").format(
                 str(game.current_state), BOARD_LENGTH_1))

        self.assertEqual(
            ley_lines, ['@', '@', '@', '@', '@', '@'],
            ("The ley-lines in a newly initialized game of " +
             "Stonehenge with a side-length of 1 should consist " +
             "of 6 @s, but {} was found instead.").format(ley_lines))

        self.assertEqual(
            cells, ['A', 'B', 'C'],
            ("The cell letters in a newly initialized game of " +
             "Stonehenge with a side-length of 1 should be " +
             "A, B, and C, but {} was found instead.").format(cells))
    def test_stonehenge_format(self, input):
        """
        Test the str output of Stonehenge's state to make sure we can extract
        the correct tokens.
        """
        game = StonehengeGame(True)
        state_str = str(game.current_state)

        characters_found = []

        # Count the number of valid characters
        for line in state_str.split("\n"):
            line = line.strip().split()
            for ch in line:
                if ch in "@ABC12":
                    characters_found.append(ch)

        self.assertEqual(len(characters_found), 9,
                         ("The string form of a state in Stonehenge that " +
                          "was initialized with a board length of 1 should " +
                          "have 9 symbols in it representing the ley-lines," +
                          " and cells, but {} symbols were found.").format(
                              len(characters_found)))

        self.assertEqual(characters_found, [
            '@', '@', '@', 'A', 'B', '@', 'C', '@', '@'
        ], ("From top-to-bottom and left-to-right, the symbols" +
            " found in a newly initialized game of Stonehenge " +
            "with a side-length of 1 should be @, @, @, A, B, " +
            '@, C, @, @, but {} were found instead.').format(characters_found))
    def test_stonehenge_init(self, input):
        """
        Test to make sure Stonehenge can be initialized with a boolean
        parameter and having the correct attributes/methods.
        """
        game = StonehengeGame(True)

        self.assertTrue(
            isinstance(game, StonehengeGame),
            ("The game initialized with value 2 should be of " +
             "type {} (the class mapped to by playable_games['h']" +
             ") but an object of type {} was returned" + " instead.").format(
                 playable_games['h'].__name__,
                 type(game).__name__))

        self.assertTrue(
            hasattr(game, 'current_state'), 'An initialized game ' +
            'should have the attribute current_state.')

        self.assertTrue(
            hasattr(game, 'get_instructions'), 'An initialized game ' +
            'should have the method get_instructions.')

        self.assertTrue(
            hasattr(game, 'is_over'),
            'An initialized game ' + 'should have the method is_over.')

        self.assertTrue(
            hasattr(game, 'is_winner'),
            'An initialized game ' + 'should have the method is_winner.')

        self.assertTrue(
            hasattr(game, 'str_to_move'),
            'An initialized game ' + 'should have the method str_to_move.')
    def test_is_winner_p1_true(self, input):
        """
        Test is_winner('p1') to ensure that it returns True when the game is
        over and p1 is the winner.
        """
        game = StonehengeGame(True)
        current_state = game.current_state
        current_state = current_state.make_move(game.str_to_move("A"))
        game.current_state = current_state
        sample_board = ("      1   @\n     /   /\n1 - 1 - B\n     \\ / \\\n" +
                        "  @ - C   @\n       \\\n        1")
        is_winner = game.is_winner("p1")

        self.assertTrue(is_winner,
                        ("Calling is_winner('p1') on a game where p1 " +
                         "won should return True, such as in the" +
                         " board:\n{}").format(sample_board))
    def test_make_move_keeps_state(self):
        """
        Test make_move() to make sure it doesn't modify the current_state of
        a game.
        """
        with patch('builtins.input', return_value='1'):
            game = StonehengeGame(True)

        original_state = game.current_state

        move_to_make = game.str_to_move("A")
        new_state = game.current_state.make_move(move_to_make)
        after_move_state = game.current_state

        self.assertEqual(
            original_state, after_move_state,
            "After calling make_move, the current_state of a " +
            "game should not be changed.")
    def test_stonehenge_rough_outcome_other_player_winning_moves(self, input):
        """
        Test to make sure the rough_outcome of a state whose moves will all
        result in states where the other player can immediately win returns -1.
        """
        game = StonehengeGame(True)
        new_state = game.current_state

        moves_to_make = ["D", "A", "C", "E", "G"]
        for move in moves_to_make:
            new_state = new_state.make_move(game.str_to_move(move))

        ro = new_state.rough_outcome()

        self.assertEqual(
            ro, -1, ("rough_outcome() should return -1 for a state where" +
                     " all moves will result in states where the other " +
                     "player can immediately win but {} was returned " +
                     "instead.").format(ro))
    def test_stonehenge_rough_outcome_winning_move_immediate(self, input):
        """
        Test to make sure the rough_outcome of a state that has a move available
        which can win the game immediately returns 1.
        """
        game = StonehengeGame(True)
        new_state = game.current_state

        moves_to_make = ["A", "B"]
        for move in moves_to_make:
            new_state = new_state.make_move(game.str_to_move(move))

        ro = new_state.rough_outcome()

        self.assertEqual(
            ro, 1, ("rough_outcome() should return 1 for a state where " +
                    "there is a move that will lead to the the current " +
                    "player winning immediately but {} was returned " +
                    "instead.").format(ro))
 def test_get_current_player_name_true(self, input):
     """
     Test get_current_player_name to ensure it returns "p1" if Stonehenge
     was initialized with True for is_p1_turn.
     """
     game = StonehengeGame(True)
     self.assertEqual(
         'p1', game.current_state.get_current_player_name(),
         "Calling get_current_player_name() on a game " +
         "initialized with 'True' as the parameter passed " +
         "in should return 'p1'.")
    def test_stonehenge_make_move(self, input):
        """
        Test make_move() on Stonehenge to make sure it can apply a move and
        correctly return the next state.
        """
        game = StonehengeGame(True)
        moves = game.current_state.get_possible_moves()
        self.assertEqual(len(moves), 3,
                         ("get_possible_moves() on a game of " +
                          "Stonehenge with a side-length of 1 " +
                          "should return 3 moves, but instead " +
                          "{} were returned.").format(len(moves)))

        new_state = game.current_state.make_move(moves[0])

        expected_ley_lines = [["1", "@", "1", "@", "@", "1"],
                              ["@", "1", "1", "@", "1", "@"],
                              ["@", "1", "@", "1", "@", "1"]]

        expected_cells = [["1", "B", "C"], ["A", "1", "C"], ["A", "B", "1"]]

        resulting_ley_lines, resulting_cells = self.extract_stonehenge_values(
            new_state)

        # Create formatted versions of the expected and resulting values

        str_format = "Ley-lines: {}; Cells: {}"
        formatted_expected = ""
        for i in range(len(expected_ley_lines)):
            formatted_expected += str_format.format(expected_ley_lines[i],
                                                    expected_cells[i]) + "\n"

        formatted_expected = formatted_expected.strip()

        formatted_resulting = str_format.format(resulting_ley_lines,
                                                resulting_cells)

        # Make sure each of the resulting states can be found in the expected
        # states.
        found_match = False

        for j in range(len(expected_ley_lines)):
            if (resulting_ley_lines == expected_ley_lines[j]) and \
                    (resulting_cells == expected_cells[j]):
                found_match = True

        self.assertTrue(found_match,
                        ("Applying a move to a newly initialized " +
                         "game of Stonehenge with side-length 1 should " +
                         "result in a state with one of the properties from " +
                         "the following:\n{}\nBut the state formed had the " +
                         "following:\n{}").format(formatted_expected,
                                                  formatted_resulting))
    def test_stonehenge_repr_different_players_same_value(self):
        """
        Test to make sure the __repr__ of 2 states that have the same value
        but which have different current_players are different.
        """
        with patch('builtins.input', return_value='2'):
            game_1 = StonehengeGame(True)

        with patch('builtins.input', return_value='2'):
            game_2 = StonehengeGame(False)

        # Apply the moves A -> G to the initial state of game 1 (which starts
        # with player 1)
        game_1_state = game_1.current_state
        game_1_state = game_1_state.make_move(game_1.str_to_move("A"))
        game_1_state = game_1_state.make_move(game_1.str_to_move("G"))

        # Apply the moves G -> A to the initial state of game 2 (which starts
        # with player 2)
        game_2_state = game_2.current_state
        game_2_state = game_2_state.make_move(game_2.str_to_move("G"))
        game_2_state = game_2_state.make_move(game_2.str_to_move("A"))

        self.assertNotEqual(
            repr(game_1_state), repr(game_2_state),
            "2 states that have the same values but different" +
            " players should return different __repr__s.")
    def test_stonehenge_rough_outcome_state_over(self, input):
        """
        Test to make sure the rough_outcome of a state that's over returns the
        score of the current player (e.g. -1 if the current player lost).
        """
        game = StonehengeGame(True)
        new_state = game.current_state.make_move("A")
        ro = new_state.rough_outcome()

        self.assertEqual(
            ro, -1, "rough_outcome() should return -1 for a state that " +
            "is over an where the current player at that state " +
            "has lost, but {} was returned instead.".format(ro))
    def test_stonehenge_str_one_move(self, input):
        """
        Test the str output of Stonehenge's state to make sure the number
        is extracted correctly after one move is made.
        """
        game = StonehengeGame(True)
        game.current_state = game.current_state.make_move(
            game.str_to_move("A"))
        ley_lines, cells = self.extract_stonehenge_values(game.current_state)
        expected_ley_lines = ['1', '@', '1', '@', '@', '1']
        self.assertEqual(ley_lines, expected_ley_lines,
                         ("The ley-lines in a newly initialized game of " +
                          "Stonehenge with a side-length of 1 where " +
                          "cell 'A' was taken by Player 1 should be {}" +
                          ", but {} was found instead.").format(
                              expected_ley_lines, ley_lines))

        self.assertEqual(
            cells, ['1', 'B', 'C'],
            ("The cell letters in a newly initialized game of " +
             "Stonehenge with a side-length of 1 where " +
             "cell 'A' was taken by Player 1 should be " +
             "1, B, and C, but {} was found instead.").format(cells))
    def test_stonehenge_takes_input(self, input):
        """
        Test to make sure Stonehenge can be initialized, taking in a boolean
        as a parameter and requiring a number as input.
        """
        game = StonehengeGame(True)

        actual_patch = input()
        expected_patch = "END"

        self.assertEqual(
            actual_patch, expected_patch,
            "When initializing a game of Stonehenge, only one " +
            "input (e.g. 3) should be taken in.")
    def test_stonehenge_repr_same_players_same_value(self, input):
        """
        Test to make sure the __repr__ of 2 states that have the same value
        and the same player, but which were reached differently, have the
        same __repr__.
        """
        game = StonehengeGame(True)
        initial_state = game.current_state

        # Apply the moves A -> G -> B to the initial state
        state_1 = initial_state.make_move(game.str_to_move("A"))
        state_1 = state_1.make_move(game.str_to_move("G"))
        state_1 = state_1.make_move(game.str_to_move("B"))

        # Apply the moves B -> G -> A to the initial state
        state_2 = initial_state.make_move(game.str_to_move("B"))
        state_2 = state_2.make_move(game.str_to_move("G"))
        state_2 = state_2.make_move(game.str_to_move("A"))

        self.assertEqual(
            repr(state_1), repr(state_2),
            "2 states that have the same values and the same " +
            "current player but which were reached differently " +
            "should return the same __repr__.")
    def test_stonehenge_to_end(self, input):
        """
        Test to make sure Stonehenge can reach an end state correctly
        through the move sequence A, G, D, E, F
        """
        game = StonehengeGame(True)
        current_state = game.current_state

        self.assertFalse(
            game.is_over(current_state),
            "A new game of Stonehenge returned True " +
            "when .is_over() was called.")

        moves_to_use = ['A', 'G', 'D', 'E', 'F']

        expected_states = [
            BOARD_LENGTH_2_AFTER_A, BOARD_LENGTH_2_AFTER_AG,
            BOARD_LENGTH_2_AFTER_AGD, BOARD_LENGTH_2_AFTER_AGDE,
            BOARD_LENGTH_2_AFTER_AGDEF
        ]

        # Make sure the student solution initialized correctly
        ley_lines, cells = self.extract_stonehenge_values(game.current_state)
        self.assertEqual(
            ley_lines, ['@' for i in range(9)],
            ("The ley-lines in a newly initialized game of " +
             "Stonehenge with a side-length of 2 should consist " +
             "of 9 @s, but {} was found instead.").format(ley_lines))

        self.assertEqual(
            cells, [i for i in "ABCDEFG"],
            ("The cell letters in a newly initialized game of " +
             "Stonehenge with a side-length of 1 should be " +
             "A, B, and C, but {} was found instead.").format(cells))

        # Apply each move and make sure they match the solution
        moves_used = ''
        for i in range(len(moves_to_use)):
            move = moves_to_use[i]
            moves_used += move
            expected_state = expected_states[i]

            current_state = current_state.make_move(game.str_to_move(move))

            # Make sure str(current_state)'s extracted values match the solution
            ley_lines, cells = self.extract_stonehenge_values(current_state)
            (expected_ley_lines, expected_cells) = \
                self.extract_stonehenge_values(expected_state)

            self.assertEqual(
                ley_lines, expected_ley_lines,
                ("The ley-lines reached after applying the moves" +
                 " {} to a game of Stonehenge with a side-length" +
                 " of 2 should be {} but {} were found instead. " +
                 "The str returned looked like:\n{}\nWhile " +
                 "something like this was expected:\n{}").format(
                     moves_used, expected_ley_lines, ley_lines,
                     str(current_state), expected_state))

            self.assertEqual(
                cells, expected_cells,
                ("The ley-cells reached after applying the moves" +
                 " {} to a game of Stonehenge with a side-length" +
                 " of 2 should be {} but {} were found instead. " +
                 "The str returned looked like:\n{}\nWhile " +
                 "something like this was expected:\n{}").format(
                     moves_used, expected_cells, cells, str(current_state),
                     expected_state))
    def test_stonehenge_get_possible_moves(self, input):
        """
        Test get_possible_moves() to make sure it returns the correct set
        of moves for Stonehenge with a side length of 1.
        """
        game = StonehengeGame(True)
        moves = game.current_state.get_possible_moves()

        self.assertEqual(
            3, len(moves),
            ("get_possible_moves() on a newly initialized " +
             "game of Stonehenge with a side length of 1 should " +
             "return 3 moves but " + "{} were returned.").format(len(moves)))

        expected_ley_lines = [["1", "@", "1", "@", "@", "1"],
                              ["@", "1", "1", "@", "1", "@"],
                              ["@", "1", "@", "1", "@", "1"]]

        expected_cells = [["1", "B", "C"], ["A", "1", "C"], ["A", "B", "1"]]

        resulting_ley_lines = []
        resulting_cells = []

        for move in moves:
            new_state = game.current_state.make_move(game.str_to_move(move))
            lines, cells = self.extract_stonehenge_values(new_state)
            resulting_ley_lines.append(lines)
            resulting_cells.append(cells)

        # Create formatted versions of the expected and resulting values

        str_format = "Ley-lines: {}; Cells: {}"
        formatted_expected = ""
        for i in range(len(expected_ley_lines)):
            formatted_expected += str_format.format(expected_ley_lines[i],
                                                    expected_cells[i]) + "\n"

        formatted_expected = formatted_expected.strip()

        formatted_resulting = ""
        for i in range(len(resulting_ley_lines)):
            formatted_resulting += str_format.format(resulting_ley_lines[i],
                                                     resulting_cells[i]) + "\n"

        formatted_resulting = formatted_resulting.strip()

        # Make sure each of the resulting states can be found in the expected
        # states.
        for i in range(len(expected_ley_lines)):
            lines_to_find = resulting_ley_lines[i]
            cells_to_find = resulting_cells[i]
            found_match = False

            for j in range(len(expected_ley_lines)):
                if (lines_to_find == expected_ley_lines[j]) and \
                        (cells_to_find == expected_cells[j]):
                    found_match = True

            self.assertTrue(found_match,
                            ("The states reachable from a newly initialized " +
                             "game of Stonehenge with side-length 1 should " +
                             "have the following properties:\n{}\nBut the " +
                             "following were found:\n{}").format(
                                 formatted_expected, formatted_resulting))