From 1dd54e893763a7faf53d2143223ae529d1e046b1 Mon Sep 17 00:00:00 2001 From: Stefan Date: Mon, 24 Nov 2025 17:53:48 +0100 Subject: [PATCH] fixes --- public/sounds/cc0_click.wav | 1 - src/components/ChessGame.tsx | 5 +- src/components/GameAnalysisModal.tsx | 7 ++- src/components/GameOverModal.tsx | 88 +++++++++++++++++++--------- 4 files changed, 69 insertions(+), 32 deletions(-) delete mode 100644 public/sounds/cc0_click.wav diff --git a/public/sounds/cc0_click.wav b/public/sounds/cc0_click.wav deleted file mode 100644 index 1becba2..0000000 --- a/public/sounds/cc0_click.wav +++ /dev/null @@ -1 +0,0 @@ -404: Not Found \ No newline at end of file diff --git a/src/components/ChessGame.tsx b/src/components/ChessGame.tsx index 5e565d0..c66949f 100644 --- a/src/components/ChessGame.tsx +++ b/src/components/ChessGame.tsx @@ -199,12 +199,13 @@ export default function ChessGame({ initialFen, initialPgn, initialPersonality, // Pre-Analysis (P0) useEffect(() => { - if (stockfish && gameRef.current.turn() === 'w' && !isAnalyzing && !gameOverState) { + const playerTurn = playerColor === 'white' ? 'w' : 'b'; + if (stockfish && gameRef.current.turn() === playerTurn && !isAnalyzing && !gameOverState) { stockfish.evaluate(gameRef.current.fen(), stockfishDepth).then(evalResult => { setEvalP0(evalResult); }).catch(err => console.error("Pre-analysis failed:", err)); } - }, [fen, stockfish, stockfishDepth, isAnalyzing, gameOverState]); + }, [playerColor, fen, stockfish, stockfishDepth, isAnalyzing, gameOverState]); const updateCapturedPieces = useCallback(() => { const history = gameRef.current.history({ verbose: true }); diff --git a/src/components/GameAnalysisModal.tsx b/src/components/GameAnalysisModal.tsx index ace3589..5592f18 100644 --- a/src/components/GameAnalysisModal.tsx +++ b/src/components/GameAnalysisModal.tsx @@ -38,19 +38,22 @@ export function GameAnalysisModal({ fen, stockfish, apiKey, language, onClose }: // 3. LLM Summary if (apiKey && evalResult) { const model = getGenAIModel(apiKey, "gemini-2.5-flash"); + const evalInPawns = (evalResult.score / 100).toFixed(2); const prompt = ` You are a Chess Grandmaster Analyst. Analyze this position for the user. DATA: - FEN: ${fen} -- Evaluation: ${evalResult.score} cp (positive = White advantage, negative = Black advantage) +- Evaluation: ${evalInPawns} pawns (${evalResult.score} centipawns) + Note: Positive = White advantage, Negative = Black advantage + 100 centipawns = 1 pawn - Mate in: ${evalResult.mate ?? "N/A"} - Best Move: ${evalResult.bestMove} - Opening: ${openingData ? `${openingData.name} (${openingData.eco})` : "Unknown/Midgame"} INSTRUCTIONS: -1. Summarize who is winning and why (based on score). +1. Summarize who is winning and why (based on score). Use the pawn value (e.g., "White is up 2.5 pawns" not "250 centipawns"). 2. Identify the key strategic factors (space, piece activity, king safety). 3. Mention the opening if relevant. 4. Keep it concise (max 3-4 sentences). diff --git a/src/components/GameOverModal.tsx b/src/components/GameOverModal.tsx index 279b474..44d21b6 100644 --- a/src/components/GameOverModal.tsx +++ b/src/components/GameOverModal.tsx @@ -10,6 +10,8 @@ export interface MoveHistoryItem { evalBefore: number; // cp evalAfter: number; // cp bestMove?: string; + category?: 'inaccuracy' | 'mistake' | 'blunder'; + cpLoss?: number; } interface GameOverModalProps { @@ -31,41 +33,56 @@ export function GameOverModal({ result, winner, history, apiKey, language, onClo const analyzeGame = async () => { setIsLoading(true); try { - // 1. Identify Mistakes (Blunders) - // A blunder is roughly a drop of > 100cp (1 pawn) or missing a mate - const detectedMistakes = history.filter(item => { - const delta = item.evalAfter - item.evalBefore; - // Note: eval is from White's perspective. - // If White moves, eval should ideally go up or stay same. - // If eval drops significantly, it's a mistake. - return delta <= -100; - }); + // 1. Identify Mistakes with proper categorization + // Standard chess analysis thresholds: + // - Inaccuracy: 50-100 centipawns loss + // - Mistake: 100-300 centipawns loss + // - Blunder: 300+ centipawns loss + const detectedMistakes = history.map(item => { + const delta = item.evalBefore - item.evalAfter; // Positive = eval got worse for player + let category: 'inaccuracy' | 'mistake' | 'blunder' | null = null; + + if (delta >= 300) category = 'blunder'; + else if (delta >= 100) category = 'mistake'; + else if (delta >= 50) category = 'inaccuracy'; + + return { ...item, category, cpLoss: delta }; + }).filter(item => item.category !== null) as MoveHistoryItem[]; + setMistakes(detectedMistakes); // 2. LLM Analysis if (apiKey) { const model = getGenAIModel(apiKey, "gemini-2.5-flash"); + const blunders = detectedMistakes.filter(m => m.category === 'blunder'); + const mistakes = detectedMistakes.filter(m => m.category === 'mistake'); + const inaccuracies = detectedMistakes.filter(m => m.category === 'inaccuracy'); + const mistakesText = detectedMistakes.map(m => - `Move ${m.moveNumber}: Played ${m.move} (Eval dropped from ${m.evalBefore} to ${m.evalAfter}). Best move was likely ${m.bestMove}.` + `Move ${m.moveNumber}: ${m.move} (${m.category?.toUpperCase()}: -${m.cpLoss}cp, eval ${m.evalBefore} → ${m.evalAfter}). Best: ${m.bestMove}` ).join("\n"); const prompt = ` You are a Chess Coach. The game is over. Result: ${result} (${winner === "Draw" ? "Draw" : winner + " Won"}). -Here are the player's (White) key mistakes (Blunders): -${mistakesText || "No major blunders detected."} +Player's Performance Summary: +- Blunders (300+ cp loss): ${blunders.length} +- Mistakes (100-300 cp loss): ${mistakes.length} +- Inaccuracies (50-100 cp loss): ${inaccuracies.length} + +${mistakesText ? `Detailed Mistakes:\n${mistakesText}` : "No significant mistakes detected - excellent play!"} INSTRUCTIONS: 1. Briefly comment on the game result. -2. If there were mistakes, explain WHY they were bad and what the player should have looked for (tactics, hanging pieces, etc.). -3. If no mistakes, praise the solid play. -4. Be encouraging but educational. +2. If there were mistakes, explain WHY the worst ones were bad and what the player should have looked for (tactics, hanging pieces, positional errors, etc.). +3. If no mistakes, praise the solid play and suggest areas for improvement. +4. Be encouraging but educational. Focus on learning. 5. Respond in ${language.toUpperCase()}. OUTPUT FORMAT: -Plain text paragraph. +Plain text paragraph (2-3 sentences). `; const resultGen = await model.generateContent(prompt); @@ -112,18 +129,35 @@ Plain text paragraph. Key Moments / Mistakes
- {mistakes.map((m, idx) => ( -
- Move {m.moveNumber}: {m.move} - | - Eval: {m.evalBefore} ➝ {m.evalAfter} - {m.bestMove && ( -
- Best was likely: {m.bestMove} + {mistakes.map((m, idx) => { + const categoryColors = { + inaccuracy: 'bg-yellow-50 dark:bg-yellow-900/10 border-yellow-200 dark:border-yellow-900/30 text-yellow-700 dark:text-yellow-400', + mistake: 'bg-orange-50 dark:bg-orange-900/10 border-orange-200 dark:border-orange-900/30 text-orange-700 dark:text-orange-400', + blunder: 'bg-red-50 dark:bg-red-900/10 border-red-200 dark:border-red-900/30 text-red-700 dark:text-red-400' + }; + const categoryColor = categoryColors[m.category || 'inaccuracy']; + + return ( +
+
+ Move {m.moveNumber}: {m.move} + + {m.category} +
- )} -
- ))} +
+ Loss: -{m.cpLoss}cp + | + Eval: {m.evalBefore} → {m.evalAfter} +
+ {m.bestMove && ( +
+ Best: {m.bestMove} +
+ )} +
+ ); + })}
)}