数独游戏-如何用代码实现

数独游戏-如何用代码实现

最近开始喜欢起来玩数独,在手机上找来几个数独小游戏玩着玩着突然想起我是个程序员.........

那我何不自己写一个数独软件,网上查了一下数独有6,670,903,752,021,072,936,960(约有6.67×10的21次方)种组合 。我xxxx.......算了算了不想那么多了,先自己尝试做一个简单的

下面的是我设计思路

​ 首先给没有玩过的同学普及一下数独:数独(shù dú)是源自18世纪瑞士的一种数学游戏。是一种运用纸、笔进行演算的逻辑游戏。玩家需要根据9×9盘面上的已知数字,推理出所有剩余空格的数字,并满足每一行、每一列、每一个粗线宫 (3*3)内的数字均含1-9,不重复

​ 那么第一件事考虑如何如何生成一个9*9并且每一行,每一列,每一宫都不重复的二维数组?

​ 突然我灵机一动,如果..我手写个二维数组怎么样.

int[][] arrray1 = new int[9][];

arrray1[0] = new int[9] { 5, 6, 4, 8, 9, 7, 2, 3, 1 };

arrray1[1] = new int[9] { 9, 7, 8, 3, 1, 2, 6, 4, 5 };

arrray1[2] = new int[9] { 3, 1, 2, 6, 4, 5, 9, 7, 8 };

arrray1[3] = new int[9] { 6, 4, 5, 9, 7, 8, 3, 1, 2 };

arrray1[4] = new int[9] { 7, 8, 9, 1, 2, 3, 4, 5, 6 };

arrray1[5] = new int[9] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };

arrray1[6] = new int[9] { 4, 5, 6, 7, 8, 9, 1, 2, 3 };

arrray1[7] = new int[9] { 8, 9, 7, 2, 3, 1, 5, 6, 4 };

arrray1[8] = new int[9] { 2, 3, 1, 5, 6, 4, 8, 9, 7 };

​ 嗯~ o( ̄▽ ̄)o感觉也不是不可以,如果每行扣去4个的话 C94 有126种 那一共应该有1134种组合

​ 上面的数组是固定的如果在写几组数组呢?好像是可以有更多的组合但是成本太高,并且要找到每一宫每一列都不重复的组合也需要花费些时间而且也失去了乐趣,

​ 换一个角度想如果不能变当前这个二维数组,可不可以通过其他方式来改变这个数组,可以使用一个一位数组同样是1-9的数字,通过判断二维数组中和一维数组相等的数据并取下一个位置的值,目的就是让一维数组把二维数组中的值循环变一下,因为一维数组也是1-9的不重复数字因此不会影响到行和列及宫的组合,大家可以自行验证一下.

private static int[][] creatSudokuArray(int[][] seedArray, List randomList)

{

for (int i = 0; i < 9; i++)

{

for (int j = 0; j < 9; j++)

{

for (int k = 0; k < 9; k++)

{

if (seedArray[i][j] == randomList[k])

{

seedArray[i][j] = randomList[(k + 1) % 9];

break;

}

}

}

}

return seedArray;

}

这样生成的数独也就有9!=362880 在通过扣掉若干个格子那最终的结果

OK 数独生成了那下面具体就是需要去画一个9*9的矩阵并且把生成的数独填充进去然后扣掉部分

我这里使用的是winform实现的具体填充代码就不说了下面会给贴出github的源码地址大家可以去下载,主要还是说思路.

扣数据部分也是用随机数来操作的单纯的去掉几个感觉太死板了,附代码

///

/// 生成数独选择难度

///

///

private void printArray(int[][] a)

{

int length = 4;

switch (this.comboBox1.Text)

{

case "简单":

length = 4;

break;

case "中等":

length = 5;

break;

case "困难":

length = 6;

break;

}

Random random = new Random();

for (int i = 0; i < 9; i++)

{

for (int j = 0; j < 9; j++)

{

int randomNum = random.Next(9);

if (randomNum > length)

{

tbArray[i, j].Text = a[i][j].ToString();

}

else

{

tbArray[i, j].ReadOnly = false;

}

}

}

}

最后就是要去验证自己填进去的数字是否满足数独的要求

那么我们就需要每一行,每一列,每一宫的检查,行和列很简单只要验证当前行或者列是否有重复数据或者去重之后的长度是否小于9即可,相信大家可以通过各种语言来实现了把,下面贴出我的代码

///

/// 校验行

///

public bool rowCheck()

{

for (int y = 0; y < 9; y++)

{

List lines = new List();

for (int x = 0; x < 9; x++)

{

string value = tbArray[x, y].Text;

if (!String.IsNullOrEmpty(value))

{

lines.Add(Convert.ToInt32(value));

}

else

{

MessageBox.Show("请确认是否填写完毕");

return false;

}

}

if (lines.Distinct().Count() < 9)

{

MessageBox.Show("验证失败,请检查完重新提交");

return false;

}

}

return true;

}

///

/// 校验列

///

public bool cellCheck()

{

for (int y = 0; y < 9; y++)

{

List lines = new List();

for (int x = 0; x < 9; x++)

{

string value = tbArray[y, x].Text;

if (!string.IsNullOrEmpty(value))

{

lines.Add(Convert.ToInt32(value));

}

else

{

MessageBox.Show("请确认是否填写完毕");

return false;

}

}

if (lines.Distinct().Count() < 9)

{

MessageBox.Show("验证失败,请检查完重新提交");

return false;

}

}

return true;

}

那么每一宫怎么检验呢?其实和行列的方法相识我们可以吧每一宫的数据计算出来放到一个数组中最后相同的方式去判断即可

///

/// 校验九宫格每一宫是否有重复

///

public bool palaceCheck()

{

int[,] newArrary = block();

for (int y = 0; y < 9; y++)

{

List lines = new List();

for (int x = 0; x < 9; x++)

{

lines.Add(newArrary[x, y]);

}

if (lines.Distinct().Count() < 9)

{

MessageBox.Show("验证失败,请检查完重新提交");

return false;

}

}

return true;

}

//将每块的数字保存至一个二维数组

public int[,] block()

{

int[,] b = new int[9, 9];

for (int i = 0; i < 9; i++)

for (int j = 0; j < 9; j++)

{

//将数独从左至右从上至下分为9块,求该单元格属于第几块,将该块数字保存至b第几行

int rowOfB = i / 3 * 3 + j / 3;

//每块有9个数字,求该数字属于第几个,保存至b第几列

int columnOfB = i % 3 * 3 + j % 3;

b[rowOfB, columnOfB] = Convert.ToInt32(tbArray[i, j].Text);

}

return b;

}

整个的实现思路就完成了,下面的就是具体优化了,下面是我的最终效果

源码:https://github.com/boPrivateSpace/Sudu

小伙伴们有更好的方式可以分享出来大家一起讨论