基于Monte Carlo Tree Search的国际象棋Bot未达预期水平排查
蒙特卡洛树搜索国际象棋Bot的BUG排查求助
我正在为Sebastian Lague的「Tiny Chess Bots」竞赛开发一款国际象棋Bot,它采用带Upper Confidence Bounds的Monte Carlo Tree Search实现,但当前表现极差——即便将迭代次数提升至100万+,仍会出现第4步丢皇后这类低级失误,因此我怀疑代码存在问题。
以下是C# Mybot.cs文件的完整代码,其中Monte Carlo Tree Search的实现位于文件后半部分:
public static class Rand{ public static Random random = new (); } public class Node{ public int visits = 2; public double score = 1; public Move move; public Node parent; public List<Node> children = new List<Node>{}; //public Node LastBestChild = null!; public Node(Move m, Node p){ move = m; //move is from parent to node parent = p; } public void ExpandNode(Board b){ Move[] moves = b.GetLegalMoves(); for(int l=0; l<moves.Length; l++){ children.Add(new Node(moves[l], this)); } } public void Update(double result){ visits += 1; //result: 0 for loss and 1 for win and 0.5 for draw score += result; } public bool IsLeaf(){ return (children.Count == 0); } public bool HasParent(){ return (parent != null); } public double UCBValue(){ return ((score/visits)+ 0.4*Math.Sqrt(Math.Log(parent.visits)/visits)); } public Node MaxUCB(){ double maxVal = children[0].UCBValue(); Node maxNode = children[0]; foreach(Node n in children){ double temp = n.UCBValue(); //Console.WriteLine(n.move.ToString() + ": UCB = " + temp.ToString()); if(temp > maxVal || (temp == maxVal && Rand.random.Next(0,1) == 0)){ maxVal = temp; maxNode = n; } } return maxNode; } public Node MaxVisits(){ double maxVal = children[0].visits; Node maxNode = children[0]; foreach(Node n in children){ Console.WriteLine(n.move.ToString() + ": visits = " + n.visits.ToString() + ": score = " + n.score.ToString()); double temp = n.visits; if(temp > maxVal || (temp == maxVal && Rand.random.Next(0,1) == 0)){ maxVal = temp; maxNode = n; } } Console.WriteLine("###########################################################################"); return maxNode; } }; public class MyBot : IChessBot { public Move Think(Board board, Timer timer) { Move[] moves = board.GetLegalMoves(); Node root = new Node(Move.NullMove, null!); for(int i = 0; i < 20000; i++){ Board board1 = Board.CreateBoardFromFEN(board.GetFenString()); Node currentNode = root; //Select while (!currentNode.IsLeaf()){ currentNode = currentNode.MaxUCB(); //Update Board when moving from Node to Node board1.MakeMove(currentNode.move); } //Expansion if (board1.GetLegalMoves().Length > 0){ currentNode.ExpandNode(board1); currentNode = currentNode.children[Rand.random.Next(0, currentNode.children.Count)]; //Update Board when moving from Node to Node board1.MakeMove(currentNode.move); } //Rollouts Move[] legalMoves = board1.GetLegalMoves(); int numCurrentMoves = legalMoves.Length; bool colour = board.IsWhiteToMove; while (!board1.IsInCheckmate() && !board1.IsDraw()){ Move moveToMake = legalMoves[Rand.random.Next(0, numCurrentMoves)]; //Update board board1.MakeMove(moveToMake); legalMoves = board1.GetLegalMoves(); numCurrentMoves = legalMoves.Length; colour = !colour; } //Get results double result =0; if(board1.IsDraw()){ result = 0.5; } else{ if (board1.IsWhiteToMove == colour){ result = 1; } } //Back propagation while (currentNode.HasParent()){ currentNode.Update(result); currentNode = currentNode.parent; result = 1-result; } currentNode.Update(result); } //Select Best Move Move bestMove = root.MaxVisits().move; return bestMove; } }
我已尝试以下调试手段,但均未解决问题:
- 反转布尔标志——怀疑因多一次取反而导致最大化了损失而非收益;
- 记录顶层节点的分数与访问量——未发现明显异常;
- 调整迭代次数与UCBValue函数中的探索常数——效果甚微。
现寻求帮助排查代码错误,以提升Bot的对局水平。
内容的提问来源于stack exchange,提问作者Jakub Skop
相关产品推荐
相关产品推荐

