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 ...
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.
@ 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
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