Tic Tac Toe 的 Minimax 算法 [python]
Minimax Alogrithm for TicTacToe [python]
我正在尝试在我的井字游戏中实现极小极大算法。我看了几个视频,用 minimax 算法分析了多个程序,我想我现在知道它是如何工作的了。我的程序正在运行,但算法似乎不知道他在做什么。它在板上输出焊盘,但不会阻止我或试图获胜。就像它是随机的。如果有人可以看看我的 minimax 算法并告诉我哪里出了问题,那就太好了!也很高兴告诉我我的解释有什么问题,不要只是投反对票。
from copy import deepcopy
class Board:
def __init__(self, board=None):
self.winning_combos = (
[0, 1, 2], [3, 4, 5], [6, 7, 8],
[0, 3, 6], [1, 4, 7], [2, 5, 8],
[0, 4, 8], [2, 4, 6])
if board is not None:
self.board = board
else:
self.board = [None for i in range(9)]
def check_combos(self):
""" checks every combo if its used """
for symbol in ['X', 'O']:
for win_comb in self.winning_combos:
sum = 0
for field in win_comb:
if self.board[field] == symbol:
sum += 1
if sum == 3:
return symbol
return None
def complete(self):
""" check if the game is complete, caused by win or draw """
cc = self.check_combos()
if cc is not None:
return cc
if len(self.empty_pads()) <= 0:
return "DRAW"
return False
def show(self):
""" print board """
print(str(self.board[0:3]) + "\n" +
str(self.board[3:6]) + "\n" +
str(self.board[6:9]))
def empty_pads(self):
""" returns list with indexes of every unused/empty field/pad """
list = []
for pad in range(len(self.board)):
if self.board[pad] is None:
list.append(pad)
return list
def set(self, position, player):
""" sets the players symbol on the given position """
self.board[position] = player
def copy(self):
return deepcopy(self)
def get_enemy_player(player):
if player == 'X':
return 'O'
return 'X'
def get_player_value(player):
""" X = max, O = min """
if player == 'X':
return 1
else:
return -1
def get_player_by_value(value):
if value == -1:
return "O"
elif value == 1:
return "X"
else:
return "NONE"
def max_v(node):
if node.depth == 0 or node.board.complete():
return get_player_value(node.board.complete())
bestVal = -100
for child in node.children:
v = minimax(child)
if v >= bestVal:
bestVal = v
node.bestmove = child.move
return bestVal
def min_v(node):
if node.depth == 0 or node.board.complete():
return get_player_value(node.board.complete())
bestVal = 100
for child in node.children:
v = minimax(child)
if v <= bestVal:
bestVal = v
node.bestmove = child.move
return bestVal
def minimax(node):
if node.depth == 0 or node.board.complete():
return get_player_value(node.board.complete())
if get_player_value(node.player) == 1:
return max_v(node)
elif get_player_value(node.player) == -1:
return min_v(node)
class Node:
def __init__(self, depth, player, board, pad):
self.depth = depth
self.player = player
self.board = board
self.move = pad
self.board.set(pad, self.player)
self.bestmove = int
self.children = []
self.CreateChildren()
def CreateChildren(self):
if self.depth > 0 and not self.board.complete():
for index in self.board.empty_pads():
board = self.board.copy()
self.children.append(Node(self.depth - 1, get_enemy_player(self.player), board, index))
if __name__ == "__main__":
board = Board()
board.show()
while not board.complete():
player = 'X'
player_move = int(input('Move: ')) - 1
if player_move not in board.empty_pads():
continue
board.set(player_move, player)
board.show()
if board.complete():
break
player = get_enemy_player(player)
node = Node(9, player, board.copy(), player_move)
minmax = minimax(node)
print(node.bestmove+1)
for child in node.children:
print("move: " + str(child.move + 1) + " --> " + get_player_by_value(minmax) + " win")
board.set(node.bestmove, player)
board.show()
print(board.complete())
PS:我知道为什么 "moves: " 输出总是一样,但这不是重点。
我发现您的程序存在多个问题。
至于你的实际问题:你的程序表现得好像计算机不区分输和平。在您的代码中,我找不到您为平局分配 0 的值,而您似乎为赢分配了 1,为输分配了 -1。您的代码应该宁愿平局也不愿输,但它认为没有区别。这就是它看起来 "Like it's random" 的原因。我这里的分析可能不对,但是下面的问题解释了为什么我很难说。
您的风格应该改进,以提高可读性和易维护性并避免错误。你的代码对我来说太难理解了,部分原因是...
您的评论太少,除了您以外,其他人无法理解代码的作用。过几个月你就记不住了,所以在代码里记下来。
你的空行太多,违反了PEP8,让屏幕上的代码更难看清。
你没有足够认真地对待你的输出,正如你所说 "ou[t]put is always the same, but that's not the point." 任何人,包括你,都很难说出没有良好输出的代码中发生了什么。处理它,并添加一些临时打印或日志记录语句,告诉您更多关于内部发生的事情。
您的一些例程 return 不同类型的值。 complete()
函数有时 return 是字符串 "DRAW"
,有时是布尔值 False
,有时是来自 self.check_combos()
的值,无论是什么类型。您的例程 max_v()
和 min_v()
有时 return 来自 get_player_value()
的字符串值,有时来自变量 bestVal
.
的整数
我正在尝试在我的井字游戏中实现极小极大算法。我看了几个视频,用 minimax 算法分析了多个程序,我想我现在知道它是如何工作的了。我的程序正在运行,但算法似乎不知道他在做什么。它在板上输出焊盘,但不会阻止我或试图获胜。就像它是随机的。如果有人可以看看我的 minimax 算法并告诉我哪里出了问题,那就太好了!也很高兴告诉我我的解释有什么问题,不要只是投反对票。
from copy import deepcopy
class Board:
def __init__(self, board=None):
self.winning_combos = (
[0, 1, 2], [3, 4, 5], [6, 7, 8],
[0, 3, 6], [1, 4, 7], [2, 5, 8],
[0, 4, 8], [2, 4, 6])
if board is not None:
self.board = board
else:
self.board = [None for i in range(9)]
def check_combos(self):
""" checks every combo if its used """
for symbol in ['X', 'O']:
for win_comb in self.winning_combos:
sum = 0
for field in win_comb:
if self.board[field] == symbol:
sum += 1
if sum == 3:
return symbol
return None
def complete(self):
""" check if the game is complete, caused by win or draw """
cc = self.check_combos()
if cc is not None:
return cc
if len(self.empty_pads()) <= 0:
return "DRAW"
return False
def show(self):
""" print board """
print(str(self.board[0:3]) + "\n" +
str(self.board[3:6]) + "\n" +
str(self.board[6:9]))
def empty_pads(self):
""" returns list with indexes of every unused/empty field/pad """
list = []
for pad in range(len(self.board)):
if self.board[pad] is None:
list.append(pad)
return list
def set(self, position, player):
""" sets the players symbol on the given position """
self.board[position] = player
def copy(self):
return deepcopy(self)
def get_enemy_player(player):
if player == 'X':
return 'O'
return 'X'
def get_player_value(player):
""" X = max, O = min """
if player == 'X':
return 1
else:
return -1
def get_player_by_value(value):
if value == -1:
return "O"
elif value == 1:
return "X"
else:
return "NONE"
def max_v(node):
if node.depth == 0 or node.board.complete():
return get_player_value(node.board.complete())
bestVal = -100
for child in node.children:
v = minimax(child)
if v >= bestVal:
bestVal = v
node.bestmove = child.move
return bestVal
def min_v(node):
if node.depth == 0 or node.board.complete():
return get_player_value(node.board.complete())
bestVal = 100
for child in node.children:
v = minimax(child)
if v <= bestVal:
bestVal = v
node.bestmove = child.move
return bestVal
def minimax(node):
if node.depth == 0 or node.board.complete():
return get_player_value(node.board.complete())
if get_player_value(node.player) == 1:
return max_v(node)
elif get_player_value(node.player) == -1:
return min_v(node)
class Node:
def __init__(self, depth, player, board, pad):
self.depth = depth
self.player = player
self.board = board
self.move = pad
self.board.set(pad, self.player)
self.bestmove = int
self.children = []
self.CreateChildren()
def CreateChildren(self):
if self.depth > 0 and not self.board.complete():
for index in self.board.empty_pads():
board = self.board.copy()
self.children.append(Node(self.depth - 1, get_enemy_player(self.player), board, index))
if __name__ == "__main__":
board = Board()
board.show()
while not board.complete():
player = 'X'
player_move = int(input('Move: ')) - 1
if player_move not in board.empty_pads():
continue
board.set(player_move, player)
board.show()
if board.complete():
break
player = get_enemy_player(player)
node = Node(9, player, board.copy(), player_move)
minmax = minimax(node)
print(node.bestmove+1)
for child in node.children:
print("move: " + str(child.move + 1) + " --> " + get_player_by_value(minmax) + " win")
board.set(node.bestmove, player)
board.show()
print(board.complete())
PS:我知道为什么 "moves: " 输出总是一样,但这不是重点。
我发现您的程序存在多个问题。
至于你的实际问题:你的程序表现得好像计算机不区分输和平。在您的代码中,我找不到您为平局分配 0 的值,而您似乎为赢分配了 1,为输分配了 -1。您的代码应该宁愿平局也不愿输,但它认为没有区别。这就是它看起来 "Like it's random" 的原因。我这里的分析可能不对,但是下面的问题解释了为什么我很难说。
您的风格应该改进,以提高可读性和易维护性并避免错误。你的代码对我来说太难理解了,部分原因是...
您的评论太少,除了您以外,其他人无法理解代码的作用。过几个月你就记不住了,所以在代码里记下来。
你的空行太多,违反了PEP8,让屏幕上的代码更难看清。
你没有足够认真地对待你的输出,正如你所说 "ou[t]put is always the same, but that's not the point." 任何人,包括你,都很难说出没有良好输出的代码中发生了什么。处理它,并添加一些临时打印或日志记录语句,告诉您更多关于内部发生的事情。
您的一些例程 return 不同类型的值。
complete()
函数有时 return 是字符串"DRAW"
,有时是布尔值False
,有时是来自self.check_combos()
的值,无论是什么类型。您的例程max_v()
和min_v()
有时 return 来自get_player_value()
的字符串值,有时来自变量bestVal
. 的整数