web-dev-qa-db-fra.com

Algorithme minimax pour Tictactoe In Python

J'ai récemment inscrit à CS50 AI python Cours et les projets à faire consiste à mettre en œuvre un algorithme minimax pour un jeu TictactoToe. J'ai cherché de l'aide et recherché Stackoverflow, mais je n'ai trouvé aucune réponse à laquelle pourrait m'aider. La partie graphique de celle-ci est déjà mise en œuvre et tout ce que vous avez à faire est de programmer les fonctions données d'un modèle et je crois que je les ai bien compris avec la seule exception de la partie d'algorithme, les fonctions sont les suivantes. :

import math
import copy

X = "X"
O = "O"
EMPTY = None


def initial_state():
    """
    Returns starting state of the board.
    """
    return [[EMPTY, EMPTY, EMPTY],
            [EMPTY, EMPTY, EMPTY],
            [EMPTY, EMPTY, EMPTY]]


def player(board):
    """
    Returns player who has the next turn on a board.
    """
    if board == initial_state():
        return X

    xcounter = 0
    ocounter = 0
    for row in board:
        xcounter += row.count(X)
        ocounter += row.count(O)

    if xcounter == ocounter:
        return X
    else:
        return O


def actions(board):
    """
    Returns set of all possible actions (i, j) available on the board.
    """
    possible_moves = []
    for i in range(3):
        for j in range(3):
            if board[i][j] == EMPTY:
                possible_moves.append([i, j])
    return possible_moves


def result(board, action):
    """
    Returns the board that results from making move (i, j) on the board.
    """
    boardcopy = copy.deepcopy(board)
    try:
        if boardcopy[action[0]][action[1]] != EMPTY:
            raise IndexError
        else:
            boardcopy[action[0]][action[1]] = player(boardcopy)
            return boardcopy
    except IndexError:
        print('Spot already occupied')


def winner(board):
    """
    Returns the winner of the game, if there is one.
    """
    columns = []
    # Checks rows
    for row in board:
        xcounter = row.count(X)
        ocounter = row.count(O)
        if xcounter == 3:
            return X
        if ocounter == 3:
            return O

    # Checks columns
    for j in range(len(board)):
        column = [row[j] for row in board]
        columns.append(column)

    for j in columns:
        xcounter = j.count(X)
        ocounter = j.count(O)
        if xcounter == 3:
            return X
        if ocounter == 3:
            return O

    # Checks diagonals
    if board[0][0] == O and board[1][1] == O and board[2][2] == O:
        return O
    if board[0][0] == X and board[1][1] == X and board[2][2] == X:
        return X
    if board[0][2] == O and board[1][1] == O and board[2][0] == O:
        return O
    if board[0][2] == X and board[1][1] == X and board[2][0] == X:
        return X

    # No winner/tie
    return None


def terminal(board):
    """
    Returns True if game is over, False otherwise.
    """
    # Checks if board is full or if there is a winner
    empty_counter = 0
    for row in board:
        empty_counter += row.count(EMPTY)
    if empty_counter == 0:
        return True
    Elif winner(board) is not None:
        return True
    else:
        return False


def utility(board):
    """
    Returns 1 if X has won the game, -1 if O has won, 0 otherwise.
    """
    if winner(board) == X:
        return 1
    Elif winner(board) == O:
        return -1
    else:
        return 0


def minimax(board):
    current_player = player(board)

    if current_player == X:
        v = -math.inf
        for action in actions(board):
            k = min_value(result(board, action))    #FIXED
            if k > v:
                v = k
                best_move = action
    else:
        v = math.inf
        for action in actions(board):
            k = max_value(result(board, action))    #FIXED
            if k < v:
                v = k
                best_move = action
    return best_move

def max_value(board):
    if terminal(board):
        return utility(board)
    v = -math.inf
    for action in actions(board):
        v = max(v, min_value(result(board, action)))
    return v    #FIXED

def min_value(board):
    if terminal(board):
        return utility(board)
    v = math.inf
    for action in actions(board):
        v = min(v, max_value(result(board, action)))
    return v    #FIXED

La dernière partie est l'emplacement de la fonction minimax (tableau), elle est censée prendre l'état actuel de la carte et calculer le meilleur déplacement possible selon que l'AI est le joueur 'X' ou 'O' (il peut être n'importe lequel de Les deux), le joueur "X" tente de maximiser le score et que "O" devrait minimiser l'utilisation de la fonction utilitaire (tableau) qui renvoie une 1 pour x gagnant, -1 pour "O" gagnant ou 0 pour les liens. Jusqu'à présent, les mouvements d'IA ne sont pas optimaux et je peux facilement gagner contre elle lorsque je ne devrais pas, comme dans le meilleur scénario, tout ce que je devais obtenir est une cravate car l'IA est censé calculer chaque mouvement possible à ce stade. Mais je ne sais pas ce qui va mal ...

4
Albert

D'abord un mot sur le débogage: si vous souhaitez imprimer le calcul effectué dans les appels récursifs, vous pouvez tracer l'exécution du problème et trouver rapidement la réponse.

Mais votre problème semble être au sommet de l'arbre. Dans votre appel minimax, si le joueur actuel est x, vous appelez le max_value sur chacun des enfants de l'état, puis prenez le maximum de ces résultats. Cependant, cela applique la fonction max deux fois au sommet de l'arbre. Le joueur suivant du jeu est O, vous devez donc appeler la fonction MIN_Value pour le prochain joueur.

Donc, dans l'appel minimax, vous devez appeler min_value si le courant_player est X et max_value si le courant_player est O.

1
Nathan S.

@ Harsh-Kothari, le Page du projet CS5 dit

Il est important de noter que le conseil d'origine doit être non modifié: puisque Minimax nécessitera en fin de compte en tenant compte de nombreux États du conseil d'administration différents au cours de son calcul. Cela signifie que simplement mettre à jour une cellule à bord en soi n'est pas une mise en œuvre correcte de la fonction de résultat. Vous voudrez probablement faire une copie profonde du conseil avant de faire des changements.

Pour éviter la modification de la sous-liste, nous utilisons DeepCopy au lieu de copier

En savoir plus sur la copie () et DeepCopy () ici

0
chaha0s

modifier le code des actions (conseil) à cette

possibleActions = set()

for i in range(0, len(board)):
    for j in range(0, len(board[0])):
        if board[i][j] == EMPTY:
            possibleActions.add((i, j))

return possibleActions
0
ahmedgamal