Click a piece, click a destination: move (legal destinations are tinted) Hold N, B or R while clicking: under-promote instead of queening G: new game F: swap sides (the engine takes over your colour) U: undo your move and the engine's reply SPACE: let the engine play your move too ↑ / ↓: search depth, 1 – 5 Q: quiescence search on / off A: measure pruning: run the same search with and without alpha-beta T: run the perft suite 1 / 2 / 3: perft suite depth The right-hand panel shows, per iterative-deepening iteration, the node count, the effective branching factor and the evaluation in pawns, then how long the whole search took. Underneath: the measured pruning table, and the perft results in green or red. The EBF is nodes^(1/depth) computed on main-search nodes only — quiescence nodes are extra plies of captures and would inflate it into nonsense (at depth 1 they doubled it). Measured from the opening position it reads 21 / 8.1 / 9.1 for depths 1 / 2 / 3 — against a real chess branching factor near 30, that is alpha-beta's √b claim showing up in the readout on every single move.
A complete chess engine — full legal move generation, castling, en passant, promotion, checkmate and stalemate — with an alpha-beta search that reports its own node counts, a live pruning measurement, and the perft suite that proves the move generator right, all on the stage next to the board. VERIFICATION: PERFT Perft counts the leaf nodes of the legal move tree. It is the only test that finds move-generation bugs, because an off-by-one in castling rights or a mishandled en passant produces a game that plays almost legal chess and looks fine. The project ships five standard positions and checks them against the published counts. Run under the headless harness at depth 3: | Position | Depth 3 | Published | | | --- | --- | --- | --- | | START | 8902 | 8902 | ✅ | | KIWIPETE | 97862 | 97862 | ✅ | | ENDGAME | 2812 | 2812 | ✅ | | PROMOTION | 9467 | 9467 | ✅ | | TRICKY | 62379 | 62379 | ✅ | Depths 1 and 2 also match on all five (20/400, 48/2039, 14/191, 6/264, 44/1486). Between them these positions exercise castling both sides for both colours, castling through check, en passant, promotion including under-promotion, pins and check evasion — none of which the initial position reaches in three plies, which is exactly why one perft number is not enough. After every perft run the board state is fully restored: side to move, castling rights, en passant square, incremental evaluation and the move stack all return to their exact prior values, which is an independent check that unmake is a true inverse of make. VERIFICATION: PRUNING, MEASURED NOT ASSERTED A runs the same search twice from the current position — same move ordering, same evaluation, same tree — once with the alpha-beta window and once with a window so wide it can never cut, which is plain minimax. Quiescence is off in both, because quiescence is itself an alpha-beta technique and has no unpruned counterpart. From the initial position: | Depth | Minimax | Alpha-beta | Saved | | --- | --- | --- | --- | | 1 | 21 | 21 | 1.0× | | 2 | 421 | 126 | 3.3× | | 3 | 9323 | 1120 | 8.3× | The saving compounds with depth, which is the whole point — that is alpha-beta turning a branching factor of b into roughly √b. And a pleasing cross-check falls out of it: **21, 421 and 9323 are exactly 1+20, 1+20+400 and 1+20+400+8902** — the cumulative perft sums. Plain minimax visits every node of the tree perft counts, so two entirely separate routines in the project agree on the size of the game tree to three plies. Had move generation been wrong, both would have been wrong together, but they would not have matched the published perft values. TECHNICAL DECISIONS 10×12 mailbox board. The 64 real squares sit inside a 120-square array ringed by a two-deep border of sentinels. A knight can jump from any real square and land inside the array; a sliding ray stops on the sentinel exactly as it stops on a piece. The result is that **there is not one bounds check anywhere in move generation** — which in Scratch, where every comparison is a block, is the difference between a playable engine and a slideshow. A move is one number: from10000 + to100 + flag. Scratch has no structs, and a parallel-list move stack would mean four lists kept in step across recursion. Packing into a single integer stays exact (well inside the 2⁵³ a double holds) and makes the shared move stack one list. Evaluation is incremental. All original - code, art and sound. See Inside is open.