Skip to content
← Back to projects

Concept · Mentoring

What learners build

A learning path where every session ends with something built, and one lesson you can run: a tic-tac-toe player that learns which moves it never needs to look at.

Planned · this page is a concept

Run it below ↓

This is a concept illustration, not a record of real sessions. It names no learner, course or organisation. The learning path, the lesson, the code and every position in the demo were written from scratch for this page.

The idea

When I mentor or teach, the explanation is rarely the part that sticks. What sticks is the small thing a learner builds and gets working, because that is where an idea stops being words.

Notes from real sessions belong to the people in them, so they do not belong on a public page. Instead, this page writes the pattern out again from scratch. There is a short learning path that says what each session covers and what learners make, and one lesson you can run in your browser.

The learning path

Each session has one idea, one small thing to build and one check that it works. This example path is on game search, and it was written for this page.

  1. Session 1: Games as trees

    Covers
    Positions, moves and turns, and why every game of tic-tac-toe is one path through a single tree.
    Builds
    A function that lists the legal moves, and a counter for every possible game.
    Check
    The count should come to 255,168 games.
  2. Session 2: Minimax

    Covers
    Scoring finished games and passing the best score back up the tree, with X taking the highest and O the lowest.
    Builds
    A player that never loses at tic-tac-toe.
    Check
    Played against itself from an empty board, it always draws.
  3. Session 3: Alpha-beta pruning

    Covers
    Two bounds, alpha and beta, that let the search skip branches that cannot change the answer.
    Builds
    The same player, with a counter for every position it visits.
    Check
    It reaches the same result as minimax and visits far fewer positions.
    Run it below ↓
  4. Session 4: Move ordering

    Covers
    Why trying strong moves first lets pruning cut deeper.
    Builds
    A search order that tries the centre, then the corners, then the edges.
    Check
    Compare the counts before and after. The outcome stays the same.
  5. Session 5: When the tree is too big

    Covers
    Depth limits and scoring rules for games like Connect Four, where the search cannot reach the end.
    Builds
    A player that stops at a set depth and scores the board with a simple rule.
    Check
    Play it against a player that moves at random, and count the results.
  6. Session 6: Explain it back

    Covers
    Writing up what was built in plain words, and testing the parts that are easy to get wrong.
    Builds
    A short README and a few tests for the win check.
    Check
    Someone new can run it and follow it without help.

Run the lesson: minimax, then alpha-beta

Concept illustration, not a real system. The positions are made up, and nothing here comes from a real session or a real learner.

Pick a starting position or play a few moves, then compare the two searches. They always agree on how the game ends with best play. Alpha-beta gets there by visiting fewer positions, and the rows below show where it saves the most.

  1. 01

    Minimax

    Try every move until the game ends. A finished game scores +1 if X wins, −1 if O wins and 0 for a draw. X takes the highest score, O takes the lowest, and the scores pass back up the tree.

  2. 02

    Alpha-beta

    Carry two numbers down the tree. Alpha is the best score X can already count on, and beta is the best score O can already count on. Once alpha reaches beta, nothing left in that branch can change the answer, so the search stops there.

  3. 03

    Order

    Pruning cuts deepest when strong moves are tried first. Switch the search order and watch the alpha-beta count change while the outcome stays the same.

Synthetic positions · written from scratch · runs in your browser

O to move

Choose an empty square to play the next mark. Arrow keys move between squares, and Enter or Space plays.

Best move

Start from

X opened in the centre. O to move, with eight squares to choose from.

Search order

Squares 1 to 9, left to right and top to bottom.

Positions visited from here

Minimax
55,505
Alpha-beta
2,316
Fewer
96%
Branches cut
1,014

Best moveTop left. With best play from here, it is a draw.

Move by move

Each row is one move from this position, in search order, with what it leads to under best play. The top bar is minimax and the bottom bar is alpha-beta, on the same scale.

  1. Top leftDraw

    Minimax6,812
    Alpha-beta703

    New best so far

  2. Top middleX wins

    Minimax7,064
    Alpha-beta166

    Cannot beat top left. Alpha-beta stopped once it knew.

  3. Top rightDraw

    Minimax6,812
    Alpha-beta254

    Cannot beat top left. Alpha-beta stopped once it knew.

  4. Middle leftX wins

    Minimax7,064
    Alpha-beta106

    Cannot beat top left. Alpha-beta stopped once it knew.

  5. Middle rightX wins

    Minimax7,064
    Alpha-beta174

    Cannot beat top left. Alpha-beta stopped once it knew.

  6. Bottom leftDraw

    Minimax6,812
    Alpha-beta282

    Cannot beat top left. Alpha-beta stopped once it knew.

  7. Bottom middleX wins

    Minimax7,064
    Alpha-beta311

    Cannot beat top left. Alpha-beta stopped once it knew.

  8. Bottom rightDraw

    Minimax6,812
    Alpha-beta319

    Cannot beat top left. Alpha-beta stopped once it knew.

O to move. Best move: top left, and with best play it is a draw. Minimax visited 55,505 positions and alpha-beta visited 2,316, 96% fewer.

The code

function minimax(board, player, order, stats) {  stats.nodes += 1;  const score = finalScore(board);  if (score !== null) return score;  let best = player === "X" ? -Infinity : Infinity;  for (const cell of order) {    if (board[cell]) continue;    board[cell] = player;    const value = minimax(board, other(player), order, stats);    board[cell] = null;    best = player === "X" ? Math.max(best, value) : Math.min(best, value);  }  return best;}

Marked lines are where alpha-beta differs from minimax.

How it is built

Role
Mentor and author
Mentoring
2024 – 2025
Data
Synthetic positions
Runs
In your browser
  • JavaScript
  • React
  • Next.js
  • Tailwind CSS

The search engine is plain JavaScript with no library behind it. It runs in your browser, and nothing is stored or sent anywhere.

The two search functions are the ones in the code panel. The only extra is a counter that adds one for every position a search visits, and every number on the page comes from it. The page runs both searches from the same position, one move at a time, so each row can show what that move cost.

The board is nine native buttons. Arrow keys move between squares, and a live region reads out the result after each change. Nothing moves on its own, so the lesson works the same with reduced motion turned on.

My role

I mentor and teach, and this card is where the concepts and small projects from that work will be written up. I wrote everything on this page myself, from the learning path to the search code.

In a session, I try to find the one idea that unlocks the next step, then help the learner build something small enough to finish and check before we stop.

What comes next

This page is a plan. The full path will be written up one session at a time, and every example on it will be rewritten from scratch or built on synthetic data. Nothing from real sessions, and nothing about the people in them, will be published.

A concept page. The lesson and every position in it are synthetic and written from scratch.

Back to projects