話說以前把ui篇說了,接下來就是整個遊戲的核心部分了。git
廢話很少說,完成AI部分總共有幾個難點github
1. 計算機如何落子數組
2. 判斷勝負ide
在闡述代碼以前,先上流程圖。備註一下:玩家先手(黑子) 電腦白子ui
總流程圖:spa
具體步驟:code
步驟一:初始化工做blog
1 用一個三維數組來存放五子棋的全部贏法索引
2 用兩個數組來存放玩家的贏法總數,一個存放計算機的贏法總數遊戲
這裏理解起來是比較難的,先放代碼
var count = 0 ;// 贏法數組的索引 // 橫線贏法 for(var i = 0;i < 15;i++){ for(var j = 0;j < 11;j++){ for(var k = 0;k < 5;k++){ wins[i][j+k][count] = true } count ++; } } // 豎線贏法 for(var i = 0;i < 15;i++){ for(var j = 0;j < 11;j++){ for(var k = 0;k < 5;k++){ wins[j+k][i][count] = true } count ++; } } // 斜線贏法 左上右下 for(var i = 0;i < 11;i++){ for(var j = 0;j < 11;j++){ for(var k = 0;k < 5;k++){ wins[i+k][j+k][count] = true } count ++; } } // 斜線贏法 左下右上 for(var i = 0;i <11 ;i++){ for(var j = 14;j > 3;j--){ for(var k = 0;k < 5;k++){ wins[i+k][j-k][count] = true } count ++; } } console.log(count) // 初始化統計數組 for(var i = 0;i < count;i++){ myWin[i] = 0 computerWin[i] = 0 }
這裏贏法數組部分理解可能有問題,我用左右方向舉例說明
// 橫線贏法
for(var i = 0;i < 15;i++){
for(var j = 0;j < 11;j++){
for(var k = 0;k < 5;k++){
wins[i][j+k][count] = true
}
count ++;
}
}
i 表示當前行,j表示當前的行的每一個棋子,爲何j的最大值是10呢,要連成5顆才能贏。枚舉法說明
這樣,恰好把每一行的每一個棋子在這行上的橫向贏的總數統計了,其餘方向同樣
步驟二:在棋盤上點擊,畫棋子的主要工做
1 . 判斷遊戲是否結束
2. 計算機落子
判斷遊戲是否結束:
畫完棋子以後(當前是玩家),循環遍歷贏法數組,若是在這個點的wins[i][j][k] == true,玩家myWins[k] ++ ,計算機在在該點上就沒有贏的可能了,把computerWin[k]置爲一個無效值,當myWin[k] == 5的時候,說明玩家贏了。
這裏詳細解釋一下:
咱們在以前統計贏法的時候,對於每一種贏法都對應五個點,而myWins[]數組又是與wins[]數組對應的,每存在一個點使得wins[][][k]=true時,讓myWins[k]的值加1,當myWins[k]=5時,說明已經有五顆棋子連成一塊兒了。
那還有問題,會不會出現黑白棋子一塊兒連成5顆的時候就myWins[k]等於5呢?不會,由於在點擊的時候就已經判斷當前是黑棋仍是白棋了。只有是玩家的狀況才畫棋子。若是以前沒有進行判斷則會出現這種狀況。
// 畫棋子 // 沒有棋子才能落子 if(chessBoardArr[i][j] == 0){ oneStep(i,j,me) chessBoardArr[i][j] = 1 for(var k = 0; k < count;k++){ if(wins[i][j][k]){ myWin[k] ++; computerWin[k] = 6; if(myWin[k] == 5){ alert('你贏了!') over = true } } } if(!over){ me = !me; computerAI() } }
若是還沒結束,就到計算機落子了,另外一個難點又來了。
計算機落棋規則:找到棋盤上沒有落子的棋格,並分別計算玩家和計算機在該點上的分值並與當前的最高分進行比較,計算機在比較以後的最高分的棋格落子。
首先:咱們須要使用兩個二維數組來分別存放玩家和計算機在棋盤上每一格的分數,max來存放當前全部的最高分,u、v存放當前最高分的座標
// 得分數組 var myScore = [] var computerScore = [] var max = 0 //用來保存最高分 var u = 0,v = 0 ;//用來保存最高分的座標 for(var i = 0;i < 15;i++){ myScore[i] = [] computerScore[i] = [] for(var j = 0;j < 15;j++){ myScore[i][j] = 0 computerScore[i][j] = 0 } }
其次:遍歷棋盤上沒有棋子的每一格,並對該格遍歷贏法數組,判斷在該格上落子是否有價值。分兩種狀況:分別統計玩家和計算機在該點上第k種贏法的當前連子數,連子數越高,分數也就越高。
對該棋格統計完分數以後,分別把玩家和計算機在該棋格上的分數和最高分進行比較:
A. 玩家
1. myScore[i][j]>max ,那咱們認爲這個點是最好的,把最高分重置爲當前玩家在這個點的分數,最高分的座標也改爲這個點的座標。
2. myScore[i][j] == max , 若是計算機在該點上的分數大於計算機在最高分這個點的分數,那咱們也認爲這個點比較好,更新最大分數的值和座標
B 計算機
和玩家的狀況是同樣的。
不過這裏須要注意一點,由於當前是計算機,還須要對玩家進行一個阻礙,因此在連子數累加分的時候能夠設置比玩家連子數累加分高一些。
接下來:遍歷完整個棋盤以後,畫棋子,而且判斷是否結束(判斷邏輯跟玩家的同樣)
// 遍歷棋盤 for(var i = 0;i < 15;i++){ for(var j = 0;j < 15;j++){ if(chessBoardArr[i][j] == 0){ // 若是沒有棋子 // 循環比遍歷贏法數組,判斷在這個點落子有價值 for(var k = 0;k < count;k++){ if(wins[i][j][k]){ // 分值計算 if(myWin[k] == 1){ myScore[i][j] += 200 } else if(myWin[k] == 2){ myScore[i][j] += 400 } else if(myWin[k] == 3){ myScore[i][j] += 2000 } else if(myWin[k] == 4){ myScore[i][j] += 10000 } //電腦 if(computerWin[k] == 1){ computerScore[i][j] += 220 } else if(computerWin[k] == 2){ computerScore[i][j] += 420 } else if(computerWin[k] == 3){ computerScore[i][j] += 2100 } else if(computerWin[k] == 4){ computerScore[i][j] += 20000 } } } // 遍歷贏法數組 e if(myScore[i][j] > max){ max = myScore[i][j] u = i v = j } else if(myScore[i][j] == max){ if(computerScore[i][j] > computerScore[u][v]){ u = i v = j } } if(computerScore[i][j] > max){ max = computerScore[i][j] u = i v = j } else if(computerScore[i][j] == max){ if(myScore[i][j] > myScore[u][v]){ u = i v = j } } } } } // 循環遍歷 棋盤 e oneStep(u,v,false) chessBoardArr[u][v] = 2 for(var k = 0; k < count;k++){ if(wins[u][v][k]){ computerWin[k] ++; myWin[k] = 6; if(computerWin[k] == 5){ alert('電腦贏了!') over = true } } } if(!over){ me = !me; }
最後:要麼結束,要麼繼續博弈
說到這裏,整個五指棋-人機大戰算是完成了,其中邏輯部分我的感受仍是有點繞的,在網上看了不少五指棋邏輯的資料,分爲VCF和VCT,這個版本應該算VCF吧。還待繼續研究。
上面有些地方可能說的不恰當,但願大神指正,也但願有興趣的小夥伴多多分享交流。