极光日报
首发于极光日报

手把手教你实现一个低配版象棋 AlphaGo

简评:本文通过实际范例教大家如何实现一个低配版国际象棋 AI。

文中的基本概念如下,先自行搜索以方便理解:

  • move-generation
  • board evaluation
  • minimax
  • alpha beta pruning

在每个步骤中,我们将使用其中某种技术来改进我们的算法,并且演示他们是如何影响象棋 AI 的执行效果。

你可以在 GitHub 上查看算法的最终版。

我无法打败我自己写的象棋程序,真不知道这是我太弱还是算法太好。
—— lhartikk

Step 1: 移动生成和边界可视化

使用 chess.js 库进行移动生成,并使用 chessboard.js 来显示棋盘。移动生成库基本上实现了棋的所有规则。基于此,我们可以计算出所有合法棋步。

移动生成功能的可视化。起始位置用作输入,输出是从该位置出发的所有可能的移动。

使用这些库将有助于我们专注于最有趣的任务:最佳棋步算法的构建。

首先,我们创建一个从所有可移动中返回随机移动的函数:

黑手随机移动,不动戳大

Step 2 : 位置评估

现在我们来评估哪一方在某一位置更强。可以用这个表来判断:

通过评估功能,我们可以创建一个选择提供最高评估的动作的算法:

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;

};

我们的算法现在可以在一块区域内实现评估。

Step 3: 使用 Minimax 搜索树

接下来,我们要创建一个搜索树,算法可以从中选择最佳方法。这里用 Minimax 算法实现。

在该算法中,将所有可能移动的递归树探索到给定的深度,并且在树叶处评估位置。

之后,我们将最小值或最大值返回给父节点,这取决于它是移动白色还是黑色。(也就是说,我们尽可能地减少或最大限度地提高每一级的成果。)

白色的最佳举措是 b2-c3,因为我们可以保证我们能够达到评估为 -50 的位置
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;
    }
};

利用最小值,我们的算法开始了解象棋的基本策略:

极小值算法的有效性很大程度上取决于我们可以实现的搜索深度,我们将在以下步骤中进行改进。

Step 4: Alpha-beta pruning

Alpha-beta pruning 是最小化算法的优化,允许我们忽略搜索树中的一些分支。这有助于我们在使用相同的资源的同时更深入地评估最小值搜索树。如果我们发现导致比之前动作更糟糕的情况时,这个算法能停止评估搜索树。它不会影响算法结果,反而能加速其进行。

使用 alpha-beta,可以显着提升极小值算法,如下例所示:

按照这个链接来尝试这个国际象棋 AI 的 alpha-beta 改进版。

Step 5: 改进评估功能

初始评估功能非常简陋。为了改善这一点,我们增加一些因素。比如,棋盘中心的骑士比边缘的骑士更好(因为它有更多的选择,因此更加活跃)。

chess-programming-wiki 中描述的改进表格:

改进后我们能得到稍微聪明一点的象棋 AI,至少从业余玩家的眼中是这样的。

结论

即使是这样一个简单的象棋算法,它的优势也在于几乎不会产生显而易见的错误。但这同时也显示,它缺乏战略性思考。

本文阐述的方法已经足够编程基本象棋的棋艺算法,AI 的部分其实只有区区 200 行代码(不包括 move-generation),实现起来并不难。

GitHub 链接看最终版本,更多信息可以查看 chess programming wiki


原文地址:

A step-by-step guide to building a simple chess AI

分类收藏:

产品 | 设计 | 技术 | HackLife | Photography

推荐阅读:
Python 的学习阶段zhuanlan.zhihu.com图标

编辑于 2018-11-05

文章被以下专栏收录

    简介:每日导读(或翻译)三篇优质英文文章,内容 80% 涉及硅谷/编程/科技/,期待共同成长。