java实现解数独的算法

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<String> possibleNumbers;
    private List<String> 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<Integer, BeginIndexRangeVO> map_gongNum_beginIndexRange = new HashMap<>();

    private static final Map<Integer,Set<String>> map_gongNum_numbers = new HashMap<>();
    private static final Map<Integer,Set<String>> map_rowNum_numbers = new HashMap<>();
    private static final Map<Integer,Set<String>> map_colNum_numbers = new HashMap<>();

    private static Map<String, PointVO> 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<PointVO> 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<String> possibleNumbers = deepCopyList( point.getPossibleNumbers() );
        List<String> 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<String> 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<String> 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<String> 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<String> deepCopyList(List<String> list) {
        List<String> 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<String> 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<String> 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<String> 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<String> 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<String> 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<Integer> colNumList_blank = new ArrayList<Integer>();
        List<Integer> colNumList_canNotFill = new ArrayList<Integer>();
        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<Integer> rowNumList_blank = new ArrayList<>();
        List<Integer> 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<String, PointVO> points2CodeAndPointMap( List<PointVO> points ){
        Map<String, PointVO> 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<Integer, Integer> 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<String> 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<String> list) {
        if( list == null || list.size() == 0 ){
            return "";
        }
        String str = "";
        for( String item:list ){
            str += item;
        }
        return str;
    }

    public static List<PointVO> initChuPan(int[][] chupan) {
        List<PointVO> 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        

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值