【发布时间】: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