I tried to make a MiniMax AI using a tutorial,
The AI doesn't work and just goes on the bottom row and build up each time, reporting a column index of 0, 1, 2, 3, 4, 5, 6
and then for the next row and so on. The only thing I managed to find a lot different in the code output compared to mine and the tutorials working version was the depth of the minimax algorithm as it goes along. Where this was the depth of the working one as it recurred, and this was the depth of my one (it was longer than this but my console didn't keep all of it). I've tried a lot of things to get it working including reworking the board system to multiple lists inside a list and many other things like deleting some parts of the code and reworking scores but it doesn't seem to change.
My minimax function is:
def minimax(board, depth, alpha, beta, maximizingPlayer):
print (depth)
validLocations = getValidLocations(board)
isTerminal = isTerminalNode(board)
if depth == 0 or isTerminal:
if isTerminal:
if checkWin(board, computerDisc):
return (None, math.inf)
elif checkWin(board, playerDisc):
return (None, -math.inf)
else: # Game is over, no more spaces
return (None, 0)
else: # Depth is zero
# print ("THIS IS DEPTH 0")
return (None, scorePos(board, computerDisc))
if maximizingPlayer:
value = -math.inf
column = random.choice(validLocations)
for c in validLocations:
boardCopy = copy.deepcopy(board)
dropPiece(boardCopy, c, computerDisc)
newScore = minimax(boardCopy, depth-1, alpha, beta, False)[1]
if newScore > value:
value = newScore
column = c
alpha = max(alpha, value)
if alpha >= beta:
break
return (column, value)
else: # Minimizing player
value = math.inf
column = random.choice(validLocations)
for c in validLocations:
boardCopy = copy.deepcopy(board)
dropPiece(boardCopy, c, playerDisc)
newScore = minimax(boardCopy, depth-1, alpha, beta, True)[1]
if newScore < value:
value = newScore
column = c
beta = min(beta, value)
if alpha >= beta:
break
return (column, value)
And the tutorials function is:
def minimax(board, depth, alpha, beta, maximizingPlayer):
valid_locations = get_valid_locations(board)
is_terminal = is_terminal_node(board)
if depth == 0 or is_terminal:
if is_terminal:
if winning_move(board, AI_PIECE):
return (None, 100000000000000)
elif winning_move(board, PLAYER_PIECE):
return (None, -10000000000000)
else: # Game is over, no more valid moves
return (None, 0)
else: # Depth is zero
return (None, score_position(board, AI_PIECE))
if maximizingPlayer:
value = -math.inf
column = random.choice(valid_locations)
for col in valid_locations:
row = get_next_open_row(board, col)
b_copy = board.copy()
drop_piece(b_copy, row, col, AI_PIECE)
new_score = minimax(b_copy, depth-1, alpha, beta, False)[1]
if new_score > value:
value = new_score
column = col
alpha = max(alpha, value)
if alpha >= beta:
break
return column, value
else: # Minimizing player
value = math.inf
column = random.choice(valid_locations)
for col in valid_locations:
row = get_next_open_row(board, col)
b_copy = board.copy()
drop_piece(b_copy, row, col, PLAYER_PIECE)
new_score = minimax(b_copy, depth-1, alpha, beta, True)[1]
if new_score < value:
value = new_score
column = col
beta = min(beta, value)
if alpha >= beta:
break
return column, value
When mine didn't work I tried to make it as close as possible to the tutorials function but it still doesn't seem to work. What do I need to do so it works?
The full programs: Mine: https://repl.it/@MyloBishop/Connect-4 Tutorials: https://github.com/KeithGalli/Connect4-Python/blob/master/connect4_with_ai.py
The board is passed passed to your minimax function as a parameter node, but inside the function you use board.
Reference program: def minimax(board, depth, alpha, beta, maximizingPlayer):
Your program: def minimax(node, depth, alpha, beta, maximizingPlayer):
Your recursive minimax function is therefore not working as expected.
Edit: To fix this replace board inside minimax with node (don't change node in the function definition to board)
Edit 2: Also check the function scorePos - you have a hardcoded computerDisc instead of using the function argument.
Edit 3: Additionally, other helper functions like isBoardFull() and isSpaceFree() should operate on the board copy, not the global variable.