示例#1
0
        /// <summary>
        /// 与えられた指し手に従い、局面を更新する。
        /// </summary>
        /// <param name="move"></param>
        public void DoMove(Move move)
        {
            Debug.Assert(SideToMove == move.SideToMove);
            Debug.Assert(move.Drop || Board[move.FileFrom, move.RankFrom] == move.PieceFrom);
            Debug.Assert(move.Drop || Board[move.FileTo, move.RankTo] == move.PieceTo);

            // 相手の駒を取る
            if (move.PieceTo != Piece.NoPiece)
            {
                RemovePiece(move.FileTo, move.RankTo);

                Debug.Assert(move.PieceTo.ToColor() != SideToMove);
                Debug.Assert(move.PieceTo.AsOpponentHandPiece().ToColor() == SideToMove);
                PutHandPiece(move.PieceTo.AsOpponentHandPiece());
            }

            if (move.Drop)
            {
                // 駒を打つ指し手
                Debug.Assert(move.PieceFrom.ToColor() == SideToMove);
                RemoveHandPiece(move.PieceFrom);
            }
            else
            {
                // 駒を移動する指し手
                RemovePiece(move.FileFrom, move.RankFrom);
            }

            PutPiece(move.FileTo, move.RankTo,
                     move.Promotion
                ? move.PieceFrom.AsPromoted()
                : move.PieceFrom);
            Debug.Assert(Board[move.FileTo, move.RankTo].ToColor() == SideToMove);

            SideToMove = SideToMove.ToOpponent();
            Hash      ^= Zobrist.Instance.Side;

            if (move.PieceFrom == Piece.BlackKing)
            {
                BlackKingFile = move.FileTo;
                BlackKingRank = move.RankTo;
            }
            else if (move.PieceFrom == Piece.WhiteKing)
            {
                WhiteKingFile = move.FileTo;
                WhiteKingRank = move.RankTo;
            }

            ++Play;

            LastMove = move;

            var newState = new PositionState();

            newState.Previous = State;
            State             = newState;
        }
示例#2
0
        /// <summary>
        /// 与えられた指し手に従い、局面を1手戻す。
        /// </summary>
        /// <param name="move"></param>
        public void UndoMove(Move move)
        {
            Debug.Assert(SideToMove != move.SideToMove);

            State = State.Previous;

            LastMove = null;

            --Play;

            Hash      ^= Zobrist.Instance.Side;
            SideToMove = SideToMove.ToOpponent();

            if (move.PieceFrom == Piece.BlackKing)
            {
                BlackKingFile = move.FileFrom;
                BlackKingRank = move.RankFrom;
            }
            else if (move.PieceFrom == Piece.WhiteKing)
            {
                WhiteKingFile = move.FileFrom;
                WhiteKingRank = move.RankFrom;
            }

            RemovePiece(move.FileTo, move.RankTo);

            if (move.Drop)
            {
                // 駒を打つ指し手
                Debug.Assert(move.PieceFrom.ToColor() == SideToMove);
                PutHandPiece(move.PieceFrom);
            }
            else
            {
                // 駒を移動する指し手
                Debug.Assert(move.PieceFrom.ToColor() == SideToMove);
                PutPiece(move.FileFrom, move.RankFrom, move.PieceFrom);
            }

            // 相手の駒を取る
            if (move.PieceTo != Piece.NoPiece)
            {
                Debug.Assert(move.PieceTo.ToColor() != SideToMove);
                RemoveHandPiece(move.PieceTo.AsOpponentHandPiece());
                PutPiece(move.FileTo, move.RankTo, move.PieceTo);
            }
        }
示例#3
0
 public bool MovedIntoCheck()
 {
     return(InCheck(SideToMove.Other()));
 }
示例#4
0
        /// <summary>
        /// Generates and enumerates all non-castling moves which are legal in this position.
        /// </summary>
        public IEnumerable <MoveInfo> GenerateLegalMoves()
        {
            Debug.Assert(CheckInvariants());

            ulong sideToMoveVector    = colorVectors[SideToMove];
            ulong oppositeColorVector = colorVectors[SideToMove.Opposite()];
            ulong occupied            = sideToMoveVector | oppositeColorVector;

            foreach (var movingPiece in EnumHelper <Piece> .AllValues)
            {
                // Enumerate over all squares occupied by this piece.
                ulong coloredPieceVector = pieceVectors[movingPiece] & colorVectors[SideToMove];
                foreach (var sourceSquare in coloredPieceVector.AllSquares())
                {
                    // Initialize possible target squares of the moving piece.
                    ulong targetSquares = 0;
                    switch (movingPiece)
                    {
                    case Piece.Pawn:
                        ulong legalCaptureSquares = (oppositeColorVector | enPassantVector)
                                                    & Constants.PawnCaptures[SideToMove, sourceSquare];
                        ulong legalMoveToSquares = ~occupied
                                                   & Constants.PawnMoves[SideToMove, sourceSquare]
                                                   & Constants.ReachableSquaresStraight(sourceSquare, occupied);

                        targetSquares = legalCaptureSquares | legalMoveToSquares;
                        break;

                    case Piece.Knight:
                        targetSquares = Constants.KnightMoves[sourceSquare];
                        break;

                    case Piece.Bishop:
                        targetSquares = Constants.ReachableSquaresDiagonal(sourceSquare, occupied);
                        break;

                    case Piece.Rook:
                        targetSquares = Constants.ReachableSquaresStraight(sourceSquare, occupied);
                        break;

                    case Piece.Queen:
                        targetSquares = Constants.ReachableSquaresStraight(sourceSquare, occupied)
                                        | Constants.ReachableSquaresDiagonal(sourceSquare, occupied);
                        break;

                    case Piece.King:
                        targetSquares = Constants.Neighbours[sourceSquare];
                        break;
                    }

                    // Can never capture one's own pieces. This also filters out targetSquare == sourceSquare.
                    targetSquares &= ~sideToMoveVector;

                    foreach (var targetSquare in targetSquares.AllSquares())
                    {
                        // Reset/initialize move.
                        MoveInfo move = new MoveInfo();

                        ulong targetVector = targetSquare.ToVector();

                        // Since en passant doesn't capture a pawn on the target square, separate captureVector from targetVector.
                        bool  isCapture     = false;
                        Piece capturedPiece = default;
                        ulong captureVector;
                        if (movingPiece == Piece.Pawn && enPassantVector.Test(targetVector))
                        {
                            // Don't capture on the target square, but capture the pawn instead.
                            move.MoveType = MoveType.EnPassant;
                            captureVector = EnPassantCaptureVector;
                            capturedPiece = Piece.Pawn;
                            isCapture     = true;
                        }
                        else
                        {
                            // Find the possible piece on the target square.
                            captureVector = targetVector;
                            isCapture     = EnumHelper <Piece> .AllValues.Any(x => pieceVectors[x].Test(captureVector), out capturedPiece);
                        }

                        // Remove whatever was captured.
                        if (isCapture)
                        {
                            colorVectors[SideToMove.Opposite()] = colorVectors[SideToMove.Opposite()] ^ captureVector;
                            pieceVectors[capturedPiece]         = pieceVectors[capturedPiece] ^ captureVector;
                        }

                        // Move from source to target.
                        ulong moveDelta = sourceSquare.ToVector() | targetVector;
                        colorVectors[SideToMove]  = colorVectors[SideToMove] ^ moveDelta;
                        pieceVectors[movingPiece] = pieceVectors[movingPiece] ^ moveDelta;

                        // See if the friendly king is now under attack.
                        bool kingInCheck = IsSquareUnderAttack(FindKing(SideToMove), SideToMove);

                        // Move must be reversed before continuing.
                        colorVectors[SideToMove]  = colorVectors[SideToMove] ^ moveDelta;
                        pieceVectors[movingPiece] = pieceVectors[movingPiece] ^ moveDelta;
                        if (isCapture)
                        {
                            colorVectors[SideToMove.Opposite()] = colorVectors[SideToMove.Opposite()] ^ captureVector;
                            pieceVectors[capturedPiece]         = pieceVectors[capturedPiece] ^ captureVector;
                        }

                        if (!kingInCheck)
                        {
                            move.SourceSquare = sourceSquare;
                            move.TargetSquare = targetSquare;
                            if (movingPiece == Piece.Pawn && Constants.PromotionSquares.Test(targetVector))
                            {
                                move.MoveType  = MoveType.Promotion;
                                move.PromoteTo = Piece.Knight; yield return(move);

                                move.PromoteTo = Piece.Bishop; yield return(move);

                                move.PromoteTo = Piece.Rook; yield return(move);

                                move.PromoteTo = Piece.Queen; yield return(move);
                            }
                            else
                            {
                                yield return(move);
                            }
                        }
                    }
                }
            }

            Debug.Assert(CheckInvariants());
        }
示例#5
0
        /// <summary>
        /// This method is for internal use only.
        /// </summary>
        /// <remarks>
        /// This is a copy of TryMakeMove() but without the checks and with make = true.
        /// Only use if absolutely sure that the given Move is correct, or it will leave the position in a corrupted state.
        /// </remarks>
        internal void FastMakeMove(Move move)
        {
            Debug.Assert(CheckInvariants());

            ulong targetVector = move.TargetSquare.ToVector();

            // Remove whatever was captured.
            if (move.IsCapture)
            {
                if (move.MoveType != MoveType.EnPassant)
                {
                    colorVectors[SideToMove.Opposite()] = colorVectors[SideToMove.Opposite()] ^ targetVector;
                    pieceVectors[move.CapturedPiece]    = pieceVectors[move.CapturedPiece] ^ targetVector;
                }
                else
                {
                    // Don't capture on the target square, but capture the pawn instead.
                    colorVectors[SideToMove.Opposite()] = colorVectors[SideToMove.Opposite()] ^ EnPassantCaptureVector;
                    pieceVectors[move.CapturedPiece]    = pieceVectors[move.CapturedPiece] ^ EnPassantCaptureVector;
                }
            }

            // Move from source to target.
            ulong moveDelta = move.SourceSquare.ToVector() | targetVector;

            colorVectors[SideToMove]       = colorVectors[SideToMove] ^ moveDelta;
            pieceVectors[move.MovingPiece] = pieceVectors[move.MovingPiece] ^ moveDelta;

            // Update en passant vectors.
            if (move.IsPawnTwoSquaresAheadMove)
            {
                // If the moving piece was a pawn on its starting square and moved two steps ahead,
                // it can be captured en passant on the next move.
                enPassantVector        = Constants.EnPassantSquares[SideToMove, move.SourceSquare];
                EnPassantCaptureVector = targetVector;
            }
            else
            {
                // Reset en passant vectors.
                enPassantVector        = 0;
                EnPassantCaptureVector = 0;
            }

            if (move.MoveType == MoveType.Promotion)
            {
                // Change type of piece.
                pieceVectors[move.MovingPiece] = pieceVectors[move.MovingPiece] ^ targetVector;
                pieceVectors[move.PromoteTo]   = pieceVectors[move.PromoteTo] ^ targetVector;
            }
            else if (move.MoveType == MoveType.CastleQueenside)
            {
                // Move the rooks as well when castling.
                var rookDelta = Constants.CastleQueensideRookDelta[SideToMove];
                colorVectors[SideToMove] = colorVectors[SideToMove] ^ rookDelta;
                pieceVectors[Piece.Rook] = pieceVectors[Piece.Rook] ^ rookDelta;
            }
            else if (move.MoveType == MoveType.CastleKingside)
            {
                // Move the rooks as well when castling.
                var rookDelta = Constants.CastleKingsideRookDelta[SideToMove];
                colorVectors[SideToMove] = colorVectors[SideToMove] ^ rookDelta;
                pieceVectors[Piece.Rook] = pieceVectors[Piece.Rook] ^ rookDelta;
            }

            // Update castling rights. Must be done for all pieces because everything can capture a rook on its starting position.
            if (castlingRightsVector.Test())
            {
                // Revoke castling rights if kings or rooks are gone from their starting position.
                castlingRightsVector &= ~RevokedCastlingRights(moveDelta);
            }

            SideToMove = SideToMove.Opposite();

            Debug.Assert(CheckInvariants());
        }
示例#6
0
        private bool CheckInvariants()
        {
            // Disjunct colors.
            if (colorVectors[Color.White].Test(colorVectors[Color.Black]))
            {
                return(false);
            }
            ulong occupied = colorVectors[Color.White] | colorVectors[Color.Black];

            // Disjunct pieces.
            foreach (Piece piece1 in EnumHelper <Piece> .AllValues)
            {
                for (Piece piece2 = piece1 + 1; piece2 <= Piece.King; ++piece2)
                {
                    if (pieceVectors[piece1].Test(pieceVectors[piece2]))
                    {
                        return(false);
                    }
                }
            }

            // Colors and pieces must match exactly.
            if (occupied != (
                    pieceVectors[Piece.Pawn]
                    | pieceVectors[Piece.Knight]
                    | pieceVectors[Piece.Bishop]
                    | pieceVectors[Piece.Rook]
                    | pieceVectors[Piece.Queen]
                    | pieceVectors[Piece.King]))
            {
                return(false);
            }

            // There is exactly one king on both sides.
            ulong whiteKing = colorVectors[Color.White] & pieceVectors[Piece.King];

            if (!whiteKing.Test())
            {
                return(false);
            }
            if (!whiteKing.IsMaxOneBit())
            {
                return(false);
            }
            ulong blackKing = colorVectors[Color.Black] & pieceVectors[Piece.King];

            if (!blackKing.Test())
            {
                return(false);
            }
            if (!blackKing.IsMaxOneBit())
            {
                return(false);
            }

            // The enemy king cannot be in check.
            Square enemyKing = FindKing(SideToMove.Opposite());

            if (IsSquareUnderAttack(enemyKing, SideToMove.Opposite()))
            {
                return(false);
            }

            // Pawns cannot be on the back rank.
            if (pieceVectors[Piece.Pawn].Test(Constants.Rank1 | Constants.Rank8))
            {
                return(false);
            }

            // Castling rights can only be set if king/rooks are in their starting position.
            if (castlingRightsVector.Test(Constants.C8))
            {
                if (!blackKing.Test(Constants.E8))
                {
                    return(false);
                }
                if (!Constants.A8.Test(colorVectors[Color.Black] & pieceVectors[Piece.Rook]))
                {
                    return(false);
                }
            }
            if (castlingRightsVector.Test(Constants.G8))
            {
                if (!blackKing.Test(Constants.E8))
                {
                    return(false);
                }
                if (!Constants.H8.Test(colorVectors[Color.Black] & pieceVectors[Piece.Rook]))
                {
                    return(false);
                }
            }
            if (castlingRightsVector.Test(Constants.C1))
            {
                if (!whiteKing.Test(Constants.E1))
                {
                    return(false);
                }
                if (!Constants.A1.Test(colorVectors[Color.White] & pieceVectors[Piece.Rook]))
                {
                    return(false);
                }
            }
            if (castlingRightsVector.Test(Constants.G1))
            {
                if (!whiteKing.Test(Constants.E1))
                {
                    return(false);
                }
                if (!Constants.H1.Test(colorVectors[Color.White] & pieceVectors[Piece.Rook]))
                {
                    return(false);
                }
            }

            // En passant invariants.
            if (!enPassantVector.Test())
            {
                if (EnPassantCaptureVector.Test())
                {
                    return(false);
                }
            }
            else
            {
                // enPassantVector must be empty.
                if (occupied.Test(enPassantVector))
                {
                    return(false);
                }

                if (Constants.Rank4.Test(EnPassantCaptureVector))
                {
                    // There must be a white pawn on the en passant capture square.
                    if (!EnPassantCaptureVector.Test(colorVectors[Color.White] & pieceVectors[Piece.Pawn]))
                    {
                        return(false);
                    }
                    // enPassantVector must be directly south.
                    if (EnPassantCaptureVector.South() != enPassantVector)
                    {
                        return(false);
                    }
                    // Starting square must be empty.
                    if (occupied.Test(EnPassantCaptureVector.South().South()))
                    {
                        return(false);
                    }
                }
                else if (Constants.Rank5.Test(EnPassantCaptureVector))
                {
                    // There must be a black pawn on the en passant capture square.
                    if (!EnPassantCaptureVector.Test(colorVectors[Color.Black] & pieceVectors[Piece.Pawn]))
                    {
                        return(false);
                    }
                    // enPassantVector must be directly north.
                    if (EnPassantCaptureVector.North() != enPassantVector)
                    {
                        return(false);
                    }
                    // Starting square must be empty.
                    if (occupied.Test(EnPassantCaptureVector.North().North()))
                    {
                        return(false);
                    }
                }
                else
                {
                    return(false);
                }
            }

            return(true);
        }
示例#7
0
        /// <summary>
        /// Validates a move against the current position and optionally performs it.
        /// </summary>
        /// <param name="moveInfo">
        /// The move to validate and optionally perform.
        /// </param>
        /// <param name="make">
        /// True if the move must actually be made, false if only validated.
        /// </param>
        /// <returns>
        /// A valid legal <see cref="Move"/> structure if <see cref="MoveInfo.Result"/> is equal to
        /// <see cref="MoveCheckResult.OK"/>, or an incomplete <see cref="Move"/> if one of the other <see cref="MoveCheckResult"/> values.
        /// If <paramref name="make"/> is true, the move is only made if <see cref="MoveCheckResult.OK"/> is returned.
        /// </returns>
        /// <exception cref="ArgumentOutOfRangeException">
        /// Thrown when any of the move's members have an enumeration value which is outside of the allowed range.
        /// </exception>
        public Move TryMakeMove(ref MoveInfo moveInfo, bool make)
        {
            // Range checks.
            moveInfo.ThrowWhenOutOfRange();

            Debug.Assert(CheckInvariants());

            Move move = new Move
            {
                SourceSquare = moveInfo.SourceSquare,
                TargetSquare = moveInfo.TargetSquare
            };

            ulong sourceVector = move.SourceSquare.ToVector();
            ulong targetVector = move.TargetSquare.ToVector();

            ulong sideToMoveVector    = colorVectors[SideToMove];
            ulong oppositeColorVector = colorVectors[SideToMove.Opposite()];
            ulong occupied            = sideToMoveVector | oppositeColorVector;

            // Reset result before returning or checking anything.
            moveInfo.Result = MoveCheckResult.OK;

            if (sourceVector == targetVector)
            {
                // Can never move to the same square.
                moveInfo.Result |= MoveCheckResult.SourceSquareIsTargetSquare;
            }

            // Obtain moving piece.
            if (!EnumHelper <Piece> .AllValues.Any(x => pieceVectors[x].Test(sourceVector), out move.MovingPiece))
            {
                moveInfo.Result |= MoveCheckResult.SourceSquareIsEmpty;
            }
            else if (!sideToMoveVector.Test(sourceVector))
            {
                // Allow only SideToMove to make a move.
                moveInfo.Result |= MoveCheckResult.NotSideToMove;
            }

            // Can only check the rest if the basics are right.
            if (moveInfo.Result != 0)
            {
                return(move);
            }

            if (sideToMoveVector.Test(targetVector))
            {
                // Do not allow capture of one's own pieces.
                moveInfo.Result |= MoveCheckResult.CannotCaptureOwnPiece;
            }

            // Check legal target squares and specific rules depending on the moving piece.
            switch (move.MovingPiece)
            {
            case Piece.Pawn:
                ulong legalCaptureSquares = (oppositeColorVector | enPassantVector)
                                            & Constants.PawnCaptures[SideToMove, move.SourceSquare];
                ulong legalMoveToSquares = ~occupied
                                           & Constants.PawnMoves[SideToMove, move.SourceSquare]
                                           & Constants.ReachableSquaresStraight(move.SourceSquare, occupied);

                if ((legalCaptureSquares | legalMoveToSquares).Test(targetVector))
                {
                    if (Constants.PromotionSquares.Test(targetVector))
                    {
                        move.MoveType = MoveType.Promotion;
                        if (moveInfo.MoveType == MoveType.Promotion)
                        {
                            // Allow only 4 promote-to pieces.
                            if (moveInfo.PromoteTo == Piece.Pawn || moveInfo.PromoteTo == Piece.King)
                            {
                                moveInfo.Result |= MoveCheckResult.MissingPromotionInformation;
                            }
                            else
                            {
                                move.PromoteTo = moveInfo.PromoteTo;
                            }
                        }
                    }
                    else if (enPassantVector.Test(targetVector))
                    {
                        move.MoveType = MoveType.EnPassant;
                    }
                    else if (Constants.PawnTwoSquaresAhead[SideToMove, move.SourceSquare].Test(targetVector))
                    {
                        move.IsPawnTwoSquaresAheadMove = true;
                    }
                }
                else
                {
                    moveInfo.Result |= MoveCheckResult.IllegalTargetSquare;
                }
                break;

            case Piece.Knight:
                if (!Constants.KnightMoves[move.SourceSquare].Test(targetVector))
                {
                    moveInfo.Result |= MoveCheckResult.IllegalTargetSquare;
                }
                break;

            case Piece.Bishop:
                if (!Constants.ReachableSquaresDiagonal(move.SourceSquare, occupied).Test(targetVector))
                {
                    moveInfo.Result |= MoveCheckResult.IllegalTargetSquare;
                }
                break;

            case Piece.Rook:
                if (!Constants.ReachableSquaresStraight(move.SourceSquare, occupied).Test(targetVector))
                {
                    moveInfo.Result |= MoveCheckResult.IllegalTargetSquare;
                }
                break;

            case Piece.Queen:
                if (!Constants.ReachableSquaresStraight(move.SourceSquare, occupied).Test(targetVector) &&
                    !Constants.ReachableSquaresDiagonal(move.SourceSquare, occupied).Test(targetVector))
                {
                    moveInfo.Result |= MoveCheckResult.IllegalTargetSquare;
                }
                break;

            case Piece.King:
                if (!Constants.Neighbours[move.SourceSquare].Test(targetVector))
                {
                    // Castling moves. If castlingRightsVectors[sideToMove] is true somewhere, the king must be in its starting position.
                    ulong castlingTargets = castlingRightsVector
                                            & (SideToMove == Color.White ? Constants.Rank1 : Constants.Rank8);

                    if (castlingTargets.Test(targetVector))
                    {
                        bool isKingSide = Constants.KingsideCastlingTargetSquares.Test(targetVector);

                        var rookDelta = isKingSide
                                          ? Constants.CastleKingsideRookDelta[SideToMove]
                                          : Constants.CastleQueensideRookDelta[SideToMove];

                        // All squares between the king and rook must be empty.
                        if ((rookDelta & Constants.ReachableSquaresStraight(move.SourceSquare, occupied)) == rookDelta)
                        {
                            if (isKingSide)
                            {
                                move.MoveType = MoveType.CastleKingside;
                                if (IsSquareUnderAttack(move.SourceSquare, SideToMove) ||
                                    IsSquareUnderAttack(move.SourceSquare + 1, SideToMove))
                                {
                                    // Not allowed to castle out of or over a check.
                                    moveInfo.Result |= MoveCheckResult.FriendlyKingInCheck;
                                }
                            }
                            else
                            {
                                move.MoveType = MoveType.CastleQueenside;
                                if (IsSquareUnderAttack(move.SourceSquare, SideToMove) ||
                                    IsSquareUnderAttack(move.SourceSquare - 1, SideToMove))
                                {
                                    // Not allowed to castle out of or over a check.
                                    moveInfo.Result |= MoveCheckResult.FriendlyKingInCheck;
                                }
                            }

                            // Not an illegal target square, so break here already.
                            break;
                        }
                    }

                    moveInfo.Result |= MoveCheckResult.IllegalTargetSquare;
                }
                break;
            }

            // Check for illegal move types.
            CompareMoveTypes(move.MoveType, moveInfo.MoveType, ref moveInfo.Result);

            if (moveInfo.Result.IsLegalMove())
            {
                // Since en passant doesn't capture a pawn on the target square, separate captureVector from targetVector.
                ulong captureVector;
                if (move.MoveType == MoveType.EnPassant)
                {
                    // Don't capture on the target square, but capture the pawn instead.
                    captureVector      = EnPassantCaptureVector;
                    move.CapturedPiece = Piece.Pawn;
                    move.IsCapture     = true;
                }
                else
                {
                    // Find the possible piece on the target square.
                    captureVector  = targetVector;
                    move.IsCapture = EnumHelper <Piece> .AllValues.Any(x => pieceVectors[x].Test(captureVector), out move.CapturedPiece);
                }

                // Remove whatever was captured.
                if (move.IsCapture)
                {
                    colorVectors[SideToMove.Opposite()] = colorVectors[SideToMove.Opposite()] ^ captureVector;
                    pieceVectors[move.CapturedPiece]    = pieceVectors[move.CapturedPiece] ^ captureVector;
                }

                // Move from source to target.
                ulong moveDelta = sourceVector | targetVector;
                colorVectors[SideToMove]       = colorVectors[SideToMove] ^ moveDelta;
                pieceVectors[move.MovingPiece] = pieceVectors[move.MovingPiece] ^ moveDelta;

                // Find the king in the resulting position.
                Square friendlyKing = FindKing(SideToMove);

                // See if the friendly king is now under attack.
                if (IsSquareUnderAttack(friendlyKing, SideToMove))
                {
                    moveInfo.Result |= MoveCheckResult.FriendlyKingInCheck;
                }

                if (make && moveInfo.Result == MoveCheckResult.OK)
                {
                    if (move.IsPawnTwoSquaresAheadMove)
                    {
                        // If the moving piece was a pawn on its starting square and moved two steps ahead,
                        // it can be captured en passant on the next move.
                        enPassantVector        = Constants.EnPassantSquares[SideToMove, move.SourceSquare];
                        EnPassantCaptureVector = targetVector;
                    }
                    else
                    {
                        // Reset en passant vectors.
                        enPassantVector        = 0;
                        EnPassantCaptureVector = 0;
                    }

                    if (move.MoveType == MoveType.Promotion)
                    {
                        // Change type of piece.
                        pieceVectors[move.MovingPiece] = pieceVectors[move.MovingPiece] ^ targetVector;
                        pieceVectors[move.PromoteTo]   = pieceVectors[move.PromoteTo] ^ targetVector;
                    }
                    else if (move.MoveType == MoveType.CastleQueenside)
                    {
                        // Move the rooks as well when castling.
                        var rookDelta = Constants.CastleQueensideRookDelta[SideToMove];
                        colorVectors[SideToMove] = colorVectors[SideToMove] ^ rookDelta;
                        pieceVectors[Piece.Rook] = pieceVectors[Piece.Rook] ^ rookDelta;
                    }
                    else if (move.MoveType == MoveType.CastleKingside)
                    {
                        // Move the rooks as well when castling.
                        var rookDelta = Constants.CastleKingsideRookDelta[SideToMove];
                        colorVectors[SideToMove] = colorVectors[SideToMove] ^ rookDelta;
                        pieceVectors[Piece.Rook] = pieceVectors[Piece.Rook] ^ rookDelta;
                    }

                    // Update castling rights. Must be done for all pieces because everything can capture a rook on its starting position.
                    if (castlingRightsVector.Test())
                    {
                        // Revoke castling rights if kings or rooks are gone from their starting position.
                        castlingRightsVector &= ~RevokedCastlingRights(moveDelta);
                    }

                    SideToMove = SideToMove.Opposite();
                }
                else
                {
                    // Reverse move.
                    colorVectors[SideToMove]       = colorVectors[SideToMove] ^ moveDelta;
                    pieceVectors[move.MovingPiece] = pieceVectors[move.MovingPiece] ^ moveDelta;
                    if (move.IsCapture)
                    {
                        colorVectors[SideToMove.Opposite()] = colorVectors[SideToMove.Opposite()] ^ captureVector;
                        pieceVectors[move.CapturedPiece]    = pieceVectors[move.CapturedPiece] ^ captureVector;
                    }
                }
            }

            Debug.Assert(CheckInvariants());

            return(move);
        }