@@ -20,6 +20,7 @@ import {
2020 MaiaEvaluation ,
2121 StockfishEvaluation ,
2222 GameNode ,
23+ Termination ,
2324} from 'src/types'
2425import { WindowSizeContext , TreeControllerContext , useTour } from 'src/contexts'
2526import { Loading } from 'src/components'
@@ -57,13 +58,91 @@ import { MAIA_MODELS } from 'src/constants/common'
5758import { applyEngineAnalysisData } from 'src/lib/analysis'
5859
5960const EVAL_BAR_RANGE = 4
61+ const CUSTOM_PGN_RESULT_OVERRIDES_STORAGE_KEY =
62+ 'maia_custom_pgn_result_overrides'
63+ const PGN_RESULT_TOKEN_REGEX = / ( .* ?) (?: \s + ) ( 1 - 0 | 0 - 1 | 1 \/ 2 - 1 \/ 2 | \* ) \s * $ /
6064const DEFAULT_STOCKFISH_EVAL_BAR = {
6165 hasEval : false ,
6266 pawns : 0 ,
6367 displayPawns : 0 ,
6468 label : '--' ,
6569}
6670
71+ const splitTrailingPgnResult = (
72+ pgn : string ,
73+ ) : { pgnWithoutResult : string ; result ?: string } => {
74+ const match = pgn . match ( PGN_RESULT_TOKEN_REGEX )
75+ if ( ! match ) {
76+ return { pgnWithoutResult : pgn }
77+ }
78+
79+ return {
80+ pgnWithoutResult : match [ 1 ] . trim ( ) ,
81+ result : match [ 2 ] ,
82+ }
83+ }
84+
85+ const getCustomPgnResultOverrides = ( ) : Record < string , string > => {
86+ if ( typeof window === 'undefined' ) return { }
87+
88+ try {
89+ const raw = window . localStorage . getItem (
90+ CUSTOM_PGN_RESULT_OVERRIDES_STORAGE_KEY ,
91+ )
92+ if ( ! raw ) return { }
93+ const parsed = JSON . parse ( raw )
94+ return parsed && typeof parsed === 'object' ? parsed : { }
95+ } catch ( error ) {
96+ console . warn ( 'Failed to read custom PGN result overrides:' , error )
97+ return { }
98+ }
99+ }
100+
101+ const setCustomPgnResultOverride = ( gameId : string , result : string ) : void => {
102+ if ( typeof window === 'undefined' ) return
103+
104+ try {
105+ const overrides = getCustomPgnResultOverrides ( )
106+ overrides [ gameId ] = result
107+ window . localStorage . setItem (
108+ CUSTOM_PGN_RESULT_OVERRIDES_STORAGE_KEY ,
109+ JSON . stringify ( overrides ) ,
110+ )
111+ } catch ( error ) {
112+ console . warn ( 'Failed to store custom PGN result override:' , error )
113+ }
114+ }
115+
116+ const getCustomPgnResultOverride = ( gameId : string ) : string | undefined => {
117+ const override = getCustomPgnResultOverrides ( ) [ gameId ]
118+ return typeof override === 'string' ? override : undefined
119+ }
120+
121+ const resultTokenToWinner = (
122+ result : string ,
123+ ) : Termination [ 'winner' ] | undefined => {
124+ if ( result === '1-0' ) return 'white'
125+ if ( result === '0-1' ) return 'black'
126+ if ( result === '1/2-1/2' ) return 'none'
127+ return undefined
128+ }
129+
130+ const applyCustomResultOverride = (
131+ game : AnalyzedGame ,
132+ result ?: string ,
133+ ) : AnalyzedGame => {
134+ if ( ! result || result === '*' ) return game
135+
136+ return {
137+ ...game ,
138+ termination : {
139+ ...( game . termination || { } ) ,
140+ result,
141+ winner : resultTokenToWinner ( result ) ,
142+ } ,
143+ }
144+ }
145+
67146const AnalysisPage : NextPage = ( ) => {
68147 const { startTour, tourState } = useTour ( )
69148
@@ -162,12 +241,16 @@ const AnalysisPage: NextPage = () => {
162241 updateUrl = true ,
163242 ) => {
164243 const game = await fetchAnalyzedMaiaGame ( id , type )
244+ const gameWithOverrides =
245+ type === 'custom'
246+ ? applyCustomResultOverride ( game , getCustomPgnResultOverride ( id ) )
247+ : game
165248
166249 if ( setCurrentMove ) setCurrentMove ( 0 )
167250
168- setAnalyzedGame ( { ...game , type } )
251+ setAnalyzedGame ( { ...gameWithOverrides , type } )
169252 setCurrentId ( [ id , type ] )
170- await loadGameAnalysisCache ( { ...game , type } )
253+ await loadGameAnalysisCache ( { ...gameWithOverrides , type } )
171254
172255 if ( updateUrl ) {
173256 router . push ( `/analysis/${ id } /${ type } ` , undefined , {
@@ -348,12 +431,19 @@ const Analysis: React.FC<Props> = ({
348431 ( type : 'fen' | 'pgn' , data : string , name ?: string ) => {
349432 ; ( async ( ) => {
350433 try {
434+ const pgnPayload =
435+ type === 'pgn' ? splitTrailingPgnResult ( data ) : undefined
351436 const { game_id } = await storeCustomGame ( {
352437 name : name ,
353- pgn : type === 'pgn' ? data : undefined ,
438+ pgn :
439+ type === 'pgn' ? pgnPayload ?. pgnWithoutResult || data : undefined ,
354440 fen : type === 'fen' ? data : undefined ,
355441 } )
356442
443+ if ( pgnPayload ?. result && pgnPayload . result !== '*' ) {
444+ setCustomPgnResultOverride ( game_id , pgnPayload . result )
445+ }
446+
357447 setShowCustomModal ( false )
358448 router . push ( `/analysis/${ game_id } /custom` )
359449 } catch ( error ) {
0 commit comments