private static void ApplyTokensToPly( GamePly ply, List <Tokens.Token> metaTokens, PgnConfiguration pgnConfiguration) { foreach (var mt in metaTokens) { if (mt.TryAsComment(out var comment)) { var annotations = comment.Parts .Where(p => p.IsAnnotation) .Select(p => { if (p.TryAsAnnotation(out var annotation)) { return(annotation); } return(null); }) .ToList(); foreach (var annotation in annotations) { ply.Annotations.Add(new Annotation() { Text = annotation.Text, }); } } else if (mt.TryAsLine(out var line)) { (GamePly linePly, _) = ParsePlies(line.Line, pgnConfiguration); ply.AlternateNextMoves.Add(linePly); } } }
/// <summary> /// Parses a tokenized line. /// </summary> /// <param name="lineTokens">A collection of tokens from the body of a game, or /// from a recursive line.</param> /// <param name="pgnConfiguration">The configuration to use for the game.</param> /// <returns>The root game ply and the final result token.</returns> /// <exception cref="PgnFormatException">Thrown if the content of a result token contained invalid data.</exception> internal static (GamePly, GameResult) ParsePlies( IEnumerable <Tokens.Token> lineTokens, PgnConfiguration pgnConfiguration) { GameResult resultFromToken = GameResult.Ongoing; GamePly currentPly = new GamePly(); GamePly resultPly = currentPly; var tokenBuffer = new List <Tokens.Token>(); foreach (var t in lineTokens) { if (t.TryAsPly(out var ply)) { currentPly.San = ply.San; if (currentPly.PreviousPly != null) { ApplyTokensToPly( currentPly.PreviousPly, tokenBuffer, pgnConfiguration); } tokenBuffer.Clear(); currentPly.NextPlyInMainLine = new GamePly(); var previousPly = currentPly; currentPly = currentPly.NextPlyInMainLine; currentPly.PreviousPly = previousPly; resultPly = previousPly; } else if (t.TryAsNull(out var nullMove)) { currentPly.San = nullMove.NullMoveText; if (pgnConfiguration.RewriteSan) { currentPly.San = RewriteSan(currentPly.San, pgnConfiguration); } currentPly.SanIsNullMove = true; if (currentPly.PreviousPly != null) { ApplyTokensToPly( currentPly.PreviousPly, tokenBuffer, pgnConfiguration); } tokenBuffer.Clear(); currentPly.NextPlyInMainLine = new GamePly(); var previousPly = currentPly; currentPly = currentPly.NextPlyInMainLine; currentPly.PreviousPly = previousPly; resultPly = previousPly; } else if (t.IsComment || t.IsLine || t.IsNag) { tokenBuffer.Add(t); } else if (t.TryAsResult(out var resultToken)) { if (resultToken.IsWhiteWins) { resultFromToken = GameResult.WhiteWins; } else if (resultToken.IsBlackWins) { resultFromToken = GameResult.BlackWins; } else if (resultToken.IsDraw) { resultFromToken = GameResult.Draw; } else if (resultToken.IsUnspecified) { resultFromToken = GameResult.Ongoing; } else { throw new PgnFormatException($"An unknown result was encountered: '{resultToken}'"); } } else if (t.IsMoveNumber || t.IsBlackMoveNumber) { // We don't really care about these } else { throw new PgnFormatException("Unknown token or known tag in incorect context."); } } if (currentPly.PreviousPly == null) { return(null, resultFromToken); } GamePly firstPly = resultPly; if (resultPly.NextPlyInMainLine?.San == null) { resultPly.NextPlyInMainLine = null; } while (firstPly.PreviousPly != null) { firstPly = firstPly.PreviousPly; } return(firstPly, resultFromToken); }
/// <summary> /// Flatten a game into its indexed form and a list of the positions. /// </summary> /// <param name="pgnGame">The parsed PGN game object to flatten.</param> /// <param name="datasetId">The dataset for this import job.</param> /// <returns>A 2-tuple with the indexed game and a list of positions.</returns> public (IndexedGame Game, List <GamePositionRow> Positions) FlattenPgnGame( PgnGame pgnGame, DatasetId datasetId) { var indexedGame = this.CreateGameFromPgnGame(pgnGame, datasetId); var board = new Board(); var pieceValue = new PieceValue(); var pos = new Position(board, pieceValue); var game = GameFactory.Create(pos); if (pgnGame.AllTags.ContainsKey("FEN")) { var fen = pgnGame.AllTags["FEN"].Value; game.NewGame(fen); } else { game.NewGame(); } var positions = new List <GamePositionRow>(); var pgnPlies = pgnGame.MainLineAsList; int plyCount = 0; GamePly prevPly = null; var pliesSoFar = new List <GamePly>(); string gameId = Guid.NewGuid().ToString(); OpeningPosition openingPosition = null; foreach (var ply in pgnPlies) { var position = new GamePositionRow(); position.GameUniqueId = gameId; position.GamePlyNumber = plyCount; position.GameMoveNumber = (plyCount / 2) + 1; var fen = game.GetFen().ToString(); var fenParts = fen.Split(' '); if (!ply !.SanIsNullMove) { Rudz.Chess.Types.Move move; try { move = this.PlyToChessMove(game, ply); } catch (FlattenNoMatchException e) { string posFen = game.Pos.GenerateFen().ToString(); Console.WriteLine(e); move = this.PlyToChessMove(game, ply); throw; } var movePrinter = new MoveAmbiguity(game.Pos); position.NextMoveSan = ply !.San; position.NextMoveUci = movePrinter.ToNotation(move, MoveNotations.Uci); var state = new State(); game.Pos.MakeMove(move, state); }