【问题标题】:Monte-Carlo-Tree Search not working蒙特卡洛树搜索不起作用
【发布时间】:2017-08-12 07:14:09
【问题描述】:

我目前正在为棋盘游戏 Hex 编写 AI。我想使用 Monte-Carlo-Tree-Search 来做到这一点,并且已经尝试实现它。然而,人工智能做出了令人难以置信的愚蠢(随机)动作,我不知道为什么它不起作用。

import java.util.ArrayList;
import java.util.Random;

/**
 * Created by Robin on 18.03.2017.
 */
public class TreeNode {


    private static final Random random = new Random();
    private static final double epsion=10e-5;
    protected double nvisits;
    protected double totValue;
    protected int move=-1;

    private HexBoard board;
    protected ArrayList<TreeNode>children ;



    public TreeNode(HexBoard board){
        this.board =board;
    }


    //Copy-Constructor
    public TreeNode(TreeNode treeNode){
        this.nvisits=treeNode.nvisits;
        this.totValue=treeNode.totValue;
        this.move=treeNode.move;
        this.board = new HexBoard(treeNode.board);

    }

    public void update(double value){
        totValue+=value*board.color;
        nvisits++;
    }



    public void expand(){
        assert(children==null);
        children = new ArrayList<>(121-board.moveCount);
        for(int i=0;i<121;i++){
            if(board.board[i]!=HexBoard.EMPTY)
                continue;

                TreeNode newNode = new TreeNode(board);
                newNode.move =i;
                children.add(newNode);

        }
    }

    public void calculateIteration(){
        ArrayList<TreeNode>visited = new ArrayList<>();
        TreeNode current =this;
        visited.add(current);

        while(!current.isLeafNode()){
            current =current.select();
            board.makeMove(current.move);
            visited.add(current);
        }

        //Found a leaf node
        double value;
        if(current.board.getWinner()==0){
            current.expand();
            TreeNode newNode =current.select();
            value =playOut(newNode.board);
        }else{
            value =current.board.getWinner();
        }

        //update all the nodes

        for(int i=1;i<visited.size();i++){
            visited.get(i).update(value);
            board.undoMove(visited.get(i).move);
        }
        visited.get(0).update(value);
    }

    public static int playOut(HexBoard board){
        int winner=0;

        if(board.moveCount==121) {
            winner=board.getWinner();

            return winner;
        }

        //Checking-Movecount vs actual stones on the board


        final double left =121-board.moveCount;
        double probibility =1/left;
        double summe =0;
        double p =random.nextDouble();

        int randomMove =0;
        for(int i=0;i<121;i++){
            if(board.board[i]!=HexBoard.EMPTY)
                continue;

            summe+=probibility;

            if(p<=summe && probibility!=0) {
                randomMove = i;
                break;
            }
        }

        board.makeMove(randomMove);
        winner =playOut(board);
        board.undoMove(randomMove);

        return winner;
    }


    public TreeNode select(){

        TreeNode bestNode=null;
        double bestValue =-10000000;
        for(TreeNode node : children){

            double uctvalue =(node.nvisits==0)?100000:(node.totValue/(node.nvisits)+Math.sqrt((Math.log(this.nvisits))/(2*node.nvisits)));
            uctvalue+=epsion*random.nextDouble();

            if(uctvalue>bestValue){
                bestValue=uctvalue;
                bestNode =node;
            }
        }

        return bestNode;
        ///
    }

    public boolean isLeafNode(){
        return (children==null);
    }
}

我在方法 calcualteIteration() 中的实现是否正确?

我知道这可能不是一个非常吸引人的问题,但我会很感激任何帮助

【问题讨论】:

  • 这太宽泛了。请进行一些调试以将其缩小为更简单的问题和minimal test case
  • 你真的在跟踪哪个玩家做了哪些动作吗?你在你的迭代中交替轮流吗?对我来说,这有点像你只是让当前玩家在你的模拟中填满整个棋盘,它假装没有对手。还是我错过了什么?此外,告诉我们您正在运行多少次模拟,以及您最终如何决定在“真实”游戏中玩哪个动作会很有用
  • 对不起,我应该澄清这一点。 board.makemove() 函数在两个玩家之间交替。我尝试了 100-50000 次模拟,结果几乎相同(糟糕的随机动作)。根节点的“最佳”兄弟节点是具有最高 uct 值的节点,将由 AI 播放
  • @CheckersGuy 首先,您的 select() 实现应该考虑要移动哪个玩家。如果你的对手被允许移动,你应该否定 uctvalue 的 totValue/visits 计算(注意:不要否定整个 uctvalue,只有你计算分数的部分。sqrt 下的部分不应该被否定)。在你当前的 select() 实现中,AI 假设他的对手会帮助他。
  • 最后,我不会使用最高的 uct-value 来决定走哪一步,而只使用 totValue/visits 部分(平均得分)。平方根下的 uct-value 部分只会激发您探索搜索树中您尚未探索过的部分,但在“真正的”游戏中采取行动时,这不再重要。至于模拟数量,100 听起来肯定太少了,但接近 50K 应该没问题(对于明显优于随机玩家)

标签: java artificial-intelligence montecarlo tree-search


【解决方案1】:

OP 在问题后在 cmets 中添加了额外信息。该额外信息的重要部分是实现了makeMove() 方法来检查下一个要玩的玩家(以确保对棋盘的更新是正确的)。

鉴于该信息,在 OP 中实现 select() 是不正确的,因为它在计算 UCT 分数时没有考虑要移动哪个玩家。 UCT 分数由“利用”部分(第一部分,计算所有先前模拟的平均分数)和“探索”部分(平方根下的部分,相对于其父节点很少访问的节点增加)组成)。当允许对手下一步行动时,这个等式的利用部分应该被否定。如果不这样做,AI 基本上会假设对手愿意积极帮助 AI,而不是假设对手会试图为自己赢得胜利。

【讨论】:

  • 谢谢。现在它运行得很好刚刚用 5000 次模拟测试它,我赢不了:P
  • 最佳值是最高获胜分数,而不是 uct 值(用于指导树的进一步探索),尤其是在引入随机分量之后。其他实施者在大约 1000-1500 次播放后实现了完美播放。
猜你喜欢
  • 1970-01-01
  • 2018-06-27
  • 2020-04-09
  • 2012-02-21
  • 2018-05-03
  • 1970-01-01
  • 2014-07-11
  • 1970-01-01
  • 2018-11-20
相关资源
最近更新 更多