很早之前大概是2014年的时候,我用WPF就是C#啦,写了一个数独游戏,那时之所以有这个想法,主要还是因为更早之前玩数独游戏时基本没有把题目解出,有点小受伤,但本葛葛毕竟是程序员啊,我解不出,可以用程序解出嘛。最近又用JavaScript把数独游戏的解法又实现了一遍,于是想记录下来,大家可以一起探讨
那这个数独算法要怎么设计呢,我将整个数独分成了以下几个部分。首先我们需要建造基本的结构体:
1、建一个9*9大小的二维数组,用于存储数独各个节点的值BrickNode。
2、创建数独单个节点的数据结构体BrickNode,它包含两个信息number(值,初始值为0)和kind(种类,是原始的值例如上图中橙色的种类,用户编辑的值如上图的绿色种类),例如左上角的原始值2的表示为number=2,kind=1
3、表示坐标的结构体MsPoint,它包含两个值x和y,例如左上角的2的坐标值为x=0,y=0
接下来就是我们主要的算法设计,那这个算法要怎么设计呢?我的第一想法是使用递归,这个主要的算法要做的事情就是:
1、判断是否已经得到了答案,如果得到则结束,否则继续
2、深度克隆一份当前的数独
3、判断当前的坐标是否是最后一个节点,且number值不等于0即已经填充了一个有效值。如果满足这两个条件,则表示数独已经计算完,且得到了答案。如果不满足则继续
4、判断当前的坐标上节点是否是原始节点。如果是的话,则获取下一个坐标,再从1重新开始,即递归方法;如果不是的话则进行第5步操作。
5、获取当前坐标可填充的数字的数组,如果该数组的长度为0,则表示当前坐标没有任何数字可填充,即无解,直接return,结束当前分支的递归;如果当前数组长度不为空,则遍历数组,取遍历的值将其填充到当前数独数组,判断当前的坐标是否是最后一个节点且number值不等于0,是的话则表示已经计算完了,设置标记是否已经取到答案的变量值为true,即已经计算完了,其它递归分支都不用计算了,如果不满足是最后一个节点,则获取下一个坐标,从第1步再继续递归。
具体代码实现如下所示:
calculating: function (sudokuParam, point) {
if (this.gotAnswer || this.stop){
return;
}
//深度克隆
var sudoku = sudokuParam.clone();
//判断当前的坐标是否是最后一个节点,且number值不等于0(即是否已经填充了一个有效值)
if (point.X == 8 && point.Y == 8 && sudoku[point.X][point.Y].number != 0) {
//得到答案了
this.answer = sudoku;
//标记已经得到答案了
this.gotAnswer = true;
return;
}
//判断当前节点的种类是否是原始值(即数独题给出的基本值,如上图的橙色方块)
if (sudoku[point.X][point.Y].kind == BrickKind.Original){
//根据当前数独及当前坐标的值,获取下个坐标,并继续计算
this.calculating(sudoku, this.getNextPoint(sudoku, point));
}
else
{
//获取当前坐标可填充入的数字结构体,结果是一个数组
var brickNodes = this.getAvailableBrickNodes(sudoku, point);
//数组长度为0,即当前分支下的数组无解,直接退出当前递归分支
if (brickNodes.count == 0){
return;
}
// 如果当前数组长度不为空,则遍历数组
for (var i = 0 ; i < brickNodes.length; i++) {
var brickNode = brickNodes[i];
//取遍历的值将其填充到当前数独数组的当前坐标下
sudoku[point.X][point.Y] = brickNode;
//判断当前的坐标是否是最后一个节点且number值不等于0,是的话则表示已经计算完了
if (point.X == 8 && point.Y == 8 && sudoku[8][8].number != 0) {
this.answer = sudoku;
//设置标记是否已经取到答案的变量值为true,即已经计算完了,其它递归分支都不用计算了
this.gotAnswer = true;
return;
}
//如果不满足是最后一个节点,则获取下一个坐标,从第1步再继续递归
this.calculating(sudoku, this.getNextPoint(sudoku, point));
}
}
}
这里面用到两个方法,这两个方法在这里只阐述具体的用处和解决的思路,其它则不再赘述。
1、getAvailableBrickNodes(sudoku, point)作用是获取当前坐标可填入的数字,即判断竖排1-9有哪些数字可用,横排1-9有哪些数字可用,方格内1-9有哪些数字可用,取三方的并集。第一个参数sudoku为当前数独的二维数组,point指的是当前坐标值
2、getNextPoint(sudoku, point)作用是获取下一个坐标的值,这个方法比较简单,主要点在于判断当前的x是否已经是到8了,如果到了则x要加1而y要充值未0,基于这一点,还要判断当前点是否是原始点,是原始点的话还要一直前进,直到遇到非原始点,然后将该点坐标作为返回值返回。同样第一个参数sudoku为当前数独的二维数组,point指的是当前坐标值
以下是我使用WPF编写的一个数独游戏,不仅仅是数独算法啦,还涉及到了WPF界面的设计。感兴趣的同学可以下载试试:WPF Fashion Sudoku 数独游戏http://download.csdn.net/detail/leyyang/9587039