判斷是否存在任一條路徑
在這個連連看遊戲中,是有可能存在死局的,也就是沒有任何兩個圖案可以用兩個轉彎內的線連接起來時。這時我們需要讓電腦能夠自動判斷這種狀況並做出反應,讓玩家可以更明確知道是否有可行的路徑。
那要如何判斷是否存在任一可能路徑呢?有幾個條件:
- 第一點:電腦能夠判斷連線是否合法
- 第二點:遍歷所有可能的圖案去確認是否存在可能路徑
第一點在上一篇我們已經做到了,因此在這一篇,我們要找到一個較省時的方式去遍歷所有可能的連線是否存在。
搜尋方法構思
下圖是我在思考電腦自動搜尋時的搜尋邏輯:
因為我希望電腦在搜尋時,能夠避免搜尋已搜尋過的路徑,因此應要紀錄已搜尋過的組合有那些。
為了方便紀錄與判別是否已搜尋過,我決定以圖案來做搜尋依據,由最盤面最左上開始,每遇到一個符號,就判別該符號是否有任何可能可以連線的兩個圖案。
所以我們會需要下面幾件事情:
- 遍歷盤面,並在找到路徑時停止搜尋
- 紀錄已搜尋過的符號並避免重複搜尋
- 列出現有符號可能兩兩連線的所有排列組合
- 判別兩點間能否連線
程式實作
第四點我們在上一篇文章裡已經完成該功能,因此現在我們應該要做的有1~3項,這部份的程式碼如下:
//取得第一條搜尋到的已知存在路徑 public getFirstExistPath():Path{ var searchedValue = [];//用以紀錄已搜尋過符號 //由最左上開始做判斷 for (var i =0;i<this.board.length;i++){ for (var j = 0; j<this.board[i].length;j++){ let value = this.board[i][j]; //判斷盤面上現在是有符號的(null代表沒有符號) //並且這個符號之前還沒有被搜尋過 if(value!= null && searchedValue.indexOf(value) == -1){ searchedValue.push(value); let positionsArr = this.getPositionByValue(value);//取得盤面上所有這個符號的位置 let permutationsArr = this.getPairNumPermutations(positionsArr.length);//取得可能存在的連線的點的排列組合 //getPairNumPermutations回傳的格式是[[0,1],[0,2],[0,3],[1,2],[1,3],[2,3]],裡面數字為index //嘗試每一個可能的排列組合 for(var k = 0;k<permutationsArr.length;k++){ let v = permutationsArr[k]; let path = new Path(positionsArr[v[0]], positionsArr[v[1]],this); if(path.canLinkInLine()){ return path; } } } } } return null; }[/code] <code>getPositionByValue</code>這個函數主要是要取得盤面上所有這個符號的位置,方法內容如下: [code lang="js"] public getPositionByValue(value:number):Array<point>{ let arr = new Array<point>(); for (var i =0;i<this.board.length;i++){ for (var j = 0; j<this.board[i].length;j++){ if (this.board[i][j] == value){ arr.push(new Point(i, j)); } } } return arr; }[/code] <code>getPairNumPermutations</code>這個函數則是在列出,相同圖案任選2個所有可能的排列組合 如果傳入的是4,也就是C4取2=6,一共會有六種排序的可能性。 查一下維基百科,裡面有相關的解釋:<a href="https://zh.wikipedia.org/wiki/%E7%B5%84%E5%90%88" rel="noopener noreferrer" target="_blank">排列組合</a> 因為兩點間的路徑不會受到先後順序的影響而影響是否能連線,並且相同的點不可連線,這邊我一律讓第一個數字(index)小於第二個數字(index),因此用這個判斷式<code>i != j && i <= j</code>來排除重覆的組合。 用下面的函數,若是同樣的符號有4個,則輸入值為4,輸出值會是[[0,1],[0,2],[0,3],[1,2],[1,3],[2,3]] [code lang="js"] private pairNumPermutations = {}; /** * 取得輸入的index中,2個2個一組的所有可能排列組合 * 回傳的格式是[[0,1],[0,2],[0,3],[1,2],[1,3],[2,3]] */ public getPairNumPermutations(num:number){ if(this.pairNumPermutations[num] != null){ return this.pairNumPermutations[num]; } let data = []; for(var i = 0; i <num;i++){ for(var j = 0; j <num;j++){ if(i != j && i <= j){ data.push([i,j]); } } } this.pairNumPermutations[num] = data; return data; }[/code] <h3>可行路徑提示</h3> 上面做完了電腦自動搜尋路徑的功能,可以使用在兩個地方,第一個地方是,當盤面沒有任何路徑可以走時,要自動重整盤面。 這部份的程式碼如下: [code lang="js"] //判斷還有沒有路走 if(board.gameRoundEnd()){ alert("恭喜完成遊戲!"); board = new Board(); vm.boardContent = board.board; }else if(board.getFirstExistPath() == null){ vm.reloadTimes++; board.rearrangeBoard();//重整盤面 }
而重整盤面的程式碼如下:
public rearrangeBoard(){
let values = this.getAllValueInBoard().sort((a, b) => (Math.random() > .5) ? 1 : 0);
for (var i =0;i
然後再隨機打亂陣列排序,再依序填入盤面上所有有圖案的格子內,就可以達到重整盤面但是不影響到空格的位置。
今日成果
連連看遊戲邏輯至此已大致完成囉!下一篇開始我們會實際開始製作一款實際上具有畫面、音效、特效等真正的網頁連連看遊戲
LIVE DEMO: 今日成果展示
完整程式碼可由此下載:ironman20181022