随机生成一个数独
最近学习用Java编写GUI程序,感觉从做一个小东西入手最好,选择了编写一些小游戏开始,第一个为数独游戏。
http://en.wikipedia.org/wiki/Sudoku
编写数独游戏第一步考虑的就是该如何生成一个原始的数独题目,要想生成数独题目最简单的办法就是先随机生成一个完整的数独,然后从中摘除一些数字,这样就可以生成一道数独题。所以需要考虑如何随机的生成一个完整的数独,这里考虑使用回溯法生成:
- 在数独的某个位置插入一个1~9的值
- 如果这个值能正常插入,则递归在下一个位置插入
- 如果这个位置没有值可以插入,则回溯上一个位置插入的值
- 递归以上步骤直到所有的位置全部都插入了合适的值
import java.util.Random; public class Main { private static int[] num = new int[81]; private static Random random = new Random(); /** * 随机产生一个数独 * @return 返回一个随机产生的数独 */ public static int[] generate() { for (int i = 0; i < 81; i++) { num[i] = 0; } solve(0); return num; } /** * 递归产生数独位置i的值 * @param 数独位置i * @return 位置i是否可以填入值 */ private static boolean solve(int i) { /* 如果已经填满81个格子则返回true */ if (i == 81) { return true; } /* 如果位置i已经填入了合适的值则递归产生下一个位置的值 */ else if (num[i] != 0) { return solve(i + 1); } /* 如果恰好需要填入位置i的值 */ else { /* 用数组randOrder存储每个位置可能产生的值,即为1~9 */ int[] randOrder = new int[10]; for (int val = 1; val < 10; val++) { randOrder[val] = val; } /* 将数组randOrder变为一个随机存储1~9的数组 */ for (int val = 1; val < 10; val++) { int rand = random.nextInt(10); int tmp = randOrder[rand]; randOrder[rand] = randOrder[val]; randOrder[val] = tmp; } /* 在位置i随机填入一个值,并且判断是否有效 */ for (int val = 1; val < 10; val++) { /* 如果在位置i填入的1~9中的某个随机数有效 */ if (isLegal(i, randOrder[val])) { /* 则将此随机值放入位置i */ num[i] = randOrder[val]; /* 探索i的下一个位置是否能正确填入,如果可以则返回true */ if (solve(i + 1)) { return true; } } } } /* 如果在位置i不能填入1~9中的任何值,则需要回溯 */ num[i] = 0; return false; } /** * 在位置i填入value数字是否有效,通过按行列和小矩阵判断 * @param 填入的位置i * @param 填入位置i的数字value * @return 在位置i填入数字value是否有效 */ private static boolean isLegal(int i, int value) { /* 判断行是否有效 */ if (!isRowLegal(i, value)) { return false; } /* 判断列是否有效 */ if (!isColLegal(i, value)) { return false; } /* 判断小矩阵是否有效 */ if (!isSubLegal(i, value)) { return false; } return true; } /** * 判断在位置i填入value行规则是否满足 * @param 填入的位置i * @param 填入位置i的数字value * @return 在位置i填入数字value行规则是否有效 */ private static boolean isRowLegal(int i, int value) { int row = i / 9; for (int val = 0; val < 9; val++) { if (value == num[row * 9 + val]) { return false; } } return true; } /** * 判断在位置i填入value列规则是否满足 * @param 填入的位置i * @param 填入位置i的数字value * @return 在位置i填入数字value列规则是否有效 */ private static boolean isColLegal(int i, int value) { int col = i % 9; for (int val = 0; val < 9; val++) { if (value == num[val * 9 + col]) { return false; } } return true; } /** * 判断在位置i填入value小矩阵规则是否满足 * @param 填入的位置i * @param 填入位置i的数字value * @return 在位置i填入数字value小矩阵规则是否有效 */ private static boolean isSubLegal(int i, int value) { int row = i / 9; int col = i % 9; int xOff = row / 3 * 3; int yOff = col / 3 * 3; for (int x = 0; x < 3; x++) { for (int y = 0; y < 3; y++) { if (value == num[(xOff + x) * 9 + yOff + y]) { return false; } } } return true; } public static void main(String[] args) { int[][] grid = new int[9][9]; int[] sudoku = new int[81]; for (int i = 0; i < 81; i++) { sudoku[i] = 0; } sudoku = generate(); for (int i = 0; i < 9; i++) { for (int j = 0; j < 9; j++) { grid[i][j] = sudoku[i * 9 + j]; } } System.out.println("***********************"); for (int i = 0; i < 9; i++) { for (int j = 0; j < 9; j++) { System.out.print(grid[i][j] + " "); if (j % 3 == 2) { System.out.print("| "); } } System.out.println(); if (i % 3 == 2) { System.out.println("***********************"); } } } }
运行结果:
*********************** 2 3 9 | 7 6 8 | 1 5 4 | 8 4 7 | 1 5 3 | 6 9 2 | 1 5 6 | 2 4 9 | 8 7 3 | *********************** 3 7 8 | 5 2 1 | 9 4 6 | 6 1 5 | 4 9 7 | 3 2 8 | 9 2 4 | 3 8 6 | 5 1 7 | *********************** 5 8 1 | 6 7 2 | 4 3 9 | 4 9 2 | 8 3 5 | 7 6 1 | 7 6 3 | 9 1 4 | 2 8 5 | *********************** *********************** 2 4 6 | 7 5 3 | 9 8 1 | 9 1 8 | 6 4 2 | 5 7 3 | 5 7 3 | 9 8 1 | 6 4 2 | *********************** 4 5 7 | 3 2 9 | 8 1 6 | 8 3 9 | 5 1 6 | 7 2 4 | 6 2 1 | 8 7 4 | 3 5 9 | *********************** 3 8 2 | 1 9 7 | 4 6 5 | 7 6 4 | 2 3 5 | 1 9 8 | 1 9 5 | 4 6 8 | 2 3 7 | ***********************
-------------------------------
问道,修仙
-------------------------------