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