从Sudoku解决方案中删除单元格以使其成为一个难题

时间:2013-02-13 17:08:10

标签: c# .net algorithm sudoku

我正在编写一个Sudoku应用程序,目前正在开发游戏生成算法。我设法弄清楚如何快速生成解决方案(不解决)。不过,我对如何删除一些数字实际上将它变成一个谜题感到难过。我的第一个倾向是根据难度随机删除一定数量的单元格,但这不是正确的算法,因为它经常呈现无法解决的难题或具有多种解决方案。它也可能产生不能反映所要求的难度的谜题。

这是我到目前为止的代码。我删除了大部分不相关的代码,但如果你想看到一些未实现但在下面使用的内容,请告诉我。如果你愿意,我也可以尝试使用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);
        }
    }
}

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

2 个答案:

答案 0 :(得分:2)

这是paper on sudoku generation

我认为您需要一个数独求解器,它还会计算可用解决方案的数量,然后以总是只有一个可用解决方案的方式减去数字。

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

答案 1 :(得分:0)

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

每次删除一个单元格或线索后,您需要检查Sudoku是否只有一个独特的解决方案。

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

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

本文非常好地解释了如何在Sudokus中使用跳舞链接算法: http://garethrees.org/2007/06/10/zendoku-generation/#section-2

这是用JavaScript编写的Sudokus中跳舞链接算法的另一个描述: http://www.sudokubum.com/documentation.html

以下是关于跳舞链接算法的完整论文: http://lanl.arxiv.org/pdf/cs/0011047