/// <summary>
        /// Adds en passant to Zobrist hash.
        /// </summary>
        /// <param name="color">The piece color.</param>
        /// <param name="field">The field bitboard.</param>
        /// <param name="bitboard">The bitboard.</param>
        public static void AddEnPassant(Color color, ulong field, Bitboard bitboard)
        {
            var fieldIndex    = BitOperations.GetBitIndex(field);
            var fieldPosition = BitPositionConverter.ToPosition(fieldIndex);

            bitboard.Hash ^= ZobristContainer.EnPassant[fieldPosition.X - 1];
        }
Exemple #2
0
        /// <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;
                }
            }
        }
        /// <summary>
        /// Clears en passant from Zobrist hash.
        /// </summary>
        /// <param name="color">The piece color.</param>
        /// <param name="bitboard">The bitboard.</param>
        public static void ClearEnPassant(Color color, Bitboard bitboard)
        {
            var enPassantToParse = bitboard.EnPassant[(int)color];

            while (enPassantToParse != 0)
            {
                var fieldLSB = BitOperations.GetLSB(enPassantToParse);
                enPassantToParse = BitOperations.PopLSB(enPassantToParse);

                var fieldIndex    = BitOperations.GetBitIndex(fieldLSB);
                var fieldPosition = BitPositionConverter.ToPosition(fieldIndex);

                bitboard.Hash ^= ZobristContainer.EnPassant[fieldPosition.X - 1];
            }
        }
        /// <summary>
        /// Gets a en passant position from the specified bitboard.
        /// </summary>
        /// <param name="enPassant">The en passant bitboard.</param>
        /// <returns>The en passant position (null if en passant bitboard is equal to zero)</returns>
        private Position?GetEnPassantPosition(ulong enPassant)
        {
            if (enPassant == 0)
            {
                return(null);
            }

            var lsb = BitOperations.GetLSB(enPassant);

            BitOperations.PopLSB(enPassant);

            var bitIndex = BitOperations.GetBitIndex(lsb);

            return(BitPositionConverter.ToPosition(bitIndex));
        }
        /// <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.GetRookAttacks(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>
        /// 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>
        /// Generates rook patterns for every field.
        /// </summary>
        /// <returns>The 64-element array with patterns.</returns>
        public ulong[] Generate()
        {
            var predefinedMoves = new ulong[64];

            for (var i = 0; i < 64; i++)
            {
                var fieldBit = 1ul << i;
                var position = BitPositionConverter.ToPosition(i);

                var filePattern = GetFilePattern(position) & ~BitConstants.TopBottomEdge;
                var rankPattern = GetRankPattern(position) & ~BitConstants.RightLeftEdge;

                predefinedMoves[i] = ~fieldBit & (filePattern | rankPattern);
            }

            return(predefinedMoves);
        }
Exemple #8
0
        /// <summary>
        /// Calculates pattern for the specified field and shift.
        /// </summary>
        /// <param name="fieldIndex">The index field.</param>
        /// <param name="shift">The shift (the direction in which the calculating is made).</param>
        /// <returns>The pattern for the specified field.</returns>
        public ulong CalculatePattern(int fieldIndex, Position shift)
        {
            var attacks         = 0ul;
            var currentPosition = BitPositionConverter.ToPosition(fieldIndex);

            currentPosition += shift;
            while (currentPosition.IsValid())
            {
                var positionBitIndex = BitPositionConverter.ToBitIndex(currentPosition);
                var bit = 1ul << positionBitIndex;
                attacks |= bit;

                currentPosition += shift;
            }

            return(attacks);
        }
        /// <summary>
        /// Generates available moves for the specified bitboard and shift.
        /// </summary>
        /// <param name="initialFieldIndex">The initial field index.</param>
        /// <param name="occupancy">The bitboard occupancy.</param>
        /// <param name="shift">The shift (direction of available moves generating).</param>
        /// <returns>The available moves bitboard (where 1 means possible position, otherwise 0).</returns>
        public ulong Calculate(int initialFieldIndex, ulong occupancy, Position shift)
        {
            var attacks         = 0ul;
            var bit             = 0ul;
            var currentPosition = BitPositionConverter.ToPosition(initialFieldIndex);

            currentPosition += shift;
            while (currentPosition.IsValid() && (bit & occupancy) == 0)
            {
                var positionBitIndex = BitPositionConverter.ToBitIndex(currentPosition);

                bit      = 1ul << positionBitIndex;
                attacks |= bit;

                currentPosition += shift;
            }

            return(attacks);
        }
Exemple #10
0
        /// <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;
                    }
                }
            }
        }
Exemple #11
0
        /// <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>
        /// Initializes a new instance of the <see cref="FriendlyAttacksList"/> class.
        /// </summary>
        /// <param name="attacks">The array of attack bitboards (where index means field index (source piece position),
        ///                       and set bits are the destination attack positions.</param>
        /// <param name="pieces">The list of pieces.</param>
        public FriendlyAttacksList(ulong[] attacks, FriendlyPiecesList pieces)
        {
            for (var i = 0; i < 64; i++)
            {
                var fieldAttackers = attacks[i];
                var targetPosition = BitPositionConverter.ToPosition(i);

                while (fieldAttackers != 0)
                {
                    var attackerLSB = BitOperations.GetLSB(fieldAttackers);
                    fieldAttackers = BitOperations.PopLSB(fieldAttackers);

                    var attackerIndex    = BitOperations.GetBitIndex(attackerLSB);
                    var attackerPosition = BitPositionConverter.ToPosition(attackerIndex);
                    var attackerColor    = pieces.First(p => p.Position == attackerPosition).Color;

                    Add(new FriendlyAttack(attackerColor, attackerPosition, targetPosition));
                }
            }
        }
        /// <summary>
        /// Calculates Zobrist hash for en passant.
        /// </summary>
        /// <param name="hash">The current hash.</param>
        /// <param name="enPassant">The array of en passant.</param>
        /// <returns>The updated Zobrist hash.</returns>
        private static ulong CalculateEnPassant(ulong hash, ulong[] enPassant)
        {
            for (var colorIndex = 0; colorIndex < 2; colorIndex++)
            {
                var enPassantToParse = enPassant[colorIndex];

                while (enPassantToParse != 0)
                {
                    var pieceLSB = BitOperations.GetLSB(enPassantToParse);
                    enPassantToParse = BitOperations.PopLSB(enPassantToParse);

                    var fieldIndex    = BitOperations.GetBitIndex(pieceLSB);
                    var fieldPosition = BitPositionConverter.ToPosition(fieldIndex);

                    hash ^= ZobristContainer.EnPassant[fieldPosition.X - 1];
                }
            }

            return(hash);
        }
Exemple #14
0
        /// <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));
                }
            }
        }
Exemple #15
0
        /// <summary>
        /// Initializes a new instance of the <see cref="FriendlyPiecesList"/> class.
        /// </summary>
        /// <param name="pieces">The array of pieces.</param>
        public FriendlyPiecesList(ulong[] pieces)
        {
            for (var i = 0; i < 12; i++)
            {
                var pieceArray = pieces[i];

                while (pieceArray != 0)
                {
                    var lsb = BitOperations.GetLSB(pieceArray);
                    pieceArray = BitOperations.PopLSB(pieceArray);

                    var bitIndex = BitOperations.GetBitIndex(lsb);
                    var position = BitPositionConverter.ToPosition(bitIndex);

                    var pieceType  = (PieceType)(i % 6);
                    var pieceColor = (Color)(i / 6);

                    Add(new FriendlyPiece(position, pieceType, pieceColor));
                }
            }
        }
        /// <summary>
        /// Calculates SEE for the specified field and initial attacker.
        /// </summary>
        /// <param name="field">The field to analyze.</param>
        /// <param name="initialAttacker">The initial attacker.</param>
        /// <param name="attackers">The bitboard with all attackers that will be a part of SEE.</param>
        /// <param name="initialColor">The color of the first attacker.</param>
        /// <param name="bitboard">The bitboard.</param>
        /// <returns>The SEE result with the score and other data.</returns>
        private SEEResult CalculateScoreForField(ulong field, ulong initialAttacker, ulong attackers, Color initialColor, Bitboard bitboard)
        {
            var enemyColor = ColorOperations.Invert(initialColor);

            var seeResult = new SEEResult
            {
                InitialAttackerFrom = BitPositionConverter.ToPosition(BitOperations.GetBitIndex(initialAttacker)),
                InitialAttackerTo   = BitPositionConverter.ToPosition(BitOperations.GetBitIndex(field)),

                InitialAttackerType = GetPieceType(initialAttacker, initialColor, bitboard),
                AttackedPieceType   = GetPieceType(field, enemyColor, bitboard)
            };

            seeResult.Score = MaterialValues.PieceValues[(int)seeResult.AttackedPieceType];
            attackers      &= ~initialAttacker;

            var currentColor = enemyColor;
            var currentSign  = -1;

            var currentPieceOnField = seeResult.InitialAttackerType;

            while (attackers != 0)
            {
                var leastValuablePieceType = GetAndPopLeastValuablePiece(ref attackers, currentColor, bitboard);
                if (!leastValuablePieceType.HasValue)
                {
                    break;
                }

                seeResult.Score += currentSign * MaterialValues.PieceValues[(int)currentPieceOnField];

                currentPieceOnField = leastValuablePieceType.Value;
                currentColor        = ColorOperations.Invert(currentColor);
                currentSign        *= -1;
            }

            return(seeResult);
        }