I Built ChessIQ to Train Positional Chess Evaluation, Not Just Tactics A developer built ChessIQ, a positional evaluation trainer that asks players to judge whether White or Black is better in real game positions before revealing Stockfish evaluations and AI-generated explanations. The tool pre-generates explanations to keep feedback instant and uses a normalization layer mapping AI-generated themes into 73 canonical positional concepts for consistent analytics. ChessIQ also features adaptive puzzle difficulty ratings based on community performance. If you've played competitive chess, you've probably hit this moment: Neither side has a tactic. Nothing is hanging. No forced combination to calculate. But a strong player glances at the board for three seconds and says: "White is clearly better here." That judgment — fast, confident, pre-calculation — is one of the hardest skills in chess to practice deliberately. So I built ChessIQ to fix that. ChessIQ is a positional evaluation trainer. Instead of asking you to find the best move like a typical puzzle site, it shows you a real game position and asks one question: White is better / Equal / Black is better You commit to your answer before seeing the engine. Then ChessIQ shows you the Stockfish evaluation and a pre-generated AI explanation of the positional ideas behind it. You can try it without an account at chessiqapp.com . Traditional chess puzzles are great at training calculation . You see a position, find a tactic, calculate variations, get the move. But real games aren't always like that. A huge portion of chess is spent in positions where there's no immediate tactic — and you're instead asking: Strong players answer these almost intuitively. ChessIQ is designed to make that intuition something you can practice, measure, and improve . The three-way format matters here — it's fast enough to do many positions per session while still producing a meaningful signal about your positional judgment. The frontend is conventional HTML/CSS/JS backed by Firebase. The interesting part is upstream. The preprocessing pipeline looks roughly like this: Lichess games → candidate position extraction → filtering → Stockfish analysis → metadata generation → AI explanation pre-generated → Firebase → ChessIQ A few design decisions I'm glad I made early: AI explanations are generated ahead of time. No LLM call sits in the training loop. Feedback is instant, and API costs don't scale with user interactions. The AI explanation is feedback, not ground truth. Stockfish is still the underlying evaluation signal. The model's job is to turn that signal and position metadata into something a player can actually learn from. Every puzzle gets tagged with positional themes — things like Piece Activity , King Safety , Weak Squares , Outposts , Passed Pawns . The problem: an LLM doesn't naturally produce a consistent taxonomy. One explanation might say "weak squares" . Another says "weak dark squares" . Another says "strong knight outpost" . Fine individually — but if each generated phrase becomes its own category, your analytics become useless. My solution: a normalization layer that maps generated themes into a canonical vocabulary of 73 positional concepts . AI explanation text → raw themes extracted → mapped to canonical 73-theme vocabulary → stored as structured data The explanation stays natural and descriptive. The rest of the app gets stable, consistent identifiers it can actually use for thematic training and analytics. ChessIQ has two different rating concepts, and they're genuinely different problems. Player Position Rating PR The player's long-term progression metric. Designed to answer: how consistently good am I at evaluating chess positions? It uses compression curves, provisional periods, streak bonuses, and inactivity decay to reward genuine sustained improvement — not luck. Puzzle difficulty rating A static Easy/Medium/Hard label goes stale as real player data accumulates. A position that looked Hard during preprocessing might consistently get answered correctly by real players. So ChessIQ tracks community performance on individual positions and updates their difficulty ratings adaptively over time. I'm treating this as an ongoing experiment — sparse data and selection effects are real problems at this scale, and I'd rather have a smaller dataset I can inspect than a huge one I can't reason about. Missed positions enter a personal review queue — but on a completion-based schedule, not a time-based one. If someone does 100 puzzles in one session, and someone else does 5 puzzles over a week, they should get a similar training experience. Tying review to the calendar would make the first person's experience wildly different from the second's. Current open question I'm sitting with: is reviewing the exact same position the right unit? Or should review surface a structurally similar position that tests the same concept in a new setting? ChessIQ v1.4.8 ships with: A few open problems I'd love input on: 1. Calibrating "Equal" Engine evaluations are continuous. Turning them into three discrete buckets is lossy. Where should the Equal boundaries sit — and should they change based on position type or player strength? 2. Fixed taxonomy vs. embeddings Is a canonical 73-theme vocabulary the right abstraction for personalization? Or would embeddings and clustering eventually produce better, more nuanced results? 3. What to measure for actual improvement PR going up is a proxy metric. What I'd really like to know: does ChessIQ make someone's positional evaluations more accurate on positions they've never seen before ? That's a much harder measurement problem. If you're into chess, training systems, rating design, or just want to see how a Stockfish + Firebase + Python data pipeline fits together — I'd love feedback. Accounts are free and add PR tracking, history, achievements, and your personal review queue. Or just play anonymously. Happy to answer questions about the data pipeline, the PR algorithm, the theme normalization layer, or anything else in the comments. 🏆