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
- Generate the game tree: Identify the possible moves and resulting game states from the current position.
- Reach terminal states: Continue exploring the game tree until a win, loss, draw or chosen search depth is reached.
- Assign utility values: Give terminal states values based on their outcomes. For example, a win for MAX can be
+1, a draw0and a win for MIN-1. - Propagate values upward: At MIN nodes, select the minimum child value. At MAX nodes, select the maximum child value.
- 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.
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
- Optimal play for suitable games: When the complete game tree can be searched and both players play optimally, Minimax finds the best move.
- Works well for deterministic games: It is effective when the game has clearly defined states, actions and outcomes.
- Simple decision model: Its MAX-MIN structure makes the decision process relatively easy to understand and implement.
- General-purpose game-playing approach: The same basic strategy can be applied to many two-player adversarial games.
Limitations of Minimax
- High computational cost: The number of game states grows rapidly as the branching factor and search depth increase.
- Limited scalability: Searching the complete game tree is impractical for games with very large state spaces, such as Go.
- 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.
- 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)
| Feature | Minimax | MCTS |
|---|---|---|
| Search approach | Systematically explores the game tree | Uses simulations to explore promising moves |
| Best suited for | Games with manageable search spaces | Games with very large search spaces |
| Example | Chess, Tic-Tac-Toe | Go and other complex games |