您的位置:首页 > 移动开发 > 微信开发

数独计算小程序开发(一)

2009-12-22 15:46 357 查看
前些日子,女朋友给了我一个数独游戏,弄了好久没弄出来。计算量有点大,于是就想通过编程来计算了,就做了这么个东西来玩玩。

首先数独规则:

在9*9的方格矩阵中,根据已知数字填入1-9数字。最后满足在每行、每列、每个3*3宫中的9个格中不重复的存在1-9的所有数。OVER!

说说实现吧,数独的数据信息的是记录在一个二维数组中的。每个元素为如下数据结构

typedef struct shuduelement
{
BOOL			bKnown;		/* 次元素值是否为已知 */

/* bKnown为TRUE时则为已知值,为FALSE时则为可能值或初始的0,
按相应的bit位存储*/
unsigned int	nValue;
} SDElement;


其中nValue存储元素的值,因为要计算和存储可能值(有多个值)而且为了计算的方便和效率,元素的值是通过设置位来表示的。如值为5时nValue = B10000;可能值为5、7、8时nValue = B11010000;通过这样的表达方式,在实际的判定算法中也可以很方便的计算,且效率很高。

首先有个最基本的算法(互斥性算法)。因为每行每列每宫中不能有相同数,所以可以由此原则和数独初始的已知数来算出每格的可能值。
]/*
*	计算指定元素的可能值,若其值已知则退出不计算
*/
void calcPossibleValue(SDElement rgnShuDu[][MAX_COL_NUM],const int row,const int col)
{
int		startRow, startCol;		/* 所在宫的起始位置 */
int		i, j;
unsigned int	num = 0;

if(!rgnShuDu[row][col].bKnown)	//值未知
{
startRow = (row / PALACE_LEN) * PALACE_LEN;
startCol = (col / PALACE_LEN) * PALACE_LEN;
/* 统计所在宫已知数 */
for(i = startRow; i < startRow + PALACE_LEN; i++)
for(j = startCol; j < startCol + PALACE_LEN; j++)
{
if(rgnShuDu[i][j].bKnown)
num |= rgnShuDu[i][j].nValue;
}

/* 统计所在行和列的已知数 */
for(i = 0; i < MAX_COL_NUM; i++)
if(rgnShuDu[row][i].bKnown)
num |= rgnShuDu[row][i].nValue;
for(i = 0; i < MAX_ROW_NUM; i++)
if(rgnShuDu[i][col].bKnown)
num |= rgnShuDu[i][col].nValue;

rgnShuDu[row][col].nValue = num ^ ALL_POSSIBILITY;
}
}


然后还有个必须值算法,根据的是每行每列每宫中的值必须1~9都有(因为互斥而且有9格),通过这种算法也能找出某些值。

/**
*	(局部函数)计算一个元素的必须的值,已知元素直接返回,如一个宫内
*其余的元素都不能为某值(已知数也不是那个值),则此元素一定为此某值,
*因为一个宫内必须有1~9的所有数。此算法人实际使用较多(我老婆),
*	前提条件,所有未知元素已根据互斥性算出了可能值
*	输入:	rgnShuDu要操作和保存数的二维数组
*			row,col分别指定要计算的元素的行列数
*	注意:元素可以算出必须值的可能性不大,故可能有效可能会浪费时间
*/
static void calcMustValue(SDElement rgnShuDu[][MAX_COL_NUM], const int row, const int col)
{
int		startRow, startCol;		/* 所在宫的起始位置 */
int		i, j;
unsigned int	num = 0;

if(!rgnShuDu[row][col].bKnown)
{
startRow = (row / PALACE_LEN) * PALACE_LEN;
startCol = (col / PALACE_LEN) * PALACE_LEN;
/* 统计所在宫内所有非指定元素的值,包括已知数 */
for(i = startRow; i < startRow + PALACE_LEN; i++)
for(j = startCol; j < startCol + PALACE_LEN; j++)
{
if(i != row || j != col)
num |= rgnShuDu[i][j].nValue;
}

/* 不为零则这个元素比为某个值,这样此元素值已知 */
if((num = num ^ ALL_POSSIBILITY) != 0)
{
rgnShuDu[row][col].bKnown = TRUE;
rgnShuDu[row][col].nValue = num;
}
}
}


计算一次就是调用上面的两个算法:

void calcAllPossibleValue(SDElement rgnShuDu[][MAX_COL_NUM])
{
int		i = 0, j = 0;

for(i = 0; i < MAX_ROW_NUM; i++)
for(j = 0; j < MAX_COL_NUM; j++)
calcPossibleValue(rgnShuDu, i, j);

for(i = 0; i < MAX_ROW_NUM; i++)
for(j = 0; j < MAX_COL_NUM; j++)
calcMustValue(rgnShuDu, i, j);
}


在这里我没有实现迭代计算,因为现在的打算是通过算法将结果分析出来,而不是遍历试出来(虽然这样也不错),以后会实现的,但这不是重点。

值得一说的是,每格元素是通过行号和列好来指定坐标的。因为数独有9*9个输入框,edit空间很多,为了能够方便输入输出,我在resource.h头文件中更改了每个控件的ID值,如位于第3行5列(行从上向下,列从左向右,都以0开始)的元素ID为IDC_NUM35 = 0x1135,还写了了几个宏来实现从二维数组坐标和控件ID间进行相互转换的操作:

]/**
*	输入控件很多为了能够更好的之间处理,调整了resource.h中的各输入控件
*的值,ID皆为IDC_NUMxx的形式,如:IDC_NUM35 = 0x1135,表示的是第3行第5列
*的输入控件,由此,可以容易由一控件ID获取其行列数:()前面的11是为了避免
*和别的ID值重复)
*/
#define ROW_MASK	0xF0	/* 获取行数的掩码 */
#define COL_MASK	0x0F	/* 获取列数的掩码 */

/* 由控件获取行数 */
#define GET_ROW(IDC_NUMxx)	((IDC_NUMxx & ROW_MASK) >> 4)
/* 由控件获取列数 */
#define GET_COL(IDC_NUMxx)	(IDC_NUMxx & COL_MASK)
/* 由行数列数获取控件ID */
#define GET_ID(row,col)		(0x1100 | col | (row<<4))
这样子在输入输出就会很方便, 而且为以后的添加的一些功能能够奠定基础。

现在还是alpha版,功能还不完善,尚未添加:对原始输入数据正确性的检测、新计算出的值的高亮显示等,因为没有实现迭代,所以每次计算后,要用户手动将新计算出的值输入到输入区,然后进行第二次计算,直至全部计算完毕。

完整代码下载: 数独辅助程序源码_0.5Alpha版

感兴趣的可以看看,如果有什么意见和建议就留言,希望多多交流。欢迎转载,请保留原始链接:

http://blog.csdn.net/zha_1525515/archive/2009/12/22/5056282.aspx
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: