I have created a minimax function with alpha beta pruning that I call with iterative deepening. The problem is that when timer is done the function keeps running until it finishes on the depth it started with before the timer ran out.
What I want: When timer runs out the minimax function should quit and either return none (I keep best move outside of minimax, see code for minimax call below), or return the previously calculated best move. I just can't seem to figure out how to implement that in the minimax function, everything I tried results in it still completing the depth it is currently on.
Minimax function:
def minimax(gamestate, depth, alpha, beta, maximizing_player):
if depth == 0 or gamestate.is_check_mate or gamestate.is_stale_mate:
return None, evaluate(gamestate)
gamestate.is_white_turn = not maximizing_player
children = gamestate.get_valid_moves()
best_move = children[0]
if maximizing_player:
max_eval = -math.inf
for child in children:
board_copy = copy.deepcopy(gamestate)
board_copy.make_move(child)
current_eval = ai_minimax(board_copy, depth - 1, alpha, beta, False)[1]
if current_eval > max_eval:
max_eval = current_eval
best_move = child
alpha = max(alpha, current_eval)
if beta <= alpha:
break
return best_move, max_eval
else:
min_eval = math.inf
for child in children:
board_copy = copy.deepcopy(gamestate)
board_copy.make_move(child)
current_eval = ai_minimax(board_copy, depth - 1, alpha, beta, True)[1]
if current_eval < min_eval:
min_eval = current_eval
best_move = child
beta = min(beta, current_eval)
if beta <= alpha:
break
return best_move, min_eval
Function call with iterative deepening:
for depth in range(1, max_search_depth):
time_start = time.time()
move, evaluation = minimax(gamestate, depth, alpha, beta, maximizing_player)
time_end = time.time()
timer = time_end - time_start
if timer > max_search_time:
break
I often solve this kind of problem using a custom
Timeout
class.You can run your recursive function inside a
with statement
like this: