Example #1
0
        /// <summary> Creates an empty queen set with bitboards initialized. </summary>
        public static QueenSet Empty()
        {
            var bbCount = Board.BitboardCount;
            var dim     = Board.Dimension;

            var tFilesInv = ~BitBase.TrailingBitFiles;
            var tRanksInv = ~BitBase.TrailingBitRanks;

            var emptySet = new QueenSet(new ulong[bbCount], new ulong[bbCount]);

            // Exclude all files beyond N:
            for (var i = dim - 1; i < bbCount; i += dim)
            {
                emptySet.Fill[i] = tFilesInv;
            }

            // Exclude all ranks beyond N:
            for (var i = bbCount - dim; i < bbCount; i++)
            {
                emptySet.Fill[i] |= tRanksInv;
            }

            return(emptySet);
        }
Example #2
0
        protected override void Solve()
        {
            /* We don't need to check every position in the first rank.
             * In fact, we only need to check half of them (+1 if N is odd).
             * This is because placing the first queen on the other half
             * of the rank will produce the same fundamental results - only
             * mirrored horizontally. */

            var halfWay = Master.N / 2 + Master.N % 2;

            /* The main search stage begins after all root nodes are collected.
             * A root node is basically a description of an incomplete set
             * containing the first 2 queens placed on the first 2 ranks. The
             * reason for the whole concept is parallelism. Each thread will
             * handle one path starting from some root node. Why 2 queens and
             * not just the first one? Well, that way there are more starting
             * points meaning more iterations, and therefore the parallel loop
             * will work more efficiently. */

            var nodes = new ConcurrentBag <Node>();

            /* Also we need some original fill mask that takes redundant utmost
             * bits into consideration. Those are bits that need to be excluded
             * from the leftmost and topmost bitboards in case N is not a power of 8.
             * By doing that right away, we don't need to worry about it anymore.
             * The Empty() function handles the redundant bits. */

            var fillMask = QueenSet.Empty().Fill;

            // Gather the root nodes:
            Parallel.For(0, halfWay, Master.ThreadingOptions, i =>
            {
                // The index of the bitboard containing the examined square:
                var bbIdx = i / 8;
                // The first queen's bit-position:
                var bitPos = 1UL << (i % 8);
                // The first queen's attack mask:
                var attacks = BitBase.Attacks[bbIdx][bitPos];
                // The root fill mask will combine the attack mask and the original fill mask:
                var rootFill = new ulong[Board.BitboardCount];

                for (var j = 0; j < Board.BitboardCount; j++)
                {
                    rootFill[j] = attacks[j] | fillMask[j];
                }

                // Now go through the 2nd rank and get all the ways of placing
                // the 2nd queen, effectively collecting a set of root nodes:
                for (var j = 0; j < Board.Dimension; j++)
                {
                    AddNode(nodes, rootFill, j, bbIdx, bitPos, 0xFF00);
                }
            });

            // Perform the search per each root node:
            Parallel.ForEach(nodes, Master.ThreadingOptions, node =>
            {
                Search(node, 0, 2, 0xFF0000);

                // Notify the user that something is going on (helpful in case of big N):
                Events.OnProgress();
            });

            // All solutions should be found at this point.
            Events.OnAllSolutionsFound();
        }