• 数独的优化回朔算法(四)


    FillCell

    这个方法是填充数独表的递归方法,看长长滴代码:

    代码
    /// <summary>
    /// 填充单元格
    /// </summary>
    /// <param name="table"></param>
    /// <param name="index">索引</param>
    /// <returns>返回结果</returns>
    private bool FillCell(Cell[,] table, int index)
    {
        RelativeCellMethod removeCandidateMethod 
    = new RelativeCellMethod(RemoveCellCandidate);
        RelativeCellMethod recoverCandidateMethod 
    = new RelativeCellMethod(RecoverCellCandidate);

        
    if (index >= 81)
        {
    //如果索引超出范围,则表示数独已成功生成,直接返回
            return true;
        }
        
    if (table[index / 9, index % 9].Value != 0)
        {
    //如果索引的单元格已赋值,则直接跳到下一个索引赋值
            return FillCell(table, index + 1);
        }
        
    bool flag = true;
        List
    <int> nextCandidates = new List<int>();
        
    //预先保存好改单元格的候选数序列,如果所有候选数都不成功,则把候选数全部还原之后再返回
        nextCandidates.AddRange(table[index / 9, index % 9].Candidate);

        
    while (table[index / 9, index % 9].Candidate.Count > 0 && flag)
        {
    //如果单元格候选数个数大于0,且标记为真,则循环试探候选数
            SetValue(table[index / 9, index % 9]);//为单元格赋值
            flag &= DealRelativeCell(removeCandidateMethod, table, index);//移除相关单元格的对应这个值的候选数
            if (!flag)
            {
    //如果移除候选数失败,则恢复候选数,并继续下个循环
                DealRelativeCell(recoverCandidateMethod, table, index);
            }
            
    else
            {
    //如果移除候选数成功,则继续试探填充下一个单元格
                flag &= FillCell(table, index + 1);
                
    if (!flag)
                {
    //如果填充下一个单元格失败,则恢复候选数,并继续下个循环
                    DealRelativeCell(recoverCandidateMethod, table, index);
                }
                
    else
                {
    //如果填充下一个单元格成功,则直接返回(运行到这里肯定表示整个数独已成功生成!)
                    return true;
                }
            }
            flag 
    = !flag;//把标志取反,继续下个循环
        }
        
    if (table[index / 9, index % 9].Candidate.Count == 0)
        {
    //如果所有候选数都是过了且全部失败,恢复此单元格的候选数,并返回false
            table[index / 9, index % 9].Candidate.AddRange(nextCandidates);
            
    return false;
        }
        
    return flag;
    }

    前两行定义了两个代理方法,在while方法中,一个一个的试单元格的候选数填入到单元格中,如果填入失败,则恢复候选数,继续尝试下一个候选数,如果所有候选数都不能正确生成数独,那么跳出while循环,函数返回false,返回到上一个单元格,将上一个单元格的值重置为0,试另外的候选数,以此类推。直到索引index大于等于81,则表示所有单元格都已成功赋值,那么全部返回true。

    接下来只需要写一个显示数独的方法:

     

    Show

    如下:

    代码
    /// <summary>
    /// 显示数独
    /// </summary>
    /// <param name="table"></param>
    private void Show(Cell[,] table)
    {
        
    for (int i = 0; i < 9; i++)
        {
            
    for (int j = 0; j < 9; j++)
            {
                Console.Write(
    "{0,2}", table[i, j].Value);

            }
            Console.WriteLine();
        }
        Console.WriteLine(
    "----------------------------------------------");

    }

    还有一个初始化数独表,然后开始填充数独的方法:

    StartFillTable

    代码
    /// <summary>
    /// 开始生成数独
    /// </summary>
    public void StartFillTable()
    {
        Cell[,] table 
    = new Cell[99];
        
    while (true)
        {
            
    for (int i = 0; i < 9; i++)
                
    for (int j = 0; j < 9; j++//初始化数独表
                    table[i, j] = new Cell();

            
    bool flag = FillCell(table, 0);//填充数独表
            if (flag)//如果生成数独成功,则显示这个数独
                Show(table);
            Console.ReadKey();
        }
    }

    我是用控制台应用程序编写的,如果是用Silverlight或者其他的呈现会更好看!,下面是Main方法和结果显示:

     

    class Program
    {
        
    static void Main(string[] args)
        {
            CellMethod method 
    = new CellMethod();
            method.StartFillTable();
        }
    }

    只要按回车键就可以生成一个不一样的新数独!

    对于有兴趣的读者,如果是要继续开发成数独游戏,可以随机的遮罩一定数量的格子,难度越高遮罩的格子越多,这样能保证的是数独游戏一定有解,但是解不一定唯一。如果要保证解唯一,那么就需要再用一个类似这个的解数独的方法,让计算机先做我们遮罩后的数独,看是不是有且仅有一个解,如果不是,那么重新选择遮罩区域,直到计算机发现解唯一为止,然后输出到前端,给玩家来玩!

    下一篇是整个程序的源代码。

  • 相关阅读:
    C# 反射数据库数据过程中值类型存在DBNull的处理方法 == System.DBNull.Value
    Git 未能顺利结束 (退出码 128)解决办法 git常用命令流程图
    IDEA之The directory xxxxx is under Git, but is not registered in the Settings.
    git push提交成功后如何撤销回退 我是手动修改再提交一次 。。或者IDEA有代码修改历史记录功能 进行回退
    IDEA,发现Project文件目录不见了,只剩External Libraries的原因
    分布式事务 seata
    idea设置类,方法模板 阿里检测
    随笔
    初学一点点空间分解和有理标准型
    Vue2+Cesium1.9+热力图开发笔记
  • 原文地址:https://www.cnblogs.com/cdts_change/p/1656260.html
Copyright © 2020-2023  润新知