← Back to Guides

Why the Chess AI Took Up to 40 Seconds a Move

Dev log: what was slow, how I fixed it, and how I checked the fix

Summary

Until September 2026, the Hard AI in Omokboard's Chess took an average of 5 seconds per move, and close to 40 seconds at worst. Now it averages 0.6 seconds and never took longer than 1.3 seconds (measured over 782 moves on October 18, 2026). Here is what was slow, how I fixed it, and how I checked that the new calculator never breaks the rules.

How Slow It Was

I had the AIs play each other and timed each move (September 24, 2026, on a laptop CPU with Node.js; the old Hard figures come from 6 games).

VersionAverageMax
Old Hard (3-move search)5.2 s39.9 s
Old Normal (2-move search)0.8 s5.3 s

Phones are several times slower than this. When it was the AI's turn, the screen looked frozen.

The Cause: Searching With the Rules Library

Chess rules on Omokboard are handled by a library called chess.js (0.10.3). It's plenty fast for checking whether your move is legal or whether the game is checkmate. The problem was that the AI used the same library to play out positions thousands or tens of thousands of times while choosing a move.

Every time this library lists the legal moves, it builds the notation for each one (like Nf3 or Qxe5+) and checks whether that move gives check or checkmate. Checking for checkmate means generating every legal move again, so a single search repeated the same work hundreds of thousands of times. On top of that, the old AI counted "how many moves are available" when scoring a position, which built the move list yet again for every position it scored.

The First Attempt Made It Slower

The old Hard AI had another weakness. It simply stopped calculating at the third move, so it sometimes judged a position in the middle of a trade and gave a piece away for free. Chess programmers call this the horizon effect.

So I first added "keep calculating while captures remain" on top of the same library, and one middlegame position took 65 seconds (65,740 ms) for a single move. I had stacked more work on top of a slow part. Before the AI could get stronger, it had to get faster.

The Fix: A Separate Calculator Just for the AI

I split the job in two. A new, lightweight calculator (chess-ai.js) decides where to move, while chess.js still plays the actual move and handles the rules (threefold repetition, the 50-move rule, and so on).

  • Board representation: It uses the 0x88 scheme, a classic trick that stores the board in a 128-square array so a single bitwise operation tells whether a piece has left the board.
  • Legal moves only, no notation: It generates only the legal moves and never builds move notation.
  • Depth that fits the time: Hard always reads 3 moves ahead and uses a 4-move search only when it finishes within 1 second. A slow device plays at 3 moves, a fast one at 4.
  • Finish the trade: If a trade is still in progress where the search ends, it keeps calculating until there are no captures left (up to 8 more moves). This is what stops the horizon effect.
  • Don't repeat when winning: The new calculator doesn't know about repeated positions, so when the AI is ahead it uses chess.js to skip any move that would allow a threefold-repetition draw.

How I Checked the Calculator Gets the Rules Right

If the calculator misses a legal move or invents an illegal one, the AI plays illegal chess. I checked it two ways.

1. Perft: This is the standard test chess programs use. It counts every possible sequence of moves to a set depth from a set position and compares the total with well-known reference values. A single wrong move changes the number.

PositionDepthReferenceResult
Starting position4 moves197,281Match
Kiwipete (a test position mixing castling, en passant, and pins)3 moves97,862Match

2. Cross-checking with the rules library: For each of 43,614 positions taken from randomly played games, I compared the number of legal moves counted by the new calculator and by chess.js. Not one differed.

Results

AI vs AI results right after the fix (September 24, 2026):

MatchupResult
New Hard vs Old Hard8 games: 7 wins, 1 draw
New Hard vs New Normal20 games: 19 wins, 1 draw

That day, New Hard averaged 0.6 seconds per move, with a maximum of 1.04 seconds. When I measured again on October 18, 2026, with 20 games per matchup, New Hard won all 20 games against New Normal and averaged 0.6 seconds per move, with a maximum of 1.3 seconds. The full results are in AI Difficulty Benchmarks.

It came at a cost. The new calculator scores a position only by piece values and square values. Without the "number of available moves" term, New Normal went 1 win, 15 draws, and 4 losses in 20 games against Old Normal, so it got slightly weaker. In exchange, the occasional 5-second freezes from Old Normal are gone.

Weak Spots That Remain

The scoring is simple, so it doesn't separately consider king safety or pawn structure, such as doubled or isolated pawns. It struggles against a slow attack on its king or a long squeeze on a pawn weakness, and it can't see tactics that land more than four moves out. If you want to beat the Hard AI, aim there. Basic tactics are in the Chess guide.

Check It Yourself

Play Chess now →