Watch a Game AI Think: Minimax and Alpha-Beta, Live

Our Tic-Tac-Toe, Connect 4, Checkers, Othello and Chess opponents all run the same idea: search the game tree, assume the opponent plays their best, and pick the move with the best guaranteed outcome. That idea is minimax, and alpha-beta pruning is what makes it fast. Below you can step through both on a real board and watch the second one skip work the first one wastes.

Squares are positions; the top square is the AI to move (a MAX layer), the next is your reply (a MIN layer), and so on. Leaves score +1 (AI wins), −1 (you win) or 0 (draw). Step to watch each layer take the best (max) or worst-for-the-AI (min) of its children. Turn on alpha-beta and the grayed branches are the ones it proves it can skip — same move, fewer nodes.

The whole algorithm, in two functions

Minimax is the tree walk you just watched. Alpha-beta is the same walk plus two running bounds — alpha (the best MAX can already guarantee) and beta (the best MIN can already guarantee) — that let it stop exploring a branch the moment it cannot change the decision.

Minimax
function minimax(node, isMax):
  if node is terminal:
    return score(node)          # +1 / -1 / 0
  if isMax:
    best = -inf
    for child in node.moves:    # AI's turn
      best = max(best, minimax(child, false))
    return best
  else:
    best = +inf
    for child in node.moves:    # your turn
      best = min(best, minimax(child, true))
    return best
Alpha-beta pruning
function ab(node, alpha, beta, isMax):
  if node is terminal:
    return score(node)
  if isMax:
    best = -inf
    for child in node.moves:
      best = max(best, ab(child, alpha, beta, false))
      alpha = max(alpha, best)
      if beta <= alpha: break   # prune rest
    return best
  else:
    best = +inf
    for child in node.moves:
      best = min(best, ab(child, alpha, beta, true))
      beta = min(beta, best)
      if beta <= alpha: break   # prune rest
    return best

Same answer, provably — pruning never changes the value at the root, only how many nodes you touch to find it. On full-depth Tic-Tac-Toe that's a 93% cut (549,945 → 36,528 nodes, ~0.3 ms).

Why "just search deeper" gets expensive fast

A search that looks d moves ahead visits roughly bd nodes, where b is the branching factor — how many moves you typically have. Drag the depth and switch games to feel it. (Branching factors are approximate published averages, for illustration.)

That growth is exactly why alpha-beta and the tricks in our real engines matter — and why a deeper search is not always a better one: a shallow tic-tac-toe search still lost 2 of 200 games.

The same idea, five different games

Every opponent on the site is this algorithm with a different board, a different way of scoring a position, and different tricks to search deeper without searching everything.

GameBoardBranching (approx.)How it scores a positionSearch tricks
Tic-Tac-Toe3×3≤ 9 (~4) Win / lose / draw — solved exactly at full depth Minimax + alpha-beta, full depth on 3×3 Play →
Connect 47×6≤ 7 (~4) Threats, central control, connected pieces Bitboard negamax + alpha-beta + transposition table + iterative deepening Play →
Checkers8×8~2.8 Material, kings, position Iterative-deepening negamax + alpha-beta + capture quiescence Play →
Othello8×8~10 Corners, mobility, positional square weights Iterative-deepening negamax + alpha-beta + exact endgame solve Play →
Chess8×8~35 Material, king safety, pawn structure, open files Negamax + alpha-beta + null-move + quiescence + check extensions + move ordering Play →

Read the actual code

The pseudocode above is the shape; here is a real, unminified engine that runs one of these opponents in your browser — iterative-deepening negamax with alpha-beta pruning and capture-aware quiescence, about 230 lines of vanilla JavaScript: the LK Forge checkers engine on GitHub Gist.

And you don't have to take the node counts on faith. This self-contained script runs plain minimax and alpha-beta over the exact Tic-Tac-Toe position above and prints the counts — 14 nodes down to 10 with pruning — with no dependencies: node reproduce-minimax.mjs.

Read the rest of the cluster

Share this X Facebook Reddit