private void ReportStatus(MonteCarloTreeSearchNode rootNode)
 {
     // Update Status
     if (statusUpdate.ElapsedMilliseconds > millisecondsBetweenUpdates && OnStatus != null)
     {
         EngineStatus status = new EngineStatus();
         foreach (var child in rootNode.Children)
         {
             double eval = Math.Round((child.Visits > 0 ? 200.0 * child.Wins / child.Visits : 0) - 100.0, 2);
             string pv   = "";
             var    c    = child;
             while (c != null && c.Move >= 0 && c.Move <= 80)
             {
                 pv += /*(c.LastToMove == Player.One ? "b " : "o ") +*/ Constants.TileNames[c.Move] + " (" + c.Wins + "/" + c.Visits + ")   ";
                 c   = c.Children?.OrderBy(x => x.Visits)?.ThenBy(x => x.Wins)?.LastOrDefault();
             }
             status.Add(child?.Move ?? 80, eval, pv, child.Visits);
         }
         status.Sort();
         OnStatus?.Invoke(this, status);
         statusUpdate = Stopwatch.StartNew();
     }
 }
        private int MonteCarloTreeSearch(Board rootState)
        {
            var rootNode = new MonteCarloTreeSearchNode(rootState, GetMoves);
            var forceEnd = false;

            var parallelism = 8;
            var semaphore   = new Semaphore(parallelism, parallelism);

            for (int i = 0; i < maxIterations && !cancel.Cancelled && !forceEnd; i++)
            {
                var node  = rootNode;
                var state = new Board(rootState);
                simulationCount++;

                // Select
                while (node.Untried.Count == 0 && node.Children.Count > 0)
                {
                    node = node.SelectChild();
                    state.MakeMove(node.Move);
                    visitedNodes++;
                }

                // Expand
                if (node.Untried.Count > 0)
                {
                    var move = node.Untried[random.Next(node.Untried.Count)];
                    state.MakeMove(move);
                    node = node.AddChild(state, move);
                    visitedNodes++;
                }

                semaphore.WaitOne();

                Task.Run(() =>
                {
                    // Simulate
                    while (state.Winner == Player.Empty && state.Turn < VolcanoGame.Settings.TournamentAdjudicateMaxTurns)
                    {
                        var moves = GetRandomMoves(state);
                        if (moves.Count == 0)
                        {
                            break;
                        }
                        state.MakeMove(moves[random.Next(moves.Count)]);
                        visitedNodes++;
                    }

                    // Backpropagate
                    while (node != null)
                    {
                        if (forceEnd)
                        {
                            break;
                        }

                        //var fastWinReward = 0.5 * (state.Turn - rootState.Turn) / VolcanoGame.Settings.TournamentAdjudicateMaxTurns;
                        //node.Update(state.Winner == node.LastToMove ? 1.0 - fastWinReward : 0.0);

                        node.Update(state.Winner == node.LastToMove ? 1.0 : 0.0);
                        node = node.Parent;
                        visitedNodes++;
                    }

                    semaphore.Release();
                });

                // Cut Short
                foreach (var child in rootNode.Children)
                {
                    // If we have a potential move that has a 100% win rate and it's been visited a lot of times, stop searching
                    if (child.Visits > 500 && child.Wins == child.Visits)
                    {
                        forceEnd = true;
                    }
                }

                // Update Status
                if (statusUpdate.ElapsedMilliseconds > millisecondsBetweenUpdates && OnStatus != null)
                {
                    EngineStatus status = new EngineStatus();
                    foreach (var child in rootNode.Children)
                    {
                        double eval = Math.Round((child.Visits > 0 ? 200.0 * Math.Min(child.Wins, child.Visits) / child.Visits : 0) - 100.0, 2);
                        string pv   = "";
                        var    c    = child;
                        while (c != null && c.Move >= 0 && c.Move <= 80)
                        {
                            pv += Constants.TileNames[c.Move] + " (" + Math.Round(c.Wins, 2) + "/" + c.Visits + ")   ";
                            c   = c.Children?.OrderBy(x => x.Visits)?.ThenBy(x => x.Wins)?.LastOrDefault();
                        }
                        status.Add(child?.Move ?? 80, eval, pv, child.Visits);
                    }
                    status.Sort();
                    OnStatus(this, status);
                    statusUpdate = Stopwatch.StartNew();
                }
            }

            forceEnd = true;

            return(rootNode.Children.OrderBy(x => x.Visits).LastOrDefault().Move);
        }
Пример #3
0
        private int MonteCarloTreeSearch(Board rootState)
        {
            var rootNode = new MonteCarloTreeSearchNode(rootState, GetMoves);
            var forceWin = false;

            while (!cancel.Cancelled && !forceWin)
            {
                var node  = rootNode;
                var state = new Board(rootState);

                state.allowHash     = _allowHash;
                state.fastWinSearch = _allowFastWinSearch;

                if (_allowHash)
                {
                    state.winHashes = winHashes;
                }

                simulationCount++;

                // Select
                while (node.Untried.Count == 0 && node.Children.Count > 0)
                {
                    node = node.SelectChild(_ucbFactor);
                    state.MakeMove(node.Move);
                    visitedNodes++;
                }

                // Expand
                if (node.Untried.Count > 0)
                {
                    var move = node.Untried[random.Next(node.Untried.Count)];
                    state.MakeMove(move);
                    node = node.AddChild(state, move);
                    visitedNodes++;
                }

                // Simulate
                while (state.Winner == Player.Empty && state.Turn < VolcanoGame.Settings.TournamentAdjudicateMaxTurns)
                {
                    var moves = state.GetMoves();
                    if (moves.Count == 0)
                    {
                        break;
                    }
                    state.MakeMove(moves[random.Next(moves.Count)]);
                    visitedNodes++;
                }

                // Backpropagate
                while (node != null)
                {
                    node.Update(state.Winner == node.LastToMove ? 1.0 : 0.0);
                    node = node.Parent;
                    visitedNodes++;
                }

                // Cut Short
                if (_allowForcedWins)
                {
                    foreach (var child in rootNode.Children)
                    {
                        // If we have a potential move that has a 100% win rate and it's been visited a lot of times, stop searching
                        if (child.Visits > 500 && child.Wins == child.Visits)
                        {
                            forceWin = true;
                        }
                    }
                }

                // Update Status
                if (statusUpdate.ElapsedMilliseconds > millisecondsBetweenUpdates && OnStatus != null)
                {
                    EngineStatus status = new EngineStatus();
                    foreach (var child in rootNode.Children)
                    {
                        double eval = Math.Round((child.Visits > 0 ? 200.0 * child.Wins / child.Visits : 0) - 100.0, 2);
                        string pv   = "";
                        var    c    = child;
                        while (c != null && c.Move >= 0 && c.Move <= 80)
                        {
                            pv += Constants.TileNames[c.Move] + " (" + c.Wins + "/" + c.Visits + ")   ";
                            c   = c.Children?.OrderBy(x => x.Visits)?.ThenBy(x => x.Wins)?.LastOrDefault();
                        }
                        status.Add(child?.Move ?? 80, eval, pv, child.Visits);
                    }
                    status.Sort();
                    OnStatus?.Invoke(this, status);
                    statusUpdate = Stopwatch.StartNew();
                }
            }

            return(rootNode.Children.OrderBy(x => x.Visits).LastOrDefault().Move);
        }