闲来无事时玩了一下数独,于是想着自己能不能写一个数独游戏,要做一个数独游戏,首先得生成一张完整的随机的数独表,看了很多其他人的做法,都不能保证是随机生成的,于是我研究了一下数独表,发现了其中的一些规律

从图中可以看出,满足数独规则的情况下,两个红线方框和两个绿色方框里的6个数肯定是一样的,那么我如果先随机生成1至9不重复的数填入第一个块里,那么第一行剩下的6个数就只需要从第一个块里下面那6个数里取,至于填入的时候就先对每个格子进行判断,这个各自所在的行、列、块有哪些数,把那6个数拿来与这些不能填的数取一个差集,那么获得的集合里的数就可以随便填一个了,到后面的格子做差集时就肯定只会有一个元素了,这样就保证了生成的表是随机的。

private void CreateTABLE(){            //生成第1块的9个元素string row1 = RandomNUM("123456789");for (int i = 0; i < 3; i++)    //填充第1行9个元素{for (int j = 0; j < 3; j++){Sodoku_table[i, j] = row1[i * 3 + j];}}while (CreateNUM(0, 3, false) || CreateNUM(0, 4, false) || CreateNUM(0, 5, false) || CreateNUM(0, 6, false) || CreateNUM(0, 7, false) || CreateNUM(0, 8, false) ||CreateNUM(1, 3, false) || CreateNUM(1, 4, false) || CreateNUM(1, 5, false) || CreateNUM(1, 6, false) || CreateNUM(1, 7, false) || CreateNUM(1, 8, false) ||CreateNUM(2, 3, false) || CreateNUM(2, 4, false) || CreateNUM(2, 5, false) || CreateNUM(2, 6, false) || CreateNUM(2, 7, false) || CreateNUM(2, 8, false) ||CreateNUM(3, 0, true) || CreateNUM(4, 0, true) || CreateNUM(5, 0, true) || CreateNUM(6, 0, true) || CreateNUM(7, 0, true) || CreateNUM(8, 0, true) ||CreateNUM(3, 1, true) || CreateNUM(4, 1, true) || CreateNUM(5, 1, true) || CreateNUM(6, 1, true) || CreateNUM(7, 1, true) || CreateNUM(8, 1, true) ||CreateNUM(3, 2, true) || CreateNUM(4, 2, true) || CreateNUM(5, 2, true) || CreateNUM(6, 2, true) || CreateNUM(7, 2, true) || CreateNUM(8, 2, true) ||CreateNUM(3, 3, false) || CreateNUM(3, 4, false) || CreateNUM(3, 5, false) || CreateNUM(3, 6, false) || CreateNUM(3, 7, false) || CreateNUM(3, 8, false) ||CreateNUM(4, 3, false) || CreateNUM(4, 4, false) || CreateNUM(4, 5, false) || CreateNUM(4, 6, false) || CreateNUM(4, 7, false) || CreateNUM(4, 8, false) ||CreateNUM(5, 3, false) || CreateNUM(5, 4, false) || CreateNUM(5, 5, false) || CreateNUM(5, 6, false) || CreateNUM(5, 7, false) || CreateNUM(5, 8, false) ||CreateNUM(6, 3, false) || CreateNUM(6, 4, false) || CreateNUM(6, 5, false) || CreateNUM(6, 6, false) || CreateNUM(6, 7, false) || CreateNUM(6, 8, false) ||CreateNUM(7, 3, false) || CreateNUM(7, 4, false) || CreateNUM(7, 5, false) || CreateNUM(7, 6, false) || CreateNUM(7, 7, false) || CreateNUM(7, 8, false) ||CreateNUM(8, 3, false) || CreateNUM(8, 4, false) || CreateNUM(8, 5, false) || CreateNUM(8, 6, false) || CreateNUM(8, 7, false) || CreateNUM(8, 8, false)) ;         //如果某一个格子填入失败,就重新生成,直到完成   }private bool CreateNUM(int n, int m, bool Iscloumn)  //n为行,m为列,Iscloumn是表明按行填充格子,还是按列填充格子{if(Iscloumn)  //如果是按照列,那么就把n、m互换{int tem = n;n = m;m = tem;}List<char> Exceptnum = new List<char>();    //保存差集List<char> Intersectnum = new List<char>();   //保存交集for(int i = 0; i < m; i++)  //添加格子所在行或列的已有的元素到差集里{if(!Iscloumn){Exceptnum.Add(Sodoku_table[n, i]);}else{Exceptnum.Add(Sodoku_table[i, n]);}}int row = n % 3;while (row > 0)   //添加格子所在块里已有的元素到差集{for (int i = (m / 3) * 3; i < (m / 3 == 1 ? 6 : 9); i++){if(!Iscloumn){Exceptnum.Add(Sodoku_table[row + (n / 3) * 3 - 1, i]);}else{Exceptnum.Add(Sodoku_table[i, row + (n / 3) * 3 - 1]);}}row--;}if(n / 3 > 0)   //第4行(列)或第7行(列)及以下的格子需要添加它上(左)方1至3行(列)或1至6行(列)的元素到差集{for(int i = 0; i < (n / 3) * 3; i++){if(!Iscloumn){Exceptnum.Add(Sodoku_table[i, m]);}else{Exceptnum.Add(Sodoku_table[m, i]);}}}for (int i = 0; i < 3; i++)   //添加元素到交集{if(!Iscloumn){if (n % 3 == 0){Intersectnum.Add(Sodoku_table[n + 1, i]);Intersectnum.Add(Sodoku_table[n + 2, i]);}else if (n % 3 == 1){Intersectnum.Add(Sodoku_table[n - 1, i]);Intersectnum.Add(Sodoku_table[n + 1, i]);}else if (n % 3 == 2){Intersectnum.Add(Sodoku_table[n - 1, i]);Intersectnum.Add(Sodoku_table[n - 2, i]);}}else{if (n % 3 == 0){Intersectnum.Add(Sodoku_table[i, n + 1]);Intersectnum.Add(Sodoku_table[i, n + 2]);}else if (n % 3 == 1){Intersectnum.Add(Sodoku_table[i, n - 1]);Intersectnum.Add(Sodoku_table[i, n + 1]);}else if (n % 3 == 2){Intersectnum.Add(Sodoku_table[i, n - 1]);Intersectnum.Add(Sodoku_table[i, n - 2]);}}}List<char> Temp = "123456789".Except(Exceptnum).ToList();  //做差集try{if(!Iscloumn)  //做交集,并随机取一个数填入格子{Sodoku_table[n, m] = Intersectnum.Intersect(Temp).ToList()[rnd.Next(Intersectnum.Intersect(Temp).ToList().Count)];   }else{Sodoku_table[m, n] = Intersectnum.Intersect(Temp).ToList()[rnd.Next(Intersectnum.Intersect(Temp).ToList().Count)];}}catch{return true;}return false;}private string RandomNUM(string num){char[] str = num.ToCharArray();//初始字符2113数5261组            for (int i = 0; i < str.Length; i++)//从str[0]开始随4102机交换两1653字符{char c = str[i];int index = rnd.Next(num.Length);str[i] = str[index];str[index] = c;}string result = new string(str);return result;}

现在完成了第一步,生成了一张随机的表,接下来需要根据难度随机抽取一定数量的格子置为空白,因为生成的是一张完整的数独表,所以肯定有解,但是怎么保证抽取这些格子之后数独是唯一解呢,于是我先用程序解一遍数独,然后判断这张表是否与原表相等,如果不等就找出第一个不等的位置,然后把这个位置固定住,不把它置空,然后再解一遍表,不等就又固定第一个开始跑偏的位置,但是这样仍然不能保证数独表的解唯一,因为在解表的时候,采取了回溯法,即先先寻找并填写那些唯一数单元格,然后判断是否填满,没有的话又顺序尝试填写有多个候选数的单元格,如果发现有的格子没有数可以填就回溯到上一个填的格子,把它改为另外一个候选数,直到格子填满就解出了表,事实上有可能某个格子有几个候选数都可能解除这张表来,所以答案就不唯一了,我前面得到与原表匹配的表可能只是运气好,等我们自己做的时候会发现某个格子填另外一个数也能把表填完,所以我在找到匹配的表之后再继续做了判断,继续回溯,找到有哪些格子的候选数不唯一,然后从最后一个候选数不唯一的格子开始,改变它数为其他候选数,这样再继续解这个表,当然现在肯定不可能得到与原表匹配的表了,如果解不出这张表就说明刚刚生成的已经是只有唯一解的表了,但是如果这样都还是能解出一张表的话,就说明刚刚抽的那些空不能保证表唯一解,那么就把这个格子的数也固定成原表的数,之后再把这个表拿去解,直到生成唯一解的表。

private void CreateBLANK(int num)  //num为置为空白的数量{char[,] Sodoku_tem_table = new char[9, 9];for (int i = 0; i < 81; i++){blank[i] = '1';Sodoku_tem_table[i / 9, i % 9] = Sodoku_table[i / 9, i % 9];}            while (num > 0)  //生成空白格{int tem = rnd.Next(81);if (blank[tem] == '1'){blank[tem] = '0';Sodoku_tem_table[tem / 9, tem % 9] = ' ';num--;}}bool isWhile = true;   //先寻找并填写那些唯一数单元格bool isFind = false;   //找到唯一数单元格bool is2While = false;   //顺序尝试填写有多个候选数的单元格bool isSolution = false;   //找到一个解bool isMatch = false;  //找到匹配解bool isOnly = false;   //找到唯一解  bool isMultiSolution = false;  //找到除匹配解外的解int number = 0;int MultiSolutionLocation = 0;  //记录引起多解的位置string Savestep = "";   //记录回溯步骤while (isWhile || !isSolution || !isMatch || !isOnly){int row = number / 9;int colunm = number % 9;if (Sodoku_tem_table[row, colunm] == ' '){List<char> Exceptnum = new List<char>();for (int i = 0; i < 9; i++){if (Sodoku_tem_table[row, i] != ' '){Exceptnum.Add(Sodoku_tem_table[row, i]);}if (Sodoku_tem_table[i, colunm] != ' '){Exceptnum.Add(Sodoku_tem_table[i, colunm]);}}for (int i = row / 3 * 3; i < row / 3 * 3 + 3; i++){for (int j = colunm / 3 * 3; j < colunm / 3 * 3 + 3; j++){if (Sodoku_tem_table[i, j] != ' '){Exceptnum.Add(Sodoku_tem_table[i, j]);}}}if (!is2While){if (("123456789").Except(Exceptnum).ToList().Count == 1){Sodoku_tem_table[row, colunm] = ("123456789").Except(Exceptnum).ToList()[0];isFind = true;}}else{if (("123456789").Except(Exceptnum).ToList().Count == 1){Savestep += ("123456789").Except(Exceptnum).ToList()[0] + "*1*" + number + "*";Sodoku_tem_table[row, colunm] = ("123456789").Except(Exceptnum).ToList()[0];}else if (("123456789").Except(Exceptnum).ToList().Count == 0){bool isBack = true;while (isBack){string[] Step = Savestep.Split('*');int count = int.Parse(Step[Step.Length - 3]);if (--count > 0){number = int.Parse(Step[Step.Length - 2]);Sodoku_tem_table[number / 9, number % 9] = Step[Step.Length - 4][count - 1];Step[Step.Length - 3] = count.ToString();Savestep = "";for (int i = 0; i < Step.Length - 1; i++){Savestep += Step[i] + "*";}isBack = false;}else{Sodoku_tem_table[int.Parse(Step[Step.Length - 2]) / 9, int.Parse(Step[Step.Length - 2]) % 9] = ' ';                                                                        try{Savestep = Savestep.Remove(Savestep.Length - 1 - Step[Step.Length - 2].Length - 1 - Step[Step.Length - 3].Length - 1 - Step[Step.Length - 4].Length);}catch{return;  //因为生成的是一张正确的数独表,不可能第一次就找不到解,所以找到唯一解}if (Savestep.Length <= 0){return;  //因为生成的是一张正确的数独表,不可能第一次就找不到解,所以找到唯一解}}}}else{for (int i = 0; i < ("123456789").Except(Exceptnum).ToList().Count; i++){Savestep += ("123456789").Except(Exceptnum).ToList()[i];}Savestep += "*" + ("123456789").Except(Exceptnum).ToList().Count + "*" + number + "*";Sodoku_tem_table[row, colunm] = ("123456789").Except(Exceptnum).ToList()[("123456789").Except(Exceptnum).ToList().Count - 1];}}}number++;if (number == blank.Length){number = 0;if (!is2While){if (!isFind){isWhile = false;is2While = true;}isFind = false;}isSolution = true;for (int i = 0; i < 9 && isSolution; i++){for (int j = 0; j < 9 && isSolution; j++){if (Sodoku_tem_table[i, j] == ' '){isSolution = false;}}}if (isSolution){if(isMatch){isMultiSolution = true;  //找到匹配解之后还找到其他解}if(!isMultiSolution){isMatch = true;for (int i = 0; i < 9 && isMatch; i++){for (int j = 0; j < 9 && isMatch; j++){if (Sodoku_tem_table[i, j] != Sodoku_table[i, j]){blank[i * 9 + j] = '1';for (int z = 0; z < 81; z++){if (blank[z] == '1'){Sodoku_tem_table[z / 9, z % 9] = Sodoku_table[z / 9, z % 9];}else if (blank[z] == '0'){Sodoku_tem_table[z / 9, z % 9] = ' ';}}isWhile = true;is2While = false;isSolution = false;isMatch = false;                                                                    }}}}                                          if(isMatch){                            isOnly = true;string[] Step = Savestep.Split('*');for(int i = Step.Length - 3; i > 0 && isOnly; i -= 3){int count = int.Parse(Step[i]);if(count-- > 1){number = MultiSolutionLocation = int.Parse(Step[i + 1]);                                    Sodoku_tem_table[number / 9, number % 9] = Step[i - 1][count - 1];Step[i] = count.ToString();Savestep = "";for (int j = 0; j <= i + 1; j++)  //重新拼接回溯步骤,去掉后面的部分{Savestep += Step[j] + "*";}for (int j = i + 4; j < Step.Length - 1; j += 3)  //把后面的填的数清除以便重新循环{count = int.Parse(Step[j]);Sodoku_tem_table[count / 9, count % 9] = ' ';}isWhile = true;is2While = false;isSolution = false;isMatch = false;                                    isOnly = false;}                                }}else{if(isMultiSolution){blank[MultiSolutionLocation] = '1';for (int z = 0; z < 81; z++){if (blank[z] == '1'){Sodoku_tem_table[z / 9, z % 9] = Sodoku_table[z / 9, z % 9];}else if (blank[z] == '0'){Sodoku_tem_table[z / 9, z % 9] = ' ';}}isWhile = true;is2While = false;isSolution = false;isMatch = false;isOnly = false;isMultiSolution = false;}}}}}}

到这里就生成了一张只有唯一解的表,接下来就只需要完善游戏的其他功能就可以啦!

我用c#完成了这个游戏,感兴趣的可以去下载https://download.csdn.net/download/qq_40636929/13134218

数独游戏,随机生成只有唯一解的数独表相关推荐

  1. 001-unity2D游戏随机生成地图

    unity2D游戏随机生成地图 教程与素材地址出处 地图控制脚本.cs(所有变量使用用中文便于理解,实际开发中请勿这样使用) using System.Collections; using Syste ...

  2. 猜数字游戏随机生成一个随机数,并给玩家三次机会猜测

    """猜数字游戏随机生成一个随机数,并给玩家三次机会猜测""" import random #import的作用是导入random这个伪造随 ...

  3. Python代码实现猜数字游戏随机生成数字进行比对

    Python代码实现猜数字游戏随机生成数字进行比对 import random secret = random.randint(1,10) print("---------------猜数字 ...

  4. Android数字游戏之数独(自动随机生成不同难度的数独)

    一:概述 我在去年写的一个小游戏,数字游戏其中有三个数字游戏. 1.数独(这篇博客讲讲这个) 2.扫雷 3.数字排序 相信大家都玩过数独吧,或者看别人玩过吧.简单介绍下规则.就是往九宫格中填数字,但是 ...

  5. C语言简单数独游戏终盘生成

    前言 这一篇文章介绍的是移动变换法,有详细的移动变化法的图文解析,在文末有完整的可用于查看移动变换法生成数独终盘过程的代码 实现思路 移动变换法这一方法是很简单的一种方法,实现起来也比较容易,但同时它 ...

  6. C语言生成一个随机的九行九列数独,一个随机生成数独的C++程序

    下面是编程之家 jb51.cc 通过网络收集整理的代码片段. 编程之家小编现在分享给大家,也给大家做个参考. //mySIZE是数独棋盘的边长,棋盘是mySIZE*mySIZE的大小 int mySI ...

  7. python数独游戏源代码_使用Python编写数独游戏自动出题程序

    数独是一个很好玩的游戏,可以锻炼推理能力.下面的代码可以自动生成数独游戏题目. from random import shuffle, randrange def generate(): # 初始网格 ...

  8. python漂亮界面 数独游戏源代码_使用Python编写数独游戏自动出题程序

    原标题:使用Python编写数独游戏自动出题程序 数独是一个很好玩的游戏,可以锻炼推理能力.下面的代码可以自动生成数独游戏题目. fromrandom importshuffle, randrange ...

  9. c语言猜四位数游戏猜10次,C语言猜数字游戏--随机生成4个不相同的数字从小到大排序,用户开始游戏,如果用户猜对数字和数字对应的位置,界面回馈A,如果数字正确位置不正确,则回馈B...

    1.看程序运行截图吧!! 由于博主本人较笨,就不动画演示了,如果动画的话可能将是一个漫长的过程! 猜数字游戏.png 2.游戏题目 随机生成4个不相同的数字从小到大排序,用户开始游戏,如果用户猜对数字 ...

  10. 猜数字小游戏(随机生成’三剑客‘)

    大家好,我是一只励志要翻身的小牛,废话少说,下面就给大家介绍第一个C语言小游戏--猜数字小游戏! 文章目录 一.游戏规则 二.效果展示 三.游戏设计思路 四.游戏交互界面 1.创建游戏菜单 2.创建游 ...

最新文章

  1. SAP SD基础知识之交货单不完全日志
  2. 基于WASM的无侵入式全链路A/B Test实践
  3. java comparator内部类_java - Java Comparator使用.reverseOrder()但内部类 - 堆栈内存溢出...
  4. Video视频背景设计企业模板
  5. 实现MySQL逗号数据计数
  6. 二自由度振动仿真:matlab直接解微分方程virtual.lab motion仿真
  7. C#中IQueryable和IEnumberable的区别
  8. linux localhost发邮件失败,测试邮件系统:telnet localhost 25时的问题~
  9. 计算机环模实验报告,误差配套实验报告
  10. u盘跑显卡维修测试软件,GPU Caps Viewer(显卡检测工具)
  11. 分享一个查看U盘闪存的工具,SA们别买到假货了!
  12. js获取map对象的key和value
  13. linux服务器通过代理连接网络
  14. java分享微博_java_java实现的新浪微博分享代码实例,weibo.java {@link IWeiboShareAPI#handle - phpStudy...
  15. sudo: /etc/sudoers is world writable sudo: no valid sudoers sources found, quitting sudo: 解决方法
  16. 20200804自编译openwrtx86_64固件,源码来自L大
  17. MyCat相关知识及测试要点
  18. WPF 获取主程序(主窗口)对象
  19. JAVA在鼠标点击位置绘制圆,单击鼠标后在JPanel上绘制圆圈
  20. 特斯拉第二季度电动汽车销量下降近 18%

热门文章

  1. 自动控制原理复习第七章——非线性系统分析
  2. C语言打印打印ASCLL表
  3. css静态网页设计 北京旅游(1页) 北京旅游网页设计制作 简单静态HTML网页作品 我的旅游网页作业成品 学生旅游网站模板
  4. SVN同步分支代码到主干
  5. c++ primer第5版中文版.pdf_伍德里奇计量经济学导论现代观点第5版 pdf
  6. Map集合遍历的四种方式
  7. U盘的文件夹变成快捷方式,原来是这个病毒在作祟hypertrm.exe
  8. 工作流Activiti 6.x
  9. oracle数据库基础笔试题,Oracle数据库入门笔试试题及参考答案
  10. 华为LACP的相关配置命令