Exemple #1
0
        /// <summary>
        /// To check whether the specified solver can solve the puzzle.
        /// </summary>
        /// <param name="grid">The puzzle.</param>
        /// <returns>
        /// A <see cref="bool"/> value indicating whether the solver
        /// solved the puzzle successfully.
        /// </returns>
        public bool CanSolve(IReadOnlyGrid grid)
        {
            var cloneation = grid.Clone();

            var steps    = new List <TechniqueInfo>();
            var searcher = new SingleTechniqueSearcher(false, false);
            var bag      = new Bag <TechniqueInfo>();

            while (!cloneation.HasSolved)
            {
                searcher.GetAll(bag, cloneation);
                if (bag.Count == 0)
                {
                    break;
                }

                foreach (var step in bag)
                {
                    if (RecordTechnique(steps, step, cloneation))
                    {
                        return(true);
                    }
                }

                bag.Clear();
            }

            return(false);
        }
        /// <summary>
        /// Swap to regions.
        /// </summary>
        /// <param name="this">(<see langword="this"/> parameter) The grid.</param>
        /// <param name="region1">The region 1.</param>
        /// <param name="region2">The region 2.</param>
        /// <returns>The result.</returns>
        /// <exception cref="ArgumentException">
        /// Throws when two specified region argument is not in valid range (0..27)
        /// or two regions are not in same region type.
        /// </exception>
        public static Grid SwapTwoRegions(this IReadOnlyGrid @this, int region1, int region2)
        {
            if (region1 < 0 || region1 >= 18)
            {
                throw new ArgumentException("The specified argument is out of valid range.", nameof(region1));
            }
            if (region2 < 0 || region2 >= 18)
            {
                throw new ArgumentException("The specified argument is out of valid range.", nameof(region2));
            }
            if (region1 / 9 != region2 / 9)
            {
                throw new ArgumentException("Two region should be the same region type.");
            }

            var result = @this.Clone();

            for (int i = 0; i < 9; i++)
            {
                int   c1   = RegionCells[region1][i];
                int   c2   = RegionCells[region2][i];
                short temp = result.GetMask(c1);
                result.SetMask(c1, result.GetMask(c2));
                result.SetMask(c2, temp);
            }

            return(result);
        }
        /// <summary>
        /// Rotate the grid <c><see cref="Math.PI"/></c> degrees.
        /// </summary>
        /// <param name="this">(<see langword="this"/> parameter) The grid.</param>
        /// <returns>The result.</returns>
        public static Grid RotatePi(this IReadOnlyGrid @this)
        {
            var result = @this.Clone();

            for (int i = 0; i < 81; i++)
            {
                int   z    = PiRotateTable[i];
                short temp = result.GetMask(i);
                result.SetMask(i, result.GetMask(z));
                result.SetMask(z, temp);
            }
            return(result);
        }
Exemple #4
0
 /// <inheritdoc/>
 public override AnalysisResult Solve(IReadOnlyGrid grid)
 {
     if (grid.IsValid(out var solution, out bool?sukaku))
     {
         // Solve the puzzle.
         try
         {
             return(AnalyzeDifficultyStrictly
                                         ? SolveWithStrictDifficultyRating(
                        grid, grid.Clone(), new List <TechniqueInfo>(), solution, sukaku.Value)
                                         : SolveNaively(grid, grid.Clone(), new List <TechniqueInfo>(), solution, sukaku.Value));
         }
         catch (WrongHandlingException ex)
         {
             return(new AnalysisResult(
                        puzzle: grid,
                        solverName: SolverName,
                        hasSolved: false,
                        solution: null,
                        elapsedTime: TimeSpan.Zero,
                        solvingList: null,
                        additional: ex.Message,
                        stepGrids: null));
         }
     }
     else
     {
         return(new AnalysisResult(
                    puzzle: grid,
                    solverName: SolverName,
                    hasSolved: false,
                    solution: null,
                    elapsedTime: TimeSpan.Zero,
                    solvingList: null,
                    additional: "The puzzle does not have a unique solution (multiple solutions or no solution).",
                    stepGrids: null));
     }
 }
        /// <summary>
        /// Mirror top-bottom the grid.
        /// </summary>
        /// <param name="this">(<see langword="this"/> parameter) The grid.</param>
        /// <returns>The result grid.</returns>
        public static Grid MirrorTopBottom(this IReadOnlyGrid @this)
        {
            var result = @this.Clone();

            for (int i = 0; i < 9; i++)
            {
                for (int j = 0; j < 4; j++)
                {
                    short temp = result.GetMask(i * 9 + j);
                    result.SetMask(i * 9 + j, result.GetMask((8 - i) * 9 + j));
                    result.SetMask((8 - i) * 9 + j, temp);
                }
            }

            return(result);
        }
        /// <summary>
        /// Mirror left-right the grid.
        /// </summary>
        /// <param name="this">(<see langword="this"/> parameter) The grid.</param>
        /// <returns>The result grid.</returns>
        public static Grid MirrorLeftRight(this IReadOnlyGrid @this)
        {
            var result = @this.Clone();

            for (int i = 0; i < 4; i++)
            {
                for (int j = 0; j < 9; j++)
                {
                    short temp = result.GetMask(i * 9 + j);
                    result.SetMask(i * 9 + j, result.GetMask(i * 9 + (8 - j)));
                    result.SetMask(i * 9 + (8 - j), temp);
                }
            }

            return(result);
        }
        /// <summary>
        /// Mirror anti-diagonal the grid.
        /// </summary>
        /// <param name="this">(<see langword="this"/> parameter) The grid.</param>
        /// <returns>The result grid.</returns>
        public static Grid MirrorAntidiagonal(this IReadOnlyGrid @this)
        {
            var result = @this.Clone();

            for (int i = 0; i < 9; i++)
            {
                for (int j = 0; j < 8 - i; j++)
                {
                    short temp = result.GetMask(i * 9 + j);
                    result.SetMask(i * 9 + j, result.GetMask((8 - j) * 9 + (8 - i)));
                    result.SetMask((8 - j) * 9 + (8 - i), temp);
                }
            }

            return(result);
        }
        /// <summary>
        /// Mirror diagonal the grid.
        /// </summary>
        /// <param name="this">(<see langword="this"/> parameter) The grid.</param>
        /// <returns>The result grid.</returns>
        public static Grid MirrorDiagonal(this IReadOnlyGrid @this)
        {
            var result = @this.Clone();

            for (int i = 1; i < 9; i++)
            {
                for (int j = 0; j < i; j++)
                {
                    short temp = result.GetMask(i * 9 + j);
                    result.SetMask(i * 9 + j, result.GetMask(j * 9 + i));
                    result.SetMask(j * 9 + i, temp);
                }
            }

            return(result);
        }
        /// <summary>
        /// To find all backdoors in a sudoku grid.
        /// </summary>
        /// <param name="result">The result list.</param>
        /// <param name="grid">A sudoku grid to search backdoors.</param>
        /// <param name="depth">The depth to search.</param>
        /// <exception cref="InvalidOperationException">
        /// Throws when the grid is invalid (has no solution or multiple solutions).
        /// </exception>
        private static void SearchForBackdoors(
            IList <IReadOnlyList <Conclusion> > result, IReadOnlyGrid grid, int depth)
        {
            if (!grid.IsValid(out var solution))
            {
                throw new InvalidOperationException("The puzzle does not have unique solution.");
            }

            var tempGrid = grid.Clone();

            if (depth == 0)
            {
                // Search backdoors (Assignments).
                if (TestSolver.CanSolve(tempGrid))
                {
                    // All candidates will be marked.
                    for (int c = 0; c < 81; c++)
                    {
                        for (int d = 0, z = solution[c]; d < 9; d++)
                        {
                            result.Add(new[] { new Conclusion(d == z ? Assignment : Elimination, c, d) });
                        }
                    }
                }
                else
                {
                    for (int cell = 0; cell < 81; cell++)
                    {
                        if (tempGrid.GetStatus(cell) != Empty)
                        {
                            continue;
                        }

                        int digit = solution[cell];
                        tempGrid[cell] = digit;

                        if (TestSolver.CanSolve(tempGrid))
                        {
                            // Solve successfully.
                            result.Add(new[] { new Conclusion(Assignment, cell, digit) });
                        }

                        // Restore data.
                        // Simply assigning to trigger the event to re-compute all candidates.
                        tempGrid[cell] = -1;
                    }
                }

                return;
            }

            // Store all incorrect candidates to prepare for search elimination backdoors.
            var incorrectCandidates = (
                from cell in Enumerable.Range(0, 81)
                where grid.GetStatus(cell) == Empty
                let Value = solution[cell]
                            from digit in Enumerable.Range(0, 9)
                            where !grid[cell, digit] && Value != digit
                            select cell * 9 + digit).ToArray();

            // Search backdoors (Eliminations).
            for (int i1 = 0, count = incorrectCandidates.Length; i1 < count + 1 - depth; i1++)
            {
                int c1 = incorrectCandidates[i1];
                tempGrid[c1 / 9, c1 % 9] = true;

                if (depth == 1)
                {
                    if (TestSolver.CanSolve(tempGrid))
                    {
                        result.Add(new[] { new Conclusion(Elimination, c1) });
                    }
                }
                else                 // depth > 1
                {
                    for (int i2 = i1 + 1; i2 < count + 2 - depth; i2++)
                    {
                        int c2 = incorrectCandidates[i2];
                        tempGrid[c2 / 9, c2 % 9] = true;

                        if (depth == 2)
                        {
                            if (TestSolver.CanSolve(tempGrid))
                            {
                                result.Add(
                                    new[]
                                {
                                    new Conclusion(Elimination, c1),
                                    new Conclusion(Elimination, c2)
                                });
                            }
                        }
                        else                         // depth == 3
                        {
                            for (int i3 = i2 + 1; i3 < count + 3 - depth; i3++)
                            {
                                int c3 = incorrectCandidates[i3];
                                tempGrid[c3 / 9, c3 % 9] = true;

                                if (TestSolver.CanSolve(tempGrid))
                                {
                                    result.Add(
                                        new[]
                                    {
                                        new Conclusion(Elimination, c1),
                                        new Conclusion(Elimination, c2),
                                        new Conclusion(Elimination, c3)
                                    });
                                }

                                tempGrid[c3 / 9, c3 % 9] = false;
                            }
                        }

                        tempGrid[c2 / 9, c2 % 9] = false;
                    }
                }

                tempGrid[c1 / 9, c1 % 9] = false;
            }
        }