{"slug": "watch-a-game-ai-think-minimax-and-alpha-beta-in-a-browser-tab", "title": "Watch a Game AI Think: Minimax and Alpha-Beta, in a Browser Tab", "summary": "A developer built an interactive browser-based tool that visualizes the minimax algorithm and alpha-beta pruning on a Tic-Tac-Toe board, allowing users to step through the search and see how pruning skips branches. The tool demonstrates that alpha-beta pruning reduces the full-depth search tree from 549,945 nodes to 36,528, a 93% cut, enabling a provably-unbeatable move to resolve in about 0.3 ms client-side. The developer also provides pseudocode and benchmarks, and notes that the same algorithm underlies AI opponents in games like Connect 4, Checkers, Othello, and Chess.", "body_md": "Every \"AI\" opponent in a board game — Tic-Tac-Toe, Connect 4, Checkers, Othello, Chess — tends to 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 enough to run in a browser tab with no backend.\n\nI built an interactive version where you can step through minimax on a real board and toggle alpha-beta on to watch it skip work: ** play with it here**. This post is the written companion.\n\nScore a finished position from the AI's point of view: `+1`\n\nif the AI wins, `-1`\n\nif you win, `0`\n\nfor a draw. Then walk the tree of possible futures. On the AI's turn it takes the **max** of its options; on your turn it assumes you take the **min** (the worst outcome for the AI). That alternation is the whole algorithm.\n\n```\nfunction minimax(node, isMax):\n  if node is terminal:\n    return score(node)          # +1 / -1 / 0\n  if isMax:\n    best = -inf\n    for child in node.moves:    # AI's turn\n      best = max(best, minimax(child, false))\n    return best\n  else:\n    best = +inf\n    for child in node.moves:    # your turn\n      best = min(best, minimax(child, true))\n    return best\n```\n\nSearching every branch is wasteful. Once you've found a reply that already refutes a move, you don't need to look at that move's other branches — they can't change the decision. Two running bounds carry that knowledge down the tree: `alpha`\n\n(the best MAX can already guarantee) and `beta`\n\n(the best MIN can already guarantee). When they cross, you stop.\n\n```\nfunction ab(node, alpha, beta, isMax):\n  if node is terminal:\n    return score(node)\n  if isMax:\n    best = -inf\n    for child in node.moves:\n      best = max(best, ab(child, alpha, beta, false))\n      alpha = max(alpha, best)\n      if beta <= alpha: break   # prune the rest\n    return best\n  else:\n    best = +inf\n    for child in node.moves:\n      best = min(best, ab(child, alpha, beta, true))\n      beta = min(beta, best)\n      if beta <= alpha: break   # prune the rest\n    return best\n```\n\nPruning never changes the value at the root — only how many nodes you touch to find it. On a small Tic-Tac-Toe position with three empty squares, the full tree is 14 nodes and alpha-beta visits 10 of them. On the full-depth opening move it's dramatic: **549,945 nodes drop to 36,528 — a 93% cut** — which is what lets a provably-unbeatable Tic-Tac-Toe move resolve in about 0.3 ms client-side. ([The measured benchmarks are here.](https://lkforge.com/blog/benchmarking-game-ai/))\n\nA search that looks *d* moves ahead visits roughly `b^d`\n\nnodes, where *b* is the branching factor — how many moves you typically have. That number explodes:\n\nLooking just 8 moves ahead in chess is on the order of **35^8 ≈ 2.3 trillion positions**. Alpha-beta — plus move ordering, transposition tables, quiescence and friends — is how a search reaches useful depth without visiting all of them. (Branching factors are approximate published averages, à la Allis 1994, for illustration.)\n\nEvery opponent is this algorithm with a different board, a different way of scoring a position, and different tricks to search deeper without searching everything:\n\n| Game | Board | Branching (approx.) | Search tricks |\n|---|---|---|---|\n| Tic-Tac-Toe | 3×3 | ≤ 9 (~4) | Minimax + alpha-beta, full depth on 3×3 |\n| Connect 4 | 7×6 | ≤ 7 (~4) | Bitboard negamax + alpha-beta + transposition table + iterative deepening |\n| Checkers | 8×8 | ~2.8 | Iterative-deepening negamax + alpha-beta + capture quiescence |\n| Othello | 8×8 | ~10 | Iterative-deepening negamax + alpha-beta + exact endgame solve |\n| Chess | 8×8 | ~35 | Negamax + alpha-beta + null-move + quiescence + check extensions + move ordering |\n\nThe pseudocode above is the shape. Here's a real, unminified engine that runs one of these opponents in the browser — iterative-deepening negamax with alpha-beta pruning and capture-aware quiescence, about 230 lines of vanilla JS: ** the checkers engine on GitHub Gist**.\n\nEverything runs client-side, zero dependencies. If you'd rather *watch* the tree animate and prune than read about it, the interactive version is here: [Watch a Game AI Think →](https://lkforge.com/blog/minimax-alpha-beta-explained/)", "url": "https://wpnews.pro/news/watch-a-game-ai-think-minimax-and-alpha-beta-in-a-browser-tab", "canonical_source": "https://dev.to/lucian_lkb_1f009d/watch-a-game-ai-think-minimax-and-alpha-beta-in-a-browser-tab-3onj", "published_at": "2026-08-19 20:42:51+00:00", "updated_at": "2026-08-19 21:15:25.987969+00:00", "lang": "en", "topics": ["artificial-intelligence", "machine-learning", "developer-tools"], "entities": ["Tic-Tac-Toe", "Connect 4", "Checkers", "Othello", "Chess", "Allis 1994"], "alternates": {"html": "https://wpnews.pro/news/watch-a-game-ai-think-minimax-and-alpha-beta-in-a-browser-tab", "markdown": "https://wpnews.pro/news/watch-a-game-ai-think-minimax-and-alpha-beta-in-a-browser-tab.md", "text": "https://wpnews.pro/news/watch-a-game-ai-think-minimax-and-alpha-beta-in-a-browser-tab.txt", "jsonld": "https://wpnews.pro/news/watch-a-game-ai-think-minimax-and-alpha-beta-in-a-browser-tab.jsonld"}}