BeginIndexRangeVO.java:
import lombok.Getter;
import lombok.Setter;
import java.io.Serializable;
@Getter
@Setter
public class BeginIndexRangeVO implements Serializable {
private int beginRowNum;
private int beginColNum;
public BeginIndexRangeVO(int beginRowNum, int beginColNum) {
this.beginRowNum = beginRowNum;
this.beginColNum = beginColNum;
}
}
PointVO.java:
import lombok.Getter;
import lombok.Setter;
import java.io.Serializable;
import java.util.List;
@Getter
@Setter
public class PointVO implements Serializable {
/*
2 - 9 6 - - 8 5 4
4 8 6 9 5 2 7 3 1
- - - - 4 - 6 2 9
- - 2 4 - 9 5 8 6
5 - 4 - - - 3 9 2
- 9 - 2 - 5 1 4 7
- - 1 - 2 4 9 - 3
9 4 - 3 1 - 2 - 5
- 2 - - 9 - 4 1 8
*/
private String number;
private String tryNumber;
private int rowNum;
private int colNum;
private int gongNum=0;
private String code;
private List possibleNumbers;
private List possibleNumbers_bak;
public PointVO(String number, int rowNum, int colNum) {
this.number = number;
this.rowNum = rowNum;
this.colNum = colNum;
}
public String getCode(){
if( this.code != null ){
return this.code;
}
this.code = this.rowNum + "-" + this.colNum;
return this.code;
}
public int getGongNum(){
if( this.gongNum > 0 ){
return this.gongNum;
}
if( this.rowNum <= 3 ){
if( this.colNum <= 3 ){
this.gongNum = 1;
}else if( this.colNum <= 6 ){
this.gongNum = 2;
}else {
this.gongNum = 3;
}
}else if( this.rowNum <= 6 ){
if( this.colNum <= 3 ){
this.gongNum = 4;
}else if( this.colNum <= 6 ){
this.gongNum = 5;
}else {
this.gongNum = 6;
}
}else {
if( this.colNum <= 3 ){
this.gongNum = 7;
}else if( this.colNum <= 6 ){
this.gongNum = 8;
}else {
this.gongNum = 9;
}
}
return this.gongNum;
}
}
RowColVO.java:
import lombok.Getter;
import lombok.Setter;
import java.io.Serializable;
@Getter
@Setter
public class RowColVO implements Serializable {
private int rowNum;
private int colNum;
public RowColVO(int rowNum, int colNum) {
this.rowNum = rowNum;
this.colNum = colNum;
}
}
ShuduSolver.java:
import com.alibaba.fastjson.JSONObject;
import java.util.*;
public class ShuduSolver {
private static final Map map_gongNum_beginIndexRange = new HashMap<>();
private static final Map> map_gongNum_numbers = new HashMap<>();
private static final Map> map_rowNum_numbers = new HashMap<>();
private static final Map> map_colNum_numbers = new HashMap<>();
private static Map map_code_point;
private static int count_fill = 0;
static {
map_gongNum_beginIndexRange.put(1, new BeginIndexRangeVO(1, 1));
map_gongNum_beginIndexRange.put(2, new BeginIndexRangeVO(1, 4));
map_gongNum_beginIndexRange.put(3, new BeginIndexRangeVO(1, 7));
map_gongNum_beginIndexRange.put(4, new BeginIndexRangeVO(4, 1));
map_gongNum_beginIndexRange.put(5, new BeginIndexRangeVO(4, 4));
map_gongNum_beginIndexRange.put(6, new BeginIndexRangeVO(4, 7));
map_gongNum_beginIndexRange.put(7, new BeginIndexRangeVO(7, 1));
map_gongNum_beginIndexRange.put(8, new BeginIndexRangeVO(7, 4));
map_gongNum_beginIndexRange.put(9, new BeginIndexRangeVO(7, 7));
}
/**
* 优化:储存一个存储已经正确填写多少个格子的全局变量,没进行操作前先判断下是否等于81,如果已经结束了,就直接返回
* @param args
*/
public static void main(String[] args) {
/* int[][] chupan ={
{ 0,0,0, 0,0,0, 0,0,0 },
{ 0,0,0, 0,0,0, 0,0,0 },
{ 0,0,0, 0,0,0, 0,0,0 },
{ 0,0,0, 0,0,0, 0,0,0 },
{ 0,0,0, 0,0,0, 0,0,0 },
{ 0,0,0, 0,0,0, 0,0,0 },
{ 0,0,0, 0,0,0, 0,0,0 },
{ 0,0,0, 0,0,0, 0,0,0 },
{ 0,0,0, 0,0,0, 0,0,0 }
};*/
/*int[][] chupan = {
{ 2,0,0, 0,8,0, 0,1,0 },
{ 0,4,0, 1,0,0, 3,0,0 },
{ 0,0,0, 0,9,0, 0,0,0 },
{ 0,0,0, 7,0,0, 8,4,0 },
{ 0,9,0, 0,2,1, 0,0,0 },
{ 0,0,6, 0,0,0, 0,0,0 },
{ 0,3,0, 0,0,0, 5,8,0 },
{ 1,0,0, 8,0,0, 0,0,7 },
{ 0,2,7, 0,5,3, 0,0,0 }
};*/
/*int[][] chupan ={
{ 0,9,0, 3,0,0, 0,0,0 },
{ 0,0,0, 0,2,9, 0,0,1 },
{ 6,0,0, 0,0,0, 0,0,7 },
{ 0,7,0, 0,0,0, 0,1,0 },
{ 5,0,0, 0,0,0, 0,6,4 },
{ 0,0,0, 8,0,4, 0,0,9 },
{ 0,1,0, 2,0,0, 6,0,0 },
{ 7,8,0, 0,9,0, 0,0,0 },
{ 0,0,4, 0,5,0, 0,0,3 }
};*/
int[][] chupan ={
{ 1,0,4, 0,0,0, 0,0,0 },
{ 0,0,0, 0,0,5, 0,0,0 },
{ 0,6,0, 0,8,0, 0,7,3 },
{ 0,0,0, 8,0,1, 9,0,0 },
{ 6,5,0, 0,0,0, 0,0,0 },
{ 0,0,0, 3,0,0, 0,0,8 },
{ 0,2,0, 0,3,0, 0,0,7 },
{ 0,0,0, 0,0,7, 1,3,0 },
{ 4,7,0, 0,0,0, 8,9,0 }
};
List points = initChuPan( chupan );
map_code_point = points2CodeAndPointMap(points);
init_map_xxxNum_numbers( );
print( );
makeACommonTest( );
print( );
// 做笔记,记录每个空格子可能得候选数字
calculatePossibleNumbers( );
printPossibles( );
tryFill();
printPossibles();
}
/**
*
* @param rowNum
* @param colNum
* @return true:通过尝试的方式已经为该行该列填了正确的数字了,false:通过尝试的方式暂无法为该行该列填正确的数字
*/
private static boolean tryFill( int rowNum,int colNum){
String code = getCode(rowNum, colNum);
PointVO point = map_code_point.get(code);
if( point.getNumber() != null ){
// 排除非空格子
return false;
}
List possibleNumbers = deepCopyList( point.getPossibleNumbers() );
List numbers_canNotFill = new ArrayList<>();
for( String possibleNumber:possibleNumbers ){
calculatePossibleNumbers();
String errorMsg = tryFill(rowNum, colNum, possibleNumber);
if( errorMsg != null ){
// 发生了错误,所以 possibleNumber 不能填到该格子上
numbers_canNotFill.add( possibleNumber );
}
}
calculatePossibleNumbers();
possibleNumbers.removeAll( numbers_canNotFill );
String pointName = getPointName(rowNum, colNum);
if( possibleNumbers.size() == 1 ){
String number = possibleNumbers.get(0);
fillNumber( rowNum,colNum,number );
System.out.println( "通过尝试的方式确定 " + pointName + " 填写数字 " + JSONObject.toJSONString( numbers_canNotFill ) + " 都会产生矛盾,所以 " + pointName + " 的正确数字是 " + number );
return true;
}else {
// System.out.println( "通过尝试的方式只能确定 " + pointName + " 填写数字 " + JSONObject.toJSONString( numbers_canNotFill ) + " 会产生矛盾,但是还无法确定填写数字 " + JSONObject.toJSONString( possibleNumbers ) + " 是否会产生矛盾,所以暂时无法为 " + pointName + " 填写正确的数字" );
}
return false;
}
private static boolean gameSuccess(){
return count_fill == 81;
}
private static void tryFill(){
for (int rowNum = 1; rowNum <=9 ; rowNum++) {
for (int colNum = 1; colNum <=9 ; colNum++) {
boolean setSuccess = tryFill(rowNum, colNum);
if( setSuccess ){
makeACommonTest();
}
if( gameSuccess() ){
return;
}
}
}
}
/**
*
* @param rowNum
* @param colNum
* @param number
* @return 返回 errorMsg,为 null 表示让该行该列填该数字暂未导致错误( 即不确定该行该列能否填该数字 ),
* 否则表示让该行该列填该数字导致出现了错误( 即该行该列不能填该数字 )
*/
private static String tryFill(int rowNum, int colNum, String number) {
String code = getCode(rowNum, colNum);
PointVO point = map_code_point.get(code);
List possibleNumbers = new ArrayList<>();
possibleNumbers.add( number );
point.setPossibleNumbers( possibleNumbers );
boolean findConflict = scanAndTryToFindConflict();
String pointName = getPointName(rowNum, colNum);
if( findConflict ){
return "为 " + pointName + " 格子设置数字" + number + " 会导致冲突";
}else {
// System.out.println( "为 " + pointName + " 格子设置数字" + number + " 暂时不会导致冲突,所以暂不确定 " + pointName + " 能否设置数字" + number );
return null;
}
}
private static String getPointName(int rowNum, int colNum) {
return rowNum + "行" + colNum + "列";
}
/**
* 初始化三个缓存map( map_rowNum_numbers、map_colNum_numbers、map_gongNum_numbers )
*/
private static void init_map_xxxNum_numbers( ) {
for (int rowNum = 1; rowNum <=9 ; rowNum++) {
for (int colNum = 1; colNum <=9 ; colNum++) {
String code = getCode(rowNum, colNum);
PointVO point = map_code_point.get(code);
String number = point.getNumber();
if( number == null ){
continue;
}
// 行
Set numbers = map_rowNum_numbers.get(rowNum);
if( numbers == null ){
numbers = new HashSet<>();
map_rowNum_numbers.put( rowNum,numbers );
}
numbers.add( number );
// 列
numbers = map_colNum_numbers.get(colNum);
if( numbers == null ){
numbers = new HashSet<>();
map_colNum_numbers.put( colNum,numbers );
}
numbers.add( number );
// 宫
int gongNum = getGongNum(rowNum, colNum);
numbers = map_gongNum_numbers.get(gongNum);
if( numbers == null ){
numbers = new HashSet<>();
map_gongNum_numbers.put( gongNum,numbers );
}
numbers.add( number );
}
}
}
public static boolean scanAndTryToFindConflict( ){
// 寻找 possibleNumber.size = 1的空格子
Set codes = map_code_point.keySet();
boolean needReScan = false;
for( String code:codes ){
PointVO point = map_code_point.get(code);
if( point.getNumber() != null ){
continue;
}
if( point.getPossibleNumbers().size() != 1 ){
continue;
}
// 找到一个 possibleNumber.size = 1 的空格子
String possibleNumber = point.getPossibleNumbers().get(0);
// 将 possibleNumber 从同行、同列、同宫的空格子的 possibleNumbers 集合中删除,删除如果会导致集合的长度变味0,则发生了矛盾,return true
// 删除后如果出现了新的 size=1的空格子,则后面需要重新扫描,已经检查过的 size=1的空格子就不需要重建检查了,防止死循环
// 如果过程中有产生新的 size=1的,则需要递归重新扫描
// 同行
for (int colNum = 1; colNum <=9 ; colNum++) {
String code_sameRow = getCode(point.getRowNum(), colNum);
PointVO point_sameRow = map_code_point.get(code_sameRow);
if( point_sameRow.getCode().equals( code ) ){
// 排除自己
continue;
}
if( point_sameRow.getNumber() != null ){
// 排除非空格子
continue;
}
boolean remove = point_sameRow.getPossibleNumbers().remove(possibleNumber);
if( point_sameRow.getPossibleNumbers().size() == 0 ){
// 发生了矛盾的地方
return true;
}
if( remove && point_sameRow.getPossibleNumbers().size() == 1 ){
// 产生了新的 size =1的格子,需要递归重新扫描
needReScan = true;
}
}
// 同列
for (int rowNum = 1; rowNum <=9 ; rowNum++) {
String code_sameCol = getCode(rowNum, point.getColNum());
PointVO point_sameCol = map_code_point.get(code_sameCol);
if( point_sameCol.getCode().equals( code ) ){
// 排除自己
continue;
}
if( point_sameCol.getNumber() != null ){
// 排除非空格子
continue;
}
boolean remove = point_sameCol.getPossibleNumbers().remove(possibleNumber);
if( point_sameCol.getPossibleNumbers().size() == 0 ){
// 发生了矛盾的地方
return true;
}
if( remove && point_sameCol.getPossibleNumbers().size() == 1 ){
// 产生了新的 size = 1的格子,需要递归重新扫描
needReScan = true;
}
}
// 同宫
int gongNum = getGongNum(point.getRowNum(), point.getColNum());
BeginIndexRangeVO beginIndexRange = map_gongNum_beginIndexRange.get(gongNum);
int beginRowNum = beginIndexRange.getBeginRowNum();
int endRowNum = beginRowNum + 2;
int beginColNum = beginIndexRange.getBeginColNum();
int endColNum = beginColNum + 2;
for (int rowNum = beginRowNum; rowNum <=endRowNum ; rowNum++) {
for (int colNum = beginColNum; colNum <=endColNum ; colNum++) {
String code_sameGong = getCode(rowNum, colNum);
PointVO point_sameGong = map_code_point.get(code_sameGong);
if( point_sameGong.getCode().equals( code ) ){
// 排除自己
continue;
}
if( point_sameGong.getNumber() != null ){
// 排除非空格子
continue;
}
boolean remove = point_sameGong.getPossibleNumbers().remove(possibleNumber);
if( point_sameGong.getPossibleNumbers().size() == 0 ){
// 发生了矛盾的地方
return true;
}
if( remove && point_sameGong.getPossibleNumbers().size() == 1 ){
// 产生了新的 size =1的格子,需要递归重新扫描
needReScan = true;
}
}
}
}
if( needReScan ){
return scanAndTryToFindConflict( );
}else {
return false;
}
}
private static List deepCopyList(List list) {
List list_copy = new ArrayList<>();
for( String item:list ){
list_copy.add( item );
}
return list_copy;
}
private static void calculatePossibleNumbers( ) {
for (int rowNum = 1; rowNum <=9 ; rowNum++) {
for (int colNum = 1; colNum <=9 ; colNum++) {
calculatePossibleNumberForTargetRowAndCol( rowNum,colNum );
}
}
}
private static void calculatePossibleNumberForTargetRowAndCol(int targetRowNum, int targetColNum) {
String code = getCode(targetRowNum, targetColNum);
PointVO point = map_code_point.get(code);
if( point.getNumber() != null ){
return;
}
Set numbers_canNotFill = new HashSet<>();
numbers_canNotFill.addAll( map_rowNum_numbers.get(targetRowNum) );
numbers_canNotFill.addAll( map_colNum_numbers.get(targetColNum) );
int gongNum = getGongNum(targetRowNum, targetColNum);
numbers_canNotFill.addAll( map_gongNum_numbers.get(gongNum) );
List numbers_possible = new ArrayList<>();
for (int i = 1; i <=9 ; i++) {
numbers_possible.add( String.valueOf( i ) );
}
numbers_possible.removeAll( numbers_canNotFill );
point.setPossibleNumbers( numbers_possible );
point.setPossibleNumbers_bak( deepCopyList( numbers_possible ) );
point.setTryNumber( null );
}
private static void makeACommonTest( ) {
if( gameSuccess() ){
// 已经结束了
return;
}
int testTime_max = 10;
int testTime = 0;
while ( true ){
testTime++;
if( testTime > testTime_max ){
break;
}
// method1( map_code_point );
// method2( map_code_point );
method_hangBingChuxxx( );
method_lieBingChuxxx( );
method_gongBingChu( );
}
}
private static void method_lieBingChu( ) {
// 遍历每一列
for (int colNum = 1; colNum <=9 ; colNum++) {
// 对于当前列,遍历该列每一行的空格子:
for (int rowNum = 1; rowNum <=9 ; rowNum++) {
String code = getCode(rowNum, colNum);
PointVO point = map_code_point.get(code);
if( point.getNumber() != null ){
continue;
}
// 对于该列当前行的空格子:
// 计算该格子所在的行、列、宫已经填写的数字的并集去重,如果该集合的长度为8,表示已经确定了该格子填什么数字了,即为缺失的数字
Set numbers_canNotFill = new HashSet<>();
numbers_canNotFill.addAll( map_rowNum_numbers.get( rowNum ) );
numbers_canNotFill.addAll( map_colNum_numbers.get( colNum ) );
int gongNum = getGongNum(rowNum, colNum);
numbers_canNotFill.addAll( map_gongNum_numbers.get( gongNum ) );
String number_shouldFill = getSingleMissingNumber( numbers_canNotFill );
if( number_shouldFill != null ){
fillNumber( point,number_shouldFill );
// System.out.println( "列摒除:" + colNum + "列第" + rowNum + "行的数字不能填写" + JSONObject.toJSONString( numbers_canNotFill ) + ",只能填写" + number_shouldFill );
}
}
}
}
private static String getShouldFillNumber(int rowNum, int colNum) {
Set numbers_canNotFill = new HashSet<>();
numbers_canNotFill.addAll( map_rowNum_numbers.get( rowNum ) );
numbers_canNotFill.addAll( map_colNum_numbers.get( colNum ) );
numbers_canNotFill.addAll( map_gongNum_numbers.get( getGongNum(rowNum, colNum) ) );
return getSingleMissingNumber( numbers_canNotFill );
}
/*
- - - - 2 - - 8 5
6 5 2 - 7 - - - -
3 - - - 5 - - 1 2
7 2 3 9 - 4 - 5 -
5 4 - 2 - - - 3 -
- 6 9 - - 5 - 2 -
- - 5 - - - 2 6 -
2 - - 5 8 6 1 - 3
- 3 6 - - 2 5 7 -
*/
private static void method_gongBingChu( ) {
if( gameSuccess() ){
// 已经结束了
return;
}
// 遍历每一个宫:
for (int gongNum = 1; gongNum <=9 ; gongNum++) {
// 对于当前宫,遍历每一个空格子:
BeginIndexRangeVO beginIndexRange = map_gongNum_beginIndexRange.get(gongNum);
// 计算该宫全部的数字
int beginRowNum = beginIndexRange.getBeginRowNum();
int endRowNum = beginRowNum + 2;
int beginColNum = beginIndexRange.getBeginColNum();
int endColNum = beginColNum + 2;
for (int rowNum = beginRowNum; rowNum <=endRowNum ; rowNum++) {
for (int colNum = beginColNum; colNum <=endColNum ; colNum++) {
// 对于当前的空格子,计算该格子所处的行、列、宫的已填数字的并集并去重,从集合[1,2,3,4,5,6,7,8,9]
// 中删除这个集合,产生的新集合就是可填数字的集合,如果可填数字的集合的长度为1,表示已经确定了该格子需要填写的数字了
String code = getCode(rowNum, colNum);
PointVO point = map_code_point.get(code);
if( point.getNumber() != null ){
continue;
}
String number_shouldFill = getShouldFillNumber( rowNum,colNum );
if( number_shouldFill != null ){
// 已经确定该格子该填什么数字了
fillNumber( point,number_shouldFill );
// System.out.println( "宫摒除:" + gongNum + "宫的格子( " + rowNum + "行" + colNum + "列 )不能填写 xxx,只能填写" + number_shouldFill );
}
}
}
}
}
private static String getSingleMissingNumber(Set numbers) {
if( numbers == null || numbers.size() != 8 ){
return null;
}
int sum = 0;
for( String number:numbers ){
sum += Integer.valueOf( number );
}
// ps:45 = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9
return String.valueOf( 45 - sum );
}
private static boolean fillNumber( PointVO point,String number ) {
if( point == null || number == null ){
return false;
}
point.setNumber( number );
count_fill++;
// System.out.println( "count_fille = " + count_fill + ":" + point.getRowNum() + "行" + point.getColNum() + "列 填写数字 " + number );
// 更新三个 map_xxxNum_numbers
addNumber2_map_xxxNum_numbers( point.getRowNum(),point.getColNum(),number );
if( gameSuccess() ){
printPossibles();
System.exit( 0 );
}
return true;
}
/**
* 更新三个 map_xxxNum_numbers
* @param rowNum
* @param colNum
* @param number
*/
private static void addNumber2_map_xxxNum_numbers(int rowNum, int colNum, String number) {
int gongNum = getGongNum(rowNum, colNum);
map_gongNum_numbers.get( gongNum ).add( number );
map_rowNum_numbers.get( rowNum ).add( number );
map_colNum_numbers.get( colNum ).add( number );
}
private static boolean fillNumber(int rowNum, int colNum, String number) {
String code = getCode(rowNum, colNum);
PointVO point = map_code_point.get(code);
if( point == null ){
return false;
}
return fillNumber( point,number );
}
/*
- - - - 2 - - 8 5
6 5 2 - 7 - - - -
3 - - - 5 - - 1 2
7 2 3 9 - 4 - 5 -
5 4 - 2 - - - 3 -
- 6 9 - - 5 - 2 -
- - 5 - - - 2 6 -
2 - - 5 8 6 1 - 3
- 3 6 - - 2 5 7 -
*/
private static void method2( ) {
if( gameSuccess() ){
// 已经结束了
return;
}
// 遍历每一个空格子
// 找到该格子所在的宫、行、列中全部数字的并集再去重后得到一个新的集合,如果新集合的大小为8,则缺少的那个数字即是需要填写到该格子中的数字
for (int rowNum = 1; rowNum <=9 ; rowNum++) {
for (int colNum = 1; colNum <=9 ; colNum++) {
String code = getCode(rowNum, colNum);
PointVO point = map_code_point.get(code);
if( point.getNumber() != null ){
continue;
}
String number_confirm = getShouldFillNumber( rowNum,colNum );
if( number_confirm != null ){
System.out.println( rowNum + "行" + colNum + "列的格子不能填写 xxx,所以只能填写" + number_confirm );
fillNumber( point,number_confirm );
}
}
}
}
/*
- 2 - 4 - 9 1 - -
4 - 6 - 5 - - 8 9
- 7 - - 8 3 - 2 4
7 1 - 5 3 - - - -
- - - - 9 - 2 - -
- - - - 4 - - - 7
- 6 - - - - - - -
- - 7 3 - - 8 - 1
3 4 - - - 5 - 6 -
*/
private static int getGongNum(int rowNum, int colNum) {
if( rowNum <= 3 ){
if( colNum <= 3 ){
return 1;
}else if( colNum <= 6 ){
return 2;
}else {
return 3;
}
}else if( rowNum <= 6 ){
if( colNum <= 3 ){
return 4;
}else if( colNum <= 6 ){
return 5;
}else {
return 6;
}
}else {
if( colNum <= 3 ){
return 7;
}else if( colNum <= 6 ){
return 8;
}else {
return 9;
}
}
}
private static void method1( ) {
if( gameSuccess() ){
// 已经结束了
return;
}
for (int i = 1; i <=9 ; i++) {
String targetNumber = String.valueOf( i );
for (int gongNum = 1; gongNum <=9 ; gongNum++) {
setTargetNumberForTargetGong( gongNum,targetNumber );
}
}
}
/*
- - - - 2 - - 8 5
6 5 2 - 7 - - - -
3 - - - 5 - - 1 2
7 2 3 9 - 4 - 5 -
5 4 - 2 - - - 3 -
- 6 9 - - 5 - 2 -
- - 5 - - - 2 6 -
2 - - 5 8 6 1 - -
- 3 6 - - 2 5 7 -
*/
private static void method_hangBingChuxxx( ) {
if( gameSuccess() ){
// 已经结束了
return;
}
// 遍历每一行,对于某一行,看该行缺少什么数字,比如缺1,则看该行空缺的格子,哪些不能填1,如果排除后只剩下一个格子了,则该格子必须填1,
// 不能填1的依据是该格子所在的宫或者所在的列或者所在的行已经存在1了
for (int rowNum = 1; rowNum <=9 ; rowNum++) {
for( int number = 1;number<=9;number++ ){
// 检查该行是否存在当前数字
String number_str = String.valueOf( number );
if( map_rowNum_numbers.get( rowNum ).contains( number_str ) ){
continue;
}
// 该行不存在该数字
// 检测该行哪些格子不能填写该数字,也即确定该行第几列可以填写该数字
int colNum = getColNumThatCanFillTargetNumberInTargetRow( rowNum,number_str );
if( colNum > 0 & colNum < 10 ){
fillNumber( rowNum,colNum,number_str );
}
}
}
}
/*
- - - - 2 - - 8 5
6 5 2 - 7 - - - -
3 - - - 5 - - 1 2
7 2 3 9 - 4 - 5 -
5 4 - 2 - - - 3 -
- 6 9 - - 5 - 2 -
- - 5 - - - 2 6 -
2 - - 5 8 6 1 - -
- 3 6 - - 2 5 7 -
*/
private static int getColNumThatCanFillTargetNumberInTargetRow(int targetRowNum, String targetNumber) {
List colNumList_blank = new ArrayList();
List colNumList_canNotFill = new ArrayList();
for (int colNum = 1; colNum <=9 ; colNum++) {
String code = getCode(targetRowNum, colNum);
PointVO point = map_code_point.get(code);
if( point.getNumber() != null ){
continue;
}
colNumList_blank.add( colNum );
// 检测当前格子所在的宫是否存在该数字
int gongNum = getGongNum(targetRowNum, colNum);
if( map_gongNum_numbers.get( gongNum ).contains( targetNumber ) ){
colNumList_canNotFill.add( colNum );
continue;
}
// 检测当前格子所在的列是否存在该数字
if( map_colNum_numbers.get( colNum ).contains( targetNumber ) ){
colNumList_canNotFill.add( colNum );
}
}
colNumList_blank.removeAll(colNumList_canNotFill);
if( colNumList_blank.size() == 1 ){
Integer colNum_canFill = colNumList_blank.get(0);
// System.out.println( "行摒除:第" + targetRowNum + "行的第" + JSONObject.toJSONString( colNumList_canNotFill ) + "列不能填写数字" + targetNumber + "了,只能是第" + colNum_canFill + "列可以填写数字" + targetNumber + "了" );
return colNum_canFill;
}
return -1;
}
/*
- - - - 2 - - 8 5
6 5 2 - 7 - - - -
3 - - - 5 - - 1 2
7 2 3 9 - 4 - 5 -
5 4 - 2 - - - 3 -
- 6 9 - - 5 - 2 -
- - 5 - - - 2 6 -
2 - - 5 8 6 1 - -
- 3 6 - - 2 5 7 -
*/
private static void method_lieBingChuxxx( ) {
if( gameSuccess() ){
// 已经结束了
return;
}
for (int colNum = 1; colNum <=9 ; colNum++) {
// 遍历该列的每一个空的格子
for( int number=1;number<=9;number++ ){
// 检测该列是否存在该数字
String number_str = String.valueOf( number );
if( map_colNum_numbers.get( colNum ).contains( number_str ) ){
continue;
}
// 该列不存在该数字
int rowNum_canFill = getRowNumThatCanFillTargetNumberInTargetCol( colNum,number_str );
if( rowNum_canFill > 0 && rowNum_canFill < 10 ){
fillNumber( rowNum_canFill,colNum,number_str );
}
}
}
}
/*
- - - - 2 - - 8 5
6 5 2 - 7 - - - -
3 - - - 5 - - 1 2
7 2 3 9 - 4 - 5 -
5 4 - 2 - - - 3 -
- 6 9 - - 5 - 2 -
- - 5 - - - 2 6 -
2 - - 5 8 6 1 - -
- 3 6 - - 2 5 7 -
*/
/**
* 在指定列中找到可以填写指定数字的行号
* @param targetColNum
* @param targetNumber
* @return 1~9表示找到了可以填写该数字的行号,其他表示未找到可以填写该数字的行号
*/
private static int getRowNumThatCanFillTargetNumberInTargetCol(int targetColNum, String targetNumber) {
List rowNumList_blank = new ArrayList<>();
List rowNumList_canNotFill = new ArrayList<>();
for (int rowNum = 1; rowNum <=9 ; rowNum++) {
String code = getCode(rowNum, targetColNum);
PointVO point = map_code_point.get(code);
if( point.getNumber() != null ){
continue;
}
rowNumList_blank.add( rowNum );
// 检测当前格子所在的行和宫是否已经存在该数字
if( map_rowNum_numbers.get( rowNum ).contains( targetNumber ) ){
rowNumList_canNotFill.add( rowNum );
continue;
}
int gongNum = getGongNum(rowNum, targetColNum);
if( map_gongNum_numbers.get( gongNum ).contains( targetNumber ) ){
rowNumList_canNotFill.add( rowNum );
}
}
rowNumList_blank.removeAll(rowNumList_canNotFill);
if( rowNumList_blank.size() == 1 ){
Integer rowNum_canFill = rowNumList_blank.get(0);
// System.out.println( "列摒除:" + targetColNum + "列的第" + JSONObject.toJSONString( rowNumList_canNotFill ) + "行都不能填写数字" + targetNumber + ",所以数字" + targetNumber + "只能填写在第" + rowNum_canFill + "行" );
// 列摒除:9列的第[]行都不能填写数字5,所以数字5只能填写在第3行
return rowNum_canFill;
}
return -1;
}
/**
* 为指定的宫设置指定的数字
* @param targetGongNum
* @param targetNumber
* @return true 表示找到了合适的位置设置数字了,fasle无法确定哪里需要填写该数字
*/
private static boolean setTargetNumberForTargetGong(int targetGongNum, String targetNumber) {
if( map_gongNum_numbers.get( targetGongNum ).contains( targetNumber ) ){
// 该宫内已经存在该数字
return false;
}
// 未填写数字的格子的个数
int pointCount_blank = 0;
// 不能填写目标数字的格子的个数
int pointCount_canNotBeTargetNumber = 0;
// 逐个检查该宫的9个位置中还没有填写数字的位置,
// 如果该位置所在的行或列已经存在该数字了,则该位置不能填该数字,假设空位置有n个,检测到不能填该数字的空位置有n-1个,则剩下的一个位置必须填该数字,否则无法判断哪个空位置需要填该数字
BeginIndexRangeVO beginIndexRange = map_gongNum_beginIndexRange.get(targetGongNum);
int beginRowNum = beginIndexRange.getBeginRowNum();
int endRowNum = beginRowNum + 2;
int beginColNum = beginIndexRange.getBeginColNum();
int endColNum = beginColNum + 2;
int rowNum_maybeFind = 0;
int colNum_maybeFind = 0;
for (int rowNum = beginRowNum; rowNum <=endRowNum ; rowNum++) {
for (int colNum = beginColNum; colNum <=endColNum ; colNum++) {
String code = getCode(rowNum, colNum);
PointVO point = map_code_point.get(code);
if( point.getNumber() != null ){
// 该位置已经有数字了
continue;
}
pointCount_blank++;
if( map_rowNum_numbers.get( rowNum ).contains( targetNumber ) ){
// 该位置所在的行已经存在 targetNumber 了
pointCount_canNotBeTargetNumber++;
continue;
}
if( map_colNum_numbers.get( colNum ).contains( targetNumber ) ){
// 该位置所在的列已经存在targetNumber 了
pointCount_canNotBeTargetNumber++;
continue;
}
// 该位置为空,并且所在的行和列都没有出现过 targetNumber,所以该位置可能需要填写 targetNumber
rowNum_maybeFind = rowNum;
colNum_maybeFind = colNum;
}
}
if( ( pointCount_blank - pointCount_canNotBeTargetNumber ) == 1 ){
// 此时找到了填写 targetNumber 的地方了
return fillNumber( rowNum_maybeFind,colNum_maybeFind,targetNumber );
}else {
// 该宫无法确定当前数字需要填写到哪个位置
return false;
}
}
private static String getCode(int rowNum, int colNum) {
return rowNum + "-" + colNum;
}
public static Map points2CodeAndPointMap( List points ){
Map map_code_point = new HashMap<>();
for( PointVO point:points ){
map_code_point.put( point.getCode(),point );
}
return map_code_point;
}
public static void print( ){
System.out.println();
System.out.println( "--------------------------------------------------------------------------------------" );
for (int rowNum = 1; rowNum <=9 ; rowNum++) {
for (int colNum = 1; colNum <=9 ; colNum++) {
String code = getCode( rowNum,colNum );
PointVO point = map_code_point.get(code);
if( point.getNumber() == null ){
System.out.print( "-" + " ");
}else {
System.out.print( point.getNumber() + " ");
}
if( colNum % 3 == 0 ){
System.out.print(" ");
}
}
System.out.println();
if( rowNum % 3 ==0 ){
System.out.println();
}
}
}
public static void printPossibles( ){
System.out.println();
System.out.println( "--------------------------------------------------------------------------------------" );
Map map_colNum_maxLen = new HashMap<>();
for (int colNum = 1; colNum <=9; colNum++) {
int maxLen = 1;
for (int rowNum = 1; rowNum <=9 ; rowNum++) {
String code = getCode(rowNum, colNum);
PointVO point = map_code_point.get(code);
if( point.getNumber() != null ){
continue;
}
List possibleNumbers = point.getPossibleNumbers();
if( possibleNumbers.size() > maxLen ){
maxLen = possibleNumbers.size();
}
}
maxLen+=2;
map_colNum_maxLen.put( colNum,maxLen );
}
for (int rowNum = 1; rowNum <=9 ; rowNum++) {
for (int colNum = 1; colNum <=9 ; colNum++) {
String code = getCode( rowNum,colNum );
PointVO point = map_code_point.get(code);
String value = null;
if( point.getNumber() == null ){
value = "[" + getStickTogetherFormat( point.getPossibleNumbers() ) + "]";
}else {
value = point.getNumber();
}
int maxLen = map_colNum_maxLen.get( colNum );
if( value.length() < maxLen ){
value += getBlank( maxLen - value.length() );
}
System.out.print( value + " ");
if( colNum % 3 == 0 ){
System.out.print(" ");
}
}
System.out.println();
if( rowNum % 3 ==0 ){
System.out.println();
System.out.println();
}
}
}
private static String getBlank(int count) {
String blank = "";
for (int i = 0; i < count; i++) {
blank += " ";
}
return blank;
}
private static String getStickTogetherFormat(List list) {
if( list == null || list.size() == 0 ){
return "";
}
String str = "";
for( String item:list ){
str += item;
}
return str;
}
public static List initChuPan(int[][] chupan) {
List points = new ArrayList<>();
for (int rowNum = 0; rowNum < 9; rowNum++) {
int[] row = chupan[rowNum];
for (int colNum = 0; colNum <9 ; colNum++) {
int number = row[colNum];
String number_str = null;
if( number > 0 && number < 10 ){
count_fill++;
number_str = String.valueOf( number );
}
points.add( new PointVO(number_str,rowNum+1,colNum+1 ) );
}
}
return points;
}
}
测试输出样例:
--------------------------------------------------------------------------------------
1 - 4 - - - - - -
- - - - - 5 - - -
- 6 - - 8 - - 7 3
- - - 8 - 1 9 - -
6 5 - - - - - - -
- - - 3 - - - - 8
- 2 - - 3 - - - 7
- - - - - 7 1 3 -
4 7 - - - - 8 9 -
--------------------------------------------------------------------------------------
1 8 4 - - 3 5 - 9
7 3 - - - 5 - 8 1
- 6 5 1 8 - - 7 3
3 4 7 8 - 1 9 - -
6 5 8 - - - 3 1 -
- 1 - 3 - - 7 - 8
5 2 1 9 3 8 - - 7
8 9 6 - - 7 1 3 -
4 7 3 - 1 - 8 9 -
--------------------------------------------------------------------------------------
1 8 4 [267] [267] 3 5 [26] 9
7 3 [29] [246] [2469] 5 [246] 8 1
[29] 6 5 1 8 [249] [24] 7 3
3 4 7 8 [256] 1 9 [256] [256]
6 5 8 [247] [2479] [249] 3 1 [24]
[29] 1 [29] 3 [24569] [2469] 7 [2456] 8
5 2 1 9 3 8 [46] [46] 7
8 9 6 [245] [245] 7 1 3 [245]
4 7 3 [256] 1 [26] 8 9 [256]
通过尝试的方式确定 1行8列 填写数字 ["2"] 都会产生矛盾,所以 1行8列 的正确数字是 6
通过尝试的方式确定 2行3列 填写数字 ["2"] 都会产生矛盾,所以 2行3列 的正确数字是 9
--------------------------------------------------------------------------------------
1 8 4 2 7 3 5 6 9
7 3 9 6 4 5 2 8 1
2 6 5 1 8 9 4 7 3
3 4 7 8 5 1 9 2 6
6 5 8 7 9 2 3 1 4
9 1 2 3 6 4 7 5 8
5 2 1 9 3 8 6 4 7
8 9 6 4 2 7 1 3 5
4 7 3 5 1 6 8 9 2