朋友手机装有数独游戏,开会报告等无聊的场合常拿来玩玩,游戏的算法似乎并不难,想想我也能做出来。今早闲的蛋疼,就写了个数独玩玩。记录如下:
数独规则不知道的可以参考这里: http://baike.baidu.com/view/961.htm?fr=ala0_1 。游戏关键的算法就在于生成一个符合数独游戏规则的初始矩阵,首先想到的自然是号称万能解题法的“搜索+剪枝”了。
1. 产生符合数独规则的初始矩阵
第一行是随机生成的1~9的排列,第2到9行就要通过搜索来产生了。对于第2到9行的每一个空格,要从1到9逐个尝试放入,看同一列、同一行、同一个3×3的小方阵中是否出现过相同的数字,若没有就尝试放入,然后递归地搜索下一个位置的数字,若1到9都不行就返回上一个位置尝试下一个数字。直到找到一组解就返回。
先上C++代码:
#include<iostream>
#include<algorithm>
#include <ctime>
using namespace std;
int Initial_State [ 10 ] [ 10 ] ;
bool get_Initial_State( int i , int j ) //搜索第( i , j )位置处可以存储的数字,找到解则返回true,否则返回false
{
if( i > 9 || j > 9 )
return true;
for( int k = 1 ; k <= 9 ; ++k )
{
bool can = true; // can 变量用于记录数字k能否放在 ( i , j ) 处
for( int m = 1 ; m < i ; ++m )
if( Initial_State[m][j] == k ) // 检查同一列是否出现过数字k
{
can = false ;
break ;
}
if ( can )
for( int n = 1 ; n < j ; ++n )
if( Initial_State[i][n] == k ) // 检查同一行是否出现过数字k
{
can = false ;
break;
}
if( can )
{
int up1 = ( i/3 ) * 3 + 3 ; // (i,j)方格所在的3×3小方格i坐标的上限
int up2 = ( j/3 ) *3 + 3; // (i,j)方格所在的3×3小方格在j坐标的上限
#include<algorithm>
#include <ctime>
using namespace std;
int Initial_State [ 10 ] [ 10 ] ;
bool get_Initial_State( int i , int j ) //搜索第( i , j )位置处可以存储的数字,找到解则返回true,否则返回false
{
if( i > 9 || j > 9 )
return true;
for( int k = 1 ; k <= 9 ; ++k )
{
bool can = true; // can 变量用于记录数字k能否放在 ( i , j ) 处
for( int m = 1 ; m < i ; ++m )
if( Initial_State[m][j] == k ) // 检查同一列是否出现过数字k
{
can = false ;
break ;
}
if ( can )
for( int n = 1 ; n < j ; ++n )
if( Initial_State[i][n] == k ) // 检查同一行是否出现过数字k
{
can = false ;
break;
}
if( can )
{
int up1 = ( i/3 ) * 3 + 3 ; // (i,j)方格所在的3×3小方格i坐标的上限
int up2 = ( j/3 ) *3 + 3; // (i,j)方格所在的3×3小方格在j坐标的上限
if( i % 3 == 0 ) //这是针对特殊情况的处理
up1 = i ;
if( j % 3 == 0 )
up2 = j ;
for( int p = up1-2 ; p <= up1 ; ++p ) /* 检查在3×3的小方格中是否出现了同一个数字 */
{
if( can == false ) /* 跳出外层循环 */
break ;
for( int q = up2-2 ; q <= up2 ; ++q )
if( Initial_State[p][q] == k )
{
can = false ;
break ;
}
}
}
if( can )
{
Initial_State[i][j] = k ;
if( j<9 )
{
if( get_Initial_State( i , j +1 ) ) /* 到同一行的下一位置开始搜索 */
return true ;
}
else
{
if( i < 9 )
{
if( get_Initial_State( i + 1 , 1 ) ) /* 到下一行的第一个空格开始搜索 */
return true ;
}
else
return true ; /* i >= 9 && j >= 9 , 搜索结束 */
}
Initial_State[i][j] = 0 ; /* 关键这一步:找不到解就要回复原状,否则会对下面的搜索造成影响 */
}
}
return false ; /* 1到9都尝试过都不行,则返回递归的上一步 */
}
void start()
{
srand ( unsigned ( time (NULL) ) ); /* 产生random_shuffle的随机数种子 */
for( int i = 1 ; i <= 9 ; ++i )
for( int j = 1 ; j <= 9 ; ++j )
Initial_State[i][j] = 0 ;
for( int i = 1 ; i <= 9 ; ++i )
Initial_State[1][i] = i ;
random_shuffle( &( Initial_State[1][1]) , &( Initial_State[1][10]) ) ; /* 第一行随机排列产生 */
get_Initial_State( 2 , 1 ) ; /* 从第二行开始搜索 */
}
int main()
{
start( ) ;
for( int i = 1 ; i <= 9 ; ++ i )
{
for( int j = 1 ; j <= 9 ; ++j )
cout<< Initial_State [i][j] <<" " ;
cout<<endl;
}
getchar() ;
return 0 ;
}
上面的算法在找到第一个解后就返回了,这样当第一行数字确定后,则只能得到一个矩阵了。也就是说上面的程序只能产生 9! 个数独初试矩阵。
2. 获胜条件
还有一点要注意的是检查用户的解正确与否的标准不是比较用户输入得到的矩阵与我们开始产生的初试矩阵是否相同,而是判断用户的矩阵是否满足数独的游戏规则。
这是因为一个初始矩阵被挖掉一些空格后,可能会有不止一种正确解,这里我就不详细说,这样的例子很容易举出。
3. 程序源代码下载
用C++写完上面的代码,发现MFC半年没用,不会做界面了。。。于是用javascript写了出来。程序全部在一个html文件中。