import { Router, Request, Response } from 'express'; import type { WordBank, GuessResponse, CachedResult } from '@wordle/shared'; import { isValidGuess, getTargetWord } from '../validate.js'; import { computeFeedback } from '../feedback.js'; import { computeReplays } from '@wordle/shared'; import { runSolverInWorker } from '../solver-pool.js'; export function createGuessRouter(wordBank: WordBank, cache: Map): Router { const router = Router(); router.post('/', (req: Request, res: Response) => { const { wordId, guess, tryNo } = req.body ?? {}; // Validate request shape if (typeof wordId !== 'number' || typeof guess !== 'string' || typeof tryNo !== 'number') { res.status(400).json({ error: 'Invalid request: wordId (number), guess (string), and tryNo (number) required' }); return; } if (guess.length !== 5) { res.status(400).json({ error: 'Guess must be exactly 5 letters' }); return; } // Validate the guess is a real word if (!isValidGuess(wordBank, guess)) { const response: GuessResponse = { valid: false }; res.json(response); return; } // Look up target word const target = getTargetWord(wordBank, wordId); if (!target) { res.status(404).json({ error: `Word ${wordId} not found` }); return; } // Compute feedback const colors = computeFeedback(target.word, guess); const correct = colors.every((c) => c === 'g'); const isLastAttempt = tryNo >= 6; const response: GuessResponse = { valid: true, colors, correct, }; // Reveal word on loss (last attempt, not correct) if (isLastAttempt && !correct) { response.word = target.word; } // Compute difficulty and replays on final guess (win or loss) if (correct || isLastAttempt) { let result = cache.get(wordId); if (!result) { // Cache miss — compute with 3 fast solvers for immediate response result = computeReplays(target.word, wordBank.guessable); cache.set(wordId, result); // Fire background computation with all 4 solvers (including entropy) // in a worker thread so the ~14s entropy run never blocks the API. const targetWord = target.word; const guessable = wordBank.guessable; runSolverInWorker({ target: targetWord, allWords: guessable, mode: 'full' }) .then((full) => { cache.set(wordId, full); }) .catch((err) => { console.error('Solver worker failed:', err); }); } response.difficulty = result.difficulty; response.replays = result.replays; } res.json(response); }); return router; }