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