基于回溯算法破解任意关卡的一笔画玩微信小游戏

        去年一天,同桌让我帮他玩一个叫一笔画完的微信小游戏,我在那一关试了很久都没通过,突然有一个想法,我可以编码穷举出所有情况来尝试破解,于是去百度了一笔画完的相关问题,发现没有类似答案,于是便开始了两天的构思探索之旅。。。。。。

        首先,游戏界面由若干行和若干列的方格组成,其中有部分方格是空白的,有一个起始位置,行走的路线只能连续,不能重复,必须一笔连续不断的走玩所有方格才算成功,试探方法类似如度为三的完整树结构,以深度优先遍历树,寻找解答方案。

于是想到可以建模抽象解决这个问题:

1,游戏中的方格整体对应一个二维数组,单个方格对应数组中的一个元素,数组的行数和列数与二维数组的行数和列数对应

2,每个方格分别有true和false两种属性,初始化可以走的方格为true,不能走的和已经走过的为false

3,当所有初始化为true方格被走过变为false时,破解结束

下面看关键的方法片段:

    int nb = 0;
    ArrayList<Integer> nbList = new ArrayList<Integer>();
    ArrayList<Integer> xList = new ArrayList<Integer>();
    ArrayList<Integer> yList = new ArrayList<Integer>();
    //初始化步数,初始化记录每一步上下左右行走方向的数组,和每一步对应的 x,y的坐标点

    void isCan(boolean[][] isBooleanlist, int lenth, int x, int y, int row, int column) {
        //判断旁边的四个点是否符合要求  参数为空白点个数,每一步的横纵坐标,以及边界大小
        if (x + 1 < row && isBooleanlist[x + 1][y] == true) {
            nbList.add(1);//1对应向右试探
            nb++;
            isBooleanlist[x + 1][y] = false;//试探过的方格就不能再试了
            xList.add(x + 2);
            yList.add(y + 1);//记录这一步的位置
            System.out.println("初始位置是: " + (x + 1) + "," + (y + 1));
            System.out.println("第:" + nb + "步的位置是: " + (x + 2) + "," + (y + 1));
            System.out.println("                                  ");

            isCan(isBooleanlist, lenth, x + 1, y, row, column);
            //回调方法本身  进行下一步试探
        }

其中回调方法本身和记录试探的方向和坐标是该算法的核心精髓

当回调的方法判断为true,即还可以继续试探,直到试探到判断为false时,就需要返回上一步接着遍历其他情况,如此循环,直到解决问题,下面来看返回上一级试探的相关代码

if (row * column - 1 == nb) {
            System.out.println("成功");
        }

        if (row * column - lenth - 1 != nb) {
            if (nb - 1 >= 0 && nbList.get(nb - 1) == 1) {
                isBooleanlist[x][y] = true;
                x = x - 1;
                //nbList中对应的1,2,3,4,分别对应的是下一步试探的方向
            }
            if (nb - 1 >= 0 && nbList.get(nb - 1) == 2) {
                isBooleanlist[x][y] = true;
                x = x + 1;

            }
            if (nb - 1 >= 0 && nbList.get(nb - 1) == 3) {
                isBooleanlist[x][y] = true;
                y = y - 1;

            }
            if (nb - 1 > 00 && nbList.get(nb - 1) == 4) {
                isBooleanlist[x][y] = true;
                y = y + 1;

            }

            if (nb - 1 == 0) {
                System.out.println("此情况无解,可能是输入的参数有误!");
            }

            nbList.remove(nb - 1);
            xList.remove(nb - 1);
            yList.remove(nb - 1);
            nb--;
            //回退到上一步
        }

如何回退是该方法的关键,其中逻辑部分也很重要,下面来看完整代码

package Game;

import java.util.ArrayList;
import java.util.Scanner;

public class GameCrack {

    static boolean[][] isFeasible(int row, int column) {
        //初始化可以使用的小块为 true
        boolean[][] isBooleanlist;
        isBooleanlist = new boolean[row][column];
        for (int i = 0; i < row; i++)
            for (int j = 0; j < column; j++) {
                isBooleanlist[i][j] = true;
            }
        return isBooleanlist;
    }

    static boolean[][] addblank(boolean[][] isBooleanlist, int x, int y) {
        //添加空白小块为 false,已经使用的小块也为 false
        isBooleanlist[x][y] = false;
        return isBooleanlist;
    }

    static int inputinit() {
        Scanner scan = new Scanner(System.in);
        //输入初始化的方格大小
        int nexint;
        while (true) {
            int choice = scan.nextInt();
            if (0 < choice) {
                nexint = choice;
                break;
            } else {
                System.out.println("输入非法 可能超过了合理范围 请重新输入");
                continue;
            }
        }
        return nexint;
    }

    static int inputblank(int number) {
        //输入空白处的坐标,同理可输入初始位置, 有大小限制
        Scanner scan = new Scanner(System.in);
        int nexint;
        while (true) {
            int choice = scan.nextInt();
            if (0 < choice && choice <= number) {
                nexint = choice;
                break;
            } else {
                System.out.println("输入非法 可能超过了合理范围 请重新输入");
                continue;
            }
        }
        return nexint;
    }

    int nb = 0;
    ArrayList<Integer> nbList = new ArrayList<Integer>();
    ArrayList<Integer> xList = new ArrayList<Integer>();
    ArrayList<Integer> yList = new ArrayList<Integer>();
    //初始化步数 和每一步对应的 x,y的坐标点

    void isCan(boolean[][] isBooleanlist, int lenth, int x, int y, int row, int column) {
        //判断旁边的四个点是否符合要求
        //回调  连续判断
        if (x + 1 < row && isBooleanlist[x + 1][y] == true) {
            nbList.add(1);
            nb++;
            isBooleanlist[x + 1][y] = false;
            xList.add(x + 2);
            yList.add(y + 1);
            System.out.println("初始位置是: " + (x + 1) + "," + (y + 1));
            System.out.println("第:" + nb + "步的位置是: " + (x + 2) + "," + (y + 1));
            System.out.println("                                  ");

            isCan(isBooleanlist, lenth, x + 1, y, row, column);
        }

        if (x - 1 >= 0 && isBooleanlist[x - 1][y] == true) {
            nbList.add(2);
            nb++;
            isBooleanlist[x - 1][y] = false;
            xList.add(x);
            yList.add(y + 1);
            System.out.println("初始位置是: " + (x + 1) + "," + (y + 1));
            System.out.println("第:" + nb + "步的位置是: " + (x) + "," + (y + 1));
            System.out.println("                                  ");

            isCan(isBooleanlist, lenth, x - 1, y, row, column);
        }

        if (y + 1 < column && isBooleanlist[x][y + 1] == true) {
            nbList.add(3);
            nb++;
            isBooleanlist[x][y + 1] = false;
            xList.add(x + 1);
            yList.add(y + 2);
            System.out.println("初始位置是: " + (x + 1) + "," + (y + 1));
            System.out.println("第:" + nb + "步的位置是: " + (x + 1) + "," + (y + 2));
            System.out.println("                                  ");

            isCan(isBooleanlist, lenth, x, y + 1, row, column);

        }


        if (y - 1 >= 0 && isBooleanlist[x][y - 1] == true) {
            nbList.add(4);
            nb++;
            isBooleanlist[x][y - 1] = false;
            xList.add(x + 1);
            yList.add(y);
            System.out.println("初始位置是: " + (x + 1) + "," + (y + 1));
            System.out.println("第:" + nb + "步的位置是: " + (x + 1) + "," + (y));
            System.out.println("                                  ");

            isCan(isBooleanlist, lenth, x, y - 1, row, column);

        }

        if (row * column - 1 == nb) {
            System.out.println("成功");
        }

        if (row * column - lenth - 1 != nb) {
            if (nb - 1 >= 0 && nbList.get(nb - 1) == 1) {
                isBooleanlist[x][y] = true;
                x = x - 1;

            }
            if (nb - 1 >= 0 && nbList.get(nb - 1) == 2) {
                isBooleanlist[x][y] = true;
                x = x + 1;

            }
            if (nb - 1 >= 0 && nbList.get(nb - 1) == 3) {
                isBooleanlist[x][y] = true;
                y = y - 1;

            }
            if (nb - 1 > 00 && nbList.get(nb - 1) == 4) {
                isBooleanlist[x][y] = true;
                y = y + 1;

            }

            if (nb - 1 == 0) {
                System.out.println("此情况无解,可能是输入的参数有误!");
            }

            nbList.remove(nb - 1);
            xList.remove(nb - 1);
            yList.remove(nb - 1);
            nb--;
            //isBooleanlist[x][y] = true;
        }

        if (row * column - lenth - 1 == nb) {
            for (int i = 0; i < row * column - lenth - 1; i++)

            {
                System.out.println("第" + (i + 1) + "步的位置是: " + (xList.get(i)) + "," + (yList.get(i)));
            }

        }
    }

    public static void main(String[] args) {
        GameCrack gameCrack = new GameCrack();
        System.out.println("请输入横坐标");
        int row = inputinit();
        System.out.println("请输入纵坐标");
        int column = inputinit();

        boolean[][] isBooleanlist = isFeasible(row, column);

        System.out.println("请输入开头位置横坐标");
        int x = inputblank(row) - 1;
        System.out.println("请输入开头位置纵坐标");
        int y = inputblank(column) - 1;
        addblank(isBooleanlist, x, y);

        System.out.println("请输入空白点的个数");
        int lenth = inputinit();

        for (int i = 0; i < lenth; i++) {
            System.out.println("请输入第" + (i + 1) + "个空白点的横坐标");
            int m = inputblank(row) - 1;
            System.out.println("请输入第" + (i + 1) + "个空白点的纵坐标");
            int n = inputblank(column) - 1;
            addblank(isBooleanlist, m, n);
        }

        gameCrack.isCan(isBooleanlist, lenth, x, y, row, column);

    }
}

代码写于2018年,这些天突然想写博客,于是做个记录,百度发现有人在使用图像识别来确认方格位置和相关信息,并用安卓调试来实现全自动化辅助破解,于是也想尝试一下学学技术,接下来有机会更新

======================================================================== MICROSOFT FOUNDATION CLASS LIBRARY : fir ======================================================================== AppWizard has created this fir application for you. This application not only demonstrates the basics of using the Microsoft Foundation classes but is also a starting point for writing your application. This file contains a summary of what you will find in each of the files that make up your fir application. fir.dsp This file (the project file) contains information at the project level and is used to build a single project or subproject. Other users can share the project (.dsp) file, but they should export the makefiles locally. fir.h This is the main header file for the application. It includes other project specific headers (including Resource.h) and declares the CFirApp application class. fir.cpp This is the main application source file that contains the application class CFirApp. fir.rc This is a listing of all of the Microsoft Windows resources that the program uses. It includes the icons, bitmaps, and cursors that are stored in the RES subdirectory. This file can be directly edited in Microsoft Visual C++. fir.clw This file contains information used by ClassWizard to edit existing classes or add new classes. ClassWizard also uses this file to store information needed to create and edit message maps and dialog data maps and to create prototype member functions. res\fir.ico This is an icon file, which is used as the application's icon. This icon is included by the main resource file fir.rc. res\fir.rc2 This file contains resources that are not edited by Microsoft Visual C++. You should place all resources not editable by the resource editor in this file. ///////////////////////////////////////////////////////////////////////////// For the main frame window: MainFrm.h, MainFrm.cpp These files contain the frame class CMainFrame, which is derived from CFrameWnd and controls all SDI frame features. ///////////////////////////////////////////////////////////////////////////// AppWizard creates one document type and one view: firDoc.h, firDoc.cpp - the document These files contain your CFirDoc class. Edit these files to add your special document data and to implement file saving and loading (via CFirDoc::Serialize). firView.h, firView.cpp - the view of the document These files contain your CFirView class. CFirView objects are used to view CFirDoc objects. ///////////////////////////////////////////////////////////////////////////// Other standard files: StdAfx.h, StdAfx.cpp These files are used to build a precompiled header (PCH) file named fir.pch and a precompiled types file named StdAfx.obj. Resource.h This is the standard header file, which defines new resource IDs. Microsoft Visual C++ reads and updates this file. ///////////////////////////////////////////////////////////////////////////// Other notes: AppWizard uses "TODO:" to indicate parts of the source code you should add to or customize. If your application uses MFC in a shared DLL, and your application is in a language other than the operating system's current language, you will need to copy the corresponding localized resources MFC42XXX.DLL from the Microsoft Visual C++ CD-ROM onto the system or system32 directory, and rename it to be MFCLOC.DLL. ("XXX" stands for the language abbreviation. For example, MFC42DEU.DLL contains resources translated to German.) If you don't do this, some of the UI elements of your application will remain in the language of the operating system. /////////////////////////////////////////////////////////////////////////////
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值