[英]Minimax algorithm for Tic Tac Toe Python
我有点理解minimax算法是如何为Tic Tac Toe python工作的,但我不知道如何在Python中实际编码...这是我到目前为止所做的:
from copy import deepcopy
class TicTacToeBrain :
def __init__(self, player = "x") :
self._squares = {}
self._copySquares = {}
self._winningCombos = (
[0, 1, 2], [3, 4, 5], [6, 7, 8],
[0, 3, 6], [1, 4, 7], [2, 5, 8],
[0, 4, 8], [2, 4, 6])
def createBoard(self) :
for i in range(9) :
self._squares[i] = None
print(self._squares)
def showBoard(self) :
print(self._squares[0], self._squares[1], self._squares[2])
print(self._squares[3], self._squares[4], self._squares[5])
print(self._squares[6], self._squares[7], self._squares[8])
def getAvailableMoves(self) :
self._availableMoves = []
for i in range(9) :
if self._squares[i] == None :
self._availableMoves.append(i)
return self._availableMoves
def makeMove(self, position, player) :
self._squares[position] = player
self.showBoard()
def complete(self) :
if None not in self._squares.values() :
return True
if self.getWinner() != None :
return True
return False
def getWinner(self) :
for player in ("x", "o") :
for combos in self._winningCombos :
if self._squares[combos[0]] == player and self._squares[combos[1]] == player and self._squares[combos[2]] == player :
return player
if None not in self._squares.values() :
return "tie"
return None
def getEnemyPlayer(self, player) :
if player == "x" :
return "o"
return "x"
def minimax(self, node, player, depth = 0, first = True) :
if first :
best = 0
self._copySquares = deepcopy(self._squares)
if node.complete() :
if node.getWinner() == "x" :
self._squares = self._copySquares
return -1 - depth
elif node.getWinner() == "tie" :
self._squares = self._copySquares
return 0
elif node.getWinner() == "o" :
self._squares = self._copySquares
return 1 + depth
best = None
for move in node.getAvailableMoves() :
depth += 1
node.makeMove(move, player)
print()
val = self.minimax(node, node.getEnemyPlayer(player), depth, first = False)
print(val)
if player == "o" :
if val > best :
best = val
else :
if val < best :
best = val
return best
print()
print()
def printCopy(self) :
print(self._copySquares)
但是,它从来没有打印出所有的场景....有人请帮助! 这是周一的一个项目。
一些问题:
执行在第一次迭代时return
for
循环并return
:这是不成熟的,因为你永远不会测试任何其他可用的移动。 该return
应该在循环之后发生。
在for
循环的每次迭代中增加深度值是错误的。 相反,将depth+1
传递给递归调用,这样当您从那里返回时,您将继续处于相同的深度。
在递归调用之前完成的移动必须在它之后立即收回,否则for
循环的下一次迭代将不会从相同的位置开始。
best
的值需要在每次调用minimax方法时初始化,而不仅仅是在递归树的顶部。 此初始值不应为0,因为当前用户的最佳值可能低于0.因此,您需要将其初始化为极差值。
minimax方法不返回最佳移动,仅返回评估值。 由于该方法的整个目的是告诉你应该播放哪个动作,你需要两个。 因此,让方法返回一个包含两个值的元组:评估值和生成该值的移动。
一些非关键问题:
因为你想要延迟不可避免的损失,或者加速强制获胜,当玩家获胜时计算价值的公式应该越接近0,就越接近它。 因此,该公式需要改变。
由于您应该通过收回移动来恢复电路板,因此无需使用复制板和复制方块。 如果所有编码都很好,则在minimax方法的最高调用完成后,该板应该处于与该调用之前完全相同的状态。
如果不对空方块使用None
,而对单个字符,如“。”,则打印板会打印得更好。 因此,无论您在哪里引用空方格值,都要放置该字符。
你有print()
在这里和那里分开输出。 将一个放在showBoard
方法中,其余的代码可以不用它们。
鉴于以上几点,您不需要node
也不需要minimax
方法的first
参数。
这是一个评论,更正的版本。 我把原来的线留在原处,但在需要的地方将它们评论出来。
# *** not needed:
# from copy import deepcopy
class TicTacToeBrain :
def __init__(self, player = "x") :
self._squares = {}
self._copySquares = {}
self._winningCombos = (
[0, 1, 2], [3, 4, 5], [6, 7, 8],
[0, 3, 6], [1, 4, 7], [2, 5, 8],
[0, 4, 8], [2, 4, 6])
def createBoard(self) :
for i in range(9) :
# *** use a single character, ... easier to print
self._squares[i] = "."
print(self._squares)
def showBoard(self) :
# *** add empty line here, instead of in minimax
print ()
print(self._squares[0], self._squares[1], self._squares[2])
print(self._squares[3], self._squares[4], self._squares[5])
print(self._squares[6], self._squares[7], self._squares[8])
def getAvailableMoves(self) :
self._availableMoves = []
for i in range(9) :
# *** see above
if self._squares[i] == "." :
self._availableMoves.append(i)
return self._availableMoves
def makeMove(self, position, player) :
self._squares[position] = player
self.showBoard()
def complete(self) :
# *** see above
if "." not in self._squares.values() :
return True
if self.getWinner() != None :
return True
return False
def getWinner(self) :
for player in ("x", "o") :
for combos in self._winningCombos :
if self._squares[combos[0]] == player and self._squares[combos[1]] == player and self._squares[combos[2]] == player :
return player
# *** see above
if "." not in self._squares.values() :
return "tie"
return None
def getEnemyPlayer(self, player) :
if player == "x" :
return "o"
return "x"
# *** no need for `node` argument, nor `first`
# *** use `self` instead of `node` in all this method
def minimax(self, player, depth = 0) :
# *** not needed
# if first :
# best = 0
# *** not needed
# self._copySquares = deepcopy(self._squares)
# *** always start with initilisation of `best`, but with worst possible value
# for this player
if player == "o":
best = -10
else:
best = 10
if self.complete() :
if self.getWinner() == "x" :
# *** don't do this, you may still need the position to try other moves
# self._squares = self._copySquares
# *** value should be closer to zero for greater depth!
# *** expect tuple return value
return -10 + depth, None
elif self.getWinner() == "tie" :
# self._squares = self._copySquares
# *** expect tuple return value
return 0, None
elif self.getWinner() == "o" :
# self._squares = self._copySquares
# *** value should be closer to zero for greater depth!
# *** expect tuple return value
return 10 - depth, None
# *** Execution can never get here
# best = None
for move in self.getAvailableMoves() :
# *** don't increase depth in each iteration, instead pass depth+1 to
# the recursive call
# depth += 1
self.makeMove(move, player)
# *** pass depth+1, no need for passing `node` nor `first`.
# *** expect tuple return value
val, _ = self.minimax(self.getEnemyPlayer(player), depth+1)
print(val)
# *** undo last move
self.makeMove(move, ".")
if player == "o" :
if val > best :
# *** Also keep track of the actual move
best, bestMove = val, move
else :
if val < best :
# *** Also keep track of the actual move
best, bestMove = val, move
# *** don't interrupt the loop here!
# return best
# *** this is dead code:
# print()
# print()
# *** Also keep track of the actual move
return best, bestMove
def printCopy(self) :
print(self._copySquares)
以下是如何使用该类的示例:
game = TicTacToeBrain()
game.createBoard()
game.makeMove(4, "o")
game.makeMove(3, "x")
val, bestMove = game.minimax("o")
print('best move', bestMove) # --> 0 is a winning move.
看它在eval.in上运行......等待它。
我不会为此提供代码,但您可以:
在self.player
跟踪它的转弯。 这样你就不必将玩家作为参数传递给minimax,这样可以避免错误。 它也使构造函数参数有用 - 目前你不用它做任何事情。
添加方法bestMove
这就叫minimax
,但将只返回最理想的做法,而不是价值。 这将更容易管理。
使用alpha-beta修剪,以便在显然无法改善递归树中已经实现的值时停止评估其他移动。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.