怎样设计解开数独游戏

Winter

2017-09-06

很早之前大概是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

第9篇《怎样设计解开数独游戏》来自Winter(https://github.com/aiyld/aiyld.github.io)的站点

0条评论