canvas简易人机五子棋
中學時看過一本關于圍棋的漫畫《棋魂》,奈何天賦有限,圍棋至今也不會……好吧,退而求其次,五子棋相對簡單一點。對著網上的教程實現了一個簡單的五子棋:
其實ui的實現并不難,主要記錄下ai的思路吧。
// 繪制棋盤for (var i = 0; i < 15; i++) {context.beginPath();context.moveTo(15 + i * 30, 15);context.lineTo(15 + i * 30, 435);context.stroke();context.beginPath();context.moveTo(15, 15 + i * 30);context.lineTo(435, 15 + i * 30);context.stroke();};五子棋的棋盤為15*15,落子黑先白后,落子的過程其實就是在繪制旗子。// 繪制棋子function oneStep(x, y, color) {// x,y為棋子在棋盤的坐標索引,color為黑棋或白棋context.beginPath();context.arc(15 + x * 30, 15 + y * 30, 12, 0, 2 * Math.PI);context.closePath();var gradient = context.createRadialGradient(15 + x * 30 + 2, 15 + y * 30 - 2, 12, 15 + x * 30 + 2, 15 + y * 30 - 2, 0);if (color) {gradient.addColorStop(0, '#0a0a0a');gradient.addColorStop(1, '#636766');} else {gradient.addColorStop(0, '#d1d1d1');gradient.addColorStop(1, '#f9f9f9');};context.fillStyle = gradient;context.fill();}
需要一個二維數組記錄當前棋盤的落子情況,每次落子需要判斷勝負以及是否結束。
var me = true; var chessBoard = []; //創建一個二維數組用于記錄當前棋盤的落子情況 var wins = []; //三維數組記錄五子棋所有的贏法 var count = 0; //記錄五子棋所有贏法的索引 var over = false; //落子事件chess.onclick = function(e) {var x = e.offsetX;var y = e.offsetY;var i = Math.floor(x / 30);var j = Math.floor(y / 30);if (chessBoard[i][j] == 0 && !over) { //沒有落子的位置才能落子,黑子為1,白子為2oneStep(i, j, me);if (me) {chessBoard[i][j] = 1;} else {chessBoard[i][j] = 2;};console.log(chessBoard);winner(i, j, me);me = !me;if (!over) {computerAI(me);};};}先說判斷勝負,其實無論哪方,五子連珠作為勝利的條件,在15*15的棋盤上勝利的所有情況是可以枚舉出來的。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);
關鍵在于wins這個三維數組,有點難理解,舉個例子:假如五子棋只有一種贏法:
var black = [];var white = [];//分別記錄五子棋黑白的贏法數組這兩個數組結合wins數組來判斷勝負,五子棋共有572種贏法,默認黑子與白子的贏法都為0。
for (var i = 0; i < count; i++) { //開局默認黑白所有的贏法都是0black[i] = 0;white[i] = 0;};
還是用上面的例子,如果黑方在第一種贏法處落下一子,那么黑子的第一種贏法+1,同時白子此種贏法作廢。
//判斷輸贏function winner(i, j, color) {for (var k = 0; k < count; k++) {if (wins[i][j][k]) {if (color) {black[k]++;white[k] = 6;//如果某種贏法黑子已經落子,白子此種贏法就作廢} else {white[k]++;black[k] = 6;};if (black[k] == 5) {alert('黑子獲勝');over = true;};if (white[k] == 5) {alert('白子獲勝');over = true;};};};}代碼到這里,已經能實現五子棋的規則邏輯了,接下來實現ai。 var blackScore = []; var whiteScore = []; //分別記錄五子棋黑白的二維得分數組 這個ai其實挺簡單的,實現思路就是通過遍歷每一個能落子的空坐標,然后結合算法找出分數最高的一個位置落子。//AIfunction computerAI(color) {var max = 0;var u = 0;var v = 0;// 保存最大的分數和相應坐標for (var i = 0; i < 15; i++) {blackScore[i] = [];whiteScore[i] = [];for (var j = 0; j < 15; j++) {blackScore[i][j] = 0;whiteScore[i][j] = 0;};};//每個坐標的分數為零//遍歷每個空坐標,如果某種贏法已經落子的數量越大則該坐標加分越多//同理攔截對方的落子//加分的數值很重要for (var i = 0; i < 15; i++) {for (var j = 0; j < 15; j++) {if (chessBoard[i][j] == 0) {for (var k = 0; k < count; k++) {if (wins[i][j][k]) {switch (black[k]) {case 1:blackScore[i][j] += 2;break;case 2:blackScore[i][j] += 5;break;case 3:blackScore[i][j] += 20;break;case 4:blackScore[i][j] += 50;break;}switch (white[k]) {case 1:whiteScore[i][j] += 2;break;case 2:whiteScore[i][j] += 5;break;case 3:whiteScore[i][j] += 20;break;case 4:whiteScore[i][j] += 50;break;}//找出得分最高的坐標點if (blackScore[i][j] > max) {max = blackScore[i][j];u = i;v = j;} else if (blackScore[i][j] == max) {if (whiteScore[i][j] > whiteScore[u][v]) {u = i;v = j;}}if (whiteScore[i][j] > max) {max = whiteScore[i][j];u = i;v = j;} else if (whiteScore[i][j] == max) {if (blackScore[i][j] > blackScore[u][v]) {u = i;v = j;}}};};};};};oneStep(u, v, color);color ? chessBoard[u][v] = 1 : chessBoard[u][v] = 2;winner(u, v, color);me = !color;}
五子棋的棋盤上,每一個位置都存在多種贏法,此算法的邏輯就是假如一個空坐標還未落子,那么遍歷所有的贏法,如果黑方在此種贏法已經落下一子,那么這個坐標對黑方有利,加分;如果黑方落下二子,那么分數更高;白方也是同理……找出最有價值的坐標落子。
至于如何加分,可以借鑒網上的評分表:
代碼只是實現了思路,沒有優化;而且此類“”民間規則“”五子棋都是先手必勝,
在五子棋專業規則中規定,一共有26種開局。直指開局13種,斜指開局13種。
這26種開局分別是:
寒星 溪月 殘月 雨月 金星 丘月 新月?
山月 游星 長星 峽月 恒星 水月 流星
浦月 嵐月 銀月 明星 名月 彗星 花月?
松月 疏星 斜月 瑞星 云月?
其中公認黑必勝的開局有:花月,浦月
黑必敗開局有:彗星,游星
以上之適用于五子棋專業規則
在民間規則里,幾乎全部是黑先手必勝。
專業的五子棋比賽還有禁手、三手交換、五手兩打等限制,以后有機會再研究吧……
總結
以上是生活随笔為你收集整理的canvas简易人机五子棋的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 向ComboBox列表框中添加Enum的
- 下一篇: ARM(IMX6U)BSP工程文件管理(