Swing数独游戏(三):数独难题生成

前面两篇博文介绍了数独终盘生成的两种方法。

Swing数独游戏(一):终盘生成之矩阵转换法 ==> [url]http://mouselearnjava.iteye.com/blog/1941483[/url]
Swing数独游戏(二):终盘生成之随机法 ==> [url]http://mouselearnjava.iteye.com/blog/1941693[/url]

拥有了数独终盘之后,我们需要在这个终盘上挖去一些数字,然后就能产生数独难题。

在这篇博文中将简单介绍一下“挖洞”法生成数独难题的方式,并采用随机挖洞的方式,对同一份数独终盘产生不同难度的数独难题。

“挖洞”的方式可以有多种实现,一般有四种方式:

参考自:[url]http://zhangroup.aporc.org/images/files/Paper_3485.pdf[/url]

[img]http://dl2.iteye.com/upload/attachment/0089/2848/268d35b1-28e1-3acc-8029-691ceb0ec0aa.jpg[/img]

本文采用随机法来实现。


生成的步骤如下:
[b]1. 首先采用前面两篇文章提供的产生数独终盘的方式,产生1000个.txt文件,作为每一关关卡的数独终盘。[/b]

Swing数独游戏(一):终盘生成之矩阵转换法 ==> [url]http://mouselearnjava.iteye.com/blog/1941483[/url]
Swing数独游戏(二):终盘生成之随机法 ==> [url]http://mouselearnjava.iteye.com/blog/1941693[/url]

[b]2. 每个数独终盘由9个 3 * 3 的块组成,根据设定的关卡难度随机产生一个适合关卡难度的随机数,比如5, 然后在某个 3*3的块中随机剔除5个数。[/b]

剔除数据的位置记为1,代表这个位置没有数字,用户可以输入数字。保留数字的位置记为0, 用于在页面上显示出数字,但用户不能对其进行修改操作。

比如:

我们可以定义游戏难度为四个等级:简单,中等,困难和非常困难。

定义每个困难等级随机数剔除数据的数目:

[b]简单 --> [4,5]
中等 --> [5,6]
困难 --> [5,8]
非常困难 --> [6,9][/b]

/**
* 根据不同的游戏难度,获取随机数
*/
public int getRandomNumberByLevel(DifficultyLevel level) {
int randomValue = 5;

switch (level) {
case EASY:
/**
* 产生随机数[4,5]
*/
randomValue = random.nextInt(2) + 4;
break;
case MEDIUM:
/**
* 产生随机数[5,6]
*/
randomValue = random.nextInt(2) + 5;
break;
case DIFFICULT:
/**
* 产生随机数[5,8]
*/
randomValue = random.nextInt(4) + 5;
break;
case EVIL:
/**
* 产生随机数[6,9]
*/
randomValue = random.nextInt(4) + 6;
break;
default:
break;

}
return randomValue;
}



[b]3. 根据关卡的数独终盘及其根据困难等级随机挖空某些数据产生的文件一起,共同确定数独难题[/b]

比如,第一关对应的数独终盘如下图所示,终盘内容存储在文件1.txt中。
[img]http://dl2.iteye.com/upload/attachment/0089/2917/b8588a56-3f72-3e43-a5f7-c36d6b96ce65.jpg[/img]

结合为简单难度产生的“挖洞”后的文件,生成对应的数独难题:

[list]
[*]简单难度==>

[img]http://dl2.iteye.com/upload/attachment/0089/2919/d9a92345-819f-3abb-9f5a-796d77d75921.jpg[/img]


同理可以获得中等难度,困难难度等对应的数独难题。

[*]中等难度==>

[img]http://dl2.iteye.com/upload/attachment/0089/2921/21d63525-62bb-3816-aa04-1612e3239333.jpg[/img]


[*]困难难度==>

[img]http://dl2.iteye.com/upload/attachment/0089/2923/798cc6e3-b76b-3e1d-a51d-7074a2bac4ef.jpg[/img]

[/list]

这样一来,不同困难难度的数独难题就产生了,一个关卡,根据剔除数据个数的不同,将为每个困难等级产生一个挖洞文件,也就是有四个数独难题。如果随机产生1000个数独终盘,那么玩家可以玩4000个数独题目,每个困难等级1000个关卡可供选择。

[b]总结:[/b]

本篇文章使用简单的随机剔除数据的方法去产生数独难题,这个方法比较方便简单。但是并不能保证解的唯一性,也就是一道数独难题可能拥有超过一个解法。如果用户填入的数据都满足数独条件,那么他的解就是可用的,有效的,可以通过该关卡。
另外随机剔除数据可能没有剔除某些数据的关联性,可能使得尽管剔除的数据不少,但是玩起来并不难的情况。


下面是随机剔除数据的一个工具类:

package my.sudoku.utils;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Random;
import java.util.logging.Logger;

import my.sudoku.constant.SudokuContants;
import my.sudoku.enums.DifficultyLevel;

public class HoleDigUtils {

private static final Logger logger = Logger
.getLogger("my.sudoku.utils.HoleDigUtils");

private Random random = new Random();

public void digHolesByGameDifficulty(int numOfFiles, DifficultyLevel level) {
for (int num = 1; num <= numOfFiles; num++) {
int[][] array = new int[9][9];
int randomInt = 0;
for (int i = 0; i < 9; i++) {

randomInt = getRandomNumberByLevel(level);

int[] randomPositions = populateRandomArray(randomInt);

for (int j = 0; j < randomPositions.length; j++) {
int col = (i % 3) * 3 + (randomPositions[j] - 1) % 3;
int row = (i / 3) * 3 + ((randomPositions[j] - 1) / 3);

array[row][col] = 1;
}
/**
* 将array写入文件
*/
BufferedWriter bw = null;
try {
bw = new BufferedWriter(new FileWriter(new File(
SudokuContants.SUDOKU_FOLDER_NAME, buildFileName(
level, num))));
} catch (IOException e) {
logger.severe(e.getMessage());
}
StringBuilder sb = new StringBuilder();
for (int k = 0; k < 9; k++) {
sb.setLength(0);
for (int j = 0; j < 9; j++) {
sb.append(array[k][j]);
sb.append(",");
}
try {
bw.write(sb.substring(0, sb.length() - 1).toString());
bw.newLine();
} catch (IOException e) {
logger.severe(e.getMessage());
}

}
if (bw != null) {
try {
bw.close();
} catch (IOException e) {
logger.severe(e.getMessage());
} finally {
bw = null;
}
}

}
}
}

private String buildFileName(DifficultyLevel level, int fileNumberl) {
StringBuilder sb = new StringBuilder();

sb.append(fileNumberl);
switch (level) {
case EASY:
sb.append("_easy.txt");
break;

case MEDIUM:
sb.append("_medium.txt");
break;
case DIFFICULT:
sb.append("_difficult.txt");
break;
case EVIL:
sb.append("_evil.txt");
break;
default:
break;

}

return sb.toString();
}

/**
* 根据不同的游戏难度,获取随机数
*/
public int getRandomNumberByLevel(DifficultyLevel level) {
int randomValue = 5;

switch (level) {
case EASY:
/**
* 产生随机数[4,5]
*/
randomValue = random.nextInt(2) + 4;
break;
case MEDIUM:
/**
* 产生随机数[5,7]
*/
randomValue = random.nextInt(3) + 5;
break;
case DIFFICULT:
/**
* 产生随机数[5,8]
*/
randomValue = random.nextInt(4) + 5;
break;
case EVIL:
/**
* 产生随机数[6,9]
*/
randomValue = random.nextInt(4) + 6;
break;
default:
break;

}
return randomValue;
}

private int[] populateRandomArray(int numOfRandoms) {
int[] array = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int randomInt = 0;
for (int i = 0; i < 20; i++) {
randomInt = random.nextInt(8) + 1;
int temp = array[0];
array[0] = array[randomInt];
array[randomInt] = temp;
}

int[] result = new int[numOfRandoms];

System.arraycopy(array, 0, result, 0, numOfRandoms);

return result;
}

public static void main(String[] args) throws IOException {
HoleDigUtils digger = new HoleDigUtils();
/**
* 采用"挖洞"法,产生不同难度的数独难题文件。
* 文件中 0 表示 有数据且不可编辑,1 表示不显示数据,且可编辑
*/
for (DifficultyLevel level : DifficultyLevel.values()) {
digger.digHolesByGameDifficulty(
SudokuContants.NUMBER_OF_SUDOKU_ARRAYS, level);
}
}

}
一、数独说明:数独由九行,九列组成,又分为顺序排列的九宫。每行、每列及每宫都包含九个格,九个格中填放1到9的不重复的数字。 二、自动计算原理(步法): 1、基础法:找出空格中唯一可填的数字。方法是,先假设某空格中可填入九个数字,然后去掉所在行、所在列、所在宫中的已有数字。余下如果是唯一一个数字,那么这个数字就是结果 2、找唯一法:例如果某行、某列或某宫中只剩一个空格,那么九个数字中缺少的那个就是结果。 3、求唯余法:对于存在多个可能值的空格,循环取其中一个作为假设值,然后反复利用方法1和方法2去测试,如果出错冲突或导致别的空格无值可填时,说明假设的值是错误的。并对别剩余未找到唯一值的空格进行同样操作,直至找到可行的一个方案。 、自动出题,是自动求解的反向过程,先给出答案,再组合题目: 1、数独难易程度跟数独已知的数字个数有一定关系,但不是必然关系。可分为四级,根据网友“数独难度分级”的文章https://wenku.baidu.com/view/af550ed51a37f111f1855ba0.html,结果是分布在0到1之间的一系列值,值越少越容易。 2、出题时,先利用随机数往81个空格中填入不冲突的值。方法是,因为对角线的宫中的数字互不干扰,用随机数填充这宫,然后根据数独规则要求随机填入另外六宫。 3、这是最终结果,然后根据难易要求,随机将结果中的一定数量(可以用随机数)的方格清空。数独题已经形成。再根据网友提供的级别计算公式,计算形成的数独题的难易程度是否符合要求。(此时的数独答案不是唯一的) 4、难易程度具体计算公式是:两个空格所有可能值如果有依赖关系值为1,没依赖关系值为0。如此汇总所有空格之间的关系值为A,再除以空格个数B的18倍。即A/(18*B)。0—0.25为0级,0.25—0.5为1级,0.5—0.75为2级,0.75—1为3组。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值