Я потратил дни, пытаясь выяснить, почему мой MCTS не работает, и сузил его до этого
class Node
{
public readonly Node parent;
public readonly List<Node> children = new List<Node>();
public readonly IMove move;
public int simulations { get; private set; }
public double wins { get; private set; }
private bool myTurn;
public int depth { get; private set; }
public Node FindPromisingChild(double c = 1.41, bool printOut = false)
{
var unvisited = children.Where(child => child.simulations == 0);
Node mostPromisingChild= null;
if (unvisited.Count() == 0)
{
double maxScore = double.MinValue;
foreach (var child in children)
{
double score = child.wins / child.simulations + c * Math.Sqrt(Math.Log(simulations) / child.simulations);
if (score > maxScore)
{
mostPromisingChild = child;
maxScore = score;
}
if (printOut)
{
Console.WriteLine("SCORE: " + score);
Console.WriteLine("MAX SCORE: " + maxScore);
Console.WriteLine("CHILD VISITS: " + child.simulations);
Console.WriteLine("CHILD WINS: " + child.wins);
child.PrintOut(c);
}
}
} else
{
mostPromisingChild = unvisited.ElementAt(new Random().Next(unvisited.Count()));
}
return mostPromisingChild;
}
Так что это не выбор оптимального ребенка. Но когда я вызываю функцию PrintOut в «дочернем» объекте, она по какой-то причине делает это в разделе «ALT SCORE».
public void PrintOut(double c = 1.41)
{
Console.WriteLine("-------------------------------------------------");
Console.WriteLine("WINS: " + wins);
Console.WriteLine("VISITS: " + simulations);
Console.WriteLine("IS MY TURN: " + myTurn);
Console.WriteLine("MOVE: " + move);
Console.WriteLine("TOTAL VISITS: " + parent.simulations);
Console.WriteLine("ALT SCORE: " + wins / simulations + c * Math.Sqrt(Math.Log(parent.simulations) / simulations));
}
Но вот и самое странное. Значения child.simulations и child.wins в первой функции в «родительском» объекте не совпадают со значениями выигрышей и симуляций во второй функции внутри самого «дочернего» объекта. Я запускаю его в конфигурации консольного приложения. NET, и, насколько я понимаю, основная функция должна вести себя как однопоточное приложение (хотя больше потоков создается для других целей, таких как сборка мусора). У меня такое чувство, что я упускаю что-то очевидное.