3
\$\begingroup\$

I created a game which can play tic-tac-toe with: 0 players (AI vs AI), 1 player (human vs AI) or 2 player (human vs human). Here is the AI function which wraps around my innermost function. This evaluates all the candidate board positions and returns the best move.

I searched to make sure this has not been implemented before.

Could I have feedback on my Python code in my GitHub repo.

  • I would be particularly interested in knowing how to make my code clearer to read and understand.
  • I would be interested to know how I could improve this code, or the code which evaluates a given boardstate. For example I would like to make score_one_gamestate be more elegant.

  • I have a vague feeling that I could use something instead of creating many deepcopies of the game board in the score_one_gamestate function. But I do not know how to do this. Additionally I do not know whether attempting this would even be a good idea.

def optimal_ai_move(board: np.array, x_or_o: str, available: list):
    """
    The heart of the robot brain.
    Given a board state, will return the optimal move.
    :param board: numpy array of the board.
    :param x_or_o: which side is the AI playing on? #This isn't exactly true in recursive calls.
    :param available: available[(row, col)] = True, if the space hasn't been taken yet.
    :return: best_move: (row, col). The move with the greatest number of simulated wins.
    """
    possibility_dict = {}
    for candidate_move in available:
        deeper = copy.deepcopy(board)
        deeper[candidate_move] = x_or_o
        available = find_available_moves(deeper)
        possibility_dict[candidate_move] = score_one_gamestate(deeper,
                                                               turn_flipper(x_or_o),
                                                               available,
                                                               x_or_o,
                                                               score_dict=None)

    metric_dict = {candidate_move: (score_dict['wins'] + score_dict['draws']) / sum(score_dict.values())
                   for candidate_move, score_dict in possibility_dict.items()}

    best_move = max(metric_dict, key=metric_dict.get)
    return best_move, possibility_dict



def score_one_gamestate(board: np.array, x_or_o: str, available: list, ai_player_choice: str, score_dict=None,
                        weight=1) -> int:
    if score_dict is None:
        score_dict = {'wins': 0, 'losses': 0, 'draws': 0}
    is_finished = game_over(board)

    if is_finished == ai_player_choice:  # This might be wrong, we want ai_player_choice
        score_dict['wins'] += 1 / weight
        return score_dict
    elif is_finished == turn_flipper(ai_player_choice):
        score_dict['losses'] += 1 / weight
        return score_dict
    elif is_finished == 'draw':
        score_dict['draws'] += 1 / weight
        return score_dict
    else:
        weight *= len(available)
        for candidate_move in available:
            deeper = copy.deepcopy(board)
            deeper[candidate_move] = x_or_o
            available_moves = find_available_moves(deeper)
            score_dict = score_one_gamestate(deeper,
                                             turn_flipper(x_or_o),
                                             available_moves,
                                             ai_player_choice,
                                             score_dict,
                                             weight)
    return score_dict
\$\endgroup\$
0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.