1. 程式人生 > >一筆畫完破解(上)

一筆畫完破解(上)

一筆畫完是最近朋友給我推薦的一個小遊戲(微信小程式),遊戲規則是一筆畫完全部格子,不能重複,不能有空缺,休閒類遊戲,感覺挺有意思的,於是我就是想用java來幫我實現通關這個遊戲,遊戲介面如下。(第一篇部落格講怎麼破解,第二篇實現完全自動化)不管哪一關都可以用這個程式碼0.1s內自動破解找到答案!

附第一篇部落格結果動圖:

熟悉程式設計的朋友肯定會說用棧來儲存走過的位置,不難啊,難度確實不大,本篇主要是個人興趣,記錄分享一下,且難度不在演算法其實,因為時間複雜度最大的暴力破解也可以在0.1秒內破解完畢,更主要的是實現影象識別和自動化闖關。

問題分析:先把問題簡化,思考下面的問題怎麼解

很容易想到的是直接一筆畫完了,現在我們回過頭捋一下思維,我們是怎麼找到這個解的,首先有一個起點,先看他能往哪裡走,然後走一步試試,如果沒通關的話,繼續看看能往哪裡走。

於是首先得出暴力破解方式:使用遞迴,傳入當前位置,計算能往哪裡走,能走則呼叫自身,否則嘗試下一個方向,直到通關。大概思路如下如下

function step( 當前位置 ){

    if 通關?

      return

   if 能向上走?

       step( 當前位置.上面一格 )

   if 能向下走?

       step( 當前位置.下面一格 )

 //同理左右一樣...

}

雖然這種演算法非常笨,但是一種解決方案了,演算法的優化我們在需要的時候在進行(對於計算機來說這點方格根本不是事),下面把問題轉換為計算機能理解的結構:

我用int 型別二維陣列陣列 來表示這個問題(如果想優化演算法,可以採用其他資料結構或自定義),我把不可以走的地方設定為-1,把可以走但沒走的地方可以設定為0,把走過的路線按照走的順序計數,比如起點為1,第二步為2,第三步為3...

畫了個醜醜的圖,勉強看一下

好了,思路有了,該怎麼解呢,程式碼如下

public void solve(int nowX, int nowY) {
		tryNum++;

		if(hasWin())
			return;
		
		if(!hasWin && nowY > 0 && array[nowX][nowY - 1] == 0) {
			//System.out.println("←");
			array[nowX][nowY - 1] = ++step;
			solve(nowX, nowY - 1);
		}//left
		if(!hasWin && nowY < column-1 && array[nowX][nowY + 1] == 0) {
			//System.out.println("→");
			array[nowX][nowY +1] = ++step;
			solve(nowX, nowY +1);
		}//right
		if(!hasWin && nowX > 0 && array[nowX - 1][nowY] == 0) {
			//System.out.println("↑");
			array[nowX - 1][nowY] = ++step;
			solve(nowX - 1, nowY);
		}//up
		if(!hasWin && nowX < row -1 && array[nowX + 1][nowY] == 0) {
			//System.out.println("↓");
			array[nowX + 1][nowY] = ++step;
			solve(nowX + 1, nowY);
		}//down

		if(!hasWin && array[nowX][nowY] != 1) {
			 array[nowX][nowY] = 0;
			step--;
		}//如果走到這發現上下左右都不能走,並且還沒有勝利,那就是走的不對,本格子置0,退回上一步
		
	}

大概的程式碼,hasWin是是否勝利,判斷方式是當前的步數==總的可以走的格子數?有為0的格子?未過關:過關:未過關,

雖然還有很多地方可以改進,但是可以求解了已經,遇到不會的關卡也可以自己破解了,程式碼放下面,可以執行,不過還有很多地方可以改進,但這個計算量不大,沒有改進的必要暫時,附垃圾程式碼:

public class Checkpoint {
	//public Grid[][] grids;
	public int row;
	public int column;
	
	public int startX;
	public int startY;
	
	/*public int nowX;
	public int nowY;*/
	public int testCount = 0;
	int step = 1;
	
	int[][] array;
	boolean hasWin = false;
	
	public int[][] getArray(){
		return array;
	}
	
	/**
	 *  array 0代表起點,1-n代表行走順序,-1代表不可達
	 * @param array
	 */
	public Checkpoint(int[][] array, int startX, int startY) {
		this.array = array;
		row = array.length;
		column = array[0].length;
		this.array[startX][startY] = 1;
		this.startX = startX;
		this.startY = startY;
	}
	
	public boolean hasWin() {
		for(int i = 0; i < row; i++) {
			for(int j = 0; j < column; j++) {
				if(array[i][j] == 0) {
					return false;
				}
			}
		}
		hasWin = true;
		return true;
	}
	
	public void solve(int nowX, int nowY) {
		testCount++;
		//MyPoint point = stack.peek();
		if(hasWin())
			return;
		
		if(!hasWin && nowY > 0 && array[nowX][nowY - 1] == 0) {
			//System.out.println("←");
			array[nowX][nowY - 1] = ++step;
			solve(nowX, nowY - 1);
		}//left
		if(!hasWin && nowY < column-1 && array[nowX][nowY + 1] == 0) {
			//System.out.println("→");
			array[nowX][nowY +1] = ++step;
			solve(nowX, nowY +1);
		}//right
		if(!hasWin && nowX > 0 && array[nowX - 1][nowY] == 0) {
			//System.out.println("↑");
			array[nowX - 1][nowY] = ++step;
			solve(nowX - 1, nowY);
		}//up
		if(!hasWin && nowX < row -1 && array[nowX + 1][nowY] == 0) {
			//System.out.println("↓");
			array[nowX + 1][nowY] = ++step;
			solve(nowX + 1, nowY);
		}//down
		if(!hasWin && array[nowX][nowY] != 1) {
			 array[nowX][nowY] = 0;
			step--;
		}
		
	}
	
	public void print() {
		System.out.println("try:" + testCount + "   result:" + ( hasWin ?"Ok":"No Answer") + "  step:" + step);
		for(int i = 0; i < row; i++) {
			for(int j = 0; j < column; j++) {
				System.out.print(array[i][j] == 0 ? "□" : array[i][j] == -1 ? "■" : array[i][j]);
				System.out.print("\t");
			}
			System.out.println("\n\n");
		}
	}
	public void print_plus() {
		//System.out.println("try:" + testCount + "   result:" + ( hasWin ?"Ok":"No Answer"));
		for(int i = 0; i < row; i++) {
			for(int j = 0; j < column; j++) {
				if(array[i][j] == -1)
					System.out.print("■");
				if(array[i][j] == step) {
					System.out.print("End");
				}else {
					if(array[i][j] == 1) {
						System.out.print("Start");
					}
					if(i < row - 1 && array[i][j] == array[i + 1][j] - 1)
						System.out.print("↓");
					if(i > 0 && array[i][j] == array[i - 1][j] - 1)
						System.out.print("↑");
					if(j < column - 1 && array[i][j] == array[i][j + 1] - 1)
						System.out.print("→");
					if(j> 0 && array[i][j] == array[i][j - 1] - 1)
						System.out.print("←");
				}
				System.out.print("\t");
			}
			System.out.println("\n\n");
		}
	}

	public void print_plus2() throws Exception {
		TreeMap<Integer, MyPosition> map = new TreeMap<Integer,MyPosition>();
		for(int i = 0; i < row; i++) {
			for(int j = 0; j < column; j++) {
				map.put(array[i][j], new MyPosition(i,j));
			}
		}
		Iterator iterator = map.keySet().iterator();
		while (iterator.hasNext()) {
		    int key = (int) iterator.next();
		    
		    for(int i = 0; i < row; i++) {
				for(int j = 0; j < column; j++) {
					if(array[i][j] < key) {
						if(array[i][j] == -1)
							System.out.print("■");
						if(array[i][j] == step) {
							System.out.print("End");
						}else {
							if(array[i][j] == 1) {
								System.out.print("Start");
							}
							if(i < row - 1 && array[i][j] == array[i + 1][j] - 1)
								System.out.print("↓");
							if(i > 0 && array[i][j] == array[i - 1][j] - 1)
								System.out.print("↑");
							if(j < column - 1 && array[i][j] == array[i][j + 1] - 1)
								System.out.print("→");
							if(j> 0 && array[i][j] == array[i][j - 1] - 1)
								System.out.print("←");
						}
					}else {
						System.out.print(" ");
					}
					System.out.print("\t");
				}
				System.out.println("\n\n");
			}
			Thread.sleep(200);
			for(int i = 0;i++ < 40;) {
		    	System.out.println();
		    }
		}
		//System.out.println("try:" + testCount + "   result:" + ( hasWin ?"Ok":"No Answer"));
		for(int i = 0; i < row; i++) {
			for(int j = 0; j < column; j++) {
				if(array[i][j] == -1)
					System.out.print("■");
				if(array[i][j] == step) {
					System.out.print("End");
				}else {
					if(array[i][j] == 1) {
						System.out.print("Start");
					}
					if(i < row - 1 && array[i][j] == array[i + 1][j] - 1)
						System.out.print("↓");
					if(i > 0 && array[i][j] == array[i - 1][j] - 1)
						System.out.print("↑");
					if(j < column - 1 && array[i][j] == array[i][j + 1] - 1)
						System.out.print("→");
					if(j> 0 && array[i][j] == array[i][j - 1] - 1)
						System.out.print("←");
				}
				System.out.print("\t");
			}
			System.out.println("\n\n");
		}
	}
	public void caculate() {
		solve(startX,startY);
	}
	

}

輸出部分為了按照步數輸出,又想直接o(1)的時間尋找那個位置(實際上是自己懶得在寫一個順序查找了)所以使用了帶排序的雜湊:TreeMap,放進去之後,順序遍歷即可為順序輸出,空間置換時間嘛。

主函式:

public static void main(String[] args) throws Exception {

		int[][] array = {
				{0,	  -1,	0, 0,	0,    -1},
				{0,     0,	0, 0,	0,    0 },
				{0,	   0,	0, 0,	0,    0 },
				{0,	   0,	0, -1,	0,    1,},
				
				{-1,   0,	0, 0,	0,	    0},
				{0,    0,	0, 0,	-1,	    0},
				{0,    0,	0, 0,	0,	    0},
				{0,    0,	0, 0,	0,	    0}};
		array[0][1] = -1;
		array[0][5] = -1;
		array[3][3] = -1;
		array[4][0] = -1;
		array[5][4] = -1;
		array[0][1] = -1;
		array[1][1] =  1;
		Checkpoint question = new Checkpoint(array,1,1);
		
		question.caculate();
		
		question.print_plus2();
		
	}

垃圾程式碼風格,湊合著看,上面為237關的例子,我們執行發現,程式一執行就能算出一個解法了,因此暫不進行尋路演算法的優化(有興趣的同學可以找找規律,僅舉一個例子:終點的度為1,只可能存在一個終點,如果走了一步之後,發現有兩個或以上度為1的格子,那麼這一步一定走錯了,繼續探索也沒有意義了,提前返回上一個格子)。

雖然能夠1秒內解出來,但是顯然不滿足我們的需求,還要自己輸入行列個數,哪裡不能走,起點在哪裡,太麻煩了。

這一篇主要講破解方式,下一篇下一篇點這裡部落格我會分享一下怎麼用adb+java實現完全自動化闖關。