Mini-Max Algorithm in Artificial Intelligence

Last Updated : 3 Sep, 2026

Mini-Max algorithm is a decision-making algorithm used in AI for two-player, zero-sum, adversarial games. It selects the best move by assuming that both players play optimally: the maximizing player tries to maximize the score, while the minimizing player tries to minimize it.

Minimax is commonly used in games such as Tic-Tac-Toe, Chess and Checkers, where one player's gain represents the other player's loss.

Working

Minimax represents possible moves as a game tree. Each node represents a game state and each edge represents a possible move. The algorithm considers two types of players

Maximizing Player (MAX)

  • The maximizing player tries to obtain the highest utility value.
  • At a MAX node, Minimax selects the maximum value among all possible child states.

Minimizing Player (MIN)

  • The minimizing player tries to reduce the maximizing player's utility.
  • At a MIN node, Minimax selects the minimum value among all possible child states.
  • The algorithm assumes that both players make the best possible decisions.

Steps

  1. Generate the game tree: Identify the possible moves and resulting game states from the current position.
  2. Reach terminal states: Continue exploring the game tree until a win, loss, draw or chosen search depth is reached.
  3. Assign utility values: Give terminal states values based on their outcomes. For example, a win for MAX can be +1, a draw 0 and a win for MIN -1.
  4. Propagate values upward: At MIN nodes, select the minimum child value. At MAX nodes, select the maximum child value.
  5. Select the best move: MAX chooses the move with the highest resulting utility value.

Minimax Formula

For a maximizing state, the Minimax value is:

V(s) = \max_{a \in A(s)} V(\mathrm{Result}(s,a))

For a minimizing state:

V(s) = \min_{a \in A(s)} V(\mathrm{Result}(s,a))

Where:

  • V(s) is the utility value of state s.
  • A(s) is the set of possible actions from state s.
  • Result(s, a) is the state produced by taking action a.

Thus, MAX chooses the largest value, while MIN chooses the smallest value.

Terminal States

Terminal states represent the end of a game or a point where the search stops. Their utility values are determined by the game.

For a simple two-player game:

  • MAX wins:+1
  • Draw:0
  • MIN wins:-1

In a depth-limited Minimax implementation, if the search reaches the depth limit before the game ends, a heuristic evaluation function can be used instead of a terminal utility value.

Example: Tic-Tac-Toe Using Minimax

Tic-Tac-Toe is a simple example for understanding Minimax because the complete game tree is small enough to search.

In this implementation:

  • AI (X) is the maximizing player.
  • Human (O) is the minimizing player.
  • A win for X has a score of +1.
  • A win for O has a score of -1.
  • A draw has a score of 0.

For every possible AI move, Minimax simulates the opponent's possible responses and continues recursively until the game reaches a terminal state. The AI then chooses the move with the highest score.

Python Implementation

The following program implements Tic-Tac-Toe using plain Minimax. Here minimax() function recursively evaluates all possible moves.

  • When maximizing_player is True, the AI places X and selects the maximum score.
  • When maximizing_player is False, the opponent places O and selects the minimum score.
  • The recursion stops when a player wins or the board is full.
  • best_move() evaluates every legal AI move and selects the one with the highest Minimax score.
Python
import math


def print_board(board):
    for row in board:
        print(" | ".join(row))
        print("---------")
    print()


def check_winner(board):
    # Check rows and columns
    for i in range(3):
        if board[i][0] == board[i][1] == board[i][2] != ' ':
            return board[i][0]

        if board[0][i] == board[1][i] == board[2][i] != ' ':
            return board[0][i]

    # Check diagonals
    if board[0][0] == board[1][1] == board[2][2] != ' ':
        return board[0][0]

    if board[0][2] == board[1][1] == board[2][0] != ' ':
        return board[0][2]

    return None


def is_full(board):
    return all(cell != ' ' for row in board for cell in row)


def minimax(board, maximizingPlayer):
    winner = check_winner(board)

    # Terminal states
    if winner == 'X':
        return 1
    elif winner == 'O':
        return -1
    elif is_full(board):
        return 0

    # Maximizing player (AI: X)
    if maximizingPlayer:
        best_score = -math.inf

        for i in range(3):
            for j in range(3):
                if board[i][j] == ' ':
                    board[i][j] = 'X'
                    print(f"Testing AI move at ({i}, {j})")

                    score = minimax(board, False)

                    board[i][j] = ' '
                    best_score = max(best_score, score)

        return best_score

    # Minimizing player (Human: O)
    else:
        best_score = math.inf

        for i in range(3):
            for j in range(3):
                if board[i][j] == ' ':
                    board[i][j] = 'O'
                    print(f"Testing Player move at ({i}, {j})")

                    score = minimax(board, True)

                    board[i][j] = ' '
                    best_score = min(best_score, score)

        return best_score


def best_move(board):
    best_score = -math.inf
    move = None

    for i in range(3):
        for j in range(3):
            if board[i][j] == ' ':
                board[i][j] = 'X'

                print(f"Evaluating move at ({i}, {j})")
                score = minimax(board, False)

                board[i][j] = ' '

                if score > best_score:
                    best_score = score
                    move = (i, j)

    print(f"AI chooses move at {move} with score {best_score}")
    return move


def play_game():
    board = [[' ' for _ in range(3)] for _ in range(3)]

    print("Initial board:")
    print_board(board)

    while True:
        # Player's move
        try:
            player_move = tuple(
                map(int, input("Enter your move (row and column): ").split())
            )

            if len(player_move) != 2:
                raise ValueError

            row, col = player_move

            if row not in range(3) or col not in range(3):
                raise ValueError

            if board[row][col] == ' ':
                board[row][col] = 'O'
            else:
                print("Invalid move! Try again.")
                continue

        except (ValueError, IndexError):
            print("Invalid input! Enter row and column between 0 and 2.")
            continue

        print("Board after player's move:")
        print_board(board)

        if check_winner(board) or is_full(board):
            break

        # AI's move
        ai_move = best_move(board)

        if ai_move:
            board[ai_move[0]][ai_move[1]] = 'X'

        print("Board after AI's move:")
        print_board(board)

        if check_winner(board) or is_full(board):
            break

    winner = check_winner(board)

    if winner:
        print(f"Winner: {winner}")
    else:
        print("It's a tie!")


# Play the game
play_game()

Output:

Evaluating move at (1, 0)
Testing Player move at (1, 2)
Evaluating move at (1, 2)
Testing Player move at (1, 0)
AI chooses move at (1, 2) with score 0
Board after AI's move:
O | X | O
---------
| X | X
---------
X | O | O
---------
Enter your move (row and column): 1 0
Board after player's move:
O | X | O
---------
O | X | X
---------
X | O | O
---------
It's a tie!

Note: The complete output is lengthy because the Minimax algorithm evaluates multiple possible moves. The following shows a portion of the output, including the final moves and result.

Minimax Complexity

  • If b is the average branching factor and d is the search depth, the time complexity of a basic depth-first Minimax search is approximately: O(b^d)
  • Its space complexity for a depth-first implementation is approximately: O(bd)
  • The exponential growth in time complexity is one of the main reasons practical game-playing systems use techniques such as alpha-beta pruning, heuristic evaluation and other search optimizations.

Advantages of Minimax

  1. Optimal play for suitable games: When the complete game tree can be searched and both players play optimally, Minimax finds the best move.
  2. Works well for deterministic games: It is effective when the game has clearly defined states, actions and outcomes.
  3. Simple decision model: Its MAX-MIN structure makes the decision process relatively easy to understand and implement.
  4. General-purpose game-playing approach: The same basic strategy can be applied to many two-player adversarial games.

Limitations of Minimax

  1. High computational cost: The number of game states grows rapidly as the branching factor and search depth increase.
  2. Limited scalability: Searching the complete game tree is impractical for games with very large state spaces, such as Go.
  3. Requires a game model: Basic Minimax assumes that the possible actions and resulting states can be determined. It is therefore not directly suited to environments with significant randomness or incomplete information.
  4. Depth-limited search may be inaccurate: When the complete tree cannot be searched, Minimax relies on a heuristic evaluation function, which may not perfectly represent the actual value of a position.

Minimax vs. Monte Carlo Tree Search (MCTS)

FeatureMinimaxMCTS
Search approachSystematically explores the game treeUses simulations to explore promising moves
Best suited forGames with manageable search spacesGames with very large search spaces
ExampleChess, Tic-Tac-ToeGo and other complex games
Comment

Explore