We’re now ready to dive in and write our minimax() function. The result of this function will be the “value” of the best possible move. In other words, if the function returns a 1, that means a move exists that guarantees that "X" will win. If the function returns a -1, that means that there’s nothing that "X" can do to prevent "O" from winning. If the function returns a 0, then the best "X" can do is force a tie (assuming "O" doesn’t make a mistake).

Our minimax() function has two parameters. The first is the game state that we’re interested in finding the best move. When the minimax() function first gets called, this parameter is the current state of the game. We’re asking “what is the best move for the current player right now?”

The second parameter is a boolean named is_maximizing representing whose turn it is. If is_maximizing is True, then we know we’re working with the maximizing player. This means when we’re picking the “best” move from the list of moves, we’ll pick the move with the highest value. If is_maximizing is False, then we’re the minimizing player and want to pick the minimum value.

Let’s start writing our minimax() function.



We’ve started the minimax() function for you and included the base case we wrote a few exercises ago.

We now need to define what should happen if the node isn’t a leaf.

We’ll want to set up some variables that are different depending on whether is_maximizing is True or False.

Below the base case, write an if statement to check if is_maximizing is True.

Inside the if statement, create a variable named best_value. Since we’re working with the maximizing player right now, this is the variable that will keep track of the highest possible value from all of the potential moves.

Right now, we haven’t looked at any moves, so we should start best_value at something lower than the lowest possible value — -float("Inf").

Write an else statement. Inside this else statement we’ll be setting up variables for the minimizing player. In this case, best_value should start at float("Inf").

Return best_value after the else statement.


We now want to loop through all of the possible moves of input_board before the return statement. We’re looking to find the best possible move.

Remember, you can get all of the possible moves by calling available_moves() using input_board as a parameter.

After the else statement, but before you return best_value, loop through all of the possible moves and print each move.

Let’s call our function to see these print statements. Outside of your function definition, call minimax() using the parameters x_winning (the board we’re using) and True (we’re calling it as the maximizing player).


Delete the print statements for move. Rather than just printing the move in this for loop, let’s create a copy of the game board and make the move!

Inside the for loop, create a deepcopy of input_board named new_board.

Apply the move to new_board by calling the select_space() function. select_space() takes three parameters.

  • The board you want to use (new_board)
  • The space you’re selecting (the move from the for loop)
  • The symbol you’re filling the space in with. This is different depending on whether we’re the maximizing or minimizing player. In your if and else statements, create a variable named symbol. symbol should be "X" if we’re the maximizing player and "O" if we’re the minimizing player. Use symbol as the third parameter of select_space().

To help us check if you accurately made the move, return new_board outside the for loop (instead of returning best_move). We’ll fix that return statement soon!

Sign up to start coding

Mini Info Outline Icon
By signing up for Codecademy, you agree to Codecademy's Terms of Service & Privacy Policy.

Or sign up using:

Already have an account?