Swing数独游戏(一):终盘生成之矩阵转换法

数独(すうどく,Sudoku)是一种运用纸、笔进行演算的逻辑游戏。玩家需要根据9×9盘面上的已知数字,推理出所有剩余空格的数字,并满足每一行、每一列、每一个粗线宫内的数字均含1-9,不重复。

参考:[url]http://baike.baidu.com/link?url=ePXUCvpBaRKBkEA3pVfOkg3m-NBozO6a4GDS0N3E5_gK1nnJCDzd5O-YL1w7c5S3[/url]

最近手机上安装了数独游戏,算是重拾该游戏了。为了更加了解游戏生成的原理,自己在闲暇时间做了一个Swing版的数独游戏,将会写几篇博文记录一些内容。 内容大致如下:

[list]
[*][b]Swing数独游戏(一):终盘生成之矩阵转换法
[*]Swing数独游戏(二):终盘生成之随机法
[*]Swing数独游戏(三):“挖洞”法
[*]Swing数独游戏(四):关键功能及实现。
[*]... ...[/b]
[/list]

[u][b]
数独游戏一般产生的过程包括两个过程:[/b][/u]
1. 生成一个终盘 (9 x 9)。
2. 然后根据难易程度,在终盘上挖掉不同数目的数字。

这样,就能确定那些格子是有数据且不可编辑以及哪些没有数据且可编辑的。

比如,我实现的一个Swing数独游戏界面:

[img]http://dl2.iteye.com/upload/attachment/0089/2419/42b82d2a-eb52-3a0e-9014-bc67cad5fe48.jpg[/img]


产生数独终盘(9x9 的二维数组)的方法主要有两种:[b]矩阵转换法[/b]或者[b]随机法[/b].

本篇博文将介绍使用[b]矩阵转换[/b]的方法生成终盘,具体步骤如下:

[u][b][size=medium][color=darkblue]1. 指定一个数独矩阵作为矩阵转换的种子矩阵[/color][/size][/b][/u]

为了完成矩阵的转换,我们需要有可用的数独数组作为种子数组才行。

做法可以如下:
1. 先给定几个可用数独数组作为备选种子矩阵。
2. 产生一个随机数,随机选中其中的一个作为种子矩阵。

可以写一个产生种子矩阵的工具类,如:
package my.sudoku.utils;

import java.util.Random;

public final class SeedSudokuMatrixFactory {

private static final Random random = new Random();

private static final int seedSudokuArrays[][][] = {
{ { 1, 2, 3, 4, 5, 6, 7, 8, 9 }, { 4, 5, 6, 7, 8, 9, 1, 2, 3 },
{ 7, 8, 9, 1, 2, 3, 4, 5, 6 },
{ 2, 1, 4, 3, 6, 5, 8, 9, 7 },
{ 3, 6, 5, 8, 9, 7, 2, 1, 4 },
{ 8, 9, 7, 2, 1, 4, 3, 6, 5 },
{ 5, 3, 1, 6, 4, 2, 9, 7, 8 },
{ 6, 4, 2, 9, 7, 8, 5, 3, 1 },
{ 9, 7, 8, 5, 3, 1, 6, 4, 2 } },
{ { 3, 9, 4, 5, 1, 7, 6, 2, 8 }, { 5, 1, 7, 6, 2, 8, 3, 9, 4 },
{ 6, 2, 8, 3, 9, 4, 5, 1, 7 },
{ 9, 3, 5, 4, 7, 1, 2, 8, 6 },
{ 4, 7, 1, 2, 8, 6, 9, 3, 5 },
{ 2, 8, 6, 9, 3, 5, 4, 7, 1 },
{ 1, 4, 3, 7, 5, 9, 8, 6, 2 },
{ 7, 5, 9, 8, 6, 2, 1, 4, 3 },
{ 8, 6, 2, 1, 4, 3, 7, 5, 9 } },
{ { 7, 6, 1, 9, 8, 4, 2, 3, 5 }, { 9, 8, 4, 2, 3, 5, 7, 6, 1 },
{ 2, 3, 5, 7, 6, 1, 9, 8, 4 },
{ 6, 7, 9, 1, 4, 8, 3, 5, 2 },
{ 1, 4, 8, 3, 5, 2, 6, 7, 9 },
{ 3, 5, 2, 6, 7, 9, 1, 4, 8 },
{ 8, 1, 7, 4, 9, 6, 5, 2, 3 },
{ 4, 9, 6, 5, 2, 3, 8, 1, 7 },
{ 5, 2, 3, 8, 1, 7, 4, 9, 6 } },
{ { 7, 1, 5, 4, 3, 6, 2, 9, 8 }, { 4, 3, 6, 2, 9, 8, 7, 1, 5 },
{ 2, 9, 8, 7, 1, 5, 4, 3, 6 },
{ 1, 7, 4, 5, 6, 3, 9, 8, 2 },
{ 5, 6, 3, 9, 8, 2, 1, 7, 4 },
{ 9, 8, 2, 1, 7, 4, 5, 6, 3 },
{ 3, 5, 7, 6, 4, 1, 8, 2, 9 },
{ 6, 4, 1, 8, 2, 9, 3, 5, 7 },
{ 8, 2, 9, 3, 5, 7, 6, 4, 1 } } };

private SeedSudokuMatrixFactory()
{}

/**
* 随机获取一个预先定义好的数独数组
*/
public static int[][] retrieveSeedSudokuArrayByRandom() {
int randomInt = random.nextInt(seedSudokuArrays.length);

return seedSudokuArrays[randomInt].clone();

}

}


[u][b][size=medium][color=darkblue]2. 确定矩阵变换的方式[/color][/size][/b][/u]

拥有了一个数独可用矩阵,我们就可以对这个种子矩阵作转换处理去获取更多的可用数独终盘矩阵。

矩阵转换的方式有很多,在这里列举如下几个:

[u][b]a) 交换两个数的位置[/b][/u]

我们可以生成1到9组成的List,然后遍历种子矩阵,然后矩阵中的数据将用随机产生的List中的对应数字代替。做法如下:
1. 用数字对9求余,得到的值作为List的索引。
比如 i行j列的数字是3,那么index就是3
2. 根据index获取对应的值,待交换的数字就是List.get(index),
比如得到的数字是6. 那么3就会用6来代替,其它数字的替换方式一样。


package my.sudoku.utils;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class SudokuPuzzleMatrixGenerator {

/**待转换的数组种子数组*/
private int sampleArray[][] = SeedSudokuMatrixFactory.retrieveSeedSudokuArrayByRandom();

private Random random = new Random();

public int[][] generateSudokuArray() {

List<Integer> randomList = buildRandomList();
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
for (int k = 0; k < 9; k++) {
if (sampleArray[i][j] == randomList.get(k)) {
sampleArray[i][j] = randomList.get((k + 1) % 9);
break;
}
}
}
}
return sampleArray;
}

private List<Integer> buildRandomList() {
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;
}

List<Integer> randomList = new ArrayList<Integer>();
for (int i : array) {
randomList.add(i);
}
return randomList;
}

}

下图就是一个数字9和数据1交换的例子:
[img]http://dl2.iteye.com/upload/attachment/0089/2433/27ed3315-aac9-3983-b854-55276fc7b5d9.jpg[/img]


[u][b]b) 调整行或列的位置[/b][/u]

[img]http://dl2.iteye.com/upload/attachment/0089/2435/5b2b389f-1ed1-382a-8980-bfd1b7ec0f91.jpg[/img]

[color=red][b]
行和列的转换,需要保证的是:交换只发生在前三行,中间三行,最后三行,前三列,中间三列以及最后三列之间。而不能越界交换,比如第一行和第四行交换就是不允许的。[/b][/color]

public int[][] generateSudokuArray1() {
int randomRowNum = 0;
//随机交换20次
for (int i = 0; i < 20; i++) {
randomRowNum = random.nextInt(8) + 1;
for (int col = 0; col < 9; col++) {
if(randomRowNum % 3 ==0)
{
int temp = sampleArray[randomRowNum][col];
sampleArray[randomRowNum][col] = sampleArray[randomRowNum+1][col];
sampleArray[randomRowNum+1][col] = temp;
}
else
{
int temp = sampleArray[randomRowNum][col];
sampleArray[randomRowNum][col] = sampleArray[randomRowNum-1][col];
sampleArray[randomRowNum-1][col] = temp;
}

}

}
return sampleArray;
}

public int[][] generateSudokuArray2() {
int randomColumnNum = 0;

for (int i = 0; i < 20; i++) {
randomColumnNum = random.nextInt(8) + 1;

for (int row = 0; row < 9; row++) {

if(randomColumnNum %3 ==0)
{
int temp = sampleArray[row][randomColumnNum];
sampleArray[row][randomColumnNum] = sampleArray[row][randomColumnNum+1];
sampleArray[row][randomColumnNum+1] = temp;
}else
{
int temp = sampleArray[row][randomColumnNum];
sampleArray[row][randomColumnNum] = sampleArray[row][randomColumnNum-1];
sampleArray[row][randomColumnNum-1] = temp;
}

}
}
return sampleArray;
}

[u]c) 调整块的位置[/u]

[img]http://dl2.iteye.com/upload/attachment/0089/2437/43c3e6e8-0fbd-3c04-a98b-4e7a4aee34ff.jpg[/img]


[u][b]d) 矩阵旋转 [/b][/u]

[img]http://dl2.iteye.com/upload/attachment/0089/2439/13104124-061a-3882-b916-cbdc4b3da5f3.jpg[/img]


另外还可以以对角线对对称,交换数据等方式,如:

	public int[][] getArrayWithDiagonalSymmetry() {
int[][] result = new int[9][9];
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {

result[i][j] = sampleArray[j][i];
}
}
return result;
}


[color=darkblue][b]矩阵转换法生成数独终盘的方式具有方便速度块的特点。它的缺点产生的终盘的随机性不是很强,毕竟是从一个固定的种子矩阵转换而得的。[/b][/color]

下一篇博文将介绍随机法去产生数独,从而增强产生数独终盘的随机性。

测试代码及其测试结果如下:

package test;

import java.io.IOException;
import my.sudoku.utils.SudokuPuzzleMatrixGenerator;

public class Main {

public static void main(String[] args) throws IOException {

SudokuPuzzleMatrixGenerator sp = new SudokuPuzzleMatrixGenerator();

/*
* 采用换数字的方法转换矩阵
*/
System.out.println("采用换数字的方法转换矩阵");
printArray(sp.generateSudokuArray());

/*
* 采用行交换的方法转换矩阵
*/
System.out.println("采用行交换的方法转换矩阵");
printArray(sp.generateSudokuArray1());

/*
* 采用列交换的方法转换矩阵
*/
System.out.println("采用列交换的方法转换矩阵");
printArray(sp.generateSudokuArray2());


/*
* 采用对角线对称交换数据
*/
System.out.println("采用列交换的方法转换矩阵");
printArray(sp.getArrayWithDiagonalSymmetry());


}

public static void printArray(int a[][]) {
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
System.out.print(a[i][j]);
System.out.print(" ");
}
System.out.println();
}
}


}
产生的种子矩阵是:
7 6 1 9 8 4 2 3 5
9 8 4 2 3 5 7 6 1
2 3 5 7 6 1 9 8 4
6 7 9 1 4 8 3 5 2
1 4 8 3 5 2 6 7 9
3 5 2 6 7 9 1 4 8
8 1 7 4 9 6 5 2 3
4 9 6 5 2 3 8 1 7
5 2 3 8 1 7 4 9 6
采用换数字的方法转换矩阵
3 7 9 2 6 8 4 5 1
2 6 8 4 5 1 3 7 9
4 5 1 3 7 9 2 6 8
7 3 2 9 8 6 5 1 4
9 8 6 5 1 4 7 3 2
5 1 4 7 3 2 9 8 6
6 9 3 8 2 7 1 4 5
8 2 7 1 4 5 6 9 3
1 4 5 6 9 3 8 2 7
采用行交换的方法转换矩阵
2 6 8 4 5 1 3 7 9
3 7 9 2 6 8 4 5 1
4 5 1 3 7 9 2 6 8
7 3 2 9 8 6 5 1 4
5 1 4 7 3 2 9 8 6
9 8 6 5 1 4 7 3 2
6 9 3 8 2 7 1 4 5
8 2 7 1 4 5 6 9 3
1 4 5 6 9 3 8 2 7
采用列交换的方法转换矩阵
2 8 6 4 5 1 7 3 9
3 9 7 2 6 8 5 4 1
4 1 5 3 7 9 6 2 8
7 2 3 9 8 6 1 5 4
5 4 1 7 3 2 8 9 6
9 6 8 5 1 4 3 7 2
6 3 9 8 2 7 4 1 5
8 7 2 1 4 5 9 6 3
1 5 4 6 9 3 2 8 7
采用列交换的方法转换矩阵
2 3 4 7 5 9 6 8 1
8 9 1 2 4 6 3 7 5
6 7 5 3 1 8 9 2 4
4 2 3 9 7 5 8 1 6
5 6 7 8 3 1 2 4 9
1 8 9 6 2 4 7 5 3
7 5 6 1 8 3 4 9 2
3 4 2 5 9 7 1 6 8
9 1 8 4 6 2 5 3 7

下一篇博文将介绍随机法去产生数独终盘,从而增强数独终盘数据的随机性。

矩阵转换图参考自:
[url]http://zhangroup.aporc.org/images/files/Paper_3485.pdf[/url]
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值