class Game:

    def __init__(self, user_sign, computer_sign, mode,
                 size, board, start_stones):
        self.user = Player(user_sign)
        self.computer = Player(computer_sign)
        self.board = Board(size, start_stones, board, self.user, self.computer)
        self.mode = mode
        self.does_user_win = None
        self.running = True

    def play_user_turn(self, from_row, from_col, to_row, to_col):
        return self.board.move_stone(self.user, self.computer,
                                     from_row, from_col, to_row, to_col)

    def play_computer_turn(self):
        moves = self.board.possible_moves(self.computer, self.user)
        if moves:
            move = self.__choose_move(moves)
            from_row, from_col = move[0][0], move[0][1]
            to_row, to_col = move[1][0], move[1][1]
            self.board.move_stone(self.computer, self.user,
                                  from_row, from_col, to_row, to_col)
            user_moves = self.board.possible_moves(self.user, self.computer)
            if not user_moves:
                self.end(self.user)
        else:
            self.end(self.computer)

    def __choose_move(self, moves):
        if self.mode == "easy":
            index = randint(0, len(moves) - 1)
            move = moves[index]
        else:
            sorted_by_benefit = sorted(moves, key=lambda tup: tup[2])
            move = sorted_by_benefit[-1]
        return move

    def end(self, player):
        total = self.board.size * self.board.size
        player_points = player.count_of_stones()
        opponent_points = total - player_points
        if player_points != opponent_points:
            self.does_user_win = ((player is self.user and
                                   player_points > opponent_points) or
                                  (player is self.computer and
                                   player_points < opponent_points))
        self.running = False
class BoardTests(unittest.TestCase):

    def setUp(self):
        self.player1 = Player("X")
        self.player2 = Player("O")
        self.board = Board(4, 4, None, self.player1, self.player2)

    def test_are_fields_assigned(self):
        self.assertEqual(4, self.board.size)
        self.assertEqual(4, self.board._Board__start_stones)
        self.assertEqual((self.player1, self.player2),
                         self.board._Board__players)
        self.assertEqual([['X', 'X', 'X', 'X'],
                          ['_', '_', '_', '_'],
                          ['_', '_', '_', '_'],
                          ['O', 'O', 'O', 'O']], self.board._Board__board)

    def test__set_board_for_loaded_board(self):
        loaded_board = ["X,O", "O,X"]
        self.assertEqual([['X', 'O'],
                          ['O', 'X']],
                         self.board._Board__set_board(loaded_board))

    def test__set_board_by_parameteres(self):
        self.board.size = 6
        self.board._Board__start_stones = 1
        self.assertEqual([['X', '_', '_', '_', '_', '_'],
                          ['_', '_', '_', '_', '_', '_'],
                          ['_', '_', '_', '_', '_', '_'],
                          ['_', '_', '_', '_', '_', '_'],
                          ['_', '_', '_', '_', '_', '_'],
                          ['_', '_', '_', '_', '_', 'O']],
                         self.board._Board__set_board(None))

    def test__load_board(self):
        board = ["X,X", "O,O"]
        self.assertEqual([['X', 'X'],
                          ['O', 'O']],
                         self.board._Board__load_board(board))

    def test__set_board_one_stone(self):
        self.assertEqual([['X', '_', '_', '_'],
                          ['_', '_', '_', '_'],
                          ['_', '_', '_', '_'],
                          ['_', '_', '_', 'O']],
                         self.board._Board__set_board_one_stone())

    def test__set_board_stones_divisible_by_size(self):
        self.assertEqual([['X', 'X', 'X', 'X'],
                          ['_', '_', '_', '_'],
                          ['_', '_', '_', '_'],
                          ['O', 'O', 'O', 'O']],
                         self.board._Board__set_board_stones_divisible_by_size
                         ())

    def test__set_row_empty_fields(self):
        self.board.size = 3
        self.assertEqual(['_', '_', '_'],
                         self.board._Board__set_row())

    def test__set_row_player_sign(self):
        self.board.size = 3
        self.assertEqual(['X', 'X', 'X'],
                         self.board._Board__set_row(self.player1))

    def test_move_stone_jump_step(self):
        self.assertTrue(
            self.board.move_stone(self.player2, self.player1, 3, 0, 1, 1))
        self.assertEqual([['O', 'O', 'O', 'X'],
                          ['_', 'O', '_', '_'],
                          ['_', '_', '_', '_'],
                          ['_', 'O', 'O', 'O']], self.board._Board__board)

    def test_move_stone_clone_step(self):
        self.assertTrue(
            self.board.move_stone(self.player2, self.player1, 3, 1, 2, 0))
        self.assertEqual([['X', 'X', 'X', 'X'],
                          ['_', '_', '_', '_'],
                          ['O', '_', '_', '_'],
                          ['O', 'O', 'O', 'O']], self.board._Board__board)

    def test_move_stone_not_a_player_stone_at_from_field(self):
        self.assertFalse(
            self.board.move_stone(self.player2, self.player1, 2, 1, 1, 1))

    def test_move_stone_not_a_free_to_field(self):
        self.assertFalse(
            self.board.move_stone(self.player2, self.player1, 3, 1, 3, 2))

    def test_move_stone_out_of_range(self):
        self.assertFalse(
            self.board.move_stone(self.player2, self.player1, 3, 3, 2, 4))
        self.assertFalse(
            self.board.move_stone(self.player2, self.player1, 3, 4, 2, 1))
        self.assertFalse(
            self.board.move_stone(self.player2, self.player1, 3, 0, 1, 3))

    def test__is_possible_move_clone_step(self):
        self.board.size = 5
        self.board._Board__board = [['_', '_', '_', '_', '_'],
                                    ['_', '_', '_', '_', '_'],
                                    ['_', '_', 'X', '_', '_'],
                                    ['_', '_', '_', '_', '_'],
                                    ['_', '_', '_', '_', '_']]
        self.assertTrue(self.board._Board__is_possible_move(2, 2, 1, 1))
        self.assertTrue(self.board._Board__is_possible_move(2, 2, 3, 3))

    def test__is_possible_move_jump_step(self):
        self.board.size = 5
        self.board._Board__board = [['_', '_', '_', '_', '_'],
                                    ['_', '_', '_', '_', '_'],
                                    ['_', '_', 'X', '_', '_'],
                                    ['_', '_', '_', '_', '_'],
                                    ['_', '_', '_', '_', '_']]
        self.assertTrue(self.board._Board__is_possible_move(2, 2, 0, 0))
        self.assertTrue(self.board._Board__is_possible_move(2, 2, 4, 4))

    def test__is_possible_move_from_field_to_same_field(self):
        self.assertFalse(self.board._Board__is_possible_move(3, 1, 3, 1))

    def test__is_possible_move_out_of_board_bounds(self):
        self.assertFalse(self.board._Board__is_possible_move(3, 3, 2, 4))
        self.assertFalse(self.board._Board__is_possible_move(3, 4, 2, 1))

    def test__is_possible_move_out_of_field_range(self):
        self.assertFalse(self.board._Board__is_possible_move(3, 0, 1, 3))

    def test__is_in_board_range(self):
        self.assertFalse(self.board._Board__is_in_board_range(7))
        self.assertTrue(self.board._Board__is_in_board_range(3))

    def test__is_in_field_range(self):
        self.assertFalse(self.board._Board__is_in_field_range(2, 5))
        self.assertTrue(self.board._Board__is_in_field_range(2, 0))

    def test__convert_stones(self):
        self.board._Board__board = [['O', 'X', 'X'],
                                    ['O', '_', 'X'],
                                    ['O', 'O', 'X']]
        self.board._Board__convert_stones(1, 1, self.player1, self.player2)
        self.assertTrue([['X', 'X', 'X'],
                         ['X', '_', 'X'],
                         ['X', 'X', 'X']], self.board._Board__board)

    def test__opponent_stones(self):
        self.board._Board__board = [['O', 'X', 'X'],
                                    ['O', '_', 'X'],
                                    ['O', 'O', 'X']]
        self.assertEqual([(0, 0), (1, 0), (2, 0), (2, 1)],
                         self.board._Board__opponent_stones(
                            1, 1, self.player2))

    def test__is_jump_step(self):
        self.board.size = 5
        self.board._Board__board = [['_', '_', '_', '_', '_'],
                                    ['_', '_', '_', '_', '_'],
                                    ['_', '_', 'X', '_', '_'],
                                    ['_', '_', '_', '_', '_'],
                                    ['_', '_', '_', '_', '_']]
        self.assertFalse(self.board._Board__is_jump_step(2, 2, 2, 3))
        self.assertTrue(self.board._Board__is_jump_step(2, 2, 0, 4))
        self.assertTrue(self.board._Board__is_jump_step(2, 2, 4, 0))

    def test_possible_moves(self):
        possibilities = [((0, 0), (1, 0), 1), ((0, 0), (1, 1), 1),
                         ((0, 0), (1, 2), 0), ((0, 0), (2, 0), 2),
                         ((0, 0), (2, 1), 3), ((0, 0), (2, 2), 3)]
        self.assertEqual(possibilities, self.board.possible_moves(
                            self.player1, self.player2)[:6])

    def test__str__method(self):
        output = ("    0  1  2  3  \n\n" +
                  "0   X  X  X  X \n" +
                  "1   _  _  _  _ \n" +
                  "2   _  _  _  _ \n" +
                  "3   O  O  O  O \n")
        self.assertEqual(output, str(self.board))

    def test_database_format(self):
        self.assertEqual("X|X,X,X,X|_,_,_,_|_,_,_,_|O,O,O,O",
                         self.board.database_format())