/// <summary>
        /// Initializes an empty array with default values.
        /// </summary>
        public static ColorSquareIndexedArray <TValue> New()
        {
            ColorSquareIndexedArray <TValue> wrapped = default;

            wrapped.Init();
            return(wrapped);
        }
Example #2
0
        static Constants()
        {
            CastleQueensideRookDelta = EnumIndexedArray <Color, ulong> .New();

            CastleKingsideRookDelta = EnumIndexedArray <Color, ulong> .New();

            FileMasks = EnumIndexedArray <Square, ulong> .New();

            InnerFileMasks = EnumIndexedArray <Square, ulong> .New();

            RankMasks = EnumIndexedArray <Square, ulong> .New();

            InnerRankMasks = EnumIndexedArray <Square, ulong> .New();

            A1H8Masks = EnumIndexedArray <Square, ulong> .New();

            InnerA1H8Masks = EnumIndexedArray <Square, ulong> .New();

            A8H1Masks = EnumIndexedArray <Square, ulong> .New();

            InnerA8H1Masks = EnumIndexedArray <Square, ulong> .New();

            PawnMoves = ColorSquareIndexedArray <ulong> .New();

            PawnCaptures = ColorSquareIndexedArray <ulong> .New();

            EnPassantSquares = ColorSquareIndexedArray <ulong> .New();

            PawnTwoSquaresAhead = ColorSquareIndexedArray <ulong> .New();

            KnightMoves = EnumIndexedArray <Square, ulong> .New();

            Neighbours = EnumIndexedArray <Square, ulong> .New();

            fileMultipliers = EnumIndexedArray <Square, ulong> .New();

            rankShifts = EnumIndexedArray <Square, int> .New();

            a1h8Multipliers = EnumIndexedArray <Square, ulong> .New();

            a8h1Multipliers = EnumIndexedArray <Square, ulong> .New();

            ulong[] allFiles     = { FileA, FileB, FileC, FileD, FileE, FileF, FileG, FileH };
            ulong[] allRanks     = { Rank1, Rank2, Rank3, Rank4, Rank5, Rank6, Rank7, Rank8 };
            ulong[] allDiagsA1H8 = { DiagA8, DiagA7B8, DiagA6C8, DiagA5D8, DiagA4E8, DiagA3F8, DiagA2G8, DiagA1H8, DiagB1H7, DiagC1H6, DiagD1H5, DiagE1H4, DiagF1H3, DiagG1H2, DiagH1 };
            ulong[] allDiagsA8H1 = { DiagA1, DiagA2B1, DiagA3C1, DiagA4D1, DiagA5E1, DiagA6F1, DiagA7G1, DiagA8H1, DiagB8H2, DiagC8H3, DiagD8H4, DiagE8H5, DiagF8H6, DiagG8H7, DiagH8 };

            ulong[] fileMultipliers8 =
            {
                0x8040201008040200, //A
                0x4020100804020100, //B
                0x2010080402010080, //C
                0x1008040201008040, //D
                0x0804020100804020, //E
                0x0402010080402010, //F
                0x0201008040201008, //G
                0x0100804020100804  //H
            };

            ulong[] a1h8Multipliers8 =
            {
                0,                  //A8
                0,                  //A7-B8
                0x0101010101010100, //A6-C8
                0x0101010101010100, //A5-D8
                0x0101010101010100, //A4-E8
                0x0101010101010100, //A3-F8
                0x0101010101010100, //A2-G8
                0x0101010101010100, //A1-H8
                0x8080808080808000, //B1-H7
                0x4040404040400000, //C1-H6
                0x2020202020000000, //D1-H5
                0x1010101000000000, //E1-H4
                0x0808080000000000, //F1-H3
                0,                  //G1-H2
                0                   //H1
            };

            ulong[] a8h1Multipliers8 =
            {
                0,                  //A1
                0,                  //A2-B1
                0x0101010101010100, //A3-C1
                0x0101010101010100, //A4-D1
                0x0101010101010100, //A5-E1
                0x0101010101010100, //A6-F1
                0x0101010101010100, //A7-G1
                0x0101010101010100, //A8-H1
                0x0080808080808080, //B8-H2
                0x0040404040404040, //C8-H3
                0x0020202020202020, //D8-H4
                0x0010101010101010, //E8-H5
                0x0008080808080808, //F8-H6
                0,                  //G8-H7
                0                   //H8
            };

            CastleQueensideRookDelta[Color.White] = A1 | D1;
            CastleQueensideRookDelta[Color.Black] = A8 | D8;
            CastleKingsideRookDelta[Color.White]  = H1 | F1;
            CastleKingsideRookDelta[Color.Black]  = H8 | F8;

            for (Square sq = Square.H8; sq >= Square.A1; --sq)
            {
                ulong sqVector  = sq.ToVector();
                int   x         = sq.X();
                int   y         = sq.Y();
                int   a1h8Index = 7 + x - y;
                int   a8h1Index = x + y;

                FileMasks[sq]      = allFiles[x];
                InnerFileMasks[sq] = FileMasks[sq] & ~Rank1 & ~Rank8;
                RankMasks[sq]      = allRanks[y];
                InnerRankMasks[sq] = RankMasks[sq] & ~FileA & ~FileH;
                A1H8Masks[sq]      = allDiagsA1H8[a1h8Index];
                InnerA1H8Masks[sq] = A1H8Masks[sq] & ~FileA & ~FileH & ~Rank1 & ~Rank8;
                A8H1Masks[sq]      = allDiagsA8H1[a8h1Index];
                InnerA8H1Masks[sq] = A8H1Masks[sq] & ~FileA & ~FileH & ~Rank1 & ~Rank8;

                fileMultipliers[sq] = fileMultipliers8[x];
                rankShifts[sq]      = y * 8 + 1;
                a1h8Multipliers[sq] = a1h8Multipliers8[a1h8Index];
                a8h1Multipliers[sq] = a8h1Multipliers8[a8h1Index];

                PawnTwoSquaresAhead[Color.White, sq] = (sqVector & Rank2).North().North();
                PawnTwoSquaresAhead[Color.Black, sq] = (sqVector & Rank7).South().South();

                PawnMoves[Color.White, sq] = sqVector.North()
                                             | PawnTwoSquaresAhead[Color.White, sq]; // 2 squares ahead allowed from the starting square.
                PawnMoves[Color.Black, sq] = sqVector.South()
                                             | PawnTwoSquaresAhead[Color.Black, sq];

                PawnCaptures[Color.White, sq] = sqVector.North().West()
                                                | sqVector.North().East();
                PawnCaptures[Color.Black, sq] = sqVector.South().West()
                                                | sqVector.South().East();

                EnPassantSquares[Color.White, sq] = (sqVector & Rank2).North();
                EnPassantSquares[Color.Black, sq] = (sqVector & Rank7).South();

                KnightMoves[sq] = sqVector.North().North().West()
                                  | sqVector.North().North().East()
                                  | sqVector.North().West().West()
                                  | sqVector.North().East().East()
                                  | sqVector.South().West().West()
                                  | sqVector.South().East().East()
                                  | sqVector.South().South().West()
                                  | sqVector.South().South().East();

                Neighbours[sq] = sqVector.North().East()
                                 | sqVector.North()
                                 | sqVector.North().West()
                                 | sqVector.East()
                                 | sqVector.West()
                                 | sqVector.South().East()
                                 | sqVector.South()
                                 | sqVector.South().West();
            }

            // Reachability calculation is done in a few stages.
            // Given are a bitfield of occupied squares, a source square for which to calculate sliding moves,
            // and a direction, N-S, W-E, NW-SE or SW-NE.
            // Example: take a position in which a white queen is on C3, a black king on E1, and a white king on G8.
            // We'd like to calculate whether or not the black king is in check, so:
            // source square = E1, direction = NW-SE, occupied square bitfield = 2^4 (E1) + 2^18 (C3) + 2^62 (G8).
            //
            // A) Zero out everything that's not on the file/rank/diagonal to consider.
            //    In the example, only A5, B4, C3, D2, E1 are relevant, so square G8 is zeroed out.
            //    Also ignore the two edge squares, because no further squares can be blocked.
            //    In the example, this means that square E1 is zeroed out as well.
            //    Only a maximum of six bits of information remain, in the example only three (B4, C3, D2).
            // B) Multiply by a 'magic' value to map and rotate the value onto the eighth rank.
            //    This will generate some garbage on the other seven ranks.
            // C) Shift right by 57 positions to move the value to the first rank instead. This also shifts out the garbage.
            //    In the example, the result binary is: 000010
            //    Had D2 been occupied as well, the result would have been: 000110
            //    And had B4 been occupied as well, the result would have been: 000111
            // D) Given the source square and the 6-bit occupancy index, use a lookup table to obtain a bitfield of reachable
            //    squares. The end result of the example then is a bitfield in which bits are set for C3 and D2.
            //    Since there's a queen is on C3, this means that the black king is indeed in check.
            //
            // Note that the color of the piece occupying a square can be safely ignored,
            // because all squares occupied by pieces of the same color can be zeroed out afterwards.
            // The number of necessary operations is always four: zero out, multiply, shift right, lookup in table.

            // The order in a ray is determined by which bit in the occupancy index corresponds to a given blocking square.
            // This in turn is determined indirectly by the multipliers which are used in step B.
            Square[] baseFile = { Square.A8, Square.A7, Square.A6, Square.A5, Square.A4, Square.A3, Square.A2, Square.A1 };
            ulong[,] baseFileReachability = CalculateRayReachabilityTable(baseFile);
            fileReachability = new ulong[TotalSquareCount, totalOccupancies];

            Square[] baseRank = { Square.A1, Square.B1, Square.C1, Square.D1, Square.E1, Square.F1, Square.G1, Square.H1 };
            ulong[,] baseRankReachability = CalculateRayReachabilityTable(baseRank);
            rankReachability = new ulong[TotalSquareCount, totalOccupancies];

            Square[] baseA1H8 = { Square.A1, Square.B2, Square.C3, Square.D4, Square.E5, Square.F6, Square.G7, Square.H8 };
            ulong[,] baseA1H8Reachability = CalculateRayReachabilityTable(baseA1H8);
            a1h8Reachability = new ulong[TotalSquareCount, totalOccupancies];

            Square[] baseA8H1 = { Square.A8, Square.B7, Square.C6, Square.D5, Square.E4, Square.F3, Square.G2, Square.H1 };
            ulong[,] baseA8H1Reachability = CalculateRayReachabilityTable(baseA8H1);
            a8h1Reachability = new ulong[TotalSquareCount, totalOccupancies];

            // To zero out part of a diagonal before shifting it to another shorter diagonal.
            ulong[] partialDiagMasks =
            {
                ulong.MaxValue,
                ulong.MaxValue & ~FileH,
                ulong.MaxValue & ~FileG & ~FileH,
                ulong.MaxValue & ~FileF & ~FileG & ~FileH,
                ulong.MaxValue & ~FileE & ~FileF & ~FileG & ~FileH,
                ulong.MaxValue & ~FileD & ~FileE & ~FileF & ~FileG & ~FileH,
                ulong.MaxValue & ~FileC & ~FileD & ~FileE & ~FileF & ~FileG & ~FileH,
                ulong.MaxValue & ~FileB & ~FileC & ~FileD & ~FileE & ~FileF & ~FileG & ~FileH,
            };

            for (Square sq = Square.H8; sq >= Square.A1; --sq)
            {
                int x = sq.X();
                int y = sq.Y();
                // Diagonal indexes relative to the long diagonals.
                int a1h8Index = x - y;
                int a8h1Index = x + y - 7;

                for (int o = totalOccupancies - 1; o >= 0; --o)
                {
                    // Copy from the base reachability table, and shift the result to the current rank/file/diagonal.
                    fileReachability[(int)sq, o] = baseFileReachability[7 - y, o] << x;
                    rankReachability[(int)sq, o] = baseRankReachability[x, o] << (y * 8);

                    if (a1h8Index == 0)
                    {
                        a1h8Reachability[(int)sq, o] = baseA1H8Reachability[x, o];
                    }
                    else if (a1h8Index < 0)
                    {
                        ulong a1h8Diag = baseA1H8Reachability[x, o] & partialDiagMasks[-a1h8Index];
                        a1h8Reachability[(int)sq, o] = a1h8Diag << (-a1h8Index * 8);
                    }
                    else
                    {
                        ulong a1h8Diag = baseA1H8Reachability[y, o] & partialDiagMasks[a1h8Index];
                        a1h8Reachability[(int)sq, o] = a1h8Diag << a1h8Index;
                    }

                    if (a8h1Index == 0)
                    {
                        a8h1Reachability[(int)sq, o] = baseA8H1Reachability[x, o];
                    }
                    else if (a8h1Index < 0)
                    {
                        ulong a8h1Diag = baseA8H1Reachability[x, o] & partialDiagMasks[-a8h1Index];
                        a8h1Reachability[(int)sq, o] = a8h1Diag >> (-a8h1Index * 8);
                    }
                    else
                    {
                        ulong a8h1Diag = baseA8H1Reachability[7 - y, o] & partialDiagMasks[a8h1Index];
                        a8h1Reachability[(int)sq, o] = a8h1Diag << a8h1Index;
                    }
                }
            }
        }