11import { Chess } from 'chess.ts'
2- import { Key , useContext , useEffect , useMemo , useState } from 'react'
2+ import {
3+ Key ,
4+ useContext ,
5+ useEffect ,
6+ useMemo ,
7+ useState ,
8+ useCallback ,
9+ useRef ,
10+ } from 'react'
311
4- import { AnalyzedGame } from 'src/types'
12+ import { AnalyzedGame , GameNode } from 'src/types'
513import type { DrawShape } from 'chessground/draw'
614import { MAIA_MODELS } from 'src/constants/common'
715import { useTreeController , useLocalStorage } from '..'
816import { useEngineAnalysis } from './useEngineAnalysis'
9- import { useGameAnalysis } from './useGameAnalysis'
1017import { useBoardDescription } from './useBoardDescription'
1118import { useMoveRecommendations } from './useMoveRecommendations'
1219import { MaiaEngineContext } from 'src/contexts/MaiaEngineContext'
1320import { generateColorSanMapping , calculateBlunderMeter } from './utils'
1421import { StockfishEngineContext } from 'src/contexts/StockfishEngineContext'
1522
23+ export interface GameAnalysisProgress {
24+ currentMoveIndex : number
25+ totalMoves : number
26+ currentMove : string
27+ isAnalyzing : boolean
28+ isComplete : boolean
29+ isCancelled : boolean
30+ }
31+
32+ export interface GameAnalysisConfig {
33+ targetDepth : number
34+ }
35+
1636export const useAnalysisController = (
1737 game : AnalyzedGame ,
1838 initialOrientation ?: 'white' | 'black' ,
@@ -30,6 +50,151 @@ export const useAnalysisController = (
3050 const [ analysisState , setAnalysisState ] = useState ( 0 )
3151 const inProgressAnalyses = useMemo ( ( ) => new Set < string > ( ) , [ ] )
3252
53+ // Game analysis state
54+ const [ gameAnalysisConfig , setGameAnalysisConfig ] =
55+ useState < GameAnalysisConfig > ( {
56+ targetDepth : 18 ,
57+ } )
58+
59+ const [ gameAnalysisProgress , setGameAnalysisProgress ] =
60+ useState < GameAnalysisProgress > ( {
61+ currentMoveIndex : 0 ,
62+ totalMoves : 0 ,
63+ currentMove : '' ,
64+ isAnalyzing : false ,
65+ isComplete : false ,
66+ isCancelled : false ,
67+ } )
68+
69+ const gameAnalysisController = useRef < {
70+ cancelled : boolean
71+ currentNode : GameNode | null
72+ } > ( {
73+ cancelled : false ,
74+ currentNode : null ,
75+ } )
76+
77+ // Simple batch analysis functions that reuse existing analysis infrastructure
78+ const startGameAnalysis = useCallback (
79+ async ( targetDepth : number ) => {
80+ // If already analyzing, cancel the current analysis first
81+ if ( gameAnalysisProgress . isAnalyzing ) {
82+ gameAnalysisController . current . cancelled = true
83+ stockfish . stopEvaluation ( )
84+ }
85+
86+ // Reset state
87+ gameAnalysisController . current . cancelled = false
88+ gameAnalysisController . current . currentNode = null
89+
90+ const mainLine = game . tree . getMainLine ( )
91+
92+ setGameAnalysisConfig ( { targetDepth } )
93+ setGameAnalysisProgress ( {
94+ currentMoveIndex : 0 ,
95+ totalMoves : mainLine . length ,
96+ currentMove : '' ,
97+ isAnalyzing : true ,
98+ isComplete : false ,
99+ isCancelled : false ,
100+ } )
101+
102+ // Wait for engines to be ready
103+ let retries = 0
104+ const maxRetries = 50 // 5 seconds
105+
106+ while (
107+ retries < maxRetries &&
108+ ( ! stockfish . isReady ( ) || maia . status !== 'ready' ) &&
109+ ! gameAnalysisController . current . cancelled
110+ ) {
111+ await new Promise ( ( resolve ) => setTimeout ( resolve , 100 ) )
112+ retries ++
113+ }
114+
115+ if ( gameAnalysisController . current . cancelled ) {
116+ setGameAnalysisProgress ( ( prev ) => ( {
117+ ...prev ,
118+ isAnalyzing : false ,
119+ isCancelled : true ,
120+ } ) )
121+ return
122+ }
123+
124+ // Analyze each position in the main line
125+ for ( let i = 0 ; i < mainLine . length ; i ++ ) {
126+ if ( gameAnalysisController . current . cancelled ) break
127+
128+ const node = mainLine [ i ]
129+ gameAnalysisController . current . currentNode = node
130+
131+ // Update the UI to show the current node being analyzed (live update)
132+ controller . setCurrentNode ( node )
133+
134+ const moveDisplay = node . san || node . move || `Position ${ i + 1 } `
135+
136+ setGameAnalysisProgress ( ( prev ) => ( {
137+ ...prev ,
138+ currentMoveIndex : i + 1 ,
139+ currentMove : moveDisplay ,
140+ } ) )
141+
142+ // Wait for analysis to reach target depth (the useEngineAnalysis will handle this)
143+ let analysisRetries = 0
144+ const maxAnalysisRetries = 600 // 60 seconds max per position
145+
146+ while (
147+ analysisRetries < maxAnalysisRetries &&
148+ ! gameAnalysisController . current . cancelled &&
149+ ( ! node . analysis . stockfish ||
150+ node . analysis . stockfish . depth < targetDepth )
151+ ) {
152+ await new Promise ( ( resolve ) => setTimeout ( resolve , 100 ) )
153+ analysisRetries ++
154+ }
155+ }
156+
157+ // Analysis complete
158+ setGameAnalysisProgress ( ( prev ) => ( {
159+ ...prev ,
160+ isAnalyzing : false ,
161+ isComplete : ! gameAnalysisController . current . cancelled ,
162+ isCancelled : gameAnalysisController . current . cancelled ,
163+ } ) )
164+
165+ gameAnalysisController . current . currentNode = null
166+ } ,
167+ [
168+ game . tree ,
169+ gameAnalysisProgress . isAnalyzing ,
170+ stockfish ,
171+ maia . status ,
172+ controller . setCurrentNode ,
173+ ] ,
174+ )
175+
176+ const cancelGameAnalysis = useCallback ( ( ) => {
177+ gameAnalysisController . current . cancelled = true
178+ stockfish . stopEvaluation ( )
179+
180+ setGameAnalysisProgress ( ( prev ) => ( {
181+ ...prev ,
182+ isAnalyzing : false ,
183+ isCancelled : true ,
184+ } ) )
185+ } , [ stockfish ] )
186+
187+ const resetGameAnalysisProgress = useCallback ( ( ) => {
188+ setGameAnalysisProgress ( {
189+ currentMoveIndex : 0 ,
190+ totalMoves : 0 ,
191+ currentMove : '' ,
192+ isAnalyzing : false ,
193+ isComplete : false ,
194+ isCancelled : false ,
195+ } )
196+ } , [ ] )
197+
33198 const [ currentMove , setCurrentMove ] = useState < [ string , string ] | null > ( )
34199 const [ currentMaiaModel , setCurrentMaiaModel ] = useLocalStorage (
35200 'currentMaiaModel' ,
@@ -47,13 +212,7 @@ export const useAnalysisController = (
47212 inProgressAnalyses ,
48213 currentMaiaModel ,
49214 setAnalysisState ,
50- )
51-
52- const gameAnalysis = useGameAnalysis (
53- game . tree ,
54- currentMaiaModel ,
55- setAnalysisState ,
56- controller . setCurrentNode ,
215+ gameAnalysisProgress . isAnalyzing ? gameAnalysisConfig . targetDepth : 18 ,
57216 )
58217
59218 const availableMoves = useMemo ( ( ) => {
@@ -187,6 +346,14 @@ export const useAnalysisController = (
187346 arrows,
188347 stockfish : stockfish ,
189348 maia : maia ,
190- gameAnalysis,
349+ gameAnalysis : {
350+ progress : gameAnalysisProgress ,
351+ config : gameAnalysisConfig ,
352+ setConfig : setGameAnalysisConfig ,
353+ startAnalysis : startGameAnalysis ,
354+ cancelAnalysis : cancelGameAnalysis ,
355+ resetProgress : resetGameAnalysisProgress ,
356+ isEnginesReady : stockfish . isReady ( ) && maia . status === 'ready' ,
357+ } ,
191358 }
192359}
0 commit comments