Beispiel #1
0
 public ErrorCondition Move(AnnotatedMove move) => Move(move.Move);
Beispiel #2
0
        // Parses a move, but does not determine validity
        public static bool TryParseMove(string input, BoardState state, ulong piecesOnCurrentSide, out AnnotatedMove result)
        {
            // ✔ Account for castling (O-O, O-O-O)
            // ✔ Account for regular move (a4, Ng8)
            // ✔ Account for long-form move (Nb1 c3, Nb1-c3)
            // ✔ Account for long-form capture (Ne2xa4)
            // ✔ Account for short-form capture (Nxg4, axb4)
            // ✔ Account for disambiguation (Ngg4)
            // ✔ Account for piece promotions (e8=Q)
            //   ✔ Account for promoted piece must be own colour
            //   ✔ Account for if pawn moves to end without specifying promotion
            //   ✔ Account for if non-pawn specifies promotion
            //   ✔ Account for if promotion piece tries to become pawn
            //   ✔ Account for if specify promotion when not at end
            // ✔ Account for state change marks (Na4+, e2#)
            // ✔ Account for annotations (Na4!, e2??, b5!?)
            // ✔ Account for whitespace (Ne2 x a4) - fail in this case
            // ✔ Account for invalid ranks/files - fail in this case


            var trimmedInput = input.AsSpan().Trim();

            if (trimmedInput.IsEmpty || trimmedInput.IndexOf(' ') != -1)
            {
                result = AnnotatedMove.Empty;
                return(false);
            }

            var isWhiteMove = piecesOnCurrentSide == state.WhitePieces;

            var moveNotation = TrimMoveDescriptors(trimmedInput);

            if (moveNotation.IsEmpty)
            {
                result = AnnotatedMove.Empty;
                return(false);
            }

            var moveDescriptors = moveNotation.Length == trimmedInput.Length
                ? ReadOnlySpan <char> .Empty
                : trimmedInput.Slice(moveNotation.Length);

            var promotion = GetPromotion(moveDescriptors, isWhiteMove);

            if (promotion != SquareContents.Empty)
            {
                moveDescriptors = moveDescriptors.Length > 2 ? moveDescriptors.Slice(2) : ReadOnlySpan <char> .Empty;
            }

            var annotation  = GetAnnotation(moveDescriptors);
            var attackState = GetAttackState(moveDescriptors);

            // Handle castling
            if (TryHandleCastling(moveNotation, isWhiteMove, out var move))
            {
                result = new AnnotatedMove(move, annotation, attackState);
                return(true);
            }

            // Read leading piece designation
            char pieceDesignation;

            if (PieceDesignations.Contains(moveNotation[0]))
            {
                pieceDesignation = moveNotation[0];
                moveNotation     = moveNotation.Slice(1);
            }
            else
            {
                pieceDesignation = Constants.PieceNotation.Pawn;
            }


            int squareIdx = moveNotation.Length - 1;

            // Get end square (must be present)
            var parsedState = new ParsedMoveState();

            parsedState.EndRank = moveNotation[squareIdx--] - '0';
            parsedState.EndFile = Char.ToLower(moveNotation[squareIdx--]);


            var lastRankForColor = isWhiteMove ? Constants.Board.NumberOfRows : 1;

            if (promotion != 0)
            {
                if (
                    pieceDesignation != Constants.PieceNotation.Pawn || // promoted non-pawn
                    (promotion & SquareContents.Pawn) != 0 ||           // promoted to invalid piece (pawn)
                    parsedState.EndRank != lastRankForColor             // promoted but not at end
                    )
                {
                    result = AnnotatedMove.Empty;
                    return(false);
                }

                parsedState.PromotedPiece = promotion;
            }
            else if (pieceDesignation == Constants.PieceNotation.Pawn && parsedState.EndRank == lastRankForColor)
            {
                // Moved pawn to end without promoting
                result = AnnotatedMove.Empty;
                return(false);
            }

            // Ignore delimiter (if present)
            bool isCapture = false;

            if (squareIdx >= 0 && SquareDelimiters.Contains(moveNotation[squareIdx]))
            {
                isCapture = moveNotation[squareIdx] == Constants.MoveType.Capture;
                squareIdx--;
            }

            // Read start squares (if present)
            if (squareIdx >= 0 && Char.IsDigit(moveNotation[squareIdx]))
            {
                parsedState.StartRank = moveNotation[squareIdx--] - '0';
            }

            if (squareIdx >= 0 && Char.IsLetter(moveNotation[squareIdx]))
            {
                parsedState.StartFile = Char.ToLower(moveNotation[squareIdx--]);
            }

            if (parsedState.StartRank != 0 && parsedState.StartFile != 0)
            {
                var valid = IsMoveValid(parsedState);
                result = valid ? new AnnotatedMove(parsedState.ToMove(), annotation, attackState) : AnnotatedMove.Empty;
                return(valid);
            }

            if (TryInferStartSquare(state, piecesOnCurrentSide, isWhiteMove, isCapture, pieceDesignation, ref parsedState))
            {
                var valid = IsMoveValid(parsedState);
                result = valid ? new AnnotatedMove(parsedState.ToMove(), annotation, attackState) : AnnotatedMove.Empty;
                return(valid);
            }

            result = AnnotatedMove.Empty;
            return(false);
        }
Beispiel #3
0
 public static bool Equals(AnnotatedMove a, AnnotatedMove b)
 {
     return(a.Annotation == b.Annotation &&
            a.AttackState == b.AttackState &&
            a == b);
 }