I'm trying to implement the minimax algorithm in my tic tac toe game. I watched several videos, analysed multiple programs with minimax algorithm and I think I do know how it works now. My program is working but it seems like the algorithm has no clue what he is doing. It outputs pads on the board but it doesn't block me or tries to win. Like it's random. It would be nice if someone could have a look at my minimax algorithm and tell what's wrong! It would also be nice to tell me whats wrong with my explanation and don't just downvote.
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: I do know why the "moves: " ouput is always the same, but that's not the point.