我是一名年轻的程序员,正在学习python,并努力实现一个AI(使用minimax(来玩TicTacToe。我开始在网上看教程,但教程是关于JavaScript的,因此无法解决我的问题。我也看了这个问题(tictactoe的Python minimax(,但它没有任何答案,而且实现与我的有很大不同。
编辑:您将在下面找到的代码是其中一个答案(@water_ghosts(建议的编辑。
编辑#2:我删除了可能的位置,因为人工智能应该选择一个自由字段,而不是从可能的位置中选择一个位置(这不会让它在实现最小最大值时变得那么聪明:(
现在,代码根本不会出现任何错误,并且运行正常,但有一件小事:人工智能总是选择下一个可用字段。例如,在我离开获胜的情况下,它选择了下一个自由位置,而不是阻止我的获胜选项。
如果你想知道dict在那里做什么:我只是想确保程序选择了最好的索引。。。
这是我的代码:
class TicTacToe:
def __init__(self):
self.board = [" ", " ", " ", " ", " ", " ", " ", " ", " "]
self.playerSymbol = ""
self.playerPosition = []
self.aiSymbol = ""
self.aiPosition = []
self.score = 0
self.winner = None
self.scoreBoard = {
self.playerSymbol: -1,
self.aiSymbol: 1,
"tie": 0
}
self.turn = 0
self.optimalMove = int()
def drawBoard(self):
print(self.board[0] + " | " + self.board[1] + " | " + self.board[2])
print("___" + "___" + "___")
print(self.board[3] + " | " + self.board[4] + " | " + self.board[5])
print("___" + "___" + "___")
print(self.board[6] + " | " + self.board[7] + " | " + self.board[8])
def choice(self):
answer = input("What do you want to play as? (type x or o) ")
if answer.upper() == "X":
self.playerSymbol = "X"
self.aiSymbol = "O"
else:
self.playerSymbol = "O"
self.aiSymbol = "X"
def won(self):
winningPositions = [{0, 1, 2}, {3, 4, 5}, {6, 7, 8}, {0, 4, 8}, {2, 4, 6}, {0, 3, 6}, {1, 4, 7}, {2, 5, 8}]
for position in winningPositions:
if position.issubset(self.playerPosition):
self.winner = self.playerSymbol
print("Player Wins :)")
return True
elif position.issubset(self.aiPosition):
self.winner = self.aiSymbol
print("AI wins :(")
return True
if self.board.count(" ") == 0:
self.winner = "tie"
print("Guess it's a draw")
return True
return False
def findOptimalPosition(self):
bestScore = float("-Infinity")
elements = {} # desperate times call for desperate measures
for i in range(9):
if self.board[i] == " ":
self.board[i] = self.aiSymbol # AI quasi made the move here
if self.minimax(True) > bestScore:
bestScore = self.score
elements[i] = bestScore
self.board[i] = " "
return max(elements, key=lambda k: elements[k])
def minimax(self, isMaximizing):
if self.winner is not None:
return self.scoreBoard[self.winner]
if isMaximizing:
bestScore = float("-Infinity")
for i in range(9):
if self.board[i] == " ":
self.board[i] = self.aiSymbol
bestScore = max(self.minimax(False), bestScore)
self.board[i] = " "
return bestScore
else:
bestScore = float("Infinity")
for i in range(9):
if self.board[i] == " ":
self.board[i] = self.playerSymbol
bestScore = min(self.minimax(True), bestScore)
self.board[i] = " "
return bestScore
def play(self):
self.choice()
while not self.won():
if self.turn % 2 == 0:
pos = int(input("Where would you like to play? (0-8) "))
self.playerPosition.append(pos)
self.board[pos] = self.playerSymbol
self.turn += 1
self.drawBoard()
else:
aiTurn = self.findOptimalPosition()
self.aiPosition.append(aiTurn)
self.board[aiTurn] = self.aiSymbol
self.turn += 1
print("n")
print("n")
self.drawBoard()
else:
print("Thanks for playing :)")
tictactoe = TicTacToe()
tictactoe.play()
我有java背景,不习惯这样做:(如有任何帮助,将不胜感激
我愿意接受改进代码和解决此问题的建议和方法。提前感谢并保持健康,Kristi
更改此部分,即使它没有进入if statement
内部,您的实现也会返回optimalMove
,并且此时不会分配optimalMove
,所以将return
放在内部。
if score > sampleScore:
sampleScore = score
optimalMove = i
return optimalMove
play()
中的optimalMove = 0
和findOptimalField()
中的optimalMove = i
声明了两个不同的变量,每个变量都是声明它的函数的局部变量。
如果希望多个函数可以访问同一个变量,可以使用global关键字,但这通常被认为是一种糟糕的做法。它可能会让你很难对代码进行推理(例如,var = x
是在创建一个新的局部变量还是覆盖全局变量的值?(,而且它不会阻止你在声明变量之前意外使用它。
由于您来自Java背景,您可以将其转换为一个类,以获得更像您期望的行为,从而消除对全局变量的需求:
class TicTacToe:
def __init__(self):
self.board = [" ", " ", " ", " ", " ", " ", " ", " ", " "]
self.playerSymbol = ""
self.playerPosition = []
self.aiSymbol = ""
self.aiPosition = []
self.score = 0
self.playerSymbol = None
self.aiSymbol = None
...
def drawBoard(self):
print(self.board[0] + " | " + self.board[1] + " | " + self.board[2])
...
def choice(self):
answer = input("What do you want to play as? (type x or o) ")
if answer.upper() == "X":
self.playerSymbol = "X"
self.aiSymbol = "O"
...
现在,每个方法都有一个指向当前实例的显式self
参数,您可以使用它来访问属于类实例而不是特定方法的任何变量。如果在变量之前不包含self.
,则该变量仍然是声明该变量的方法的本地变量。在这种情况下,drawBoard()
方法将无法访问choice()
中定义的answer
变量。
您可以在类的任何方法中创建新的self.
变量,但最佳做法是在__init__
构造函数方法中初始化所有变量,使用None
作为尚未具有值的变量的占位符。
我将此作为答案发布,以防将来有人遇到同样的问题:(
我遇到的主要问题(除了我糟糕的编程风格(是我忘记更新列表playerPosition和aiPosition的内容。您可以查看工作代码中的其余更改:
class TicTacToe:
def __init__(self):
self.board = [" ", " ", " ", " ", " ", " ", " ", " ", " "]
self.playerSymbol = ""
self.playerPosition = []
self.aiSymbol = ""
self.aiPosition = []
self.winner = None
self.scoreBoard = None
self.turn = 0
self.optimalMove = int()
def drawBoard(self):
print(self.board[0] + " | " + self.board[1] + " | " + self.board[2])
print("___" + "___" + "___")
print(self.board[3] + " | " + self.board[4] + " | " + self.board[5])
print("___" + "___" + "___")
print(self.board[6] + " | " + self.board[7] + " | " + self.board[8])
def choice(self):
answer = input("What do you want to play as? (type x or o) ")
if answer.upper() == "X":
self.playerSymbol = "X"
self.aiSymbol = "O"
else:
self.playerSymbol = "O"
self.aiSymbol = "X"
self.scoreBoard = {
self.playerSymbol: -1,
self.aiSymbol: 1,
"tie": 0
}
def availableMoves(self):
moves = []
for i in range(0, len(self.board)):
if self.board[i] == " ":
moves.append(i)
return moves
def won_print(self):
self.won()
if self.winner == self.aiSymbol:
print("AI wins :(")
exit(0)
elif self.winner == self.playerSymbol:
print("Player Wins :)")
exit(0)
elif self.winner == "tie":
print("Guess it's a draw")
exit(0)
def won(self):
winningPositions = [{0, 1, 2}, {3, 4, 5}, {6, 7, 8},
{0, 4, 8}, {2, 4, 6}, {0, 3, 6},
{1, 4, 7}, {2, 5, 8}]
for position in winningPositions:
if position.issubset(self.playerPosition):
self.winner = self.playerSymbol
return True
elif position.issubset(self.aiPosition):
self.winner = self.aiSymbol
return True
if self.board.count(" ") == 0:
self.winner = "tie"
return True
self.winner = None
return False
def set_i_ai(self, i):
self.aiPosition.append(i)
self.board[i] = self.aiSymbol
def set_clear_for_ai(self, i):
self.aiPosition.remove(i)
self.board[i] = " "
def set_i_player(self, i):
self.playerPosition.append(i)
self.board[i] = self.playerSymbol
def set_clear_for_player(self, i):
self.playerPosition.remove(i)
self.board[i] = " "
def findOptimalPosition(self):
bestScore = float("-Infinity")
elements = {} # desperate times call for desperate measures
for i in self.availableMoves():
self.set_i_ai(i)
score = self.minimax(False)
if score > bestScore:
bestScore = score
elements[i] = bestScore
self.set_clear_for_ai(i)
if bestScore == 1:
print("you messed up larry")
elif bestScore == 0:
print("hm")
else:
print("whoops i made a prog. error")
return max(elements, key=lambda k: elements[k])
def minimax(self, isMaximizing):
if self.won():
return self.scoreBoard[self.winner]
if isMaximizing:
bestScore = float("-Infinity")
for i in self.availableMoves():
self.set_i_ai(i)
bestScore = max(self.minimax(False), bestScore)
self.set_clear_for_ai(i)
return bestScore
else:
bestScore = float("Infinity")
for i in self.availableMoves():
self.set_i_player(i)
bestScore = min(self.minimax(True), bestScore)
self.set_clear_for_player(i)
return bestScore
def play(self):
self.choice()
while not self.won_print():
if self.turn % 2 == 0:
pos = int(input("Where would you like to play? (0-8) "))
self.playerPosition.append(pos)
self.board[pos] = self.playerSymbol
self.turn += 1
self.drawBoard()
else:
aiTurn = self.findOptimalPosition()
self.aiPosition.append(aiTurn)
self.board[aiTurn] = self.aiSymbol
self.turn += 1
print("n")
print("n")
self.drawBoard()
else:
print("Thanks for playing :)")
if __name__ == '__main__':
tictactoe = TicTacToe()
tictactoe.play()
但如前所述,代码可能会工作,但在逻辑和结构方面存在许多问题,因此不要直接复制粘贴:(