《快速像素(Quixel)》遊戲“破解”攻略

前不久,一位朋友向我推薦了一款名爲《Quixel》(中文名《快速像素》)的遊戲,遊戲界面如圖:

遊戲規則如下:

1.用手指點按方格,遊戲認爲該方格被塗黑;

2.每行/列中的數字,爲該行/列中連續塗黑的格子的數量(比如出現3,則爲該行/列中有3個連續塗黑的格子;如果出現22,則爲先有兩個連續的格子,然後中間空出至少1個格子後,再有兩個連續塗黑的格子,3個數字的以此類推);

3.當塗黑的格子滿足所有行列的數字要求時,則認爲是勝利,遊戲則顯示出塗黑格子所組成的圖案,下圖爲上一張圖過關的結果:

當然,這種5*5的格子是很簡單的,可是我的朋友給我分享這個遊戲的時候,已經玩到400多關了,是一個10*10的大棋盤,難度更高。我對這種解迷類遊戲還是比較感興趣的,於是研究了一會,得出一個結論:如果我能把所有的格子從全白到全部塗黑都過一遍,那一定有一個結果的。然後便突然想到:咦?這不就是枚舉嘛!所以我就想,是不是我可以寫一個程序,遍歷出所有結果,然後用電腦去計算出一個正確的結果呢?

說幹就幹,那首先要做的,就是提煉遊戲的規則,然後根據規則,去進行編程,於是規則提煉如下:

1.格子有兩種狀態,塗黑與不塗黑,這裏我對應爲數字1和0;

2.被塗黑的格子要同時滿足行列上的要求。

規則提煉好以後,接下來就是做了,當然最簡單也是最笨的方法,就是從格子全部爲0循環到全部爲1,然後嘛,總有一款適合你~言歸正傳,上面那種方法消耗的時間過大,所以不予考慮,於是經過一番思考,我想出了一個比較容易實現的算法:

1.將棋盤視爲二維數組,數組中每個元素只有0和1兩種狀態;

2.先計算出每一行中符合要求的一維數組,分別放入一個小容器中,然後將小容器放到一個大容器中,這樣,小容器存放的是每行符合要求的一維數組,而大容器,則可以理解爲是整個棋盤,放的是所有行的可能;

3.依次從小容器中取出一條記錄,然後進行排列組合,按列取,去校驗是否符合列上的數字,如果每一列都驗證通過,則停止排列組合,輸出該二維數組。

寫到這裏,相信很多人已經有思路了,只要去按照上面的規則去完成就好,那麼這裏我在做的時候還遇到一個小問題,那就是,如果我的棋盤只是5*5,或僅僅是10*10,我只要有多少行,就去寫多少個for循環的嵌套就好,但實際情況是,隨着關數的增加,棋盤也會越來越大,所以在如何動態進行for循環的嵌套,我給出的方案是——遞歸。

好了,說到這裏,基本上邏輯的東西就完成了,下面我把代碼貼一下,大家感興趣的可以看一下,如果哪位朋友有更優化的解決方案,也歡迎提出,我這裏就算是拋磚引玉啦

public class mainLogic {
    //橫行數
    private static int[] xArray = {2, 3, 3, 4, 6, 5, 5, 23, 6, 4};
    //豎列數
    private static int[] yArray = {2, 4, 22, 6, 6, 5, 3, 5, 5, 3};

    private static int CURRENT_COUNT = 0;
    private static boolean IS_FIND_RESULT = false;

    private static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
    public static void main(String[] args) {
        int max_Length = 10;  //行數
        Date startTime = new Date();
        System.out.println("開始計算滿足要求的橫列數組集合");
        List<List<String>> allXList = getXLists(max_Length);
        System.out.println("計算完成------" + dateFormat.format(new Date()));
        List<String[][]> resultList = new ArrayList<>();
        String[][] resultArray = new String[max_Length][max_Length];
        int sumNum = getSumNum(allXList);
        System.out.println("開始驗證數據,共計驗證" + sumNum + "種組合------" + dateFormat.format(new Date()));
        caluResult(resultArray, allXList, 0, max_Length);
        Date endTime = new Date();
        System.out.println("共有" +sumNum+"種組合");
        displayStringArray(resultArray);
        System.out.println("開始時間:" + dateFormat.format(startTime));
        System.out.println("結束時間:" + dateFormat.format(endTime));
    }

    public static int getSumNum(List<List<String>> allList){
        int sum = 1;
        for (int i=0; i<allList.size(); i++){
            int count = allList.get(i).size();
            sum *= count;
        }
        return sum;
    }

    /**
     * 輸出二維數組
     * @param binaryString
     */
    public static void displayStringArray(String[][] binaryString){
        int columns = binaryString[0].length;
        for(int j=0; j<columns; j++){
            for (int k=0; k<columns; k++){
                System.out.print(binaryString[j][k]);
            }
            System.out.println();
        }
    }

    /**
     * 獲取所有橫列中符合要求的橫列數組集合
     * @param num 行數/列數/每行二進制字符串的長度
     * @return 返回結果爲所有符合橫列要求的集合的集合,有多少行,返回的集合中就有多少個元素
     */
    public static List<List<String>> getXLists(int num){
        double pow = Math.pow(2.0, Double.valueOf(num));
        int decimalNum = new Double(pow).intValue();
        List<List<String>> allList = new ArrayList<List<String>>();
        for(int i=0; i<num; i++){
            //用於存放符合第i個標準數的集合
            List<String> xList = new ArrayList<String>();
            //如果該行標準數爲0,則記錄爲全部爲0的字符串
            if (xArray[i] == 0){
                String zeroString = "";
                for (int j=0; j<num; j++){
                    zeroString += "0";
                }
                xList.add(zeroString);
            }
            else {
                for (int j = 0; j < decimalNum; j++) {
                    String binaryString = padLeft(j, num);
                    if (check(binaryString, xArray[i])) {
                        xList.add(binaryString);
                    }
                }
            }
            allList.add(xList);
        }
        return allList;
    }


    public static void caluResult(String[][] resultArray, List<List<String>> allList, int currentLevel, int sumLevel){
        if (currentLevel == sumLevel){
            CURRENT_COUNT++;
            System.out.println("當前完成第" + CURRENT_COUNT + "種排列組合------" + dateFormat.format(new Date()));
            boolean isPass = stepCheck(resultArray, sumLevel, yArray);
            if (isPass){
                System.out.println("符合要求");
                IS_FIND_RESULT = true;
            }
            else {
                System.out.println("不符合要求");
            }
            return;
        }
        for (int i=0; i<allList.get(currentLevel).size(); i++){
            if (IS_FIND_RESULT){
                break;
            }
            //因爲二維數組爲n*n數組,所以可以用層數=列數,可以直接使用,n*m數組則不可直接使用
            for (int j=0; j<sumLevel; j++){
                resultArray[currentLevel][j] = allList.get(currentLevel).get(i).substring(j, j+1);
            }
            int newLevel = currentLevel + 1;
            caluResult(resultArray, allList, newLevel, sumLevel);
        }
    }

    /**
     * 十進制數轉爲二進制數並補充0
     * @param decimalNum 輸入的十進制數
     * @param stringLength 輸出的字符串長度
     * @return
     */
    public static String padLeft(int decimalNum, int stringLength){
        char[] chars = new char[stringLength];
        for (int i=0; i<stringLength; i++){
            chars[stringLength - 1 - i] = (char)((decimalNum >> i & 1) + '0');
        }
        return new String(chars);
    }

    /**
     * 該方法用於校驗數組是否符合規定
     * 比如標準數爲32,則規則爲,有三個連續的1,在若干個位置後,還有兩個連續的1,最後是若干個0
     * 比如 0001110110 符合規則, 01110110100不符合規則
     * @param binaryString 01字符串
     * @param standrdNum 標準數
     * @return
     */
    public static boolean check(String binaryString, int standrdNum){
        List<Integer> standrdList = new ArrayList<Integer>();
        String standrdString = String.valueOf(standrdNum);
        for (int i=0; i<standrdString.length(); i++){
            standrdList.add(Integer.valueOf(standrdString.substring(i, i+1)));
        }
        //用於記錄遍歷01字符串的位置
        int currentIndex = 0;
        //取出第i位,判斷當前需要有多少個連續的1
        boolean isEqualsToStand = true;
        for (int i=0; i<standrdString.length(); i++){
            //記錄是否爲第一次檢查到01數組裏的"1"
            boolean isFirstTime = true;
            //記錄01數組中當前連續的1的數量
            int countOfOne = 0;
            for (int j=currentIndex; j<binaryString.length(); j++){
                //如果當前沒有遍歷到"1",則跳到下一位
                String tempString = binaryString.substring(j, j+1);
                if (isFirstTime && binaryString.substring(j, j+1).equals("0")){
                    currentIndex++;
                    continue;
                }
                //如果已經遍歷到"1",但當前位置爲"0",則結束
                if (!isFirstTime && binaryString.substring(j, j+1).equals("0")){
                    break;
                }
                //遍歷到"1",計數器+1,並設定爲已經遍歷到1
                else{
                    currentIndex++;
                    isFirstTime = false;
                    countOfOne++;
                }
            }
            //如果符合當前規定的連續的"1"的數量,則繼續
            if (countOfOne == standrdList.get(i)){
                continue;
            }
            //如果不符合,則退出
            else{
                isEqualsToStand = false;
                break;
            }
        }
        //判斷剩下的字符串是否全部爲0
        if (currentIndex < binaryString.length()){
            for (int i=currentIndex; i<binaryString.length(); i++){
                if (binaryString.substring(i, i+1).equals("1")){
                    isEqualsToStand = false;
                    break;
                }
            }
        }
        return isEqualsToStand;
    }

    /**
     * 對生成對每一個二維數組進行校驗
     * @param unCheckStringArray
     * @param columns
     * @param yArray
     * @return
     */
    public static boolean stepCheck(String[][] unCheckStringArray, int columns, int[] yArray){
        boolean isPass = true;
        for (int i=0; i<columns; i++){
            String binaryString = "";
            //對待檢查待二維數組進行按列取操作
            for(int j=0; j< columns; j++){
                binaryString += unCheckStringArray[j][i];
            }
            boolean columnResult = check(binaryString, yArray[i]);
            if (!columnResult){
                isPass = false;
                break;
            }
        }
        return isPass;
    }
}

貼一下輸出結果:

其實從結果不難看出,對於10*10的數組,計算量還是很大的,所以目前來說,只能算是一個半成品,優化的空間一定存在,如果有哪位朋友有更好的想法,歡迎提出~

發佈了3 篇原創文章 · 獲贊 2 · 訪問量 4423
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章