1. 程式人生 > >利用電腦玩Android版“天天連萌”刷高分(三)——連連看消除搜尋

利用電腦玩Android版“天天連萌”刷高分(三)——連連看消除搜尋

差點忘了寫接下來的這兩篇部落格了,這篇如果接不上上一篇,請勿見怪啊,因為我自己都忘了。

上兩篇分別提到了截圖和影象識別,接下來這一篇是說一下連連看的消除演算法。
這個演算法看似很厲害,其實我在這裡採用的是很笨拙的方法,就是列舉。
在上一篇已經連遊戲裡的方塊轉換成一個二維陣列,所以就通過一個兩層迴圈,遍歷每一個元素,看能不能跟其他元素消除,程式碼如下:
for (int i = 1; i < CODE_ROW - 1; i++) {
for (int j = 1; j < CODE_COL - 1; j++) {
if (imageCodes[i][j] != 0) {
Point point = search(i, j);
if (point == null) {
continue;
}
touch(new Point(i, j));
touch(point);
imageCodes[i][j] = 0;
imageCodes[point.x][point.y] = 0;
anyClear = true;
System.out
.println(String.format("消除 %d:%d %d:%d", i, j, point.x, point.y));
}
}

其中search方法是搜尋可以與該廣場消除的方塊,返回的point表示另一個方塊的座標。如果搜尋不到就返回null,傳的物件分別是縱座標與橫座標。touch是向模擬器傳送點選事件,當然每次消除我們要點選兩個。消除之後,將對應元素設為。

關於search()方法的實現,就是列舉。我們知道連連看消除有三類路線,一類是直線的,有4種情況,第二類是拐一個彎的,有8種情況,第三類是拐兩個彎的,有16種情況。我採用的是比較直接也是比較笨拙的方法,直接對它們進行列舉,程式碼如下:

/**
* 搜尋,返回最優點。
*
* @return
*/
public Point search(int x, int y) {
return LianlianKan.twoCornerSearch(imageCodes, x, y);
}
/**
* 連連看搜尋演算法。
*
* @author Geek_Soledad
*/
public static class LianlianKan {
/**
* 水平搜尋
*
* @param datas
* 陣列
* @param row
* 對比數值所在的行
* @param col
* 對比數值所在的列
* @param atRow
* 進行水平搜尋時所在的行
* @return
*/
private static Point moveHorizon(int[][] datas, int row, int col, int atRow, int atCol) {
// 再拐左
for (int k = atCol - 1; k >= 1; k--) {
if (datas[atRow][k] == datas[row][col]) {
return new Point(atRow, k);
} else if (datas[atRow][k] != 0) {
break;
}
}
// 再拐右
for (int k = atCol + 1; k < CODE_COL - 1; k++) {
if (datas[atRow][k] == datas[row][col]) {
return new Point(atRow, k);
} else if (datas[atRow][k] != 0) {
break;
}
}
return null;
}

/**
* 豎直搜尋
*
* @param datas
* 陣列
* @param row
* 對比數值所在的行
* @param col
* 對比數值所在的列
* @param atCol
* 進行水平搜尋時所在的列
* @return
*/
private static Point movePortrait(int[][] datas, int row, int col, int atRow, int atCol) {
// 再拐上
for (int k = atCol - 1; k >= 1; k--) {
if (datas[k][atCol] == datas[row][col]) {
return new Point(k, atCol);
} else if (datas[k][atCol] != 0) {
break;
}
}
// 再拐下
for (int k = atCol + 1; k < CODE_ROW - 1; k++) {
if (datas[k][atCol] == datas[row][col]) {
return new Point(k, atCol);
} else if (datas[k][atCol] != 0) {
break;
}
}
return null;
}

/**
* 兩個角搜尋
*
* @param datas
* 陣列
* @param x
* 縱座標
* @param y
* 橫座標
* @return
*/
public static Point twoCornerSearch(int[][] datas, int x, int y) {
// 向左進行搜尋
for (int i = y - 1; i >= 0; i--) {
// 向左進行直線搜尋
if (datas[x][i] == datas[x][y]) {
return new Point(x, i);
} else if (datas[x][i] != 0) {
break;
}
// 向左,然後拐上進行直角搜尋
for (int j = x - 1; j >= 0; j--) {
if (datas[j][i] == datas[x][y]) {
return new Point(j, i);
} else if (datas[j][i] != 0) {
break;
}
// 向左,拐上,再水平搜尋
Point point = moveHorizon(datas, x, y, j, i);
if (point != null) {
return point;
}
}
// 向左,然後拐下進行直角搜尋
for (int j = x + 1; j < CODE_ROW; j++) {
if (datas[j][i] == datas[x][y]) {
return new Point(j, i);
} else if (datas[j][i] != 0) {
break;
}
// 向左,拐下,再水平搜尋
Point point = moveHorizon(datas, x, y, j, i);
if (point != null) {
return point;
}
}
}
// 橫向往右搜尋
for (int i = y + 1, length = CODE_COL; i < length; i++) {
// 向右直線搜尋
if (datas[x][i] == datas[x][y]) {
return new Point(x, i);
} else if (datas[x][i] != 0) {
break;
}
// 向右,然後拐上進行直角搜尋。
for (int j = x - 1; j >= 0; j--) {
if (datas[j][i] == datas[x][y]) {
return new Point(j, i);
} else if (datas[j][i] != 0) {
break;
}
// 向右,拐上,再水平搜尋
Point point = moveHorizon(datas, x, y, j, i);
if (point != null) {
return point;
}
}
// 向右,然後拐下進行直角搜尋
for (int j = x + 1; j < CODE_ROW; j++) {
if (datas[j][i] == datas[x][y]) {
return new Point(j, i);
} else if (datas[j][i] != 0) {
break;
}
// 向右,拐下,再水平搜尋。
Point point = moveHorizon(datas, x, y, j, i);
if (point != null) {
return point;
}
}
}
// 縱向往上搜索
for (int i = x - 1; i >= 0; i--) {
if (datas[i][y] == datas[x][y]) {
return new Point(i, y);
} else if (datas[i][y] != 0) {
break;
}
// 向上,然後拐左進行直角搜尋。
for (int j = y - 1; j >= 0; j--) {
if (datas[i][j] == datas[x][y]) {
return new Point(i, j);
} else if (datas[i][j] != 0) {
break;
}
// 向上,拐左,再豎直搜尋
Point point = movePortrait(datas, x, y, i, j);
if (point != null) {
return point;
}
}
// 向上,然後拐右進行直角搜尋
for (int j = y + 1; j < CODE_COL; j++) {
if (datas[i][j] == datas[x][y]) {
return new Point(i, j);
} else if (datas[i][j] != 0) {
break;
}
// 向上,拐右,再豎直搜尋
Point point = movePortrait(datas, x, y, i, j);
if (point != null) {
return point;
}

}
}
// 縱向往下搜尋
for (int i = x + 1, length = CODE_ROW; i < length; i++) {
if (datas[i][y] == datas[x][y]) {
return new Point(i, y);
} else if (datas[i][y] != 0) {
break;
}
// 向下,然後拐左進行直角搜尋。
for (int j = y - 1; j >= 0; j--) {
if (datas[i][j] == datas[x][y]) {
return new Point(i, j);
} else if (datas[i][j] != 0) {
break;
}
// 向下,拐右,再豎直搜尋
Point point = movePortrait(datas, x, y, i, j);
if (point != null) {
return point;
}
}
// 向下,然後拐右進行直角搜尋
for (int j = y + 1; j < CODE_COL; j++) {
if (datas[i][j] == datas[x][y]) {
return new Point(i, j);
} else if (datas[i][j] != 0) {
break;
}
// 向下,拐右,再豎直搜尋
Point point = movePortrait(datas, x, y, i, j);
if (point != null) {
return point;
}
}
}
return null;
}
}

舉其中的一個例子,比如我們往右搜尋,如果發現右邊的不為0但也不和它相等,說明是其他方塊,那這條路徑就不能執行了。如果與它相等,說明是兩個相同的方塊,那麼返回這個位置的座標。如果為0,那麼我們就可以進行下一步的搜尋,比如繼續往右(直線情況),或者往上往下(有拐彎的路徑)。我把這三種情況都寫在一起了,因為我在寫的時候發現,直線路徑是直角路徑的一種特殊情況,而直角路徑其實也是兩個拐彎的路徑的特殊情況。

當每個元素都遍歷完之後,我們要判斷是否全部消除完畢,如果消除完了,方法執行結束,進行下一次截圖。如果沒有,那麼我們要再遍歷搜尋一次,因為可能存在的情況是一開始的方塊不能被消除,但隨著其他方塊被消除之後,他們可以被消除了。
另外,我在這裡還增加了一個anyClear的布林變數,這是為了避免死迴圈,比如出現以下情況:
xo
ox
這種情況下是無法被消除的,遊戲裡面將會重新組合這些廣場。所以當遍歷完發現沒有方塊被消除時,需要重新截圖。當然除了出現上面情況,實際操作時發現,其他情況也可能出現無法被消除問題,比如消除了一些方塊後,會出現perfect等圖案,這些圖會影響一些方塊的識別。這時也需要重新截圖。所以實際上startSearch方法應該如下:
public boolean startSearch(BufferedImage image) throws InterruptedException {
boolean anyClear = false;
do {
anyClear = false;
for (int i = 1; i < CODE_ROW - 1; i++) {
for (int j = 1; j < CODE_COL - 1; j++) {
if (imageCodes[i][j] != 0) {
Point point = search(i, j);
if (point == null) {
continue;
}
touch(new Point(i, j));
touch(point);
imageCodes[i][j] = 0;
imageCodes[point.x][point.y] = 0;
anyClear = true;
System.out
.println(String.format("消除 %d:%d %d:%d", i, j, point.x, point.y));
}
}
}
} while (anyClear && !isEmpty());
return anyClear;
}


這篇就寫到這裡。下一篇說一下模擬按鍵,以及針對遊戲、手機的實際情況,對程式的優化。