Post-game analysis powered by Stockfish.
Queue a game for analysis. Must be a player in the game. Game must be completed.
Response (200):
{ "status": "queued", "message": "Analysis queued" }If already queued/processing: returns current status without re-queuing.
Get analysis results.
Response (processing):
{ "status": "processing", "analysis": null }Response (done):
{
"status": "done",
"analysis": {
"id": "clx...",
"whiteAccuracy": 87.3,
"blackAccuracy": 72.1,
"opening": { "name": "Sicilian Defense", "eco": "B20" },
"feedback": [
{
"ply": 1,
"san": "e4",
"uci": "e2e4",
"fen": "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq - 0 1",
"classification": "BEST",
"bestMove": "e2e4",
"evalBefore": 0,
"evalAfter": 25
}
]
}
}- API pushes
gameIdto Redisanalysis:queuelist - Worker polls queue every 2 seconds
- For each position:
- Stockfish evaluates at depth 18 (MultiPV 1)
- For brilliant detection: MultiPV 2 on position before the move
- Each move classified by centipawn loss
- Accuracy computed per player
- Results saved to
GameAnalysis+MoveFeedbacktables
The worker includes several reliability mechanisms:
- Circuit breaker: If 3 consecutive analysis jobs fail, the Stockfish engine is automatically restarted
- Retry tracking: Failed jobs are re-queued up to 3 times (retry count tracked in Redis with 1h TTL)
- Dead letter queue: Jobs that fail 3 times are moved to
analysis:dlqand marked as error — they won't be retried - Graceful recovery: After engine restart, the worker resumes processing from the next job in the queue
| Classification | CP Loss | Color |
|---|---|---|
| Brilliant | < 5 + sacrifice + alternatives > 150 worse | Cyan |
| Great | 0 - 5 | Blue |
| Best | 0 - 10 | Green |
| Excellent | 10 - 25 | Light green |
| Good | 25 - 50 | Gray |
| Inaccuracy | 50 - 100 | Yellow |
| Mistake | 100 - 200 | Orange |
| Blunder | 200+ | Red |
| Forced | Only 1 legal move | Gray |
A move is brilliant if:
- It involves a material sacrifice (attacker value > captured value)
- Centipawn loss < 5 (it's a good move)
- The next-best alternative loses > 150 centipawns
accuracy = average(2 / (1 + exp(0.004 * cpLoss)) * 100)
Computed separately for white and black moves.
~65 common openings indexed by SAN move sequence. Longest prefix match (most specific opening that matches the game's moves).
Tracked in Redis as analysis:status:{gameId}:
queued— waiting in queueprocessing— worker is analyzingdone— results saved to DBerror— analysis failed