這個游戲的關鍵算法是選取同色區域。
我的方法是每個方塊是一個對象,包含幾項屬性:自身所在的行,自身所在的列,自身的顏色。
還加了一個id作備用,暫時沒用到。
基本思路是遞歸,顯而易見。
首先准備兩個列表。
一個用來裝被檢查過了的方塊,因為左邊方塊的右邊就等於右邊方塊的左邊,如果不檢查的話就無限遞歸到溢出了。
另一個用來裝與傳入方塊顏色相同的方塊,留待遞歸完成后作為返回值。
然后傳入第一個方塊,分別檢查它四個方向上的方塊,如果顏色和它相同的話就用對應方向上的方塊來進行下一次遞歸。
上方檢測舉例,其他方向同理:
private static void checkUp(Block block) { // 越界(上方不存在方塊)則跳出。 if ((block.getRow()-1) < 0) { return; } // 獲取上方方塊。 Block blockUpside = GameSurfaceView.blockList[block.getRow()-1][block.getColumn()]; // 如果方塊不存在或者顏色不同就跳出。 if (blockUpside == null || blockUpside.getColor() != block.getColor()) { return; } // 下一次遞歸。 checkFourSides(blockUpside); }
跳出遞歸的唯一條件是當前方塊有沒有被檢查過,因為考慮到單一職責原則,我把方塊對比的代碼都放到了單個方向檢測的方法里了,包括是否越界和顏色是否相同還有對應方向上的方塊存不存在。
這些是找到了對應方向上的方塊才有討論的必要的。
比如我看到的那個C++的條件判斷是這樣的……
//遞歸求解 void MyRecursion(LinkList _head1, LinkList _head2, int _x, int _y, int number) { if ((_x >= 10) || (_x < 0) || (_y >= 10) || (_y < 0) || (MyTraverseLinkList(_head2, _x, _y)) || (0 == MyArray[_x][_y])) { return; } else { MyInsertLinkList(_head2, _x, _y); if ((MyArray[_x][_y] == number)) { MyInsertLinkList(_head1, _x, _y); if ((_x - 1) >= 0) //遞歸 - 上 { MyRecursion(_head1, _head2, _x - 1, _y, number); } if ((_y - 1) >= 0) //遞歸 - 左 { MyRecursion(_head1, _head2, _x, _y - 1, number); } if ((_x + 1) < 10) //遞歸 - 下 { MyRecursion(_head1, _head2, _x + 1, _y, number); } if ((_y + 1) < 10) //遞歸 - 右 { MyRecursion(_head1, _head2, _x, _y + 1, number); } } } }
里面還有重復的邊界判斷。
而且這哥們的起名姿勢很有特點。
如果一個方法只做一件事的話,思路就清晰多了,像這樣。
/** 遞歸,不停檢查四面。*/ private void checkFourSides(Block block) { // 如果當前方塊已被檢查過,則跳出當前遞歸。 if (isChecked(block)) { return; } // 標記當前方塊為已被檢查過。 checkedBlocks.add(block); // 將當前方塊加入同色列表。 blocksInSameColor.add(block); // 檢查上下左右的方塊。 checkUp(block); checkDown(block); checkLeft(block); checkRight(block); }
於是像這樣,當檢查方法碰到邊界,或者碰到了不存在的方塊,或者周圍已經沒有顏色相同的方塊了的時候,遞歸停止。
最后同色列表里保存的就是首尾相連的一片同色方塊了。
然后把整個算法封裝起來,再對外暴露一個接口就搞定了。
/** 獲取存有與當前方塊顏色相同首尾相連的一片方塊的列表。*/ public static ArrayList<Block> getBlocksInSameColor(Block selectedBlock) { // 初始化兩個緩存列表。 checkedBlocks = new ArrayList<Block>(); blocksInSameColor = new ArrayList<Block>(); // 執行遞歸。 checkFourSides(selectedBlock); return blocksInSameColor; }
