英文:
Why does my tic tac toe minimax algorithm utilizing a 1d representation of a tic tac toe board and allowing for play of both sides not work?
问题
我编写了一个使用极小化极大算法来玩井字游戏的Python程序。棋盘用一个一维数组表示。玩家可以选择扮演任意一方。程序会递归生成所有合法的棋盘,并在每一轮交替最小化和最大化来检查是否有赢家。但出于某种原因,该算法从未生成过获胜的棋盘。
起初我以为与算法没有正确地最小化和最大化右侧有关,但我检查了后发现代码无明显问题。
import random
# 其他代码...
# 以下代码不要翻译
# Main Code
test()
letter = 'X'
board = [""] * 9
player = random.choice([0, 1])
print("You are playing as", player)
while True:
printBoard(board)
winner = getWinner(board)
if winner is not None:
if winner == "Tie":
print("Tie")
else:
print("Winner:", winner)
break
if player == 0:
if letter == "O":
move = findBestMove(board, letter, player)
print("Computer played move", move)
board[move] = letter
else:
move = int(input("Enter move (0-8): "))
while board[move] != "":
move = int(input("Invalid move. Enter move (0-8): "))
board[move] = letter
elif player == 1:
if letter == "X":
move = findBestMove(board, letter, player)
print("Computer played move", move)
board[move] = letter
else:
move = int(input("Enter move (0-8): "))
while board[move] != "":
move = int(input("Invalid move. Enter move (0-8): "))
board[move] = letter
letter = getOtherPlayer(letter)
英文:
I wrote a python program utilizing the minimax algorithm to play tic tac toe. The board is represented by a 1d array. The player is able to play as either side. The program recursively generates all of the legal boards and checks for a winner alternating between minimizing and maximizing every turn. For some reason the algorithm never generates a winning board.
At first I assumed it was related to the algorithm not minimizing and maximizing for the right side but I checked and I don't see any reason the code wouldn't work
import random
def getOtherPlayer(letter):
if letter == "X":
return "O"
else:
return "X"
def getWinner(board):
for i in range(3):
# Check rows
if board[i*3] == board[i*3+1] == board[i*3+2] != "":
return board[i*3]
# Check columns
if board[i] == board[i+3] == board[i+6] != "":
return board[i]
# Check diagonals
if board[0] == board[4] == board[8] != "":
return board[0]
if board[2] == board[4] == board[6] != "":
return board[2]
# Check for tie
if "" not in board:
return "Tie"
# No winner yet
return None
def test():
board = ['X', '', 'O', 'X', 'O', 'X', 'O', 'X', 'O']
print(getScore(board, 'X', False))
def getAvailableMoves(board):
return [i for i in range(len(board)) if board[i] == ""]
def getScore(board, letter, isMaximizing):
winner = getWinner(board)
if winner == letter and isMaximizing:
print('win')
return 1 # current player wins
elif winner == letter and isMaximizing is False:
print('lose')
return -1
else:
print(board)
print(letter)
print(isMaximizing)
print('tie')
return 0 # tie (no winner)
def minimax(board, letter, player):
if player == 0:
if letter == "X":
bestScore = float('inf')
isMaximizing = False
else:
bestScore = float('-inf')
isMaximizing = True
if player == 1:
if letter == "O":
bestScore = float('inf')
isMaximizing = False
else:
bestScore = float('-inf')
isMaximizing = True
winner = getWinner(board)
if winner is not None:
print("terminal")
return getScore(board, letter, isMaximizing)
availableMoves = getAvailableMoves(board)
if isMaximizing:
for move in availableMoves:
board[move] = letter
score = minimax(board, getOtherPlayer(letter), player)
board[move] = ""
bestScore = max(score, bestScore)
else:
for move in availableMoves:
board[move] = letter
print(board)
score = minimax(board, getOtherPlayer(letter), player)
board[move] = ""
bestScore = min(score, bestScore)
return bestScore
def findBestMove(board, startingLetter, player):
availableMoves = getAvailableMoves(board)
bestMove = None
bestScore = -9999
for move in availableMoves:
board[move] = startingLetter
score = minimax(board, getOtherPlayer(startingLetter), player)
board[move] = ""
if score > bestScore:
bestScore = score
bestMove = move
return bestMove
def printBoard(board):
print()
for i in range(3):
print(board[i * 3:i * 3 + 3])
# Main Code
test()
letter = 'X'
board = [""] * 9
player = random.choice([0, 1])
print("You are playing as", player)
while True:
printBoard(board)
winner = getWinner(board)
if winner is not None:
if winner == "Tie":
print("Tie")
else:
print("Winner:", winner)
break
if player == 0:
if letter == "O":
move = findBestMove(board, letter, player)
print("Computer played move", move)
board[move] = letter
else:
move = int(input("Enter move (0-8): "))
while board[move] != "":
move = int(input("Invalid move. Enter move (0-8): "))
board[move] = letter
elif player == 1:
if letter == "X":
move = findBestMove(board, letter, player)
print("Computer played move", move)
board[move] = letter
else:
move = int(input("Enter move (0-8): "))
while board[move] != "":
move = int(input("Invalid move. Enter move (0-8): "))
board[move] = letter
letter = getOtherPlayer(letter)
答案1
得分: 3
问题出在你的 getScore
函数中。
只有在赢家是 letter
时才返回非零值,但是当赢家是另一名玩家时却不返回。然而这将是你会遇到的情况:如果有一方获胜,那将是由最后移动的玩家来完成的,而在你调用 getScore
时,letter
实际上是下一个要出手的玩家。因此,即使游戏已经获胜,getScore
也会返回0。
以下是对该函数的建议修正:
def getScore(board, letter, isMaximizing):
winner = getWinner(board)
# 当游戏尚未结束,或者是平局时,返回0
if not winner or winner == '平局':
return 0
# 当我们到达这里时,我们知道游戏已经结束。
# 当 `letter` 赢家并且正在最大化时,或者
# 当 `letter` 输家并且正在最小化时,返回1
# 当 `letter` 输家并且正在最大化时,或者
# 当 `letter` 赢家并且正在最小化时,返回-1
# 简而言之:如果表达式 `winner == letter` 的布尔值与
# `isMaximizing` 的布尔值相同,则返回1,否则返回-1
return 1 if (winner == letter) == isMaximizing else -1
一个小提醒:在分析你的代码时,我发现你使用了一个变量 player
,确定了人类玩家是否使用 "X" 和 "O",但该变量是一个数字 (0 或 1)。你可以通过将 "X" 和 "O" 也用于 player
值来避免一些代码重复。
另外,为什么不始终将 "X" 设定为最大化的玩家,无论是人类还是非人类玩家呢?当然,在 findBestMove
函数中需要更加小心一些,但其他的都会变得更简单。
英文:
The problem is in your getScore
function.
It only returns a non-zero value when the winner is letter
, but not when the winner is the other player. Yet that is the situation you will have: if there is a win, it will have been played by the player that moved last, while in your calls to getScore
, letter
is actually the player who is next to play. As a consequence, getScore
will return 0 even when the game has been won.
Here is a suggested correction of that function:
def getScore(board, letter, isMaximizing):
winner = getWinner(board)
# When the game is not over yet, or it is a tie, then return 0
if not winner or winner == 'Tie':
return 0
# When we get here, we know the game has been won.
# Return 1 when `letter` is the winner and is maximizing, or
# when `letter` is the loser and is minimizing
# Return -1 when `letter` is the loser and is maximizing, or
# when `letter` is the winner and is minimizing
# In short: if the expression `winner == letter` has the same boolean value as
# `isMaximizing` return 1, else return -1
return 1 if (winner == letter) == isMaximizing else -1
A small remark: When analysing your code I found it quite confusing that you used a variable player
which determines whether the human player plays with "X" and "O", but the variable is a number (0 or 1). You can avoid some code repetition by using "X" and "O" also for the player
value.
Also, why not make "X" always the maximizing player, no matter if it is the human or non-human player? Sure, you'd need a bit more care in the findBestMove
function, but all the rest would become simpler.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论