/// <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; }
/// <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); } }
public bool MovedIntoCheck() { return(InCheck(SideToMove.Other())); }
/// <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()); }
/// <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()); }
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); }
/// <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); }