< Summary

Line coverage
99%
Covered lines: 279
Uncovered lines: 2
Coverable lines: 281
Total lines: 521
Line coverage: 99.2%
Branch coverage
98%
Covered branches: 93
Total branches: 94
Branch coverage: 98.9%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

File(s)

/home/runner/work/rudim/rudim/Rudim/Bitboard/Bitboard.Attacks.cs

#LineLine coverage
 1using Rudim.Common;
 2
 3namespace Rudim
 4{
 5    public partial struct Bitboard
 6    {
 7        public static Bitboard GetPawnAttacks(Square square, Side side)
 8        {
 1349            Bitboard resultBoard = new Bitboard(0);
 13410            Bitboard pawnBoard = new Bitboard(0);
 13411            pawnBoard.SetBit(square);
 12
 13413            if (side == Side.White)
 14            {
 6715                resultBoard.Board |= (pawnBoard.Board >> 9) & ~FileH;
 6716                resultBoard.Board |= (pawnBoard.Board >> 7) & ~FileA;
 17            }
 18            else
 19            {
 6720                resultBoard.Board |= (pawnBoard.Board << 7) & ~FileH;
 6721                resultBoard.Board |= (pawnBoard.Board << 9) & ~FileA;
 22            }
 23
 13424            return resultBoard;
 25        }
 26
 27        public static Bitboard GetKnightAttacks(Square square)
 28        {
 6629            Bitboard resultBoard = new Bitboard(0);
 6630            Bitboard knightBoard = new Bitboard(0);
 6631            knightBoard.SetBit(square);
 32
 6633            resultBoard.Board |= (knightBoard.Board << 17) & ~FileA;
 6634            resultBoard.Board |= (knightBoard.Board << 10) & ~FileAb;
 6635            resultBoard.Board |= (knightBoard.Board >> 6) & ~FileAb;
 6636            resultBoard.Board |= (knightBoard.Board >> 15) & ~FileA;
 6637            resultBoard.Board |= (knightBoard.Board << 15) & ~FileH;
 6638            resultBoard.Board |= (knightBoard.Board << 6) & ~FileGh;
 6639            resultBoard.Board |= (knightBoard.Board >> 10) & ~FileGh;
 6640            resultBoard.Board |= (knightBoard.Board >> 17) & ~FileH;
 41
 6642            return resultBoard;
 43        }
 44
 45        public static Bitboard GetKingAttacks(Square square)
 46        {
 6647            Bitboard resultBoard = new Bitboard(0);
 6648            Bitboard kingBoard = new Bitboard(0);
 6649            kingBoard.SetBit(square);
 50
 6651            resultBoard.Board |= (kingBoard.Board << 1) & ~FileA;
 6652            resultBoard.Board |= (kingBoard.Board >> 7) & ~FileA;
 6653            resultBoard.Board |= (kingBoard.Board << 9) & ~FileA;
 54
 6655            resultBoard.Board |= (kingBoard.Board >> 1) & ~FileH;
 6656            resultBoard.Board |= (kingBoard.Board << 7) & ~FileH;
 6657            resultBoard.Board |= (kingBoard.Board >> 9) & ~FileH;
 58
 6659            resultBoard.Board |= (kingBoard.Board << 8);
 6660            resultBoard.Board |= (kingBoard.Board >> 8);
 61
 6662            return resultBoard;
 63        }
 64
 65        public static Bitboard GetBishopAttacks(Square square, Bitboard occupancy)
 66        {
 525467            Bitboard resultBoard = new Bitboard(0);
 525468            int bishopRank = (int)square >> 3;
 525469            int bishopFile = (int)square & (8 - 1);
 70
 2981171            for (int rank = bishopRank + 1, file = bishopFile + 1; rank < 8 && file < 8; ++rank, ++file)
 781672                if (AddSquareToBoardAndStopAtOccupiedSquare(ref resultBoard, rank, file, occupancy)) break;
 73
 2984474            for (int rank = bishopRank - 1, file = bishopFile + 1; rank >= 0 && file < 8; --rank, ++file)
 782775                if (AddSquareToBoardAndStopAtOccupiedSquare(ref resultBoard, rank, file, occupancy)) break;
 76
 2981477            for (int rank = bishopRank - 1, file = bishopFile - 1; rank >= 0 && file >= 0; --rank, --file)
 781678                if (AddSquareToBoardAndStopAtOccupiedSquare(ref resultBoard, rank, file, occupancy)) break;
 79
 2981480            for (int rank = bishopRank + 1, file = bishopFile - 1; rank < 8 && file >= 0; ++rank, --file)
 781781                if (AddSquareToBoardAndStopAtOccupiedSquare(ref resultBoard, rank, file, occupancy)) break;
 82
 525483            return resultBoard;
 84        }
 85
 86        public static Bitboard GetRookAttacks(Square square, Bitboard occupancy)
 87        {
 10240688            Bitboard resultBoard = new Bitboard(0);
 10240689            int rookRank = (int)square >> 3;
 10240690            int rookFile = (int)square & (8 - 1);
 91
 36867892            for (int rank = rookRank + 1; rank < 8; ++rank)
 14337493                if (AddSquareToBoardAndStopAtOccupiedSquare(ref resultBoard, rank, rookFile, occupancy)) break;
 94
 36869495            for (int rank = rookRank - 1; rank >= 0; --rank)
 14338396                if (AddSquareToBoardAndStopAtOccupiedSquare(ref resultBoard, rank, rookFile, occupancy)) break;
 97
 36868498            for (int file = rookFile + 1; file < 8; ++file)
 14337899                if (AddSquareToBoardAndStopAtOccupiedSquare(ref resultBoard, rookRank, file, occupancy)) break;
 100
 368684101            for (int file = rookFile - 1; file >= 0; --file)
 143376102                if (AddSquareToBoardAndStopAtOccupiedSquare(ref resultBoard, rookRank, file, occupancy)) break;
 103
 102406104            return resultBoard;
 105        }
 106
 107        public static Bitboard GetQueenAttacks(Square square, Bitboard occupancy)
 108        {
 1109            Bitboard rookAttacks = GetRookAttacks(square, occupancy);
 1110            Bitboard bishopAttacks = GetBishopAttacks(square, occupancy);
 1111            return new Bitboard(rookAttacks.Board | bishopAttacks.Board);
 112        }
 113
 114        private static bool AddSquareToBoardAndStopAtOccupiedSquare(ref Bitboard resultBoard, int rank, int file, Bitboa
 115        {
 605879116            resultBoard.Board |= 1ul << (rank * 8) + file;
 605879117            return (1ul << (rank * 8) + file & occupancy.Board) > 0;
 118        }
 119    }
 120}

/home/runner/work/rudim/rudim/Rudim/Bitboard/Bitboard.cs

#LineLine coverage
 1using Rudim.Common;
 2using System;
 3using System.Numerics;
 4
 5namespace Rudim
 6{
 7    public partial struct Bitboard(ulong board) : IEquatable<Bitboard>
 8    {
 16817458879        public ulong Board { get; private set; } = board;
 10
 11        public int GetBit(Square square)
 12        {
 8839322913            return GetBit((int)square);
 14        }
 15
 16        public Bitboard SetBit(Square square)
 17        {
 4530502218            SetBit((int)square);
 4530502219            return this;
 20        }
 21
 22        public Bitboard ClearBit(Square square)
 23        {
 7549942524            ClearBit((int)square);
 7549942525            return this;
 26        }
 27
 28        public int GetBit(int square)
 29        {
 22981480530            return (Board & (1ul << square)) > 0 ? 1 : 0;
 31        }
 32
 33        public void SetBit(int square)
 34        {
 4530502635            Board |= 1ul << square;
 4530502636        }
 37
 38        public void ClearBit(int square)
 39        {
 27541372640            Board &= ~(1ul << square);
 27541372641        }
 42
 43        public int GetLsb()
 44        {
 20643599545            return BitOperations.TrailingZeroCount(Board);
 46        }
 47
 48        public override bool Equals(object obj)
 49        {
 8450            return obj is Bitboard bitboard &&
 8451                   Board == bitboard.Board;
 52        }
 53
 54        public override int GetHashCode()
 55        {
 056            return HashCode.Combine(Board);
 57        }
 58
 59        public bool Equals(Bitboard other)
 60        {
 8461            return Equals((object)other);
 62        }
 63    }
 64}

/home/runner/work/rudim/rudim/Rudim/Bitboard/Bitboard.Lookups.cs

#LineLine coverage
 1using Rudim.Common;
 2using System.Numerics;
 3
 4namespace Rudim
 5{
 6    public partial struct Bitboard
 7    {
 8        private const ulong FileA = 72340172838076673;
 9        private const ulong FileB = 144680345676153346;
 10        private const ulong FileG = 4629771061636907072;
 11        private const ulong FileH = 9259542123273814144;
 12        private const ulong FileAb = FileA | FileB;
 13        private const ulong FileGh = FileG | FileH;
 14
 115        public static readonly ulong[,] PawnAttacks = new ulong[Constants.Sides, Constants.Squares];
 116        public static readonly ulong[] KnightAttacks = new ulong[Constants.Squares];
 117        public static readonly ulong[] KingAttacks = new ulong[Constants.Squares];
 118        private static readonly ulong[,] BishopAttacks = new ulong[Constants.Squares, Constants.MaxBishopMask];
 119        private static readonly ulong[,] RookAttacks = new ulong[Constants.Squares, Constants.MaxRookMask];
 20
 121        private static readonly ulong[] BishopMasks = new ulong[Constants.Squares];
 122        private static readonly ulong[] RookMasks = new ulong[Constants.Squares];
 23
 124        public static readonly int[] BishopMaskBits = new int[Constants.Squares];
 125        public static readonly int[] RookMaskBits = new int[Constants.Squares];
 26
 27        static Bitboard()
 28        {
 13029            for (int square = 0; square < Constants.Squares; ++square)
 30            {
 6431                PawnAttacks[(int)Side.White, square] = GetPawnAttacks((Square)square, Side.White).Board;
 6432                PawnAttacks[(int)Side.Black, square] = GetPawnAttacks((Square)square, Side.Black).Board;
 33
 6434                KnightAttacks[square] = GetKnightAttacks((Square)square).Board;
 35
 6436                KingAttacks[square] = GetKingAttacks((Square)square).Board;
 37
 6438                BishopMasks[square] = GetBishopMask((Square)square).Board;
 6439                BishopMaskBits[square] = BitOperations.PopCount(BishopMasks[square]);
 40
 6441                RookMasks[square] = GetRookMask((Square)square).Board;
 6442                RookMaskBits[square] = BitOperations.PopCount(RookMasks[square]);
 43
 1062444                for (int index = 0; index < (1 << BishopMaskBits[square]); ++index)
 45                {
 524846                    Bitboard occupancyMapping = GetOccupancyMapping(index, BishopMaskBits[square], new Bitboard(BishopMa
 524847                    ulong magicIndex = (occupancyMapping.Board * BishopMagics[square]) >> (64 - BishopMaskBits[square]);
 524848                    BishopAttacks[square, magicIndex] = GetBishopAttacks((Square)square, occupancyMapping).Board;
 49                }
 50
 20492851                for (int index = 0; index < (1 << RookMaskBits[square]); ++index)
 52                {
 10240053                    Bitboard occupancyMapping = GetOccupancyMapping(index, RookMaskBits[square], new Bitboard(RookMasks[
 10240054                    ulong magicIndex = (occupancyMapping.Board * RookMagics[square]) >> (64 - RookMaskBits[square]);
 10240055                    RookAttacks[square, magicIndex] = GetRookAttacks((Square)square, occupancyMapping).Board;
 56                }
 57            }
 158        }
 59
 60        public static Bitboard GetBishopAttacksFromTable(Square square, Bitboard occupancy)
 61        {
 2061893762            ulong index = occupancy.Board;
 2061893763            index &= BishopMasks[(int)square];
 2061893764            index *= BishopMagics[(int)square];
 2061893765            index >>= 64 - BishopMaskBits[(int)square];
 2061893766            return new Bitboard(BishopAttacks[(int)square, index]);
 67        }
 68
 69        public static Bitboard GetRookAttacksFromTable(Square square, Bitboard occupancy)
 70        {
 2120738371            ulong index = occupancy.Board;
 2120738372            index &= RookMasks[(int)square];
 2120738373            index *= RookMagics[(int)square];
 2120738374            index >>= 64 - RookMaskBits[(int)square];
 2120738375            return new Bitboard(RookAttacks[(int)square, index]);
 76        }
 77
 78        public static Bitboard GetQueenAttacksFromTable(Square square, Bitboard occupancy)
 79        {
 966910780            return new(GetRookAttacksFromTable(square, occupancy).Board | GetBishopAttacksFromTable(square, occupancy).B
 81        }
 82
 83        // Precalculated - Refer Bitboard.FindMagicNumber()
 184        private static readonly ulong[] BishopMagics = {
 185                    572335195422784,
 186                    9225705203045892096,
 187                    1155322839151150592,
 188                    4684944281377579073,
 189                    9511901755049246721,
 190                    72218192528801800,
 191                    19757142156521488,
 192                    1266779148001381,
 193                    3602951187466322196,
 194                    2261216596869188,
 195                    31596674340110466,
 196                    11331843878028352,
 197                    13979177654425755648,
 198                    288795559522207748,
 199                    721148038749358145,
 1100                    628254355639896068,
 1101                    2747233433184108672,
 1102                    631631016576417856,
 1103                    571763293683725,
 1104                    1153485640510341152,
 1105                    72622760764965888,
 1106                    4973662945859898496,
 1107                    1156440496010170372,
 1108                    1729523414332866952,
 1109                    1130298494980176,
 1110                    2310349082885357840,
 1111                    2882356539283308768,
 1112                    579847256709136448,
 1113                    13842658983763001344,
 1114                    16285862876542411008,
 1115                    4820533887766656,
 1116                    576549817342263872,
 1117                    13837312088550279168,
 1118                    18159671634577480,
 1119                    40673410086601728,
 1120                    95912632808669696,
 1121                    144397766927056960,
 1122                    577613059818262592,
 1123                    2315344997304535046,
 1124                    4612009275386004482,
 1125                    288388774679810052,
 1126                    1162218983791854088,
 1127                    4616754767635423744,
 1128                    4899916678055348228,
 1129                    9531886212148625920,
 1130                    18085883961999392,
 1131                    146376093224403072,
 1132                    4617341907319128329,
 1133                    1154048508456608768,
 1134                    146509926817370115,
 1135                    1225120471797202948,
 1136                    547885504,
 1137                    4648005224496177152,
 1138                    576540344087347202,
 1139                    614213601338625024,
 1140                    9729235348727332879,
 1141                    1154118875380457472,
 1142                    4521376632410114,
 1143                    4611686297608687616,
 1144                    2216882865184,
 1145                    10376399751779517444,
 1146                    4612284170613301505,
 1147                    2594178955930118721,
 1148                    9297788375727620608};
 149
 1150        private static readonly ulong[] RookMagics = {
 1151                    11565244117967444096,
 1152                    594492744072699904,
 1153                    2197769949736337536,
 1154                    1188955249696573442,
 1155                    72075220583973632,
 1156                    144118555532091904,
 1157                    288255321624023816,
 1158                    4755803406625603628,
 1159                    108227130696925216,
 1160                    72690981524209728,
 1161                    1157988191899353216,
 1162                    2378463697367468544,
 1163                    9235194054747365888,
 1164                    144678174821974528,
 1165                    4644478851874944,
 1166                    576742228362330114,
 1167                    18050132645789696,
 1168                    157643854024015937,
 1169                    150083874263040,
 1170                    166633738116530192,
 1171                    2450526645086390272,
 1172                    282574622818306,
 1173                    848823010722064,
 1174                    1152923703902765348,
 1175                    4644339265323016,
 1176                    9250393773131714560,
 1177                    6917812989404913665,
 1178                    2308095360931725320,
 1179                    1315059889432952962,
 1180                    146932139022091264,
 1181                    2201179128064,
 1182                    1153203263051464836,
 1183                    1008947333225783810,
 1184                    9331493613361696768,
 1185                    576601627306233861,
 1186                    36169603235186688,
 1187                    3612168685415829508,
 1188                    151997037255066112,
 1189                    300616580864164360,
 1190                    36284991012996,
 1191                    54078654820483072,
 1192                    1170971097420611584,
 1193                    72198615062413344,
 1194                    9227893228802441344,
 1195                    2342434825042001925,
 1196                    7072883491209488,
 1197                    1729954007285497872,
 1198                    4620974832652189698,
 1199                    184717955613862016,
 1200                    1452551892478469376,
 1201                    2305878193854317696,
 1202                    9948460375102980224,
 1203                    2308385084459221120,
 1204                    9241667927523076352,
 1205                    36046394450117632,
 1206                    433190608774955520,
 1207                    2310365301614608385,
 1208                    146740276384645123,
 1209                    288300884483508738,
 1210                    4613374937141616706,
 1211                    4785108963951633,
 1212                    4648277834194814978,
 1213                    8798274129924,
 1214                    1157930883880079490};
 215    }
 216}

/home/runner/work/rudim/rudim/Rudim/Bitboard/Bitboard.Magics.cs

#LineLine coverage
 1using Rudim.Common;
 2using System.Diagnostics.CodeAnalysis;
 3using System.Linq;
 4using System.Numerics;
 5
 6namespace Rudim
 7{
 8    public partial struct Bitboard
 9    {
 10        [ExcludeFromCodeCoverage] // This is only used as a helper in the beginning to generate numbers - never used in 
 11        public static ulong FindMagicNumber(Square square, int bitsInMask, bool isBishop)
 12        {
 13            int maxIndex = 1 << bitsInMask;
 14            Bitboard[] occupancyMappings = new Bitboard[Constants.MaxMaskIndex];
 15            Bitboard[] attacks = new Bitboard[Constants.MaxMaskIndex];
 16            Bitboard mask = isBishop ? GetBishopMask(square) : GetRookMask(square);
 17
 18            for (int index = 0; index < maxIndex; ++index)
 19            {
 20                occupancyMappings[index] = GetOccupancyMapping(index, bitsInMask, mask);
 21                attacks[index] = isBishop ? GetBishopAttacks(square, occupancyMappings[index]) : GetRookAttacks(square, 
 22            }
 23
 24            for (int count = 0; count < Constants.MaxRetryCount; ++count)
 25            {
 26                ulong potentialMagicNumber = GeneratePotentialMagicNumber();
 27
 28                // Early exit impossible magics
 29                if (BitOperations.PopCount((mask.Board * potentialMagicNumber) & 0xFF00000000000000) < 6)
 30                    continue;
 31
 32                Bitboard[] magicAttacks = Enumerable.Repeat(new Bitboard(0xFFFFFFFFFFFFFFFF), Constants.MaxMaskIndex).To
 33                bool failureFlag = false;
 34                for (int index = 0; index < maxIndex; ++index)
 35                {
 36                    int magicIndex = (int)((occupancyMappings[index].Board * potentialMagicNumber) >> (64 - bitsInMask))
 37                    if (magicAttacks[magicIndex].Board == 0xFFFFFFFFFFFFFFFF)
 38                        magicAttacks[magicIndex] = attacks[index];
 39                    else if (!Equals(magicAttacks[magicIndex], attacks[index]))
 40                        failureFlag = true;
 41                }
 42                // PotentialMagicNumber is actually the magic number
 43                if (!failureFlag)
 44                    return potentialMagicNumber;
 45            }
 46            throw new ExceededMaximumRetryException("No magic number found");
 47        }
 48        public static Bitboard GetBishopMask(Square square)
 49        {
 6750            Bitboard resultBoard = new Bitboard(0);
 51            // Masking equivalent to attacks with zero blockers and no edge square
 6752            Bitboard occupancyBoard = new Bitboard(0);
 6753            int bishopRank = (int)square >> 3;
 6754            int bishopFile = (int)square & (8 - 1);
 55
 48656            for (int rank = bishopRank + 1, file = bishopFile + 1; rank < 7 && file < 7; ++rank, ++file)
 9557                if (AddSquareToBoardAndStopAtOccupiedSquare(ref resultBoard, rank, file, occupancyBoard))
 58                    break;
 59
 50460            for (int rank = bishopRank - 1, file = bishopFile + 1; rank >= 1 && file < 7; --rank, ++file)
 10161                if (AddSquareToBoardAndStopAtOccupiedSquare(ref resultBoard, rank, file, occupancyBoard))
 62                    break;
 63
 48664            for (int rank = bishopRank - 1, file = bishopFile - 1; rank >= 1 && file >= 1; --rank, --file)
 9565                if (AddSquareToBoardAndStopAtOccupiedSquare(ref resultBoard, rank, file, occupancyBoard))
 66                    break;
 67
 49268            for (int rank = bishopRank + 1, file = bishopFile - 1; rank < 7 && file >= 1; ++rank, --file)
 9769                if (AddSquareToBoardAndStopAtOccupiedSquare(ref resultBoard, rank, file, occupancyBoard))
 70                    break;
 71
 6772            return resultBoard;
 73        }
 74
 75        public static Bitboard GetRookMask(Square square)
 76        {
 6777            Bitboard resultBoard = new Bitboard(0);
 78            // Masking equivalent to attacks with zero blockers and no edge square
 6779            Bitboard occupancyBoard = new Bitboard(0);
 6780            int rookRank = (int)square >> 3;
 6781            int rookFile = (int)square & (8 - 1);
 82
 48283            for (int rank = rookRank + 1; rank < 7; ++rank)
 17484                if (AddSquareToBoardAndStopAtOccupiedSquare(ref resultBoard, rank, rookFile, occupancyBoard))
 85                    break;
 86
 49087            for (int rank = rookRank - 1; rank >= 1; --rank)
 17888                if (AddSquareToBoardAndStopAtOccupiedSquare(ref resultBoard, rank, rookFile, occupancyBoard))
 89                    break;
 90
 49091            for (int file = rookFile + 1; file < 7; ++file)
 17892                if (AddSquareToBoardAndStopAtOccupiedSquare(ref resultBoard, rookRank, file, occupancyBoard))
 93                    break;
 94
 48295            for (int file = rookFile - 1; file >= 1; --file)
 17496                if (AddSquareToBoardAndStopAtOccupiedSquare(ref resultBoard, rookRank, file, occupancyBoard))
 97                    break;
 98
 6799            return resultBoard;
 100        }
 101
 102        public static Bitboard GetOccupancyMapping(int index, int nBitsInMask, Bitboard mask)
 103        {
 107650104            Bitboard occupancyMapping = new Bitboard(0);
 107650105            Bitboard temporaryMask = new Bitboard(mask.Board);
 2502698106            for (int count = 0; count < nBitsInMask; ++count)
 107            {
 1143699108                int square = BitOperations.TrailingZeroCount(temporaryMask.Board);
 1143699109                temporaryMask.ClearBit(square);
 110
 1143699111                if ((index & (1 << count)) != 0)
 571846112                    occupancyMapping.Board |= 1ul << square;
 113            }
 107650114            return occupancyMapping;
 115        }
 116        private static ulong GeneratePotentialMagicNumber()
 117        {
 0118            return Random.NextULong() & Random.NextULong() & Random.NextULong();
 119        }
 120    }
 121}