一、问题描述:
数独是一种运用纸、笔进行演算的逻辑游戏。玩家需要根据9×9盘面上的已知数字,推理出所有剩余空格的数字,并满足每一行、每一列、每一个九宫格内的数字均含1-9,不重复
要求:设计算法随机生成不同难度的数独游戏,阐述如何评价所生成数独的难度。
- 问题分析:
(在此只涉及数独的生成,不涉及数独的求解)
数独问题的约束条件:
(1)每个矩阵的数字范围仅限1—9;
(2)每个数字在当前行不允许重复;
(3)每个数字在当前列不允许重复;
(4)每个数字在每一宫内不允许重复。
- 问题求解思路
数独的生成,最好的情况就是,先得到一个完整的数独,然后根据难度需要,随机地挖一些空格出来,保证有解。所以关键就是如何得到完整的数独,也要有一定的随机化。
先要生成一个数独数组shudo[9][9],用memset函数对数组初始化,按照规则随机填数,然后根据给定的难度等级再随机挖洞,这就是我们生成的数独游戏。
- 算法测试
1.按照问题求解思路,在生成数独数组并将其完成初始化后,用shudu_1函数先对中间的九宫格完成随机填数。
随机填数具体实现是由一个a[10]数组来完成,用一个while循环用m变量给a[0]到a[8]随机赋值,且保证每个数组元素都各不相同。而在这过程中,用到了flag变量来进行数组元素是否重复的判断。在for循环遍历a[ ]数组前,给flag赋初值为0,一旦之前保存的数中出现了与m相同的数,就将flag标记为1并跳出循环,重新生成一个随机数m给a[ ]数组赋值,直到a[ ]数组随机填充数完毕。
2.在中间的九宫格完成随机填数后,用shudu_2函数对上下左右四个九宫格进行填数。
填数方法十分简单,利用已经填好的中间九宫格的数,交叉变换填充这四个九宫格。
3.最后shudu_3函数再对四个角上的数组填数,填数原理同第二步。到这一步为止,初始的随机填数数组已经生成好了。
4.再就是根据游玩者输入的难度随机挖洞,这里设置了4个难度。
(level 1)
(level 2)
(level 3)
(level 4)
以下是完整代码:
#include <stdio.h>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int shudu[9][9], hole[9][9];
//打印数独
void ouput()
{
for (int i = 0; i < 9; ++i)
{
for (int j = 0; j < 9; ++j)
{
cout << shudu[i][j] << " ";
}
cout << endl;
}
}
// 初始化中间的九宫格
void shudu_1()
{
int a[10] = {};//初始化数组
int n = 0;
while(n < 9){
int m = rand() % 9 + 1;
int flag = 0;
for(int i = 0;i < n+1;i ++){
if(a[i] == m){
flag = 1;//如果之前保存的数中出现了和m相同的数,就把flag标记为1并跳出循环,表示需要重新生成随机数m
break;
}
}
if(flag == 0){
a[n] = m;//如果flag是0,表示前n个数中没有和m相同的数,因此可以把第n+1个元素赋值为m
n ++;
}
int k = 0;
for (int i = 3; i < 6; ++i)
for (int j = 3; j < 6; ++j)
shudu[i][j] = a[k++];
}
}
// 由中间的九宫格交叉变换,初始化上下左右四个九宫格 (交换行列)
void shudu_2()
{
for (int i = 3; i < 6; ++i)
{
int l = 0;
for (int j = 3; j < 6; ++j)
{
if (i == 3)
{
shudu[i + 1][l] = shudu[i][j];
shudu[i + 2][l + 6] = shudu[i][j];
l++;
}
else if (i == 4)
{
shudu[i + 1][l] = shudu[i][j];
shudu[i - 1][l + 6] = shudu[i][j];
l++;
}
else if (i == 5)
{
shudu[i - 2][l] = shudu[i][j];
shudu[i - 1][l + 6] = shudu[i][j];
l++;
}
}
}
for (int j = 3; j < 6; ++j)
{
int l = 0;
for (int i = 3; i < 6; ++i)
{
if (j == 3)
{
shudu[l][j + 1] = shudu[i][j];
shudu[l + 6][j + 2] = shudu[i][j];
l++;
}
else if (j == 4)
{
shudu[l][j + 1] = shudu[i][j];
shudu[l + 6][j - 1] = shudu[i][j];
l++;
}
else if (j == 5)
{
shudu[l][j - 2] = shudu[i][j];
shudu[l + 6][j - 1] = shudu[i][j];
l++;
}
}
}
}
// 初始化四个角上的四个九宫格(和上面原理相同)
void shudu_3()
{
for (int i = 0; i < 3; ++i)
{
int l = 0;
for (int j = 3; j < 6; ++j)
{
if (i == 0)
{
shudu[i + 1][l] = shudu[i][j];
shudu[i + 2][l + 6] = shudu[i][j];
l++;
}
else if (i == 1)
{
shudu[i + 1][l] = shudu[i][j];
shudu[i - 1][l + 6] = shudu[i][j];
l++;
}
else if (i == 2)
{
shudu[i - 2][l] = shudu[i][j];
shudu[i - 1][l + 6] = shudu[i][j];
l++;
}
}
}
for (int i = 6; i < 9; ++i)
{
int l = 0;
for (int j = 3; j < 6; ++j)
{
if (i == 6)
{
shudu[i + 1][l] = shudu[i][j];
shudu[i + 2][l + 6] = shudu[i][j];
l++;
}
else if (i == 7)
{
shudu[i + 1][l] = shudu[i][j];
shudu[i - 1][l + 6] = shudu[i][j];
l++;
}
else if (i == 8)
{
shudu[i - 2][l] = shudu[i][j];
shudu[i - 1][l + 6] = shudu[i][j];
l++;
}
}
}
}
// 根据选择的难度随机挖数
void dighole(int difficulty)
{
difficulty *= 20;
while (difficulty--)
{
shudu[rand() % 9][rand() % 9] = 0;
}
}
// 初始化数独
// 先随机生成中间的九宫格,再通过交叉生成上下左右四个九宫格,
// 最后通过交叉生成四个角上的九宫格
void Shudu(int difficulty)
{
memset(shudu, 0, sizeof(shudu));
shudu_1();
shudu_2();
shudu_3();
dighole(difficulty);
}
int main()
{
int d;
cout << "请选择您希望生成数独难度(输入1~4,数字越大难度越高): ";
cin >> d;
Shudu(d);
ouput();
return 0;
}
- 总结
总的来说,代码较为简单,用随机法生成数独,将一个随机的一维数组转化成二维数组,生成的数独是一个伪随机的数独。