/// <summary> /// Generates available moves. /// </summary> /// <param name="opt">The generator parameters.</param> public static void Generate(GeneratorParameters opt) { CalculateMovesForSinglePush(opt); CalculateMovesForDoublePush(opt); CalculateDiagonalAttacks(7, 9, BitConstants.HFile, opt); CalculateDiagonalAttacks(9, 7, BitConstants.AFile, opt); }
/// <summary> /// Generates available moves. /// </summary> /// <param name="pieceType">The piece type.</param> /// <param name="opt">The generator parameters.</param> public static void Generate(PieceType pieceType, GeneratorParameters opt) { var piecesToParse = opt.Bitboard.Pieces[FastArray.GetPieceIndex(opt.FriendlyColor, pieceType)]; while (piecesToParse != 0) { var pieceLSB = BitOperations.GetLSB(piecesToParse); piecesToParse = BitOperations.PopLSB(piecesToParse); var excludeFromAttacks = CalculateMoves(pieceType, pieceLSB, opt); CalculateAttacks(pieceLSB, excludeFromAttacks, opt); } }
/// <summary> /// Calculates X-Ray attacks when friendly pawn is on bishop way. /// </summary> /// <param name="pieceIndex">The field index with the specified bishop.</param> /// <param name="pattern">The bishop moves pattern.</param> /// <param name="opt">The generator parameters.</param> /// <returns>The attacks bitboard with pawn X-Ray attacks.</returns> private static ulong CalculatePawnBlockers(int pieceIndex, ulong pattern, GeneratorParameters opt) { var patternWithFriendlyBlockers = pattern; var allowedBlockers = opt.Bitboard.Pieces[FastArray.GetPieceIndex(opt.FriendlyColor, PieceType.Pawn)]; var piecePosition = BitPositionConverter.ToPosition(pieceIndex); var friendlyBlockers = pattern & opt.FriendlyOccupancy & allowedBlockers; while (friendlyBlockers != 0) { var friendlyBlockerLSB = BitOperations.GetLSB(friendlyBlockers); friendlyBlockers = BitOperations.PopLSB(friendlyBlockers); var friendlyBlockerIndex = BitOperations.GetBitIndex(friendlyBlockerLSB); var friendlyBlockerPosition = BitPositionConverter.ToPosition(friendlyBlockerIndex); switch (opt.FriendlyColor) { case Color.White when friendlyBlockerPosition.X > piecePosition.X && friendlyBlockerPosition.Y > piecePosition.Y && (friendlyBlockerLSB & (BitConstants.HFile | BitConstants.HRank)) == 0: { patternWithFriendlyBlockers |= friendlyBlockerLSB << 7; break; } case Color.White when friendlyBlockerPosition.X <piecePosition.X && friendlyBlockerPosition.Y> piecePosition.Y && (friendlyBlockerLSB & (BitConstants.AFile | BitConstants.HRank)) == 0: { patternWithFriendlyBlockers |= friendlyBlockerLSB << 9; break; } case Color.Black when friendlyBlockerPosition.X > piecePosition.X && friendlyBlockerPosition.Y < piecePosition.Y && (friendlyBlockerLSB & (BitConstants.HFile | BitConstants.ARank)) == 0: { patternWithFriendlyBlockers |= friendlyBlockerLSB >> 9; break; } case Color.Black when friendlyBlockerPosition.X < piecePosition.X && friendlyBlockerPosition.Y < piecePosition.Y && (friendlyBlockerLSB & (BitConstants.AFile | BitConstants.ARank)) == 0: { patternWithFriendlyBlockers |= friendlyBlockerLSB >> 7; break; } } } return(patternWithFriendlyBlockers); }
/// <summary> /// Calculates castling moves. /// </summary> /// <param name="opt">The generator parameters.</param> public static void CalculateCastling(GeneratorParameters opt) { var kingLSB = CastlingConstants.InitialKingBitboard; var leftRookLSB = CastlingConstants.InitialLeftRookBitboard; var rightRookLSB = CastlingConstants.InitialRightRookBitboard; var shortMoveArea = CastlingConstants.ShortCastlingMoveArea; var shortCheckArea = CastlingConstants.ShortCastlingCheckArea; var longMoveArea = CastlingConstants.LongCastlingMoveArea; var longCheckArea = CastlingConstants.LongCastlingCheckArea; var kingPosition = InitialKingPosition; if (opt.FriendlyColor == Color.Black) { kingLSB <<= 56; leftRookLSB <<= 56; rightRookLSB <<= 56; shortMoveArea <<= 56; shortCheckArea <<= 56; longMoveArea <<= 56; longCheckArea <<= 56; kingPosition += new Position(0, 7); } if (IsCastlingPossible(CastlingType.Short, opt) && IsKingOnPosition(kingLSB, opt) && IsRookOnPosition(rightRookLSB, opt) && IsCastlingAreaEmpty(shortMoveArea, opt.OccupancySummary) && !IsCastlingAreaChecked(opt.EnemyColor, shortCheckArea, opt)) { var kingDestinationPosition = kingPosition + new Position(2, 0); var move = new CastlingMove(kingPosition, kingDestinationPosition, PieceType.King, opt.FriendlyColor, CastlingType.Short); opt.Bitboard.Moves.AddLast(move); } if (IsCastlingPossible(CastlingType.Long, opt) && IsKingOnPosition(kingLSB, opt) && IsRookOnPosition(leftRookLSB, opt) && IsCastlingAreaEmpty(longMoveArea, opt.OccupancySummary) && !IsCastlingAreaChecked(opt.EnemyColor, longCheckArea, opt)) { var kingDestinationPosition = kingPosition - new Position(2, 0); var move = new CastlingMove(kingPosition, kingDestinationPosition, PieceType.King, opt.FriendlyColor, CastlingType.Long); opt.Bitboard.Moves.AddLast(move); } }
/// <summary> /// Generates available moves. /// </summary> /// <param name="opt">The generator parameters.</param> public static void Generate(GeneratorParameters opt) { var piecesToParse = opt.Bitboard.Pieces[FastArray.GetPieceIndex(opt.FriendlyColor, PieceType.Knight)]; while (piecesToParse != 0) { var pieceLSB = BitOperations.GetLSB(piecesToParse); piecesToParse = BitOperations.PopLSB(piecesToParse); var pieceIndex = BitOperations.GetBitIndex(pieceLSB); var piecePosition = BitPositionConverter.ToPosition(pieceIndex); var pattern = PatternsContainer.KnightPattern[pieceIndex]; while (pattern != 0) { var patternLSB = BitOperations.GetLSB(pattern); pattern = BitOperations.PopLSB(pattern); var patternIndex = BitOperations.GetBitIndex(patternLSB); if ((opt.Mode & GeneratorMode.CalculateMoves) != 0 && (patternLSB & opt.FriendlyOccupancy) == 0) { var to = BitPositionConverter.ToPosition(patternIndex); if ((patternLSB & opt.EnemyOccupancy) == 0 && !opt.QuiescenceSearch) { opt.Bitboard.Moves.AddLast(new QuietMove(piecePosition, to, PieceType.Knight, opt.FriendlyColor)); } else if ((patternLSB & opt.EnemyOccupancy) != 0) { opt.Bitboard.Moves.AddLast(new KillMove(piecePosition, to, PieceType.Knight, opt.FriendlyColor)); } } if ((opt.Mode & GeneratorMode.CalculateAttacks) != 0) { opt.Bitboard.Attacks[patternIndex] |= pieceLSB; opt.Bitboard.AttacksSummary[(int)opt.FriendlyColor] |= patternLSB; } } } }
/// <summary> /// Calculates double push moves. /// </summary> /// <param name="opt">The generator parameters.</param> private static void CalculateMovesForDoublePush(GeneratorParameters opt) { if ((opt.Mode & GeneratorMode.CalculateMoves) == 0 || opt.QuiescenceSearch) { return; } var piecesToParse = opt.Bitboard.Pieces[FastArray.GetPieceIndex(opt.FriendlyColor, PieceType.Pawn)]; ulong validPieces; ulong pattern; if (opt.FriendlyColor == Color.White) { validPieces = piecesToParse & BitConstants.BRank; validPieces &= ~opt.OccupancySummary >> 8; pattern = validPieces << 16; } else { validPieces = piecesToParse & BitConstants.GRank; validPieces &= ~opt.OccupancySummary << 8; pattern = validPieces >> 16; } pattern &= ~opt.OccupancySummary; while (pattern != 0) { var patternLSB = BitOperations.GetLSB(pattern); pattern = BitOperations.PopLSB(pattern); var patternIndex = BitOperations.GetBitIndex(patternLSB); var pieceLSB = opt.FriendlyColor == Color.White ? patternLSB >> 16 : patternLSB << 16; var pieceIndex = BitOperations.GetBitIndex(pieceLSB); var from = BitPositionConverter.ToPosition(pieceIndex); var to = BitPositionConverter.ToPosition(patternIndex); opt.Bitboard.Moves.AddLast(new QuietMove(from, to, PieceType.Pawn, opt.FriendlyColor)); } }
/// <summary> /// Calculates single push moves. /// </summary> /// <param name="opt">The generator parameters.</param> private static void CalculateMovesForSinglePush(GeneratorParameters opt) { if ((opt.Mode & GeneratorMode.CalculateMoves) == 0) { return; } var piecesToParse = opt.Bitboard.Pieces[FastArray.GetPieceIndex(opt.FriendlyColor, PieceType.Pawn)]; var promotionLine = GetPromotionLine(opt.FriendlyColor); var pattern = opt.FriendlyColor == Color.White ? piecesToParse << 8 : piecesToParse >> 8; pattern &= ~opt.OccupancySummary; while (pattern != 0) { var patternLSB = BitOperations.GetLSB(pattern); pattern = BitOperations.PopLSB(pattern); var patternIndex = BitOperations.GetBitIndex(patternLSB); var pieceLSB = opt.FriendlyColor == Color.White ? patternLSB >> 8 : patternLSB << 8; var pieceIndex = BitOperations.GetBitIndex(pieceLSB); var from = BitPositionConverter.ToPosition(pieceIndex); var to = BitPositionConverter.ToPosition(patternIndex); if ((patternLSB & promotionLine) == 0 && !opt.QuiescenceSearch) { opt.Bitboard.Moves.AddLast(new QuietMove(from, to, PieceType.Pawn, opt.FriendlyColor)); } else if ((patternLSB & promotionLine) != 0) { opt.Bitboard.Moves.AddLast(new PromotionMove(from, to, PieceType.Pawn, opt.FriendlyColor, PieceType.Queen, false)); opt.Bitboard.Moves.AddLast(new PromotionMove(from, to, PieceType.Pawn, opt.FriendlyColor, PieceType.Rook, false)); opt.Bitboard.Moves.AddLast(new PromotionMove(from, to, PieceType.Pawn, opt.FriendlyColor, PieceType.Bishop, false)); opt.Bitboard.Moves.AddLast(new PromotionMove(from, to, PieceType.Pawn, opt.FriendlyColor, PieceType.Knight, false)); } } }
/// <summary> /// Calculates attacks for the specified piece. /// </summary> /// <param name="pieceBitboard">The bitboard with set bit at piece position.</param> /// <param name="excludedFields">The bitboard with excluded fields from attacks calculating.</param> /// <param name="opt">The generator parameters.</param> private static void CalculateAttacks(ulong pieceBitboard, ulong excludedFields, GeneratorParameters opt) { if ((opt.Mode & GeneratorMode.CalculateAttacks) == 0) { return; } var pieceIndex = BitOperations.GetBitIndex(pieceBitboard); var blockersToRemove = opt.Bitboard.Pieces[FastArray.GetPieceIndex(opt.FriendlyColor, PieceType.Bishop)] | opt.Bitboard.Pieces[FastArray.GetPieceIndex(opt.FriendlyColor, PieceType.Queen)]; var allPiecesOccupancy = opt.OccupancySummary & ~blockersToRemove; var pattern = MagicContainer.GetBishopAttacks(pieceIndex, allPiecesOccupancy); pattern = CalculatePawnBlockers(pieceIndex, pattern, opt); pattern ^= excludedFields; while (pattern != 0) { var patternLSB = BitOperations.GetLSB(pattern); pattern = BitOperations.PopLSB(pattern); var patternIndex = BitOperations.GetBitIndex(patternLSB); opt.Bitboard.Attacks[patternIndex] |= pieceBitboard; opt.Bitboard.AttacksSummary[(int)opt.FriendlyColor] |= patternLSB; } }
/// <summary> /// Calculates moves for the specified piece. /// </summary> /// <param name="pieceType">The piece type.</param> /// <param name="pieceBitboard">The bitboard with set bit at piece position.</param> /// <param name="opt">The generator parameters.</param> /// <returns>The bitboard with available moves for the specified piece.</returns> private static ulong CalculateMoves(PieceType pieceType, ulong pieceBitboard, GeneratorParameters opt) { if ((opt.Mode & GeneratorMode.CalculateMoves) == 0) { return(0); } var pieceIndex = BitOperations.GetBitIndex(pieceBitboard); var piecePosition = BitPositionConverter.ToPosition(pieceIndex); var pattern = MagicContainer.GetBishopAttacks(pieceIndex, opt.OccupancySummary); pattern &= ~opt.FriendlyOccupancy; if (opt.QuiescenceSearch) { pattern &= opt.EnemyOccupancy; } var excludeFromAttacks = pattern; while (pattern != 0) { var patternLSB = BitOperations.GetLSB(pattern); pattern = BitOperations.PopLSB(pattern); var patternIndex = BitOperations.GetBitIndex(patternLSB); var to = BitPositionConverter.ToPosition(patternIndex); if ((patternLSB & opt.EnemyOccupancy) == 0) { opt.Bitboard.Moves.AddLast(new QuietMove(piecePosition, to, pieceType, opt.FriendlyColor)); } else { opt.Bitboard.Moves.AddLast(new KillMove(piecePosition, to, pieceType, opt.FriendlyColor)); } if ((opt.Mode & GeneratorMode.CalculateAttacks) != 0) { opt.Bitboard.Attacks[patternIndex] |= pieceBitboard; opt.Bitboard.AttacksSummary[(int)opt.FriendlyColor] |= patternLSB; } } return(excludeFromAttacks); }
/// <summary> /// Checks if check area (all fields that are on the way of king during castling) is checked. /// </summary> /// <param name="enemyColor">The enemy color.</param> /// <param name="areaToCheck">The bitboard with set bit at all fields to check.</param> /// <param name="opt">The generator parameters.</param> /// <returns>True if any of the castling area is checked, otherwise false.</returns> private static bool IsCastlingAreaChecked(Color enemyColor, ulong areaToCheck, GeneratorParameters opt) { return((opt.Bitboard.AttacksSummary[(int)enemyColor] & areaToCheck) != 0); }
/// <summary> /// Checks if rook is on initial position and can be a part of castling. /// </summary> /// <param name="rookBitboard">The bitboard with set bit at rook position.</param> /// <param name="opt">The generator parameters.</param> /// <returns>True if rook is on initial position, otherwise false.</returns> private static bool IsRookOnPosition(ulong rookBitboard, GeneratorParameters opt) { return((opt.Bitboard.Pieces[FastArray.GetPieceIndex(opt.FriendlyColor, PieceType.Rook)] & rookBitboard) != 0); }
/// <summary> /// Checks if king is on initial position and can be a part of castling. /// </summary> /// <param name="kingBitboard">The bitboard with set bit at king position.</param> /// <param name="opt">The generator parameters.</param> /// <returns>True if king is on initial position, otherwise false.</returns> private static bool IsKingOnPosition(ulong kingBitboard, GeneratorParameters opt) { return((opt.Bitboard.Pieces[FastArray.GetPieceIndex(opt.FriendlyColor, PieceType.King)] & kingBitboard) != 0); }
/// <summary> /// Checks if castling with the specified type is possible. /// </summary> /// <param name="type">The castling type.</param> /// <param name="opt">The generator parameters.</param> /// <returns>True if castling is possible, otherwise false.</returns> private static bool IsCastlingPossible(CastlingType type, GeneratorParameters opt) { return(opt.Bitboard.CastlingPossibility[FastArray.GetCastlingIndex(opt.FriendlyColor, type)]); }
/// <summary> /// Calculates diagonal attacks (and moves if possible). /// </summary> /// <param name="leftAttackShift">The left attack shift.</param> /// <param name="rightAttackShift">The right attacks shift.</param> /// <param name="ignoreFields">The bitboard with fields to ignore (white and black pieces will have different ones).</param> /// <param name="opt">The generator parameters.</param> private static void CalculateDiagonalAttacks(int leftAttackShift, int rightAttackShift, ulong ignoreFields, GeneratorParameters opt) { var piecesToParse = opt.Bitboard.Pieces[FastArray.GetPieceIndex(opt.FriendlyColor, PieceType.Pawn)]; var validPieces = piecesToParse & ~ignoreFields; var pattern = opt.FriendlyColor == Color.White ? validPieces << leftAttackShift : validPieces >> rightAttackShift; var promotionLine = GetPromotionLine(opt.FriendlyColor); while (pattern != 0) { var patternLSB = BitOperations.GetLSB(pattern); pattern = BitOperations.PopLSB(pattern); var patternIndex = BitOperations.GetBitIndex(patternLSB); var pieceLSB = opt.FriendlyColor == Color.White ? patternLSB >> leftAttackShift : patternLSB << rightAttackShift; var pieceIndex = BitOperations.GetBitIndex(pieceLSB); if ((opt.Mode & GeneratorMode.CalculateMoves) != 0) { var piecePosition = BitPositionConverter.ToPosition(pieceIndex); var enPassantField = opt.Bitboard.EnPassant[(int)opt.EnemyColor] & patternLSB; if ((patternLSB & opt.EnemyOccupancy) != 0 || enPassantField != 0) { var to = BitPositionConverter.ToPosition(patternIndex); if (enPassantField != 0) { opt.Bitboard.Moves.AddLast(new EnPassantMove(piecePosition, to, PieceType.Pawn, opt.FriendlyColor)); } else if ((patternLSB & promotionLine) != 0) { opt.Bitboard.Moves.AddLast(new PromotionMove(piecePosition, to, PieceType.Pawn, opt.FriendlyColor, PieceType.Queen, true)); opt.Bitboard.Moves.AddLast(new PromotionMove(piecePosition, to, PieceType.Pawn, opt.FriendlyColor, PieceType.Rook, true)); opt.Bitboard.Moves.AddLast(new PromotionMove(piecePosition, to, PieceType.Pawn, opt.FriendlyColor, PieceType.Bishop, true)); opt.Bitboard.Moves.AddLast(new PromotionMove(piecePosition, to, PieceType.Pawn, opt.FriendlyColor, PieceType.Knight, true)); } else { opt.Bitboard.Moves.AddLast(new KillMove(piecePosition, to, PieceType.Pawn, opt.FriendlyColor)); } } } if ((opt.Mode & GeneratorMode.CalculateAttacks) != 0) { opt.Bitboard.Attacks[patternIndex] |= pieceLSB; opt.Bitboard.AttacksSummary[(int)opt.FriendlyColor] |= patternLSB; } } }