@@ -251,6 +251,7 @@ export const useOpeningDrillController = (
251251 : index % 2 === 1
252252 return isPlayerMove
253253 } ) ,
254+ allMoves : drillGame . moves , // Store the full move sequence
254255 totalMoves : playerMoveCount ,
255256 blunders : Array ( blunders ) . fill ( 'placeholder' ) ,
256257 goodMoves : Array ( goodMoves ) . fill ( 'placeholder' ) ,
@@ -356,6 +357,125 @@ export const useOpeningDrillController = (
356357 setWaitingForMaiaResponse ( false )
357358 } , [ ] )
358359
360+ // Load a specific completed drill for analysis
361+ const loadCompletedDrill = useCallback (
362+ ( completedDrill : CompletedDrill ) => {
363+ // Set the drill as current
364+ setCurrentDrill ( completedDrill . selection )
365+
366+ // Check if this drill is already the current drill and we can reuse the game tree
367+ if (
368+ currentDrillGame &&
369+ currentDrillGame . selection . id === completedDrill . selection . id &&
370+ currentDrillGame . playerMoveCount === completedDrill . totalMoves
371+ ) {
372+ // Reuse the existing game tree and just enable analysis mode
373+ setAnalysisEnabled ( true )
374+ setContinueAnalyzingMode ( true )
375+ setWaitingForMaiaResponse ( false )
376+ return
377+ }
378+
379+ // Try to reconstruct the game tree from the finalNode path
380+ const startingFen =
381+ 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1'
382+ const gameTree = new GameTree ( startingFen )
383+
384+ // Parse the PGN to populate the tree with opening moves
385+ const pgn = completedDrill . selection . variation
386+ ? completedDrill . selection . variation . pgn
387+ : completedDrill . selection . opening . pgn
388+ const endNode = parsePgnToTree ( pgn , gameTree )
389+
390+ let finalNode = endNode
391+
392+ // Reconstruct the full game using the stored allMoves sequence
393+ if (
394+ endNode &&
395+ completedDrill . allMoves &&
396+ completedDrill . allMoves . length > 0
397+ ) {
398+ let currentNode = endNode
399+ const chess = new Chess ( endNode . fen )
400+
401+ // Replay all moves from the drill (both player and Maia moves)
402+ for ( const moveUci of completedDrill . allMoves ) {
403+ try {
404+ const moveObj = chess . move ( moveUci , { sloppy : true } )
405+ if ( moveObj ) {
406+ const newNode = gameTree . addMainMove (
407+ currentNode ,
408+ chess . fen ( ) ,
409+ moveUci ,
410+ moveObj . san ,
411+ )
412+ if ( newNode ) {
413+ currentNode = newNode
414+ finalNode = newNode
415+ }
416+ }
417+ } catch ( error ) {
418+ console . error ( 'Error replaying move:' , moveUci , error )
419+ break
420+ }
421+ }
422+ } else if ( endNode && completedDrill . playerMoves . length > 0 ) {
423+ // Fallback: use only player moves if allMoves is not available
424+ let currentNode = endNode
425+ const chess = new Chess ( endNode . fen )
426+
427+ for ( const moveUci of completedDrill . playerMoves ) {
428+ try {
429+ const moveObj = chess . move ( moveUci , { sloppy : true } )
430+ if ( moveObj ) {
431+ const newNode = gameTree . addMainMove (
432+ currentNode ,
433+ chess . fen ( ) ,
434+ moveUci ,
435+ moveObj . san ,
436+ )
437+ if ( newNode ) {
438+ currentNode = newNode
439+ finalNode = newNode
440+ }
441+ }
442+ } catch ( error ) {
443+ console . error ( 'Error replaying player move:' , moveUci , error )
444+ break
445+ }
446+ }
447+ }
448+
449+ const loadedGame : OpeningDrillGame = {
450+ id : completedDrill . selection . id + '-replay' ,
451+ selection : completedDrill . selection ,
452+ moves : completedDrill . allMoves || completedDrill . playerMoves ,
453+ tree : gameTree ,
454+ currentFen : finalNode ?. fen || endNode ?. fen || startingFen ,
455+ toPlay : finalNode
456+ ? new Chess ( finalNode . fen ) . turn ( ) === 'w'
457+ ? 'white'
458+ : 'black'
459+ : 'white' ,
460+ openingEndNode : endNode ,
461+ playerMoveCount : completedDrill . totalMoves ,
462+ }
463+
464+ setCurrentDrillGame ( loadedGame )
465+ setAnalysisEnabled ( true ) // Auto-enable analysis when loading a completed drill
466+ setContinueAnalyzingMode ( true ) // Allow moves beyond target count
467+ setWaitingForMaiaResponse ( false )
468+
469+ // Set the controller to the final position after a brief delay
470+ setTimeout ( ( ) => {
471+ if ( finalNode ) {
472+ controller . setCurrentNode ( finalNode )
473+ }
474+ } , 100 )
475+ } ,
476+ [ controller , currentDrillGame ] ,
477+ )
478+
359479 // Calculate overall performance data
360480 const overallPerformanceData = useMemo ( ( ) : OverallPerformanceData => {
361481 if ( completedDrills . length === 0 ) {
@@ -775,5 +895,8 @@ export const useOpeningDrillController = (
775895
776896 // Check if all drills are completed
777897 areAllDrillsCompleted,
898+
899+ // Load a specific completed drill for analysis
900+ loadCompletedDrill,
778901 }
779902}
0 commit comments