将数独解题中的单元格移除以制作成谜题

3
我正在编写数独应用程序,目前正致力于游戏生成算法。我设法找到了如何快速生成解决方案(而不是求解)。然而,我被如何移除一些数字以制作出一个谜题所困扰。我的第一反应是根据难度随机删除某个单元格的数量,但这并不是正确的算法,因为它经常会生成无解或有多个解的谜题。它也可能会生成与请求的难度不符的谜题。
这是我迄今为止的代码。我删除了大部分无关的代码,但如果您想看到下面使用的未实现的内容,请告诉我。如果您愿意,我还可以提供我尝试的“ Puzzlefy”方法,但我选择不立即发布它,因为它明显是错误的(即使它“有效”)。
using System;
using System.Collections.Generic;
using System.Linq;

namespace Sudoku
{
    public class Game
    {
        public enum Difficulty
        {
            VeryEasy,
            Easy,
            Medium,
            Difficult,
            Evil
        }

        private readonly int?[,] _currentItems = new int?[9,9];
        private readonly int?[,] _solution = new int?[9,9];
        private readonly int?[,] _startingItems = new int?[9,9];
        private readonly Difficulty _difficulty;

        public Game(Difficulty difficulty)
        {
            _difficulty = difficulty;
            GenerateSolution();
            Puzzlefy();
        }

        private void GenerateSolution()
        {
            var random = new Random();
            var availableNumbers = new Stack<List<int?>>(81);
            var x = 0;
            var y = 0;

            availableNumbers.Push(AllowableNumbers(_solution, 0, 0).ToList());
            while (x < 9 && y < 9)
            {
                var currentAvailableNumbers = AllowableNumbers(_solution, x, y).ToList();
                availableNumbers.Push(currentAvailableNumbers);

                // back trace if the board is in an invalid state
                while (currentAvailableNumbers.Count == 0)
                {
                    _solution[x, y] = null;
                    availableNumbers.Pop();
                    currentAvailableNumbers = availableNumbers.Peek();
                    x -= y >= 1 ? 0 : 1;
                    y = y >= 1 ? y - 1 : 8;
                }

                var index = random.Next(currentAvailableNumbers.Count);
                _solution[x, y] = currentAvailableNumbers[index];
                currentAvailableNumbers.RemoveAt(index);

                x += y < 8 ? 0 : 1;
                y = y < 8 ? y + 1 : 0;
            }
        }

        private void Puzzlefy()
        {
            CopyCells(_solution, _startingItems);

            // remove some stuff from _startingItems

            CopyCells(_startingItems, _currentItems);
        }
    }
}

我不是在寻找代码,而是算法。如何去除解决方案中的数字,使其成为一个谜题?


我不确定这是否有所帮助,但是“正确的”数独也应该是对称的 - 提供(或删除)的单元格不是随机的,而是按照从左到右,从上到下或镜像的模式进行排列。我不确定这是否会影响生成有效谜题的可能性。 - GalacticCowboy
我同意。虽然不对称的拼图并没有根本性的问题,但它在美学上令人不悦。我发现随机删除通常不会给出太糟糕的对称性,但从理论上讲,它可能会使所有的提示都在同一个角落里。 - Tyler Crompton
2个回答

3

这里有一篇关于数独生成的文章(链接)

我认为你需要一个数独解算器,它可以计算可用解决方案的数量,然后通过减去数字的方式,确保始终只有一个可用的解决方案。

您可以将相同的方法应用于向网格添加数字,然后检查可能的解决方案数量,并在解决方案数量大于1时继续添加,在解决方案数量为0时进行回溯。


我也看过那个,但它只是简单地描述了如何删除单元格并确定难度,而不是相反的方式。重复这种方法直到达到所需难度的谜题将产生非常低效的算法。 - Tyler Crompton

0

没有“简单”的方法可以从完成的数独网格中删除线索,因为删除过程不是线性的。

在每次删除单元格或线索后,您需要检查数独是否只有一个唯一解。

要检查这一点,您需要运行一个可以计算所有可能解(您可以在找到2种可能性后停止它以节省时间)的求解器。

用于解决数独,计算所有数独解并删除单元格的两个最流行的算法是回溯算法和舞蹈链接算法。

本文很好地解释了舞蹈链接算法如何在数独游戏中使用:

http://garethrees.org/2007/06/10/zendoku-generation/#section-2

这是另一个关于JavaScript编写的数独舞蹈链接算法描述:

http://www.sudokubum.com/documentation.html

这里是关于舞链算法的完整论文: http://lanl.arxiv.org/pdf/cs/0011047


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接