I implemented a minimax algorithm for a basic tic-tac-toe AI in Python like this:
def minimax(currentBoard, player):
if isGameOver(currentBoard):
score = evaluate(currentBoard)
return score
for cell in getEmptySpots(currentBoard):
x = cell[0]
y = cell[1]
currentBoard[x][y] = player
bestScore = -1000000
score = minPlay(currentBoard, -player)
currentBoard[x][y] = 0
if score > bestScore:
bestScore = score
bestMove = cell
print('Best move:')
print(bestMove)
print('\n')
return bestMove
def minPlay(currentBoard, player):
if isGameOver(currentBoard):
score = evaluate(currentBoard)
return score
for cell in getEmptySpots(currentBoard):
x = cell[0]
y = cell[1]
currentBoard[x][y] = player
bestScore = 1000000
score = maxPlay(currentBoard, -player)
currentBoard[x][y] = 0
if score < bestScore:
bestScore = score
return bestScore
def maxPlay(currentBoard, player):
if isGameOver(currentBoard):
score = evaluate(currentBoard)
return score
for cell in getEmptySpots(currentBoard):
x = cell[0]
y = cell[1]
currentBoard[x][y] = player
bestScore = -1000000
score = minPlay(currentBoard, -player)
currentBoard[x][y] = 0
if score > bestScore:
bestScore = score
return bestScore
The other supporting functions are pretty self-explanatory. However, this script does not function as it should. For example, it always seems to begin by picking [0,0] and then proceeding with a relatively constant set of moves, even when better moves are available. Also, given the following state (and states in general where a winning move is available):
where 1
represents the human and -1
represents the computer, the computer picks the move [2][1]
as the best move, instead of [2][2]
or [1][2]
which would both result in a win.
I have gone through a number of questions relating to minimax implementation in different languages and as far as I can tell my code logically makes sense. Thus I'm unsure as to what the problem could be. My full code can be found here.
There are 2 issues. The first is the logic problem in @Flopp 's answer. The second is that isGameOver
does not return true when there are no more moves left. Scores of 0
get returned as the initial max or min score.
here:
def minPlay(currentBoard, player):
if isGameOver(currentBoard):
score = evaluate(currentBoard)
return score
The relevant (fixed) line is here (it's not beautiful, it's just demonstrating that it will work):
def isGameOver(currentBoard):
return checkGameOver(currentBoard, HUMAN) or checkGameOver(currentBoard, COMPUTER) or getEmptySpots(currentBoard) == []
For minimax
, it would probably be a good idea to make sure there's an initial bestMove, too.
def minimax(currentBoard, player):
if isGameOver(currentBoard):
score = evaluate(currentBoard)
return score
allMoves = getEmptySpots(currentBoard)
bestMove = allMoves[0]
for cell in allMoves: