Beispiel #1
0
        /// <summary>
        /// Worker method to do FEN parsing.
        /// NOTE: performance could be improved by passing in the Piece[] preallocated
        /// </summary>
        /// <param name="fen"></param>
        /// <returns></returns>
        static FENParseResult DoParseFEN(string fen)
        {
            int charIndex = 0;

            void SkipAnySpaces()
            {
                while (charIndex < fen.Length && char.IsWhiteSpace(fen[charIndex]))
                {
                    charIndex++;
                }
            }

            void SkipAnySpacesOrDash()
            {
                while (charIndex < fen.Length && (fen[charIndex] == '-' || char.IsWhiteSpace(fen[charIndex])))
                {
                    charIndex++;
                }
            }

            List <PieceOnSquare> pieces = new List <PieceOnSquare>(32);

            int curRank = 0;
            int curFile = 0;

            // Parse pieces
            while (true)
            {
                char thisChar = fen[charIndex++];

                if (thisChar == ' ')
                {
                    break;
                }

                if (thisChar == '/')
                {
                    if (curRank >= 7)
                    {
                        throw new Exception("Illegal FEN, too many squares");
                    }
                    curRank++;
                    curFile = 0;
                }
                else if (char.IsDigit(thisChar))
                {
                    curFile += (thisChar - '0');
                    if (curFile > 8)
                    {
                        throw new Exception("Illegal FEN, too many files");
                    }
                }
                else
                {
                    Piece thisPiece = byteToPieces[thisChar];
                    if (thisPiece.Type == PieceType.None)
                    {
                        throw new Exception("Illegal FEN, found piece character " + thisChar);
                    }
                    pieces.Add(new PieceOnSquare(Square.FromFileAndRank(curFile, 7 - curRank), thisPiece));
                    curFile++;
                }
            }

            SkipAnySpaces();

            // 2. Side to move character
            char     sideMoveChar = char.ToLower(fen[charIndex++]);
            SideType sideToMove;

            if (sideMoveChar == 'b')
            {
                sideToMove = SideType.Black;
            }
            else if (sideMoveChar == 'w')
            {
                sideToMove = SideType.White;
            }
            else
            {
                throw new Exception($"Illegal FEN, side to move character {sideMoveChar}");
            }

            SkipAnySpaces();

            // 3. Castling availability
            bool whiteCanOO  = false;
            bool whiteCanOOO = false;
            bool blackCanOO  = false;
            bool blackCanOOO = false;

            while (charIndex < fen.Length)
            {
                char thisChar = fen[charIndex++];
                if (thisChar == 'K' || thisChar == 'H')
                {
                    whiteCanOO = true;
                }
                else if (thisChar == 'Q' || thisChar == 'A')
                {
                    whiteCanOOO = true;
                }
                else if (thisChar == 'k' || thisChar == 'h')
                {
                    blackCanOO = true;
                }
                else if (thisChar == 'q' || thisChar == 'a')
                {
                    blackCanOOO = true;
                }
                else if (thisChar == ' ')
                {
                    break;
                }
            }

            SkipAnySpaces();

            // 4. En passant target square
            int numEPChars = 0;

            PositionMiscInfo.EnPassantFileIndexEnum epColIndex = PositionMiscInfo.EnPassantFileIndexEnum.FileNone;
            int epFile = 0;
            int epRank = 0;

            while (charIndex < fen.Length)
            {
                // Try to work around error in FEN whereby dash is missing, and move number immediately follows
                if (numEPChars == 0 && char.IsDigit(fen[charIndex]))
                {
                    break;
                }

                char thisChar = fen[charIndex++];
                if (thisChar == '-' || thisChar == ' ')
                {
                    break;
                }
                else
                {
                    if (numEPChars == 0)
                    {
                        epFile = char.ToLower(thisChar) - 'a';
                    }
                    else if (numEPChars == 1)
                    {
                        epRank = char.ToLower(thisChar) - '0';
                    }
                    else
                    {
                        throw new Exception("too many en passant characters");
                    }

                    numEPChars++;
                }
            }

            SkipAnySpaces();

            if (numEPChars > 0)
            {
                if (numEPChars == 2)
                {
                    if (epFile > (byte)PositionMiscInfo.EnPassantFileIndexEnum.FileH)
                    {
                        throw new Exception("Invalid en passant file in FEN");
                    }
                    else
                    {
                        epColIndex = (PositionMiscInfo.EnPassantFileIndexEnum)epFile;
                    }
                }
                else
                {
                    throw new Exception("Invalid en passant in FEN");
                }
            }

            SkipAnySpacesOrDash(); // Sometimes we see ill-formed FENs that end with extaneous dash, e.g. "w q - -"

            // 5. Halfmove clock: This is the number of halfmoves since the last capture or pawn advance.
            int move50Count = 0;

            while (charIndex < fen.Length && charIndex < fen.Length)
            {
                char thisChar = fen[charIndex++];
                if (thisChar == ' ')
                {
                    break;
                }
                else
                {
                    move50Count = move50Count * 10 + (thisChar - '0');
                }
            }

            SkipAnySpaces();

            // Fullmove number
            int fullmoveCount = 0;

            while (charIndex < fen.Length && charIndex < fen.Length)
            {
                char thisChar = fen[charIndex++];
                if (!char.IsDigit(thisChar))
                {
                    break;
                }
                else
                {
                    fullmoveCount = fullmoveCount * 10 + (thisChar - '0');
                }
            }

            // Fill in move count with 1 if none found
            if (fullmoveCount == 0)
            {
                fullmoveCount = 1;
            }

            if (charIndex < fen.Length - 1)
            {
                string remainder = fen.Substring(charIndex);
                if (StringUtils.WhitespaceRemoved(remainder) != "")
                {
                    throw new Exception($"Unexpected characters after FEN {remainder} ");
                }
            }

            PositionMiscInfo miscInfo = new PositionMiscInfo(whiteCanOO, whiteCanOOO, blackCanOO, blackCanOOO, sideToMove, move50Count, 0, fullmoveCount, epColIndex);

            return(new FENParseResult(pieces, miscInfo));
        }
Beispiel #2
0
 /// <summary>
 /// Constructor (from set of pieces and miscellaneous information).
 /// </summary>
 /// <param name="pieces"></param>
 /// <param name="miscInfo"></param>
 public FENParseResult(List <PieceOnSquare> pieces, PositionMiscInfo miscInfo)
 {
     Pieces   = pieces;
     MiscInfo = miscInfo;
 }