/// <summary> /// Loads ChessBoard from Forsyth-Edwards Notation<br/> /// ex.:<br/> /// rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1 /// </summary> /// <param name="fen">FEN string to load</param> /// <returns>ChessBoard with according positions</returns> /// <exception cref="ChessArgumentException">Given FEN string didn't match the Regex pattern</exception> public static ChessBoard LoadFromFen(string fen) { var(succeeded, exception) = FenBoardBuilder.TryLoad(fen, out var builder); if (!succeeded && exception is not null) { throw exception; } return(BuildBoardFromFen(builder)); }
/// <summary> /// Tries to load /// ChessBoard from Forsyth-Edwards Notation<br/> /// ex.:<br/> /// rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1 /// </summary> /// <param name="fen">FEN string to load</param> /// <param name="board">Result with loaded board</param> /// <returns>Whether load is succeeded</returns> public static bool TryLoadFromFen(string fen, [NotNullWhen(true)] out ChessBoard?board) { var(succeeded, _) = FenBoardBuilder.TryLoad(fen, out var builder); if (!succeeded) { board = null; return(false); } board = BuildBoardFromFen(builder); return(true); }
private static ChessBoard BuildBoardFromFen(FenBoardBuilder builder) { var board = new ChessBoard { FenBuilder = builder, pieces = builder.Pieces }; board.headers.Add("Variant", "From Position"); board.headers.Add("FEN", builder.ToString()); board.HandleKingChecked(); board.HandleEndGame(); return(board); }
internal static (bool succeeded, ChessException?exception) TryLoad(string fen, out FenBoardBuilder?builder) { builder = null; var matches = Regexes.regexFen.Matches(fen); if (matches.Count == 0) { return(false, new ChessArgumentException(null, "FEN board string should match pattern: " + Regexes.FenPattern)); } builder = new FenBoardBuilder(); foreach (var group in matches[0].Groups.Values) { switch (group.Name) { case "1": // Set pieces to given positions int x = 0, y = 7; for (int i = 0; i < group.Length; i++) { if (group.Value[i] == '/') { y--; x = 0; continue; } if (x < 8) { if (char.IsLetter(group.Value[i])) { builder.pieces[y, x] = new Piece(group.Value[i]); x++; } else if (char.IsDigit(group.Value[i])) { x += int.Parse(group.Value[i].ToString()); } } } break; case "3": builder.Turn = PieceColor.FromChar(group.Value[0]); break; case "4": if (group.Value != "-") { if (group.Value.Contains('K')) { builder.CastleWK = true; } if (group.Value.Contains('Q')) { builder.CastleWQ = true; } if (group.Value.Contains('k')) { builder.CastleBK = true; } if (group.Value.Contains('q')) { builder.CastleBQ = true; } } break; case "5": if (group.Value == "-") { builder.EnPassant = new(); } else { builder.EnPassant = new Position(group.Value); } break; case "6": (builder.HalfMoves, builder.FullMoves) = group.Value.Split(' ').Select(s => int.Parse(s)).ToArray(); break; } } var wcap = new List <Piece>(); var bcap = new List <Piece>(); var fpieces = builder.pieces.Cast <Piece>().Where(p => p is not null); // Calculating missing pieces on according begin pieces in fen // Math.Clamp() for get max/min taken figures (2 queens possible) wcap.AddRange(Enumerable.Range(0, Math.Clamp(8 - fpieces.Where(p => p.Type == PieceType.Pawn && p.Color == PieceColor.White).Count(), 0, 8)).Select(_ => new Piece(PieceColor.White, PieceType.Pawn))); wcap.AddRange(Enumerable.Range(0, Math.Clamp(2 - fpieces.Where(p => p.Type == PieceType.Rook && p.Color == PieceColor.White).Count(), 0, 2)).Select(_ => new Piece(PieceColor.White, PieceType.Rook))); wcap.AddRange(Enumerable.Range(0, Math.Clamp(2 - fpieces.Where(p => p.Type == PieceType.Bishop && p.Color == PieceColor.White).Count(), 0, 2)).Select(_ => new Piece(PieceColor.White, PieceType.Bishop))); wcap.AddRange(Enumerable.Range(0, Math.Clamp(2 - fpieces.Where(p => p.Type == PieceType.Knight && p.Color == PieceColor.White).Count(), 0, 2)).Select(_ => new Piece(PieceColor.White, PieceType.Knight))); wcap.AddRange(Enumerable.Range(0, Math.Clamp(1 - fpieces.Where(p => p.Type == PieceType.Queen && p.Color == PieceColor.White).Count(), 0, 1)).Select(_ => new Piece(PieceColor.White, PieceType.Queen))); bcap.AddRange(Enumerable.Range(0, Math.Clamp(8 - fpieces.Where(p => p.Type == PieceType.Pawn && p.Color == PieceColor.Black).Count(), 0, 8)).Select(_ => new Piece(PieceColor.Black, PieceType.Pawn))); bcap.AddRange(Enumerable.Range(0, Math.Clamp(2 - fpieces.Where(p => p.Type == PieceType.Rook && p.Color == PieceColor.Black).Count(), 0, 2)).Select(_ => new Piece(PieceColor.Black, PieceType.Rook))); bcap.AddRange(Enumerable.Range(0, Math.Clamp(2 - fpieces.Where(p => p.Type == PieceType.Bishop && p.Color == PieceColor.Black).Count(), 0, 2)).Select(_ => new Piece(PieceColor.Black, PieceType.Bishop))); bcap.AddRange(Enumerable.Range(0, Math.Clamp(2 - fpieces.Where(p => p.Type == PieceType.Knight && p.Color == PieceColor.Black).Count(), 0, 2)).Select(_ => new Piece(PieceColor.Black, PieceType.Knight))); bcap.AddRange(Enumerable.Range(0, Math.Clamp(1 - fpieces.Where(p => p.Type == PieceType.Queen && p.Color == PieceColor.Black).Count(), 0, 1)).Select(_ => new Piece(PieceColor.Black, PieceType.Queen))); builder.WhiteCaptured = wcap.ToArray(); builder.BlackCaptured = bcap.ToArray(); return(true, null); }
/// <summary> /// Generates FEN string representing current board /// </summary> public string ToFen() { return(FenBoardBuilder.Load(this).ToString()); }
public static (bool succeeded, ChessException?exception) TryLoad(string pgn, out ChessBoard?board) { board = new(); var headersMatches = Regexes.regexHeaders.Matches(pgn); if (headersMatches.Count > 0) { // Extracting headers for (int i = 0; i < headersMatches.Count; i++) { // [Black "Geras1mleo"] // Groups[1] = Black // Groups[2] = Geras1mleo board.headers.Add(headersMatches[i].Groups[1].Value, headersMatches[i].Groups[2].Value); } } // San move can occur in header ex. in nickname of player => remove headers from string pgn = Regexes.regexHeaders.Replace(pgn, ""); // Loading fen if exist if (board.headers.TryGetValue("FEN", out var fen)) { var(succeeded, exception) = FenBoardBuilder.TryLoad(fen, out board.FenBuilder); if (!succeeded) { board = null; return(false, exception); } board.pieces = board.FenBuilder.Pieces; board.HandleKingChecked(); board.HandleEndGame(); if (board.IsEndGame) { return(true, null); } } // Remove all alternatives pgn = Regexes.regexAlternatives.Replace(pgn, ""); // Remove all comments pgn = Regexes.regexComments.Replace(pgn, ""); // Todo Save Alternative moves(bracnhes) and Comments for moves var movesMatches = Regexes.regexSanMoves.Matches(pgn); // Execute all found moves for (int i = 0; i < movesMatches.Count; i++) { var(succeeded, exception) = SanBuilder.TryParse(board, movesMatches[i].Value, out var move, true); if (!succeeded) { board = null; return(false, exception); } // If san parsing succeeded => move is valid board.executedMoves.Add(move); board.DropPieceToNewPosition(move); board.moveIndex = board.executedMoves.Count - 1; } board.HandleKingChecked(); board.HandleEndGame(); // If not actual end game but game is in fact ended => someone resigned if (!board.IsEndGame) { if (pgn.Contains("1-0")) { board.Resign(PieceColor.Black); } else if (pgn.Contains("0-1")) { board.Resign(PieceColor.White); } else if (pgn.Contains("1/2-1/2")) { board.Draw(); } } return(true, null); }