Posting about the adaptive difficulty approach I used in Chess Rocket (open-source chess tutor) because the sub-1320 Elo calibration problem doesn't get discussed much.
Stockfish UCI skill levels (0-20) map roughly to 1100-3500 Elo. Skill 0 plays around 1100. That's too strong for a 400-500 rated player, and the skill degradation isn't linear at the low end. It drops off steeply and unpredictably.
My approach for the 100-1320 Elo range: the engine picks its best move via depth-limited search, then with some probability replaces it with a random legal move. The probability is linear in Elo. At 100 it's near 1.0 (almost all random). At 1320 it's 0.0 (pure Stockfish Skill 0). Simple interpolation between those endpoints.
This gives much finer-grained difficulty where it matters most, at the beginner level.
Above 1320, I just use Stockfish's native `UCI_LimitStrength` and `UCI_Elo`, which work well in that range.
Other pieces in the project:
Opening database: 3,627 openings from Lichess, stored in SQLite. Searchable by ECO code, name, or partial move sequence.
Mistake tracking: SM-2 spaced repetition. Each mistake stores interval, ease factor, and repetition count. Positions resurface at the calculated review time, same scheduling logic as Anki.
Puzzle system: 284 puzzles across 9 sets (forks, pins, skewers, back-rank mates, beginner endgames, opening traps, etc.). Sourced from Stockfish self-play, Lichess DB, and constructed positions.
The chess tools are exposed to Claude via FastMCP (17 tools total). Claude does the coaching; Stockfish does the evaluation. They don't overlap.
GitHub:
If anyone has tried different approaches to the sub-1320 problem or has thoughts on the blending math, I'd like to hear about it.