import { Color } from '../../../../protocol/color' ; import { Annotator } from '../../annotation/annotator' ; import { FenNormalizer } from '../../tree/fennormalizer' ; import { FenToPgnMap } from '../../tree/fentopgnmap' ; import { PgnToNodeMap } from '../../tree/pgntonodemap' ; import { TreeNode } from '../../tree/treenode' ; import { BuildAnnotation } from './buildannotation' ; import { DisplayType } from './displaytype' ; export class DefaultAnnotator implements Annotator < BuildAnnotation | null > { annotate ( node , repertoireColor , pgnToNode , fenToPgn ) { const repetition = this . calculateRepetition_ ( node , pgnToNode , fenToPgn ) ; const transposition = this . calculateTransposition_ ( node , pgnToNode , fenToPgn ) ; const warning = this . calculateWarning_ ( node , repertoireColor , repetition , transposition ) ; return Promise . resolve ( warning || repetition || transposition || null ) ; } calculateRepetition_ ( node , pgnToNode , fenToPgn ) { const normalizedFen = FenNormalizer . normalize ( node . fen , node . numLegalMoves ) ; if ( ! fenToPgn [ normalizedFen ] || fenToPgn [ normalizedFen ] . length < 2 || fenToPgn [ normalizedFen ] [ 0 ] == node . pgn ) { return null ; } for ( let i = 0 ; i < fenToPgn [ normalizedFen ] . length ; i ++ ) { const repetitionPgn = fenToPgn [ normalizedFen ] [ i ] ; const repetitionNode = pgnToNode [ repetitionPgn ] ; if ( node . pgn != repetitionPgn && node . pgn . startsWith ( repetitionPgn ) && repetitionNode ) { const repetitionContent = '<b>' + node . lastMoveVerboseString + '</b> is a repetition of the position after <b>' + repetitionNode . lastMoveVerboseString + '</b>.' ; return { title : 'Repetition' , content : repetitionContent , displayType : DisplayType . INFORMATIONAL } ; } } return null ; } calculateTransposition_ ( node , pgnToNode , fenToPgn ) { const normalizedFen = FenNormalizer . normalize ( node . fen , node . numLegalMoves ) ; const pgnsForFen = fenToPgn [ normalizedFen ] ; if ( ! pgnsForFen || pgnsForFen . length < 2 ) { return null ; } const nodesWithChildrenForFen = pgnsForFen . map ( p => pgnToNode [ p ] ) . filter ( n => n . children . length ) ; let transpositionPgn ; if ( ! nodesWithChildrenForFen . length ) { transpositionPgn = pgnsForFen [ 0 ] ; } else { transpositionPgn = nodesWithChildrenForFen [ 0 ] . pgn ; } if ( node . pgn == transpositionPgn ) { return null ; } const transpositionContent = '<b>' + node . lastMoveVerboseString + '</b> transposes to: <p><b>' + ( transpositionPgn || '(start)' ) + '</b>.' ; return { title : 'Transposition' , content : transpositionContent , displayType : DisplayType . INFORMATIONAL } ; } calculateWarning_ ( node , repertoireColor , repetition , transposition ) { const warnings = [ ] ; const numChildren = node . children . length ; const displayColor = repertoireColor == Color . WHITE ? 'White' : 'Black' ; if ( numChildren > 0 ) { if ( repetition ) { warnings . push ( repetition . content + '<p>Lines containing repetitions must end on the first repeated ' + 'position.<p>To fix, delete all moves after <b>' + node . lastMoveVerboseString + '</b>.' ) ; } else if ( transposition ) { warnings . push ( transposition . content + '<p>Continuations from this position should be added to that ' + 'line instead.<p>To fix, delete all moves after <b>' + node . lastMoveVerboseString + '</b>.' ) ; } } if ( node . colorToMove == repertoireColor ) { if ( ! transposition && ! numChildren ) { warnings . push ( displayColor + ' has no reply to <b>' + node . lastMoveVerboseString + '</b>.<p>To fix, add a move for ' + displayColor + ' after <b>' + node . lastMoveVerboseString + '</b> or delete this move.' ) ; } if ( ! transposition && numChildren > 1 ) { warnings . push ( 'There are multiple moves for ' + displayColor + ' after <b>' + node . lastMoveVerboseString + '</b> (' + ( node . children . length > 2 ? 'e.g. ' : '' ) + '<b>' + node . children [ 0 ] . lastMoveVerboseString + '</b> and <b>' + node . children [ 1 ] . lastMoveVerboseString + '</b>).<p>To fix, choose at most one move for ' + displayColor + ' from this position and delete all other moves.' ) ; } } return warnings . length ? { title : 'Warnings' , content : warnings [ 0 ] , displayType : DisplayType . WARNING } : null ; } }