Adversarial search addresses two-player zero-sum games where an agent (MAX) tries to maximize its utility while an opponent (MIN) tries to minimize it, with both assumed to play optimally. The Minimax algorithm exhaustively evaluates all possible game states to determine the optimal move. Alpha-Beta Pruning dramatically improves efficiency by eliminating branches that cannot affect the final decision, without changing the result. Monte Carlo Tree Search (MCTS) offers a sampling-based alternative for games where exact tree search is intractable. These topics appear every year in GATE DA.
Game formulation
A two-player zero-sum game is defined by: (1) an initial state, (2) a terminal test indicating when the game is over, (3) utility/payoff values at terminal states (positive = good for MAX, negative = good for MIN), (4) an actions function, and (5) a transition model. The game tree is the complete search tree from the initial state to all terminal states.
| Term | Meaning |
|---|---|
| MAX player | The AI agent — wants to maximize the terminal utility |
| MIN player | The opponent — wants to minimize the utility (maximize their own payoff in zero-sum) |
| Ply | One half-move — a move by MAX or MIN (a full move = 2 plies) |
| Utility/Payoff | Numerical value of a terminal state: e.g., +1 (win), 0 (draw), −1 (loss) in chess |
| Game tree | Full tree of all possible play sequences to terminal states |
Minimax Algorithm
Minimax performs a complete depth-first traversal of the game tree. At MAX nodes, it takes the maximum child value; at MIN nodes, the minimum. The value of the root gives the guaranteed outcome with optimal play by both sides.
Recursive definition of the minimax value: terminal states return their utility; MAX chooses the child with the maximum minimax value; MIN chooses the child with the minimum.
Minimax with depth tracking
def minimax(state, is_max_turn, game):
if game.terminal_test(state):
return game.utility(state)
if is_max_turn:
best = float('-inf')
for action in game.actions(state):
val = minimax(game.result(state, action), False, game)
best = max(best, val)
return best
else:
best = float('inf')
for action in game.actions(state):
val = minimax(game.result(state, action), True, game)
best = min(best, val)
return best
def best_action(state, game):
"""Return the action that maximizes minimax value from root (MAX turn)."""
return max(game.actions(state),
key=lambda a: minimax(game.result(state, a), False, game))| Property | Minimax |
|---|---|
| Complete? | Yes (finite game tree) |
| Optimal? | Yes — against an optimal opponent |
| Time complexity | O(b^m) — exponential in depth m |
| Space complexity | O(bm) — linear (depth-first traversal) |
Exponential intractability
Chess: b ≈ 35, m ≈ 80 → 35^80 ≈ 10^123 nodes. This is physically impossible to enumerate. Practical game AI always uses alpha-beta pruning + evaluation functions that estimate non-terminal states + depth limits.
Alpha-Beta Pruning
Alpha-beta pruning is an optimization over minimax that maintains two bounds: α (best value MAX can guarantee so far along the path from root) and β (best value MIN can guarantee so far). When α ≥ β, the subtree can be pruned — MIN would never allow MAX to reach this state.
| Variable | Controlled by | Meaning | Initial value |
|---|---|---|---|
| α (alpha) | MAX player | Best (highest) value MAX can guarantee on the current path | −∞ |
| β (beta) | MIN player | Best (lowest) value MIN can guarantee on the current path | +∞ |
Pruning conditions: at a MAX node, prune if the current value v ≥ β (MIN would not choose this branch). At a MIN node, prune if v ≤ α (MAX would not choose this branch). The pruning is called 'β-cutoff' at MAX nodes and 'α-cutoff' at MIN nodes.
Alpha-beta pruning — same result as minimax, far fewer node expansions
def alphabeta(state, alpha, beta, is_max_turn, game):
"""Alpha-beta pruning. alpha=best for MAX so far, beta=best for MIN so far."""
if game.terminal_test(state):
return game.utility(state)
if is_max_turn:
v = float('-inf')
for action in game.actions(state):
v = max(v, alphabeta(game.result(state, action), alpha, beta, False, game))
if v >= beta:
return v # beta-cutoff: MIN won't allow this path
alpha = max(alpha, v)
return v
else:
v = float('inf')
for action in game.actions(state):
v = min(v, alphabeta(game.result(state, action), alpha, beta, True, game))
if v <= alpha:
return v # alpha-cutoff: MAX won't allow this path
beta = min(beta, v)
return v
# Initial call from root (MAX turn)
# result = alphabeta(root_state, float('-inf'), float('inf'), True, game)| Scenario | Nodes Expanded | Effective branching factor |
|---|---|---|
| Worst case (worst ordering) | O(b^m) — same as minimax | b (no improvement) |
| Average case (random ordering) | O(b^(3m/4)) | b^(3/4) |
| Best case (perfect ordering) | O(b^(m/2)) | b^(1/2) — can search TWICE as deep in the same time |
Move ordering matters enormously
With perfect move ordering (best moves first), alpha-beta can double the search depth for the same computation. In practice, killer moves, history heuristics, and iterative deepening with move reordering achieve near-optimal pruning. This is why modern chess engines can search 20+ plies where pure minimax could only reach 10.
Evaluation functions and depth-limited search
Since full game tree search is intractable, practical game AI cuts off search at a fixed depth and applies an evaluation function eval(s) to non-terminal states. A good evaluation function must be: (1) consistent with utility values at terminal states, (2) efficiently computable, and (3) strongly correlated with actual winning chances.
For chess, early evaluation functions used weighted linear combinations of features: eval(s) = w₁f₁(s) + w₂f₂(s) + … where features include material count (queen=9, rook=5, bishop/knight=3, pawn=1), mobility, king safety, pawn structure. Modern engines (AlphaZero, Stockfish NNUE) use deep neural networks as evaluation functions.
Monte Carlo Tree Search (MCTS)
MCTS replaces exact evaluation with sampling. It builds a partial game tree using four phases: Selection (traverse tree using UCB1 to balance exploration and exploitation), Expansion (add a new node), Simulation/Rollout (play randomly to a terminal state), and Backpropagation (update win statistics up the tree).
UCB1 score for node i: w_i = wins from node i, n_i = visits to node i, N = total parent visits, C = exploration constant (√2 is standard). The first term exploits; the second explores.
MCTS vs Alpha-Beta
Alpha-beta requires a hand-crafted evaluation function; MCTS works with just the rules of the game (via rollouts). MCTS drives AlphaGo/AlphaZero and became dominant in Go (branching factor ~250, making alpha-beta impractical). For games with smaller branching factors (chess, checkers), alpha-beta with neural evaluation (Stockfish NNUE) remains competitive.
GATE PYQ Spotlight
GATE DA — Alpha-Beta α and β semantics (1-mark)
In adversarial search, α–β pruning is applied to game trees of any depth where α is the (m) value choice formed so far for the MAX player and β is the (n) value choice formed so far for the MIN player. Which choices of (m) and (n) make this valid? (A) m=minimum, n=maximum (B) m=maximum, n=minimum (C) m=maximum, n=maximum (D) m=minimum, n=minimum Answer: (B) m=maximum, n=minimum. α is the best (maximum) value MAX has found along the current path; β is the best (minimum) value MIN has found. The pruning occurs when α ≥ β.
GATE DA Sample — Alpha-Beta pruned subtrees (2-mark)
Given a game tree with leaf utility values, identify which subtrees are pruned by alpha-beta. Key technique: Do a left-to-right DFS, maintaining α and β. At a MIN node: if you find a value ≤ α, prune remaining children (α-cutoff). At a MAX node: if you find a value ≥ β, prune remaining children (β-cutoff). The subtrees under pruned nodes are never evaluated — their values cannot change the root decision.
GATE DA — Minimax properties (1-mark)
Which of the following is FALSE about Minimax? (A) It is complete for finite game trees (B) It finds the optimal move assuming optimal opponent play (C) Alpha-beta pruning changes the final Minimax value (D) Its time complexity is O(b^m) Answer: (C) is FALSE. Alpha-beta pruning never changes the minimax value — it only skips nodes that cannot affect the result. The root value and the optimal action remain identical.
Practice questions
- What is the minimax algorithm and when does it guarantee optimal play? (Answer: Minimax: MAX player maximises value; MIN player minimises value. Algorithm recursively computes the value of each game state by assuming both players play optimally. Returns the move that leads to the best guaranteed outcome under adversarial play. Guarantees optimal play when: the game is deterministic, two-player, zero-sum, perfect information. Does not guarantee: anything for non-adversarial opponents, stochastic games (need expectiminimax), or imperfect information games (poker).)
- What is alpha-beta pruning and by how much can it reduce minimax search complexity? (Answer: Alpha-beta pruning: maintain α (best MAX value) and β (best MIN value found so far). Prune a branch if its value cannot affect the parent's decision. Example: if MAX already found a move with value 5, any MIN branch that has a value ≤ 5 is irrelevant — prune it. Complexity: minimax is O(b^d) where b = branching factor, d = depth. Alpha-beta best case (optimal ordering): O(b^{d/2}) — doubles the searchable depth. Average case (random ordering): O(b^{3d/4}). With move ordering (killer heuristic, history heuristic): approaches best case in practice.)
- What is iterative deepening minimax and why is it preferred over fixed-depth search? (Answer: Iterative deepening: run minimax to depth 1, then 2, then 3, ... until time runs out. Use the deepest completed search's result. Advantages: (1) Anytime algorithm — always has a valid move available. (2) Time management — stops at exactly the time limit. (3) Shallow searches are fast (total overhead: only 1/(b-1) × deepest search). (4) Move ordering improves: nodes found at shallow depth are ordered first at deep search — approaching alpha-beta best case. Used in virtually every competitive chess engine.)
- What is the horizon effect in game tree search and how do extensions address it? (Answer: Horizon effect: a fixed-depth search misses consequences that happen just beyond the search horizon. Example: a chess engine at depth 10 captures a pawn (gaining +1), but at move 11 the opponent checkmates — the engine doesn't see the checkmate. Extensions: quiescence search — extend search at 'noisy' positions (captures, checks) until the board is 'quiet.' Singular extensions — extend search at positions where one move is clearly best. These ensure the evaluated position is a stable snapshot, not a position mid-battle.)
- What is Monte Carlo Tree Search (MCTS) and why did it revolutionise Go AI? (Answer: MCTS: iteratively builds a search tree by: (1) Selection: navigate from root using UCB1 score balancing exploration and exploitation. (2) Expansion: add a new node. (3) Simulation: play random (or policy-guided) games to the end from the new node. (4) Backpropagation: update win statistics up the tree. Evaluation by simulation rather than hand-crafted evaluation function. Revolutionised Go: designing a heuristic evaluation function for Go's enormous state space was intractable. MCTS + neural network policies (AlphaGo) achieved superhuman play without hand-crafted evaluation.)
On LumiChats
Adversarial search principles extend beyond board games — they underpin multi-agent AI systems, negotiation algorithms, and security research where one agent must anticipate adversarial or unpredictable inputs.
Try it free