Games and adversaries: minimax, alpha-beta pruning and Monte Carlo tree search
A game as a search problem, the game tree, minimax, alpha-beta pruning, evaluation functions and MCTS — on tic-tac-toe, where everything can be computed, and the road from Deep Blue to AlphaZero.
Series AI basics Part 22 of 52
- Map of artificial intelligence: what is what and how it all fits together
- A brief history of artificial intelligence: from Turing to language models
- Data, model, training, inference: the basic terms on a single example
- How to read mathematical notation: symbols, indices, sums and quantifiers
- Sets and relations: the building blocks of definitions in AI
- Functions and mappings: why every AI model is a function
- Logic and proofs: the rules AI reasons by
- Graphs and trees: the structure that both search and neural networks stand on
- Linear algebra for machine learning: vectors, matrices and why AI mostly multiplies
- Derivatives, the gradient and the chain rule: how a model finds out which way to learn
- Gradient descent: how a model learns
- What probability is: from rolling dice to the Kolmogorov axioms
- Conditional probability and Bayes’ theorem: how to update your estimate when you learn something new
- Random variables and the cumulative distribution function: one function that tells you everything about a distribution
- Probability density and probability mass functions: why a continuous variable has zero probability at a point
- Expected value, variance and covariance: where a distribution lies, how wide it is and what goes with what
- Overview of probability distributions: Bernoulli, binomial, Poisson, uniform, normal and exponential
- Law of large numbers and central limit theorem: why averages behave so predictably
- Statistics and parameter estimation: maximum likelihood, MAP and confidence intervals
- Information theory: entropy, cross-entropy and KL divergence — where loss functions come from
- State-space search: BFS, DFS, UCS and A* with animations
- Games and adversaries: minimax, alpha-beta pruning and Monte Carlo tree search
- Constraint satisfaction: backtracking, constraint propagation and AC-3 on sudoku and a map of Czech regions
- Logic and knowledge systems: forward and backward chaining, resolution and expert systems
- Planning: STRIPS, the blocks world, heuristics from relaxation and planning in AI agents
- Local search and simulated annealing: a travelling salesman through the Czech regional capitals
- Genetic algorithms: population, crossover, mutation and selection on a backpack for a hike
- Evolution strategies and genetic programming: from the one-fifth rule to Kepler’s law
- Ant colony optimization: how a pheromone trail finds a short path
- Particle swarm optimisation: inertia, personal experience and the swarm’s experience
- Neuroevolution: evolving the weights and shape of neural networks, and the NEAT algorithm
- Fuzzy sets and membership functions: how to write down “somewhat warm”
- Fuzzy logic and probability: why “somewhat warm” does not mean “probably warm”
- Fuzzy logic: t-norms, implications and IF–THEN rules
- Fuzzy control: Mamdani and Sugeno from a fan to a pole on a cart
- Fuzzy clustering: the fuzzy c-means algorithm and points between clusters
- Neuro-fuzzy systems (ANFIS): fuzzy rules that learn from data
- Markov chains: the transition matrix, the stationary distribution and PageRank
- Hidden Markov models: the Viterbi algorithm and the dishonest casino
- Bayesian networks: why the grass is wet and what it means for the sprinkler
- The Kalman filter: how to estimate where a car is and how fast it goes from imprecise measurements
- Types of machine learning: supervised, unsupervised, semi-supervised and more
- Prompts, context and RAG: how to give a model the right information
- Variational autoencoders (VAE): new images from a continuous latent space
- Generative adversarial networks (GAN): the forger versus the detective
- Diffusion models: how pure noise becomes an image
- Markov decision processes: states, actions, rewards and the Bellman equations
- Q-learning and DQN: learning the value of actions, from a table to a neural network
- Policy gradient and PPO: learning the policy directly
- How an AI agent works: a language model in a loop with tools
- Programming with AI agents: the task, tests and review
- Evaluating and securing AI systems: how to measure and how to keep watch
Full series syllabus 52 of 83 topics published
- How a two-player game is written as a search problem and why its solution is a strategy, not a path.
- How minimax computes the guaranteed value of a position and why alpha-beta reaches the same result much faster.
- How Monte Carlo tree search finds a good move without an evaluation function and what AlphaGo made of it.
Useful to know first: State-space search: BFS, DFS, UCS and A* with animations, Graphs and trees: the structure that both search and neural networks stand on
In the maze from the previous part, the little figure was alone. The world stood still and every move led where it was supposed to. In a game, though, someone sits across from you who wants the exact opposite — and answers each of your moves with the one that hurts you most. Finding a path to the goal is no longer enough: you have to find a strategy that holds up against every reply. Chess, checkers and go were the yardstick of artificial intelligence for decades, and the algorithms in this part are behind both Deep Blue and AlphaGo.
It builds on the parts State-space search and Graphs and trees.
1. A game as a search problem
Definition — two-player game
A two-player game (deterministic, with perfect information and zero-sum) is a seven-tuple
Γ = (S, s0, player, A, T, K, U)
that satisfies:
- S is a non-empty set of states (positions) and s0 ∈ S is the initial state;
- K ⊆ S is the set of terminal states — where the game ends;
- player : S ∖ K → {MAX, MIN} says whose turn it is in a state;
- A assigns to every state s ∈ S ∖ K a finite non-empty set A(s) of moves (actions), and in terminal states A(s) = ∅;
- T is the transition function: for s ∈ S ∖ K and a ∈ A(s), T(s, a) ∈ S is the state after move a (moves are deterministic);
- the players take turns: if s ∈ S ∖ K, a ∈ A(s) and T(s, a) ∉ K, then player(T(s, a)) ≠ player(s);
- U : K → ℝ is the utility of a terminal state from the point of view of player MAX; the utility for MIN is −U (zero-sum: U(s) + (−U(s)) = 0 for every s ∈ K).
Both players know the whole seven-tuple and see the current state at every moment (perfect information). The game is finite if and only if there is no infinite sequence s0, s1, s2, … with si = T(si−1, ai) for some ai ∈ A(si−1) — so every game ends in K after finitely many moves. All the games in this part are finite.
A strategy of player MAX is a function σ that assigns to every state s ∈ S ∖ K with player(s) = MAX a move σ(s) ∈ A(s); a strategy τ of player MIN is defined the same way for states with player(s) = MIN. A pair of strategies (σ, τ) uniquely determines a game from state s0, and with it its utility.
A game is a maze in which you share the moves with an opponent. At the end the score is tallied — in tic-tac-toe +1 if MAX wins, −1 if MIN wins, and 0 for a draw. MAX wants the score as high as possible, MIN as low as possible. “Zero-sum” means you cannot win together: what one gains, the other loses.
The difference from search is fundamental. The solution is no longer a path — you cannot choose one, because every other move is made by the opponent. The solution is a strategy: a rule for how to answer every move the opponent can make.
2. The game tree
Definition — game tree
Let Γ = (S, s0, player, A, T, K, U) be a finite two-player game. The game tree of Γ is the rooted tree (see the part Graphs and trees) for which:
- the nodes are all sequences of moves (a1, …, ak), k ≥ 0, that can be played from s0, i.e. si−1 ∉ K, ai ∈ A(si−1) and si = T(si−1, ai) for i = 1, …, k; the node has the state sk;
- the root is the empty sequence () with state s0;
- the children of the node (a1, …, ak) with state sk ∉ K are the nodes (a1, …, ak, a) for all a ∈ A(sk), with states T(sk, a);
- the leaves are exactly the nodes whose state is terminal (sk ∈ K).
So a node is not a position but the history that led to it — the same state can be reached by different sequences of moves and then appears in the tree several times. Nodes where MAX is to move are drawn as triangles pointing up, MIN nodes pointing down.
Let b = maxs ∈ S ∖ K |A(s)| be the branching factor and m the greatest length of a game (number of moves). Then the tree has at most bm leaves.
All the games that can be played, drawn as a branching tree: from the root there is one branch for each first move, from each of those a branch for each reply, and so on, until the end of the game. One leaf = one finished game.
Tic-tac-toe on a 3 × 3 board is a small game, and yet its tree has 549,946 nodes and 255,168 leaves — that is how many different games can be played. Yet there are only 5,478 different positions: you reach the same position through many orders of moves. Chess has a branching factor of about 35 and a game lasts dozens of moves; in 1950 Claude Shannon estimated its tree at 10120 games. Go, with a branching factor of about 250, is many orders of magnitude further still. So we can walk the whole tree only for small games — for large ones we have to learn which branches to skip.
Example — an upper bound on the tic-tac-toe tree and Shannon's number
If tic-tac-toe were always played until the board is full, the tree would have exactly 9 · 8 · … · (9 − k + 1) = 9!/(9 − k)! nodes at depth k (k = 0, …, 9). In total
∑k=09 9!/(9 − k)! = 1 + 9 + 72 + 504 + 3,024 + 15,120 + 60,480 + 181,440 + 362,880 + 362,880 = 986,410 nodes and 9! = 362,880 leaves.
The real tree has 549,946 nodes and 255,168 leaves, because a game in which someone gets three in a row ends earlier and its branch does not branch any further. The estimate bm is therefore an upper bound; here with b falling from 9 to 1.
Shannon (Programming a Computer for Playing Chess, 1950) assumed for chess about 103 possibilities per pair of moves (white and black) and a typical game of 40 pairs of moves: (103)40 = 10120. It is a rough order-of-magnitude estimate, not an exact number.
3. Minimax
Definition — minimax value
Let Γ = (S, s0, player, A, T, K, U) be a finite two-player game. The minimax value is the function MM : S → ℝ defined recursively for every s ∈ S:
- MM(s) = U(s) if s ∈ K;
- MM(s) = maxa ∈ A(s) MM(T(s, a)) if s ∉ K and player(s) = MAX;
- MM(s) = mina ∈ A(s) MM(T(s, a)) if s ∉ K and player(s) = MIN.
The recursion is well defined: the game is finite, so every chain of recursive calls ends in a terminal state, and A(s) is finite and non-empty, so both the maximum and the minimum exist.
A move a* ∈ A(s) is an optimal move of player MAX in a state s with player(s) = MAX if and only if
a* ∈ argmaxa ∈ A(s) MM(T(s, a)), i.e. MM(T(s, a*)) = MM(s);
an optimal move of player MIN is likewise an element of the argmin. A strategy that chooses an optimal move in every state is a minimax strategy.
You compute the value of a position backwards. At the end of the game you know it — it is the score. One move earlier: if MAX is playing, he picks the best of what awaits him; if MIN is playing, he picks the worst for MAX. This way the values flow up level by level all the way to the root. It is exactly the reasoning “if I go here, he goes there, and then I…”, just carried through to the end.
The left MIN node picks 4 from the leaves 4, 6 and 9, the middle one picks 2 from 2, 7 and 8, the right one picks 3 from 5, 3 and 9. MAX at the root then chooses between 4, 2 and 3 and plays the left move with value 4. Notice that the right branch hides a nine — but MAX can never reach it, because MIN would play the three instead.
Theorem — what minimax guarantees
Let Γ be a finite two-player game and σ* a minimax strategy of player MAX. Then
- for every strategy τ of player MIN, the game played by the pair (σ*, τ) has utility ≥ MM(s0);
- if τ* is a minimax strategy of player MIN as well, the game played by the pair (σ*, τ*) has utility exactly MM(s0).
Proof (sketch), by induction on the height of the node: in a leaf MAX gets exactly U(s) = MM(s). In a MAX node he plays the move into the child with the largest value and by the induction hypothesis secures at least that value there, so at least MM(s). In a MIN node the opponent picks any child, but each has a value ≥ min = MM(s), and in each of them MAX by the hypothesis secures at least its value. For s = s0 this is point 1. By the same reasoning with the roles swapped, MIN with the minimax strategy τ* secures a utility of at most MM(s0) against every strategy of MAX; the two inequalities together give point 2. ∎
Minimax walks the tree depth-first: time O(bm), memory O(b · m).
Minimax assumes the worst: it supposes the opponent never makes a mistake. If he does, you only come out better. That is why the minimax value is guaranteed — it is what you can hold on to, however the opponent plays.
4. Minimax on tic-tac-toe
Tic-tac-toe on 3 × 3 is small enough for minimax to walk the whole tree. Anyone who has played it for more than a few minutes knows the result: the minimax value of the empty board is 0 — when both play without a mistake, it is always a draw. More interesting is what minimax says about a particular position. X (MAX) is to move, and each empty square shows the value of moving there:
Only one move wins — the centre. With it X sets up a fork: it threatens to complete the middle row and the diagonal, and O cannot block both. The move to the bottom middle holds the draw. The remaining three moves look innocent, but they lose: O answers them with a fork of its own. A person would have to think this through; minimax simply computes it.
def minimax(board):
end = winner(board) # "X", "O", "draw", or None
if end:
return {"X": 1, "O": -1, "draw": 0}[end]
values = [minimax(play(board, move)) for move in empty_squares(board)]
return max(values) if to_move(board) == "X" else min(values)
5. Alpha-beta pruning
Minimax walks the whole tree, even though half of it is pointless. Look again at the middle branch: the first leaf showed 2. So in that branch MIN gets 2 or less — and MAX already has the left branch with value 4 secured. Whatever the remaining two leaves are, MAX will not go into the middle branch. He does not even need to look at them.
Definition — alpha-beta
Let Γ be a finite two-player game. Alpha-beta is a depth-first search that carries two bounds α, β ∈ ℝ ∪ {−∞, +∞} along with it:
- α — the highest value MAX has already secured somewhere on the path from the root (initially −∞);
- β — the lowest value MIN has already secured (initially +∞).
The function AB(s, α, β) for a state s ∈ S:
- If s ∈ K, return U(s).
- If player(s) = MAX, set v ← −∞; otherwise v ← +∞.
- For the moves a ∈ A(s) one by one (in a fixed order) compute h ← AB(T(s, a), α, β) and
- in a MAX node set v ← max(v, h) and α ← max(α, v),
- in a MIN node set v ← min(v, h) and β ← min(β, v);
- Return v.
Alpha-beta at the root is the call AB(s0, −∞, +∞).
Theorem. Let Γ be a finite two-player game. Then AB(s0, −∞, +∞) = MM(s0) — alpha-beta returns the same value at the root as minimax (in inner nodes where a cutoff occurred, it returns only a bound, not the exact value). If the tree has branching factor b and depth m and the moves are ordered in the best possible order, alpha-beta examines only O(bm/2) nodes.
α is “I already have this in my pocket”, β is “the opponent will not let me have more anyway”. When you find out in some branch that the opponent can push you below what you already have secured elsewhere, there is no point exploring it further — a single refuting move is enough. The value at the root stays the same, you just find it faster.
prune when α ≥ β
- α
- the best MAX has already secured elsewhere on the path from the root. He will never go into a branch that gives less — he has a better choice
- β
- the worst for MAX that MIN can already force on him in this branch. MAX will not get more than β from here, however the remaining children turn out
- ≥
- the window [α, β] is empty: a value still worth playing for here would have to be at least α and at most β at the same time. No such value exists (on equality it would be no better than what MAX already has), so the rest of the branch cannot influence the root
Alpha-beta reached the same result — MAX plays left and gets 4 — but evaluated only 6 leaves out of 9. In the middle branch it pruned two leaves right after the two, in the right one one leaf after the three. That is why the pruned MIN nodes show only the bound “≤ 2” and “≤ 3”: we do not know the exact value, and we do not need it.
What the animation shows next to the nodes, here step by step in a table. A row captures the window of a node on entering it and then after every child that returned a value:
| step | node | what happened | α | β | note |
|---|---|---|---|---|---|
| 1 | A (MAX) | entry | −∞ | +∞ | |
| 2 | B (MIN) | entry | −∞ | +∞ | |
| 3 | B (MIN) | b1 returned 4 | −∞ | 4 | |
| 4 | B (MIN) | b2 returned 6 | −∞ | 4 | |
| 5 | B (MIN) | b3 returned 9 | −∞ | 4 | B returns 4 |
| 6 | A (MAX) | B returned 4 | 4 | +∞ | |
| 7 | C (MIN) | entry | 4 | +∞ | |
| 8 | C (MIN) | c1 returned 2 | 4 | 2 | α ≥ β: prune c2, c3; C returns only the bound ≤ 2 |
| 9 | A (MAX) | C returned 2 | 4 | +∞ | |
| 10 | D (MIN) | entry | 4 | +∞ | |
| 11 | D (MIN) | d1 returned 5 | 4 | 5 | |
| 12 | D (MIN) | d2 returned 3 | 4 | 3 | α ≥ β: prune d3; D returns only the bound ≤ 3 |
| 13 | A (MAX) | D returned 3 | 4 | +∞ | A returns 4 |
A MIN node receives α = 4 from its parent and changes only its own β; a MAX node, conversely, changes only α. The window of each node therefore only narrows. In the right branch the first leaf (5) does not trigger a cutoff — 5 is more than 4, here MIN could give MAX more than on the left — only the second leaf (3) pulls β below α.
On tic-tac-toe the difference is more dramatic. Full minimax from the empty board visits 549,946 positions, alpha-beta with the same move ordering only 18,297 — about thirty times fewer — and returns the same zero. The exponent m/2 in the theorem means that in the same time you see twice as deep. That is why move ordering matters so much: chess programs try captures first, along with moves that were good last time.
Example — how many leaves alpha-beta saves with the best ordering
The exact bound was proved by D. E. Knuth and R. W. Moore (An Analysis of Alpha-Beta Pruning, 1975): in a tree with branching factor b and depth d, with the best move ordering alpha-beta evaluates
b⌈d/2⌉ + b⌊d/2⌋ − 1 leaves instead of bd.
Chess, b = 35, depth d = 6 plies: minimax 356 = 1,838,265,625 leaves, alpha-beta 353 + 353 − 1 = 42,875 + 42,875 − 1 = 85,749 leaves — about 21,000 times fewer. And the other way round: alpha-beta gets through 12 plies with 356 + 356 − 1 ≈ 3.7 · 109 leaves, i.e. for only twice the work minimax needs for 6 plies.
The small tree from the figure (b = 3, d = 2): the bound is 3 + 3 − 1 = 5 leaves. Alpha-beta evaluated 6 of them, because the order of the leaves is not the best possible — in the right branch the three comes only second.
def alphabeta(board, alpha=-inf, beta=inf):
end = winner(board)
if end:
return {"X": 1, "O": -1, "draw": 0}[end]
maximizing = to_move(board) == "X"
v = -inf if maximizing else inf
for move in empty_squares(board):
h = alphabeta(play(board, move), alpha, beta)
if maximizing:
v = max(v, h); alpha = max(alpha, v)
else:
v = min(v, h); beta = min(beta, v)
if alpha >= beta:
break # cutoff: the rest of the branch changes nothing
return v
6. Limited depth and evaluation functions
Not even alpha-beta walks the chess tree to the end of the game. Real programs therefore search only to a fixed depth and estimate the position where they stop.
Definition — depth-limited minimax
Let Γ = (S, s0, player, A, T, K, U) be a two-player game. An evaluation function is a function E : S → ℝ such that E(s) = U(s) for all s ∈ K; for s ∉ K, E(s) is an estimate of MM(s). It is usually a weighted sum of n ≥ 1 features of the position f1, …, fn : S → ℝ with weights w1, …, wn ∈ ℝ:
E(s) = w1f1(s) + … + wnfn(s).
Depth-limited minimax is, for every d ∈ ℕ0, the function MMd : S → ℝ defined recursively on d:
- MMd(s) = E(s) if s ∈ K or d = 0;
- MMd(s) = maxa ∈ A(s) MMd−1(T(s, a)) if s ∉ K, d ≥ 1 and player(s) = MAX;
- MMd(s) = mina ∈ A(s) MMd−1(T(s, a)) if s ∉ K, d ≥ 1 and player(s) = MIN.
The value MMd(s) thus computes minimax only d plies ahead and replaces the positions where it stops with the estimate E. If d is at least the length of the longest game from s, then MMd(s) = MM(s).
You cannot think the game through to the end, so you look a few moves ahead and ask: “How good does it look here?” In chess the simplest answer is to count material — pawn 1, knight and bishop 3, rook 5, queen 9 — and subtract the opponent’s. Better functions add piece activity, king safety or pawn structure.
E(s) = ∑i wi · (pi(s) − p′i(s))
- E(s)
- the estimate of the value of the position from the point of view of MAX (white) — it is plugged in where the search stops, instead of the real minimax value
- ∑i
- over the kinds of pieces: pawn, knight, bishop, rook, queen. A sum, because in the simplest model the material of different pieces simply adds up
- wi
- the value of the piece: 1, 3, 3, 5, 9. The weight converts “pieces” into a common currency so that a rook can be compared with two knights
- pi − p′i
- how many more pieces of the given kind white has than black. A difference, not a count, because the game is zero-sum: whatever the opponent has extra is a loss
def minimax_d(s, d):
if is_terminal(s) or d == 0:
return E(s) # we cannot see further: an estimate instead of the real value
values = [minimax_d(T(s, a), d - 1) for a in actions(s)]
return max(values) if player(s) == "MAX" else min(values)
def iterative_deepening(s, time): # MAX is to move in s
d, best = 1, None
while time_left(time):
best = max(actions(s), key=lambda a: minimax_d(T(s, a), d - 1))
d += 1 # next round one ply deeper
return best
Limited depth has its trap: the horizon effect. When the search stops in the middle of an exchange of pieces — you have just taken the queen, the opponent takes it back next move — the evaluation function sees only the first half and overrates the position. Programs defend against this by searching “unquiet” positions further until they settle down. And since they never know how much time they have, they search with iterative deepening from the previous part: depth 1, 2, 3, … until time runs out. The results of the shallow passes also serve as the move ordering for the deeper ones — and alpha-beta then prunes much more.
7. Monte Carlo tree search
People tuned evaluation functions for chess for decades. For go, nobody managed to write one — a position that looks calm can be lost twenty moves later. Monte Carlo tree search (MCTS) gives up on the evaluation function entirely. Instead of estimating the position, it plays it out: randomly, many times, and looks at how often it turned out well.
Definition — one iteration of MCTS
Let Γ = (S, s0, player, A, T, K, U) be a finite two-player game in which every game ends in a win, a draw or a loss. Input: a state s0 ∉ K (the root) and a number of iterations (or a time limit). Output: a move a ∈ A(s0).
MCTS builds a search tree — a part of the game tree that starts as just the root and grows by one node in every iteration. Each node i of the tree remembers:
- its visit count ni ∈ ℕ0 — how many simulations went through the node;
- the sum of results wi ∈ [0, ni] from the point of view of the player who moved into the node, where the result of one game is 1 for a win, ½ for a draw and 0 for a loss.
A node is fully expanded if and only if it has a child in the tree for every move a ∈ A(s) of its state s. One iteration of MCTS has four steps:
- Selection — start at the root; as long as the current node is fully expanded and its state is not terminal, go to the child with the highest UCT value (definition below).
- Expansion — if the state of the current node is not terminal, pick a move a not tried yet, add a child with state T(s, a) and n = w = 0, and go to it.
- Simulation — from the state of the current node, play the game out with random moves (each of A(s) with equal probability) until a terminal state sK ∈ K.
- Backpropagation — for every node i on the way from the current node back to the root (both included), set ni ← ni + 1 and wi ← wi + the result of the game sK from the point of view of the player who moved into node i.
The iterations repeat until the number of iterations or the time runs out. Then the move from the root into the child with the largest ni is played.
Like choosing between restaurants: you go where you have liked the food most so far, but now and then you try a different one. Every move gets a chance, promising moves get more tries, and the tree grows exactly where a lot is at stake. Random playouts are dumb, but when there are a thousand of them, the average starts to say something.
def mcts(root, iterations):
for _ in range(iterations):
u = root
while u.all_children() and u.children: # 1. selection
u = max(u.children, key=uct)
if u.untried and not is_terminal(u.state): # 2. expansion
u = u.add_child(random_choice(u.untried))
s = u.state
while not is_terminal(s): # 3. simulation
s = T(s, random_choice(actions(s)))
while u is not None: # 4. backpropagation
u.n += 1
u.w += result(s, for_=u.who_moved) # 1, ½ or 0
u = u.parent
return max(root.children, key=lambda c: c.n).move
Definition — UCT
Let a node of the MCTS search tree have N ≥ 1 visits and children i = 1, …, k, each with ni ≥ 1 visits and a sum of results wi ∈ [0, ni] (notation from the MCTS definition above). Let c > 0 be a fixed exploration constant, usually c = √2. The UCT value (Upper Confidence bounds applied to Trees) of child i is
UCT(i) = wi/ni + c · √ln N/ni.
In the selection step the child i* ∈ argmaxi ∈ {1, …, k} UCT(i) is chosen. Kocsis and Szepesvári (2006) proved that as the number of iterations grows, the probability that MCTS with UCT recommends a move other than the minimax-optimal one falls to zero.
The first term is “how well it has turned out so far”, the second “how little I know it”. A move you have not tried for a long time gets a bonus that slowly grows with every visit to another move. It is the same dilemma as in reinforcement learning: exploit what works, or explore what might work better?
UCT(i) = wi/ni + c · √ln N/ni
- UCT(i)
- the score of child i in selection; the largest wins. It is not an estimate of the value of the move, but of “how much it pays to try it again”
- wi/ni
- the average result of the move (0 to 1) — exploitation: moves that have turned out well so far get priority
- c
- the exploration constant — how much curiosity weighs against the results so far. The value √2 comes from the UCB1 rule for multi-armed bandits (Auer, Cesa-Bianchi and Fischer, 2002) for results in [0, 1]; Kocsis and Szepesvári (2006) carried it over to trees. In practice it is tuned
- ln N
- grows with the number of visits to the parent, but only slowly. Thanks to it the bonus of rarely tried moves slowly grows: every move gets tried again sooner or later
- ni
- how many times the move has been tried — in the denominator, so the less we know it, the bigger the bonus. The square root corresponds (simplified) to how fast the average becomes more precise: its uncertainty falls like 1/√n
Let us run MCTS on the position minimax solved above. The numbers in the squares are the visits after 100, 300 and 1000 iterations, below them the average result for X:
After 100 iterations the centre has 47 visits and the other moves together have more. After a thousand it has 856 and an average result of 0.94; the drawing move at the bottom middle got 52 visits, the losing moves even fewer. And MCTS knows neither the concept of a fork nor any evaluation function — only the rules and chance. Yet it found the same move as minimax, which had to walk the entire rest of the tree.
Why this particular distribution of visits? Let us compute the UCT of the moves from the root after 1000 iterations (N = 1000, ln N ≈ 6.908, c = √2; colours as in the formula above):
| move | ni | wi/ni | bonus c · √ln N/ni | UCT |
|---|---|---|---|---|
| top right (2) | 27 | 0.333 | 0.715 | 1.049 |
| centre (4) | 856 | 0.941 | 0.127 | 1.068 |
| middle right (5) | 30 | 0.383 | 0.679 | 1.062 |
| bottom middle (7) | 52 | 0.548 | 0.515 | 1.064 |
| bottom right (8) | 35 | 0.429 | 0.628 | 1.057 |
The UCT values are almost the same — between 1.049 and 1.068. That is no coincidence: selection always visits the move with the highest UCT and thereby lowers its bonus (the average changes only a little in the process), so the values gradually even out. The centre has the best average, so a small bonus is enough for it to even out — that is, many visits. The other moves have a worse average and make up for it with a large bonus, for which few visits suffice. (We compute the sums in the table from unrounded values, so they may differ by 1 in the last digit.)
8. From Deep Blue to AlphaZero
- Deep Blue (1997) beat world champion Garry Kasparov. Inside was alpha-beta with a hand-written evaluation function and specialized hardware that handled about 200 million positions per second.
- Chinook played checkers against world champions as early as the 1990s, and in 2007 its authors solved the game: they went through so many positions that they proved a flawless game ends in a draw — just like tic-tac-toe on 3 × 3.
- AlphaGo (2016) beat Lee Sedol 4 : 1. It combined MCTS with neural networks: one network proposed promising moves (instead of random selection), the other estimated the value of a position (instead of a whole random playout).
- AlphaZero (2017) threw away even human games. It learned chess, shogi and go only by playing against itself — through reinforcement learning — and in each of the games beat the best program up to then.
- Games with chance and hidden information need more. In backgammon the dice decide, so chance nodes with an expected value are added to the tree (expectiminimax). In poker you do not see the opponent’s cards; even so, the program Libratus (2017) beat professionals at two-player poker.
Definition — expectiminimax
Let Γ = (S, s0, player, A, T, K, U) be a finite game as in the definition of a two-player game, except that to the players MAX and MIN a “player” CHANCE is added: player : S ∖ K → {MAX, MIN, CHANCE}, and the turn-taking condition is not required. For every state s with player(s) = CHANCE, A(s) is a finite non-empty set of possible chance outcomes r (say the numbers on a die) and a probability distribution Ps : A(s) → [0, 1] with ∑r ∈ A(s) Ps(r) = 1 is given (see the part on probability); the outcome r occurs with probability Ps(r) and leads to state T(s, r).
The expectiminimax value is the function EMM : S → ℝ defined recursively:
- EMM(s) = U(s) if s ∈ K;
- EMM(s) = maxa ∈ A(s) EMM(T(s, a)) if player(s) = MAX;
- EMM(s) = mina ∈ A(s) EMM(T(s, a)) if player(s) = MIN;
- if player(s) = CHANCE, EMM(s) is the expected value of the values of the successors: EMM(s) = ∑r ∈ A(s) Ps(r) · EMM(T(s, r)).
In MAX and MIN nodes the value is thus computed as in minimax, and in chance nodes as an average weighted by the probabilities of the outcomes. A breakdown of the last formula (Ps shortened to P):
EMM(s) = ∑r P(r) · EMM(T(s, r))
- EMM(s)
- the value of a chance node — what MAX can expect from the position on average, because neither he nor the opponent can influence the outcome of the throw
- ∑r
- over all possible chance outcomes; instead of picking the best (max) or the worst (min), nothing is picked here — every one is counted
- P(r)
- the probability of the outcome as a weight: a frequent outcome influences the value more than a rare one
- EMM(T(s, r))
- the value of the position after the given outcome, computed recursively by the same rule
The idea “look ahead before you do something” has not disappeared from the chessboards. Planning language agents try several variants of a procedure, evaluate them and continue with the most promising one — only instead of moving pieces, they choose steps of a solution.
9. Summary
- A two-player zero-sum game is a search problem in which you share the moves with an opponent; its solution is a strategy, not a path.
- Minimax computes the value backwards: MAX takes the maximum, MIN the minimum. The result is guaranteed against any opponent.
- Alpha-beta returns the same value, but prunes branches that cannot change the result; with a good move ordering it sees twice as deep.
- Large games are searched to a limited depth with an evaluation function — and watch out for the horizon effect.
- Instead of evaluating a position, MCTS plays it out randomly many times and grows the tree where it looks promising; combined with neural networks it became AlphaGo.
Want to get hands-on? Have an AI agent write tic-tac-toe with minimax, and then count how many positions alpha-beta saves when you order the moves from the centre to the edges.
Do you see it differently? Write to support@coden-app.com.