[譯]手把手教你創建國際象棋 AI

原文鏈接:A step-by-step guide to building a simple chess AI

我們先來了解一下,在我們創建一個簡單的國際象棋 AI 過程中所會接觸到的一些基本概念:

  • 棋子的移動
  • 繪制棋盤
  • Minimax(極小化極大算法)
  • Alpha-beta 剪枝

我們將一步一步將這些加入最終的算法中,并分別展示它們對算法所產生的影響。

你可以在 Github 上查看最終版本

譯者試了下最終版本,一不小心就被吊打了...??

第一步:棋子的移動和繪制棋盤

這里我們使用 chess.jschessboard.js 分別來控制棋子的移動和繪制棋盤。chess.js 庫實現了所有棋子的移動規則,基于此我們可以根據棋局狀態得到棋子所有可能的移動。

根據輸入的棋盤狀態生成所有可能的棋子移動
根據輸入的棋盤狀態生成所有可能的棋子移動

有了以上兩個類庫,我們就能將精力放在最有趣的事上——創建一個能夠找到最佳移動的 AI。

接下來就開始創建這樣一個 AI,我們先創建一個方法,它會在所有合法的移動中隨機選取一個。

var calculateBestMove =function(game) {
    //generate all the moves for a given position
    var newGameMoves = game.ugly_moves();
    return newGameMoves[Math.floor(Math.random() * newGameMoves.length)];
};

盡管,這個 AI 像一個剛懂規則的新手,但是,我們已經可以和它下棋了,這是一個好的開始。

隨機移動,[點擊試玩](https://jsfiddle.net/lhartikk/m14epfwb/4)
隨機移動,[點擊試玩](https://jsfiddle.net/lhartikk/m14epfwb/4)

第二步:棋盤狀態評估

現在,我們試著計算在棋局某一狀態下哪邊更具優勢,最簡單的方法就是根據下表來統計棋局剩余棋子權重。

棋子對應權重表
棋子對應權重表

根據這個方法,我們就能讓我們的 AI 選擇在棋局某一狀態下使棋局權重最高的移動了。

var calculateBestMove = function (game) {

    var newGameMoves = game.ugly_moves();
    var bestMove = null;
    //use any negative large number
    var bestValue = -9999;

    for (var i = 0; i < newGameMoves.length; i++) {
        var newGameMove = newGameMoves[i];
        game.ugly_move(newGameMove);

        //take the negative as AI plays as black
        var boardValue = -evaluateBoard(game.board())
        game.undo();
        if (boardValue > bestValue) {
            bestValue = boardValue;
            bestMove = newGameMove
        }
    }

    return bestMove;

};

加入了計算權重后,我們的 AI 就會盡可能地去吃對方的棋子。

盡可能地吃子,[點擊試玩](https://jsfiddle.net/lhartikk/m5q6fgtb/1/)
盡可能地吃子,[點擊試玩](https://jsfiddle.net/lhartikk/m5q6fgtb/1/)

第三步:使用極小化極大算法來探索樹

下一步,我們使用 極小化極大算法(Minimax) 來使我們的 AI 能從探索樹中選出最優移動。

首先,我們先根據給定深度遞歸構建棋子所有可能移動的樹,并用上一節的方法來計算所有子節點的權重

然后,依據不同的行棋顏色,父節點取子節點的最大或最小值,若白子則取子節點的最大值返回給父節點,反之返回最小值。

深度為 2 的情況下,極小化極大算法圖解
深度為 2 的情況下,極小化極大算法圖解
var minimax = function (depth, game, isMaximisingPlayer) {
    if (depth === 0) {
        return -evaluateBoard(game.board());
    }
    var newGameMoves = game.ugly_moves();
    if (isMaximisingPlayer) {
        var bestMove = -9999;
        for (var i = 0; i < newGameMoves.length; i++) {
            game.ugly_move(newGameMoves[i]);
            bestMove = Math.max(bestMove, minimax(depth - 1, game, !isMaximisingPlayer));
            game.undo();
        }
        return bestMove;
    } else {
        var bestMove = 9999;
        for (var i = 0; i < newGameMoves.length; i++) {
            game.ugly_move(newGameMoves[i]);
            bestMove = Math.min(bestMove, minimax(depth - 1, game, !isMaximisingPlayer));
            game.undo();
        }
        return bestMove;
    }
};

加入了極小化極大算法之后,我們的 AI 已經不再是任人宰割了。

加入了極小化極大算法,[點擊試玩](https://jsfiddle.net/lhartikk/m5q6fgtb/1/)
加入了極小化極大算法,[點擊試玩](https://jsfiddle.net/lhartikk/m5q6fgtb/1/)

極小化極大算法很大程度上取決于我們能夠探索深度,下一步我們就來優化它。

第四步:Alpha-beta 剪枝

Alpha-beta 剪枝 是對極小化極大算法的一種優化,用于減少搜索樹中需要探索的節點數。這樣在同樣的資源條件下,就增加了探索樹的搜索深度。

當探索路徑的結果比之前探索的更糟時,Alpha-beta 剪枝就不再搜索該子樹。它并不影響極小化極大算法的計算結果,而是加快極小化極大算法運算速度。無論何種情況,Alpha-beta 剪枝總是能優化計算效率,即使,我們最初探索的就是最優解。

alpha-beta 剪枝用于極小化極大算法
alpha-beta 剪枝用于極小化極大算法

如下圖所示,通過 alpha-beta 剪枝,我們能顯著減少極小化極大算法的計算次數。

深度為 4 時,使用或不使用 alpha-beta 剪枝時的計算次數
深度為 4 時,使用或不使用 alpha-beta 剪枝時的計算次數
var minimax = function (depth, game, alpha, beta, isMaximisingPlayer) {
    positionCount++;
    if (depth === 0) {
        return -evaluateBoard(game.board());
    }

    var newGameMoves = game.ugly_moves();

    if (isMaximisingPlayer) {
        var bestMove = -9999;
        for (var i = 0; i < newGameMoves.length; i++) {
            game.ugly_move(newGameMoves[i]);
            bestMove = Math.max(bestMove, minimax(depth - 1, game, alpha, beta, !isMaximisingPlayer));
            game.undo();
            alpha = Math.max(alpha, bestMove);
            if (beta <= alpha) {
                return bestMove;
            }
        }
        return bestMove;
    } else {
        var bestMove = 9999;
        for (var i = 0; i < newGameMoves.length; i++) {
            game.ugly_move(newGameMoves[i]);
            bestMove = Math.min(bestMove, minimax(depth - 1, game, alpha, beta, !isMaximisingPlayer));
            game.undo();
            beta = Math.min(beta, bestMove);
            if (beta <= alpha) {
                return bestMove;
            }
        }
        return bestMove;
    }
};

第五步:升級計算權重方法

最初計算權重的方法相當簡單就是通過計算棋盤上棋子所對應的權重,單憑這一點無法判斷棋的局勢。為了改善這一點,我們需要將棋子在棋盤中的位置因素計算在內。比如,騎士在棋盤中間就比在棋盤邊緣位置更優,這樣它可以有更多的選擇。

這里我們在 chess-programming-wiki 所提供的表格的基礎上稍作修改已適應我們的程序。

棋子位置所對應的權重表
棋子位置所對應的權重表

這樣我們的 AI 就已經像模像樣了,至少從業余玩家的角度來說。

優化后,[點擊試玩](https://jsfiddle.net/lhartikk/m5q6fgtb/1/)
優化后,[點擊試玩](https://jsfiddle.net/lhartikk/m5q6fgtb/1/)

結語

總的來說,我們所創造的這個簡單的 AI 不會犯一些愚蠢的錯誤,但依舊缺乏大局觀。

通過以上我介紹的方法,已經能夠使我們的 AI 進行基本的對戰。最終 AI 部分的代碼(不包括移動棋子)不足 200 行,這意味著它實現來非常簡單。你可以在 Github 上查看最終版本

我們還可以繼續優化我們的 AI,比如:

如果你對此感興趣,你可以到 chess programming wiki 中發現更多內容。

感謝閱讀。

更多文章歡迎訪問譯者博客,已升級為 PWA,支持離線訪問、訂閱和添加至桌面。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,572評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,071評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,409評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,569評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,360評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,895評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,979評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,123評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,643評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,559評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,742評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,250評論 5 356
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,981評論 3 346
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,363評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,622評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,354評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,707評論 2 370

推薦閱讀更多精彩內容

  • 棋類游戲對戰的實現 六洲棋 五子棋 AI對戰 藍牙對戰 在線對戰 六洲棋 六洲棋,又稱:泥棋、插方、來馬、五福棋,...
    天機否閱讀 1,236評論 0 9
  • 簡評:本文通過實際范例教大家如何實現一個低配版國際象棋 AI。 文中的基本概念如下,先自行搜索以方便理解: mov...
    極小光閱讀 1,348評論 0 2
  • 五子棋 五子棋五子棋是比較流行的棋類游戲了,玩法簡單,基本上人人會玩,在此就不介紹游戲規則了。下面使用 swift...
    天機否閱讀 20,554評論 3 29
  • 代碼提取 上面我們通過實例講解了肉眼識別Uri更部分的方式,但在代碼中又要怎樣提取呢。下面就看看Uri中提取各部分...
    LandBlink閱讀 2,105評論 0 0
  • 2017年10月14號,星期六,天氣:晴 星期六的早上想偷個懶,沒找到二寶小鑫還想著上個星期姥爺答應來接她去,這家...
    于浩晨閱讀 473評論 0 3