def play_n_random_games(player_value, game_state: Othello, number_of_games,
                            use_weighted_random):
        """
        Plays number_of_games random games for player_value starting at game_state
        :param player_value: The representation for the observed player
        :param game_state: The game_state to use
        :param number_of_games: The number of games played
        :param use_weighted_random: Boolean determining the algorithm variant
        :return: Dict with available moves as keys and integer pairs of (games_won, times_played)
        """
        winning_statistics = dict()
        # Get the set of legal moves
        possible_moves = game_state.get_available_moves()
        # Add a pair of (won_games, times_played) to the dictionary for each legal move
        for move in possible_moves:
            winning_statistics[move] = (
                1, 1)  # set games played to 1 to avoid division by zero error

        # Simulate big_n games
        for _ in range(number_of_games):
            # Copy the current game state
            simulated_game = game_state.deepcopy()
            # Play one random game and access the returned information
            if use_weighted_random:
                first_played_move, won = MonteCarlo.play_weighted_random_game(
                    player_value, simulated_game)
            else:
                first_played_move, won = MonteCarlo.play_random_game(
                    player_value, simulated_game)
            # Access the statistics stored for the move selected in the random game
            (won_games, times_played) = winning_statistics[first_played_move]
            # Increment the counters accordingly
            winning_statistics[first_played_move] = (won_games + won,
                                                     times_played + 1)
        return winning_statistics
Exemplo n.º 2
0
    def get_available_moves_of_start_tables(self, game: Othello):
        """
        search self._start_table for move sequences starting with the one of game and get next elements of those
        :return: list of available moves
        """
        if len(self._start_tables) == 0:
            self._init_start_tables()

        turn_nr = game.get_turn_nr()
        available_moves = []
        taken_mv = game.get_taken_mvs_text()
        for move_sequence in self._start_tables:
            turn = 0
            for move in move_sequence:
                # move was played
                if turn < turn_nr:
                    if taken_mv[turn] != move:
                        # move is different to start_table
                        break
                # if start sequence is finished
                elif move != "nan":
                    available_moves.append(move)
                    break
                turn += 1
        available_moves = list(dict.fromkeys(available_moves))
        if "nan" in available_moves:
            available_moves.remove("nan")
        return available_moves
    def get_move(self, game_state: Othello):
        """
        Will select the best move according to the value of the resulting game_state according to monte carlo
        :param game_state: current game state
        :return: best move in available moves
        """
        # Use start library if it is selected and still included
        if self._use_start_lib and game_state.get_turn_nr(
        ) < 21:  # check whether start move match
            moves = self._start_tables.get_available_moves_of_start_tables(
                game_state)
            if len(moves) > 0:
                return util.translate_move_to_pair(moves[random.randrange(
                    len(moves))])
        # According to experience the number of moves to consider decreases relevantly after reaching a certain
        # turn number. Therefore it is possible to increase the search depth without loosing to much time.
        # We dynamically increase the search depth after reaching turn_number 40
        search_depth = self._search_depth
        turn_number = game_state.get_turn_nr()
        if turn_number > 40:
            search_depth += turn_number // 10
        # Dict used to store a list of the moves resulting in a state with the respective value
        best_moves = dict()
        # Evaluate each available move
        for move in game_state.get_available_moves():
            # Play the move to get the resulting state
            next_state = game_state.deepcopy()
            next_state.play_position(move)

            # Evaluate the state using the selected function
            if self._use_monte_carlo:
                result = -AlphaBetaPruning.value_monte_carlo(
                    next_state,
                    search_depth - 1,
                    self._heuristic,
                    mc_count=self._mc_count)
            else:
                result = -AlphaBetaPruning.value(
                    next_state, self._search_depth - 1, self._heuristic)

            # Append the move to the list of states with that value
            if result not in best_moves.keys():
                best_moves[result] = []
            best_moves[result].append(move)

        # Determine the best result
        best_result = max(best_moves.keys())
        if self._use_monte_carlo:
            print(AlphaBetaPruning.value_monte_carlo.cache_info())
            AlphaBetaPruning.value_monte_carlo.cache_clear()
        else:
            print(AlphaBetaPruning.value.cache_info())
            AlphaBetaPruning.value.cache_clear()
        # Play one random move with the best possible result
        return best_moves[best_result][random.randrange(
            len(best_moves[best_result]))]
    def heuristic(current_player, game_state: Othello):
        """
        Calculates the value of game_state for current_player according to the Stored MonteCarlo Heuristic
        current_player is coded as the constants EMPTY_CELL, PLAYER_ONE and PLAYER_TWO
          form constants.py. Therefore the parameter is an integer values.
        """

        moves = game_state.get_available_moves()
        turn_nr = game_state.get_turn_nr()
        # get maximum of likelihood values
        return max([
            database.db.get_change_of_winning(move, turn_nr, current_player)
            for move in moves
        ])
 def preprocess_fixed_selectivity(game_state: Othello, n_s, heuristic):
     """
     Will preprocess the given game_state by only letting the n_s best moves pass
     :param game_state:
     :param n_s:
     :param heuristic:
     :return:
     """
     # Get a list of moves sorted by their heuristic value
     heuristic_values = sorted(MonteCarlo.preprocess_get_heuristic_value(
         game_state, heuristic=heuristic).items(),
                               key=operator.itemgetter(1))
     # Pass the first n_s moves on
     game_state.set_available_moves(heuristic_values[:n_s][0])
 def value_monte_carlo(game_state: Othello,
                       depth,
                       heuristic,
                       alpha=-1,
                       beta=1,
                       mc_count=100):
     """
     get score for alpha beta pruning
     :param game_state: actual game state
     :param depth: do alpha beta pruning this depth
     :param heuristic: score game state after alpha beta pruning with this heuristic
     :param mc_count: number of games which are played in each terminal node after alpha beta pruning
     :param alpha: value of alpha
     :param beta:  value of beta
     :return: score of move
     Compare https://github.com/karlstroetmann/Artificial-Intelligence/blob/master/SetlX/game-alpha-beta.stlx
     """
     if game_state.game_is_over():
         return game_state.utility(game_state.get_current_player())
     if depth == 0:
         # use monte carlo player if enabled
         # mc_count = number of played games
         mc = MonteCarlo(big_number=mc_count,
                         use_start_libs=False,
                         preprocessor_n=-1,
                         heuristic=heuristic,
                         use_multiprocessing=True)
         # get best move
         move = mc.get_move(game_state)
         # return winnings stats of best move
         prob = mc.get_move_probability(move)
         return prob
     val = alpha
     for move in game_state.get_available_moves():
         next_state = game_state.deepcopy()
         next_state.play_position(move)
         val = max({
             val, -1 * AlphaBetaPruning.value_monte_carlo(next_state,
                                                          depth - 1,
                                                          heuristic,
                                                          -beta,
                                                          -alpha,
                                                          mc_count=mc_count)
         })
         if val >= beta:
             return val
         alpha = max({val, alpha})
     return val
 def preprocess_get_heuristic_value(game_state: Othello, heuristic):
     """
     Returns a dict of each move's value
     :param game_state: game with actual board, player ...
     :param heuristic:  list of heuristics
     :return:
     """
     # Create Dict to store the value of every move
     heuristic_values = dict()
     # Iterate over the moves to calculate each moves's value
     for move in game_state.get_available_moves():
         # Create a copy of the game_state to be able to manipulate it without side effects
         copy_of_state = game_state.deepcopy()
         # Play the move to evaluate the value of the game after making the move
         copy_of_state.play_position(move)
         # Get the value of the current state
         heuristic_values[move] = heuristic(game_state.get_current_player(),
                                            copy_of_state)
     # return the heuristic_values
     return heuristic_values
 def preprocess_variable_selectivity(game_state: Othello, p_s, heuristic):
     """
     Will preprocess the given game_state by only letting moves with an value of at least p_s of the average move value pass
     :param game_state:
     :param p_s:
     :param heuristic
     :return:
     """
     # Calculate each move's value
     heuristic_value_dict = MonteCarlo.preprocess_get_heuristic_value(
         game_state, heuristic=heuristic)
     # Get a list of values
     heuristic_values = [v for _, v in heuristic_value_dict.items()]
     # Calculate the Average List Value
     average_heuristic_value = sum(heuristic_values) / len(heuristic_values)
     # Pass the moves with an value of at least p_s of the average move value
     game_state.set_available_moves([
         m for m, v in heuristic_value_dict.items()
         if v >= p_s * average_heuristic_value
     ])
 def value(game_state: Othello, depth, heuristic, alpha=-1, beta=1):
     """
     Get value for game_state according to alpha beta pruning
     :param game_state: The state to evaluate
     :param depth: do alpha beta pruning until this depth is reached
     :param heuristic: Function reference for the heuristic used to score game state after maximum search depth is reached
     :param alpha: value of alpha
     :param beta:  value of beta
     :return: value of move
     Compare https://github.com/karlstroetmann/Artificial-Intelligence/blob/master/SetlX/game-alpha-beta.stlx
     """
     if game_state.game_is_over():
         return game_state.utility(game_state.get_current_player())
     if depth == 0:
         # return heuristic of game state
         return heuristic(game_state.get_current_player(), game_state)
     val = alpha
     for move in game_state.get_available_moves():
         next_state = game_state.deepcopy()
         next_state.play_position(move)
         val = max({
             val, -1 * AlphaBetaPruning.value(next_state, depth - 1,
                                              heuristic, -beta, -alpha)
         })
         if val >= beta:
             return val
         alpha = max({val, alpha})
     return val
Exemplo n.º 10
0
 def get_move(game_state: Othello):
     """
     interface function of all players
     :param game_state: actual game state
     :return: random move in available moves
     """
     # Get the legal moves
     possible_moves = game_state.get_available_moves()
     # As the dict_keys Object returned by the function does not support indexing and Indexing is required here
     # Convert it to a list
     possible_moves = list(possible_moves)
     # Return a Random move
     return rnd.choice(possible_moves)
def get_sign(current_player, field_value):
    """
    Returns an indicator whether the field_value denotes a field as owned by current_player
      1: if the field_value indicates the field is owned by the current_player
      0: if the field_value indicates neither player owns the field
     -1: If the field_value indicates the field is owned by opponent of current_player
    Both current_player and field_value are coded as the constants EMPTY_CELL, PLAYER_ONE and PLAYER_TWO
      form constants.py. Therefore both parameters are integer values.
    """
    if field_value == current_player:
        return 1
    elif field_value == Othello.other_player(current_player):
        return -1
    else:
        return 0
Exemplo n.º 12
0
 def _play_n_random_games(count):
     """
     play count random games
     :param count: number of played games
     :return: winning statistics
     statistics = list of pair <taken moves, winner of this game>
     """
     multi_stats = []
     for i in range(count):
         # print each 100 games actual game played position
         if i % 100 == 0:
             print(f"Game No: {i}")
         g = Othello()
         g.init_game()
         # play whole game
         while not g.game_is_over():
             g.play_position(Random.get_move(g))
         winner = g.get_winner()
         # add winner and taken moves to statistic
         multi_stats.append((g.get_taken_mv(), winner))
     return multi_stats
 def heuristic(current_player, game_state: Othello):
     """
     Calculates the value of game_state for current_player according to the Nijssen Heuristic
     current_player is coded as the constants EMPTY_CELL, PLAYER_ONE and PLAYER_TWO
       form constants.py. Therefore the parameter is an integer values.
     """
     # Without any information the value is 0
     value = 0
     # Get the board
     board = game_state.get_board()
     # Get the values assigned to each field
     weight_dict = NijssenHeuristic.values
     # Iterate over the fields with an assigned value
     for (row, column) in NijssenHeuristic.values.keys():
         # Add the fields value to the heuristic value if it us owned by the current player. Subtract it otherwise
         value += get_sign(current_player,
                           board[row][column]) * weight_dict[(row, column)]
     # Return the Calculated value
     return value
    def get_move(self, game_state: Othello):
        """
        Returns the best move according to the games simulated by MonteCarlo
        :param game_state: actual game state
        :return: best move in available moves
        """
        # Use start library if it is selected and still included
        if self._use_start_lib and game_state.get_turn_nr(
        ) < 21:  # check whether start move match
            moves = self._start_tables.get_available_moves_of_start_tables(
                game_state)
            if len(moves) > 0:
                return util.translate_move_to_pair(moves[random.randrange(
                    len(moves))])
        # Create a dictionary to store information on won/lost ratios
        # winning_statistics = dict()
        # empty dictionary or win probabilities
        self._move_probability.clear()
        # Get the own symbol
        player_value = game_state.get_current_player()
        # Check whether to preprocess the available moves
        if self._preprocessor is not None:
            # Preprocess the available moves
            self._preprocessor(game_state, self._preprocessor_parameter,
                               self._heuristic)

        # Simulate big_n games
        if not self._use_multiprocessing:
            # Simulate the games in the current process
            winning_statistics = MonteCarlo.play_n_random_games(
                player_value, game_state, self._big_n,
                self._use_weighted_random)
        else:
            # Create a pool of worker processes. Set the number_of_processes explicitly
            # Workload can be distributed equally on the processes when their number is known
            number_of_processes = mp.cpu_count()
            pool = mp.Pool(processes=number_of_processes)
            # Use Worker processes asynchronous
            list_of_result_objects = [
                pool.apply_async(MonteCarlo.play_n_random_games,
                                 args=(player_value, game_state.deepcopy(),
                                       self._big_n // number_of_processes,
                                       self._use_weighted_random))
                for _ in range(number_of_processes)
            ]
            # Collect the result of the first worker
            winning_statistics = list_of_result_objects[0].get()
            # Collect the result of the other workers and combine them in one single dictionary
            for single_list in list_of_result_objects[1:]:
                MonteCarlo.combine_statistic_dicts(winning_statistics,
                                                   single_list.get())
            # Close the worker pool.
            pool.close()

        # Reduce the pair of (won_games, times_played) to a winning probability
        for single_move in winning_statistics:
            # print(winning_statistics[single_move])
            # Access the values
            (games_won, times_played) = winning_statistics[single_move]
            # Calculate the fraction
            self._move_probability[single_move] = games_won / times_played

        # Select the move with the maximum probability of winning
        selected_move = max(self._move_probability.items(),
                            key=operator.itemgetter(1))[0]
        # Return the selected move
        return selected_move