I’m working on a tic-tac-toe AI project and my computer player is making terrible moves. It keeps choosing the same corner position instead of making smart strategic decisions. After adding some debug prints, I noticed that my tracking variables best_score and current_val aren’t updating properly. They stay stuck at their initial values of negative infinity and positive infinity. I can’t figure out why they’re not changing during the search process. Here’s my implementation:
def minimax_decision(game_state):
"""
Finds the best move for the active player
"""
if game_over(game_state):
return None
current_player = get_player(game_state)
available_moves = get_actions(game_state)
if current_player == 'X':
best_score = -math.inf
best_move = available_moves[0]
for move in available_moves:
current_val = minimize(apply_action(game_state, move))
if current_val > best_score:
best_score = current_val
best_move = move
return best_move
else:
best_score = math.inf
best_move = available_moves[0]
for move in available_moves:
current_val = maximize(apply_action(game_state, move))
if current_val < best_score:
best_score = current_val
best_move = move
return best_move
def maximize(game_state):
"""
Returns maximum value from possible moves
"""
value = -math.inf
for move in get_actions(game_state):
value = max(value, minimize(apply_action(game_state, move)))
return value
def minimize(game_state):
"""
Returns minimum value from possible moves
"""
value = math.inf
for move in get_actions(game_state):
value = min(value, maximize(apply_action(game_state, move)))
return value
yep, sounds like infinite recursion. your maximize and minimize functions are missing terminal conditions. right now they just loop endlessly. make sure to add a game state check at the start of each function so your tracking variables can actually get updated.
Your maximize and minimize functions are missing the terminal condition check. They need to check if the game’s over before making recursive calls. Without this, they just call each other forever and never return actual values.
I hit this same issue implementing minimax for connect four last year. Fix it by adding a game-over check at the start of both functions that returns the utility value. For tic-tac-toe, that’s usually +1 for X win, -1 for O win, and 0 for tie.
Once you add the base case, your tracking variables will work properly since the recursive calls will actually return real scores instead of hanging. Then your AI can compare move values and make smart decisions instead of just picking the first available move.
Your minimax functions don’t have a base case - they’ll just recurse forever without checking if the game’s over.
Both maximize and minimize need to check game_over(game_state) first and return the actual score before making any recursive calls. Right now they just keep calling each other until you hit recursion limits.
Add this at the start of both functions:
if game_over(game_state):
return evaluate_score(game_state) # return actual win/loss/tie value
That’s why your tracking variables never update - the recursive calls never return anything meaningful.
Honestly though, debugging game AI is a pain. I’ve started automating these repetitive coding tasks instead of manually fixing every edge case. You can set up workflows that automatically test different minimax implementations against each other and track which versions work better.
Latenode makes this really easy - you can create automated testing pipelines that run your AI against various scenarios and get instant performance feedback. Way more efficient than manually debugging recursive functions.