# refactored from make_play to simplify
# by Russell on 3/5/21

#from ttt_package.libs.move_utils import get_open_cells
from ttt_package.libs.compare import get_transposed_games, reorient_games
from ttt_package.libs.calc_game_bound import calc_game_bound
from ttt_package.libs.maxi_min import maximin

# find the best move for this agent, based on prior games in the game_history

def best_move(this_board, agent, ttt_base, probs_calc):

    candidate_games = []
    lower_bound = 0
    upper_bound = 0
    # note that len gives the number of the move about to be made
    num_moves = len(this_board)
    bounds_list = []

    #print("best_move - this_board:", this_board)

    # TRANSPOSE the current game state into 8 different games and store in a list
    # the returned value is a list of dictionaries that contain the transposed game
    # and the source function, to allow the game to be transposed back
    tg_list = get_transposed_games(this_board)

    #print("best_move: tg_list =", tg_list)

    # for each of the 8 transposed versions of the current game in question
    # build a list of lower and upper bound tuples for the tg_list using calc_game_bound
    for tgame in tg_list:
        lower_bound = calc_game_bound(tgame["transpose"], agent, 'L')
        upper_bound = calc_game_bound(tgame["transpose"], agent, 'U')
        bounds_tuple = (lower_bound, upper_bound)
        bounds_list.append(bounds_tuple)
        #print("best_move: bounds_tuple =", bounds_tuple)

    # fetch the list of candidate games from the game history
    # we need to look at losing and drawing games so that we can thoroughly explore the action space
    # we must avoid overlooking a good move made early that resulted in a draw/loss because of a
    # later bad move - these will be resolved later via backpropagation
    candidate_games = ttt_base.get_games_list(bounds_list)

    #print("best_move: candidate_games =", candidate_games)

    # if there is at least one game that matches the current game state
    if candidate_games != False:

        # this is the list of games that match the transposed game list
        # de-transpose the candidate games to get the right cell for the next move

        # get a list of the matching detransposition games of the current game
        reoriented_candidates = reorient_games(tg_list, candidate_games)

        #print("best_move: number of reoriented_candidates games = ", len(reoriented_candidates))
        #print("best_move: number of candidate games = ", len(candidate_games))

        #print('best_move: reoriented_candidates =', reoriented_candidates)
        #print('best_move: candidate_games =', candidate_games)

        maximin_list = []

        # iterate though the game candidates
        for this_game in range(len(reoriented_candidates)):
            these_probs = []
            # get the probability element for the next move of this game candidate
            these_probs = reoriented_candidates[this_game]["probs"][num_moves].copy(
            )
            # tack on the cell # of the move
            these_probs.append(
                reoriented_candidates[this_game]["game"][num_moves])
            # append the game submission data to the list to be submitted to maximin
            maximin_list.append(these_probs)
            #print("maximin_list:", maximin_list)

        # send the list of probabilites of the detransposed recorded games for the next move
        recommended_move = maximin(maximin_list)

        #print("best_move: move = ", recommended_move)

        return recommended_move

    else:  # there are no matching games in the game history

        #print("best_move: random choice...")
        # return random_move(this_board)

        # estimate the optimal next move
        optimal_move = probs_calc.calc_next_move(this_board)
        #print("This board =", this_board)
        #print("Calculating optimal move =", optimal_move)
        return optimal_move

[((25, 14, 25, 46), 'ttt_package.libs.compare.get_transposed_games', 'get_transposed_games', ({(25, 35, 25, 45): 'this_board'}, {}), '(this_board)', False, 'from ttt_package.libs.compare import get_transposed_games, reorient_games\n'), ((32, 22, 32, 69), 'ttt_package.libs.calc_game_bound.calc_game_bound', 'calc_game_bound', ({(32, 38, 32, 56): "tgame['transpose']", (32, 58, 32, 63): 'agent', (32, 65, 32, 68): '"""L"""'}, {}), "(tgame['transpose'], agent, 'L')", False, 'from ttt_package.libs.calc_game_bound import calc_game_bound\n'), ((33, 22, 33, 69), 'ttt_package.libs.calc_game_bound.calc_game_bound', 'calc_game_bound', ({(33, 38, 33, 56): "tgame['transpose']", (33, 58, 33, 63): 'agent', (33, 65, 33, 68): '"""U"""'}, {}), "(tgame['transpose'], agent, 'U')", False, 'from ttt_package.libs.calc_game_bound import calc_game_bound\n'), ((53, 32, 53, 72), 'ttt_package.libs.compare.reorient_games', 'reorient_games', ({(53, 47, 53, 54): 'tg_list', (53, 56, 53, 71): 'candidate_games'}, {}), '(tg_list, candidate_games)', False, 'from ttt_package.libs.compare import get_transposed_games, reorient_games\n'), ((77, 27, 77, 48), 'ttt_package.libs.maxi_min.maximin', 'maximin', ({(77, 35, 77, 47): 'maximin_list'}, {}), '(maximin_list)', False, 'from ttt_package.libs.maxi_min import maximin\n')]