/// <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()); }
private static MoveInfo GetMoveInfo(Game game, string moveText, Color sideToMove) { MoveInfo moveInfo = new MoveInfo(); // Very free-style parsing, based on the assumption that this is a recognized move. if (moveText == "O-O") { moveInfo.MoveType = MoveType.CastleKingside; if (sideToMove == Color.White) { moveInfo.SourceSquare = Square.E1; moveInfo.TargetSquare = Square.G1; } else { moveInfo.SourceSquare = Square.E8; moveInfo.TargetSquare = Square.G8; } } else if (moveText == "O-O-O") { moveInfo.MoveType = MoveType.CastleQueenside; if (sideToMove == Color.White) { moveInfo.SourceSquare = Square.E1; moveInfo.TargetSquare = Square.C1; } else { moveInfo.SourceSquare = Square.E8; moveInfo.TargetSquare = Square.C8; } } else { // Piece, disambiguation, capturing 'x', target square, promotion, check/mate/nag. Piece movingPiece = Piece.Pawn; int index = 0; if (moveText[index] >= 'A' && moveText[index] <= 'Z') { movingPiece = GetPiece(moveText[index]); index++; } File? disambiguatingSourceFile = null; Rank? disambiguatingSourceRank = null; File? targetFile = null; Rank? targetRank = null; Piece?promoteTo = null; while (index < moveText.Length) { char currentChar = moveText[index]; if (currentChar == '=') { index++; promoteTo = GetPiece(moveText[index]); break; } else if (currentChar >= 'a' && currentChar <= 'h') { if (targetFile != null) { disambiguatingSourceFile = targetFile; } targetFile = (File)(currentChar - 'a'); } else if (currentChar >= '1' && currentChar <= '8') { if (targetRank != null) { disambiguatingSourceRank = targetRank; } targetRank = (Rank)(currentChar - '1'); } // Ignore 'x', '+', '#', '!', '?', increase index. index++; } moveInfo.TargetSquare = ((File)targetFile).Combine((Rank)targetRank); // Get vector of pieces of the correct color that can move to the target square. ulong occupied = ~game.CurrentPosition.GetEmptyVector(); ulong sourceSquareCandidates = game.CurrentPosition.GetVector(sideToMove) & game.CurrentPosition.GetVector(movingPiece); if (movingPiece == Piece.Pawn) { // Capture or normal move? if (disambiguatingSourceFile != null) { // Capture, go backwards by using the opposite side to move. sourceSquareCandidates &= Constants.PawnCaptures[sideToMove.Opposite(), moveInfo.TargetSquare]; foreach (Square sourceSquareCandidate in sourceSquareCandidates.AllSquares()) { if (disambiguatingSourceFile == (File)sourceSquareCandidate.X()) { moveInfo.SourceSquare = sourceSquareCandidate; break; } } // En passant special move type, if the target capture square is empty. if (!moveInfo.TargetSquare.ToVector().Test(occupied)) { moveInfo.MoveType = MoveType.EnPassant; } } else { // One or two squares backwards. Func <ulong, ulong> direction; if (sideToMove == Color.White) { direction = ChessExtensions.South; } else { direction = ChessExtensions.North; } ulong straightMoves = direction(moveInfo.TargetSquare.ToVector()); if (!straightMoves.Test(occupied)) { straightMoves |= direction(straightMoves); } sourceSquareCandidates &= straightMoves; foreach (Square sourceSquareCandidate in sourceSquareCandidates.AllSquares()) { moveInfo.SourceSquare = sourceSquareCandidate; break; } } if (promoteTo != null) { moveInfo.MoveType = MoveType.Promotion; moveInfo.PromoteTo = (Piece)promoteTo; } } else { switch (movingPiece) { case Piece.Knight: sourceSquareCandidates &= Constants.KnightMoves[moveInfo.TargetSquare]; break; case Piece.Bishop: sourceSquareCandidates &= Constants.ReachableSquaresDiagonal(moveInfo.TargetSquare, occupied); break; case Piece.Rook: sourceSquareCandidates &= Constants.ReachableSquaresStraight(moveInfo.TargetSquare, occupied); break; case Piece.Queen: sourceSquareCandidates &= Constants.ReachableSquaresDiagonal(moveInfo.TargetSquare, occupied) | Constants.ReachableSquaresStraight(moveInfo.TargetSquare, occupied); break; case Piece.King: sourceSquareCandidates &= Constants.Neighbours[moveInfo.TargetSquare]; break; default: sourceSquareCandidates = 0; break; } foreach (Square sourceSquareCandidate in sourceSquareCandidates.AllSquares()) { if (disambiguatingSourceFile != null) { if (disambiguatingSourceFile == (File)sourceSquareCandidate.X()) { if (disambiguatingSourceRank != null) { if (disambiguatingSourceRank == (Rank)sourceSquareCandidate.Y()) { moveInfo.SourceSquare = sourceSquareCandidate; break; } } else { moveInfo.SourceSquare = sourceSquareCandidate; break; } } } else if (disambiguatingSourceRank != null) { if (disambiguatingSourceRank == (Rank)sourceSquareCandidate.Y()) { moveInfo.SourceSquare = sourceSquareCandidate; break; } } else { moveInfo.SourceSquare = sourceSquareCandidate; break; } } } } return(moveInfo); }