public override PlayerTask GetMove(POGame game)
        {
            playerid = game.CurrentPlayer.PlayerId;
            thinkcounter++;
            sw.Start();

            float             timeLeft = 1 - sw.ElapsedMilliseconds / 30000.0f;
            List <PlayerTask> options  = game.CurrentPlayer.Options();

            Dictionary <PlayerTask, int> values = new Dictionary <PlayerTask, int>();
            int depth = 2;

            if (sw.ElapsedMilliseconds > 15000)
            {
                depth--;
            }
            if (sw.ElapsedMilliseconds > 25000)
            {
                depth--;
            }
            values = SimulateAll(game, options, depth);

            PlayerTask besttask = values.OrderBy(x => x.Value).ToList().Last().Key;

            game.Process(besttask);
            if (Score(game) == int.MaxValue)
            {
                Console.WriteLine("Won after: " + Math.Round(sw.ElapsedMilliseconds / 1000.0f) + "s; Turns: " + thinkcounter);
            }
            sw.Stop();
            return(besttask);
        }
        public override PlayerTask GetMove(POGame poGame)
        {
            if (controller == null)
            {
                controller = poGame.CurrentPlayer;
            }

            List <PlayerTask> options = poGame.CurrentPlayer.Options();

            if (options.Count == 1)
            {
                return(options[0]);
            }

            // For now the MCTS does not have memory.
            // It Creates a new MCTS node, simulates, and deletes it.
            MCTSNode realGame = new MCTSNode(null, null, poGame, null);

            while (realGame.N < NUM_ITERATIONS)
            {
                MCTSNode node = Select(realGame);
                node = Expand(node);
                double rolloutY6 = Rollout(node);
                BackPropagate(node, rolloutY6);
            }

            return(realGame.Children.OrderBy(x => x.Y6()).Last().Action);
        }
        public SabberStoneAction Act(SabberStoneState state)
        {
            var timer     = System.Diagnostics.Stopwatch.StartNew();
            var gameState = (SabberStoneState)state.Copy();

            if (_debug)
            {
                Console.WriteLine();
            }
            if (_debug)
            {
                Console.WriteLine(Name());
            }
            if (_debug)
            {
                Console.WriteLine($"Starting a Heuristic search in turn {(gameState.Game.Turn + 1) / 2}");
            }

            var solution = new SabberStoneAction();

            while (gameState.CurrentPlayer() == PlayerID() && gameState.Game.State != State.COMPLETE)
            {
                var poGame = new POGame(gameState.Game, _debug);
                var task   = GetMove(poGame);
                solution.AddTask((SabberStonePlayerTask)task);
                gameState.Game.Process(task);
            }

            var time = timer.ElapsedMilliseconds;

            if (_debug)
            {
                Console.WriteLine();
            }
            if (_debug)
            {
                Console.WriteLine($"Heuristic returned with solution: {solution}");
            }
            if (_debug)
            {
                Console.WriteLine($"My total calculation time was: {time} ms.");
            }

            // Check if the solution is a complete action.
            if (!solution.IsComplete())
            {
                // Otherwise add an End-Turn task before returning.
                if (_debug)
                {
                    Console.WriteLine("Solution was an incomplete action; adding End-Turn task.");
                }
                solution.Tasks.Add((SabberStonePlayerTask)EndTurnTask.Any(Player));
            }

            if (_debug)
            {
                Console.WriteLine();
            }
            return(solution);
        }
        private Dictionary <PlayerTask, int> SimulateAll(POGame initgame, List <PlayerTask> opitons, int depth)
        {
            Dictionary <PlayerTask, POGame> sims   = initgame.Simulate(opitons);
            Dictionary <PlayerTask, int>    scores = new Dictionary <PlayerTask, int>();

            foreach (KeyValuePair <PlayerTask, POGame> sim in sims)
            {
                int score = int.MinValue;
                if (sim.Key.PlayerTaskType == PlayerTaskType.END_TURN || depth <= 0)
                {
                    score = Score(sim.Value);
                }
                else
                {
                    List <PlayerTask> new_options = sim.Value.CurrentPlayer.Options();
                    if (new_options.Count() > 0)
                    {
                        score = SimulateAll(sim.Value, new_options, depth - 1).ToList().Max(x => x.Value);
                    }
                    else
                    {
                        score = Score(sim.Value);
                    }
                }
                scores.Add(sim.Key, score);
            }
            return(scores);
        }
Beispiel #5
0
 public MCTSNode(MCTSNode parent, PlayerTask task, POGame poGame)
 {
     this.parent = parent;
     this.task   = task;
     this.poGame = poGame;
     children    = new List <MCTSNode>();
 }
Beispiel #6
0
        public void InitTree(TyStateAnalyzer analyzer, POGame root, List <PlayerTask> options)
        {
            _sortedNodes.Clear();
            _explorableNodes.Clear();
            _nodesToEstimate.Clear();

            _analyzer = analyzer;
            _rootGame = root;

            //var initialResults = TyStateUtility.GetSimulatedGames(root, options, _analyzer);

            for (int i = 0; i < options.Count; i++)
            {
                PlayerTask task = options[i];

                var node = new TyTaskNode(this, _analyzer, task, 0.0f);

                //end turn is pretty straight forward, should not really be looked at later in the simulations, just simulate once and keep the value:
                if (task.PlayerTaskType == PlayerTaskType.END_TURN)
                {
                    TySimResult sim = TyStateUtility.GetSimulatedGame(root, task, _analyzer);
                    node.AddValue(sim.value);
                }
                else
                {
                    _explorableNodes.Add(node);
                    _sortedNodes.Add(node);
                }

                _nodesToEstimate.Add(task, node);
            }
        }
        private float DefaultPolicyHeuristic(Node node)
        {
            float  result           = 0;
            POGame state            = node.State;
            int    currentTurnDepth = node.TurnDepth;

            while (state.State != State.COMPLETE &&
                   (node.Action.PlayerTaskType == PlayerTaskType.END_TURN ?
                    currentTurnDepth < TurnDepth - 1 :
                    currentTurnDepth < TurnDepth))
            {
                Controller        currentPlayer = state.CurrentPlayer;
                List <PlayerTask> actions       = currentPlayer.Options();
                List <PlayerTask> oldActions    = new List <PlayerTask>(actions);
                bool uncertainity = currentPlayer.Options()
                                    .Any(option => option.PlayerTaskType == PlayerTaskType.PLAY_CARD && option.Source.Card.Name == "No Way!");

                // end potential infinite loop of 0 cost spells (happend few times with a mage)
                if (currentPlayer.CardsPlayedThisTurn.Count > 50)
                {
                    break;
                }

                if (uncertainity)
                {
                    // depending on active player choose one:
                    // 1) simulate player's card draw
                    // 2) simulate opponent's possible actions
                    actions = state.CurrentPlayer.PlayerId == player.PlayerId ?
                              ActionEstimator.DrawSimulation(currentPlayer) :
                              ActionEstimator.ActionEstimaton(state, currentPlayer);
                }

                var bestPair = state.Simulate(actions.Any() ? actions : oldActions)
                               .Where(pair => pair.Value != null)
                               .OrderBy(pair => StateRateStrategies.GetStateRateStrategy(StateRateStrategy.Greedy)(pair.Value, currentPlayer))
                               .Last();

                //Console.WriteLine(currentTurnDepth + ", " + bestPair.Key);

                if (bestPair.Key.PlayerTaskType == PlayerTaskType.END_TURN)
                {
                    currentTurnDepth++;
                }

                state = bestPair.Value;

                // this should be redundant.. but, you know.. just in case..
                if (state == null)
                {
                    return(0.5f);
                }
            }

            var firstPlayer = state.CurrentPlayer.PlayerId == player.PlayerId ? state.CurrentPlayer : state.CurrentOpponent;

            result = StateRateStrategies.GetStateRateStrategy(StateRateStrategy.Greedy)(state, firstPlayer);

            return(result);
        }
Beispiel #8
0
 public MCTSNode(POGame poGame)
 {
     parent      = null;
     task        = null;
     children    = new List <MCTSNode>();
     this.poGame = poGame;
 }
        public PlayerTask GetBestMove(POGame game)
        {
            Dictionary <PlayerTask, POGame> SimulationData = SimulateGame(game);
            PlayerTask BestMove = EvaluateSimulation(SimulationData, game);

            return(BestMove);
        }
Beispiel #10
0
        private static PlayerTask greedyTask(POGame poGame, ParametricGreedyAgent greedyAgent, Random Rnd, double CHILDREN_CONSIDERED_SIMULATING)
        {
            PlayerTask        bestTask           = null;
            double            bestScore          = Double.MinValue;
            double            score              = 0;
            List <PlayerTask> taskToSimulate     = new List <PlayerTask>();
            POGame            stateAfterSimulate = null;

            List <PlayerTask> options = poGame.CurrentPlayer.Options();

            int cutPoint = (int)Math.Ceiling(poGame.CurrentPlayer.Options().Count *CHILDREN_CONSIDERED_SIMULATING);

            while (options.Count > cutPoint)
            {
                options.Remove(options[Rnd.Next(0, options.Count - 1)]);
            }

            foreach (PlayerTask task in poGame.CurrentPlayer.Options())
            {
                taskToSimulate.Add(task);
                stateAfterSimulate = poGame.Simulate(taskToSimulate)[task];

                score = greedyAgent.scoreTask(poGame, stateAfterSimulate);
                if (score > bestScore)
                {
                    bestScore = score;
                    bestTask  = task;
                }
                taskToSimulate.Clear();
            }
            return(bestTask);
        }
Beispiel #11
0
 internal RicksNode(POGame _game, PlayerTask _task = null, RicksNode _parent = null)
 {
     game   = _game;
     parent = _parent;
     task   = _task;
     score  = 0;
 }
Beispiel #12
0
        public override PlayerTask GetMove(POGame game)
        {
            var player = game.CurrentPlayer;
            //var opponent = game.CurrentOpponent;

            // Get all simulation results for simulations that didn't fail
            //var validOpts = game.Simulate(player.Options()).Where(x => x.Value != null);
            var validOpts = game.Simulate(player.Options());

            //Put player options in a variable
            var playerOptions = player.Options();

            /*foreach (var x in playerOptions)
             * {
             *      Console.WriteLine($"Here is an option {x.PlayerTaskType}");
             * }*/

            List <PlayerTask> PlayerTaskList = new List <PlayerTask>(validOpts.Keys);

            PlayerTask endTurnTask = playerOptions.First();
            PlayerTask returnTask;

            updateStates(game);

            //Prints the game turn and the remaining mana available
            //Console.WriteLine($"Turn {game.Turn} Move {moveNum}, you have {player.RemainingMana} mana available.");
            //Prints the board state for the game turn
            //Console.WriteLine($"Board State for Turn {game.Turn}:" + game.PartialPrint());

            InspectHand(player.HandZone);

            returnTask = PlayCard(PlayerTaskList, game.CurrentPlayer, player.HandZone, player.BoardZone, game);
            if (returnTask != null)
            {
                return(returnTask);
            }

            returnTask = MinionAttack(PlayerTaskList, game);
            if (returnTask != null)
            {
                return(returnTask);
            }
            if (player.RemainingMana > 0)
            {
                returnTask = HeroPower(PlayerTaskList, game.CurrentPlayer);
                if (returnTask != null)
                {
                    return(returnTask);
                }
            }

            returnTask = heroAttack(PlayerTaskList);
            if (returnTask != null)
            {
                return(returnTask);
            }

            //Console.WriteLine($"Turn {game.Turn}: Ending Turn {endTurnTask}");
            return(endTurnTask);
        }
        public override PlayerTask GetMove(POGame game)
        {
            var player    = game.CurrentPlayer;
            var validOpts = game.Simulate(player.Options()).Where(x => x.Value != null);
            var optcount  = validOpts.Count();

            var returnValue = validOpts.Any() ?
                              validOpts.Select(x => score(x, player.PlayerId, (optcount >= 5) ? ((optcount >= 25) ? 1 : 2) : 3)).OrderBy(x => x.Value).Last().Key :
                              player.Options().First(x => x.PlayerTaskType == PlayerTaskType.END_TURN);

            return(returnValue);

            KeyValuePair <PlayerTask, int> score(KeyValuePair <PlayerTask, POGame> state, int player_id, int max_depth = 3)
            {
                int max_score = int.MinValue;

                if (max_depth > 0 && state.Value.CurrentPlayer.PlayerId == player_id)
                {
                    var subactions = state.Value.Simulate(state.Value.CurrentPlayer.Options()).Where(x => x.Value != null);

                    foreach (var subaction in subactions)
                    {
                        max_score = Math.Max(max_score, score(subaction, player_id, max_depth - 1).Value);
                    }
                }
                max_score = Math.Max(max_score, Score(state.Value, player_id));
                return(new KeyValuePair <PlayerTask, int>(state.Key, max_score));
            }
        }
Beispiel #14
0
        private PlayerTask GetSimulationTreeTask(POGame poGame, List <PlayerTask> options)
        {
            double time = TyUtility.GetSecondsSinceStart() - _turnTimeStart;

            if (time >= TyConst.MAX_TURN_TIME)
            {
                TyDebug.LogError("Turn takes too long, fall back to greedy.");
                return(GetGreedyBestTask(poGame, options));
            }

            _simTree.InitTree(_analyzer, poGame, options);

            //-1 because TurnEnd won't be looked at:
            int optionCount = options.Count - 1;
            int numEpisodes = (int)((optionCount) * _curEpisodeMultiplier);

            double simStart = TyUtility.GetSecondsSinceStart();

            for (int i = 0; i < numEpisodes; i++)
            {
                if (!IsAllowedToSimulate(simStart, i, numEpisodes, optionCount))
                {
                    break;
                }

                bool shouldExploit = ((double)i / (double)numEpisodes) > EXPLORE_TRESHOLD;
                _simTree.SimulateEpisode(_random, i, shouldExploit);
            }

            TyTaskNode bestNode = _simTree.GetBestNode();

            return(bestNode.Task);
        }
Beispiel #15
0
        //\the helper methods

        //the worth functions------------------------------------------------------------------------

        private double getWorth(POGame poGame, int playerId)         //TODO: implement
        {
            Controller own   = poGame.CurrentPlayer.PlayerId == playerId ? poGame.CurrentPlayer : poGame.CurrentOpponent;
            Controller enemy = poGame.CurrentPlayer.PlayerId == playerId ? poGame.CurrentOpponent : poGame.CurrentPlayer;

            //variables bevore the task execution
            int turn = getTurn(poGame);


            double ownStrengthFactor  = factors[0] * (getMinionStrehgth(own) + getPlayerAttackDamage(own)) + factors[1] * getMinionHealth(own);
            double ownHealthFactor    = factors[2] * (getPlayerArmor(own) + getPlayerHealth(own));
            double ownPotentialFactor = factors[3] * getMana(own) + factors[4] * getHandCardCount(own);
            double ownFactor          = (ownStrengthFactor + ownHealthFactor + ownPotentialFactor) * factors[5];

            double enemyStrengthFactor  = factors[6] * (getMinionStrehgth(enemy) + getPlayerAttackDamage(enemy)) + factors[7] * getMinionHealth(enemy);
            double enemyHealthFactor    = factors[8] * (getPlayerArmor(enemy) + getPlayerHealth(enemy));
            double enemyPotentialFactor = factors[9] * getMana(enemy) + factors[10] * getHandCardCount(enemy);
            double enemyFactor          = (enemyStrengthFactor + enemyHealthFactor + enemyPotentialFactor) * factors[11];

            return(ownFactor + enemyFactor);
            //unused potential:

            //GraveyardZone myGraveyardZone = poGame.CurrentPlayer.GraveyardZone;
            //GraveyardZone enemyGraveyardZone = poGame.CurrentOpponent.GraveyardZone;

            //concider potential synergy effects from cards (like boost for dragon cards)
            //concider evvects like shield or the unbreakable thing

            //Concider shield/armor(?) on the health of the own player
        }
Beispiel #16
0
        private PlayerTask BeamSearch(POGame game, int depth, int playerbeamWidth, int opponentBeamWidth)
        {
            Controller me = game.CurrentPlayer;

            List <HardSimulation> bestSimulations = Simulate(game, playerbeamWidth);

            LabelSimulations(bestSimulations, 0);

            for (int i = 1; i < depth; i++)
            {
                var newBestSimulations = new List <HardSimulation>();
                foreach (HardSimulation sim in bestSimulations)
                {
                    int beamWidth = sim.Game.CurrentPlayer.PlayerId == me.PlayerId
                                                ? playerbeamWidth
                                                : opponentBeamWidth;
                    List <HardSimulation> childSims = Simulate(sim.Game, beamWidth);
                    LabelSimulations(childSims, i);
                    childSims.ForEach(x => x.Parent = sim);
                    newBestSimulations.AddRange(childSims);
                }

                bestSimulations = newBestSimulations
                                  .OrderBy(x => Score(x.Game, me.PlayerId))
                                  .TakeLast(playerbeamWidth)
                                  .Reverse()
                                  .ToList();
            }

            PlayerTask nextMove = bestSimulations.Any()
                                ? bestSimulations.First().GetFirstTask()
                                : me.Options().First(x => x.PlayerTaskType == PlayerTaskType.END_TURN);

            return(nextMove);
        }
        static public float estimateFromState(string estimationMode, POGame poGame)
        {
            float score = 0.5f;

            switch (estimationMode)
            {
            case "LinearEstimation":
                score = linearEstimation(poGame);
                break;

            case "ValueEstimation":
                score = valueEstimation(poGame);
                break;

            case "GradualEstimation":
                score = gradualEstimation(poGame);
                break;

            default:
                score = 0;
                break;
            }

            return(score);
        }
Beispiel #18
0
        KeyValuePair <PlayerTask, double> getBestTask(POGame state)
        {
            double            bestScore = Double.MinValue;
            PlayerTask        bestTask  = null;
            List <PlayerTask> list      = state.CurrentPlayer.Options();

            foreach (PlayerTask t in list)
            {
                debug("---->POSSIBLE " + stringTask(t));

                double score  = 0;
                POGame before = state;
                if (t.PlayerTaskType == PlayerTaskType.END_TURN)
                {
                    score = 0;
                }
                else
                {
                    List <PlayerTask> toSimulate = new List <PlayerTask>();
                    toSimulate.Add(t);
                    Dictionary <PlayerTask, POGame> simulated = state.Simulate(toSimulate);
                    //Console.WriteLine("SIMULATION COMPLETE");
                    POGame nextState = simulated[t];
                    score = scoreTask(state, nextState);                     //Warning: if using tree, avoid overflow with max values!
                }
                debug("SCORE " + score);
                if (score >= bestScore)
                {
                    bestTask  = t;
                    bestScore = score;
                }
            }

            return(new KeyValuePair <PlayerTask, double>(bestTask, bestScore));
        }
Beispiel #19
0
        // Calculate different scores based on our hero's class
        public int Score(POGame state, int playerId)
        {
            Controller p = state.CurrentPlayer.PlayerId == playerId ? state.CurrentPlayer : state.CurrentOpponent;

            switch (h_score)
            {
            case 0:
                return(new HarderAggroScore {
                    Controller = p
                }.Rate());

            case 1:
                return(new HarderMidRangeScore {
                    Controller = p
                }.Rate());

            case 2:
                return(new HarderControlScore {
                    Controller = p
                }.Rate());

            default:
                return(new HarderMidRangeScore {
                    Controller = p
                }.Rate());
            }
        }
Beispiel #20
0
        public override PlayerTask GetMove(POGame game)
        {
            var player = game.CurrentPlayer;

            // Implement a simple Mulligan Rule
            if (player.MulliganState == Mulligan.INPUT)
            {
                List <int> mulligan = new WeightedScore().MulliganRule().Invoke(player.Choice.Choices.Select(p => game.getGame().IdEntityDic[p]).ToList());
                return(ChooseTask.Mulligan(player, mulligan));
            }

            //Lookhead n steps and choose the best scoring <PlayerTask> and each depth -> branch ->score->until depth
            //(DFS search)

            var validOpts = game.Simulate(player.Options()).Where(x => x.Value != null);
            var voptcount = validOpts.Count();


            if (validOpts.Any())
            {
                var depth  = voptcount > 5 ? (voptcount > 25 ? 1: 2) : 3;
                var scored = validOpts.Select(x => Simulate(x, player.PlayerId, depth));
                // Array.ForEach(scored.Select(x=>x.Item1).ToArray(),Console.Write);
                // Console.Write($"\r{scored.Count()}  ");
                return(scored.OrderBy(x => x.Value).Last().Key);
            }
            else
            {
                return(player.Options().First(x => x.PlayerTaskType == PlayerTaskType.END_TURN));
            }
        }
Beispiel #21
0
        // Simulates an option and returns the score that this option achieves
        // maxDepth: the maximum amount of recursive calls that is allowed
        private KeyValuePair <PlayerTask, double> Simulate(POGame poGame, int playerId, int maxDepth = 3)
        {
            List <PlayerTask> options = poGame.CurrentPlayer.Options();

            Dictionary <PlayerTask, double> scores = new Dictionary <PlayerTask, double>();

            options.ForEach(o => scores[o] = 0.0);

            poGame.Simulate(options).ToList().ForEach(simulation =>
            {
                if (simulation.Value == null)
                {
                    scores[simulation.Key] = Double.MinValue;
                    return;
                }

                scores[simulation.Key] += ComputeScore(simulation.Value, playerId);
                if (maxDepth > 0 && playerId == poGame.CurrentPlayer.PlayerId)
                {
                    scores[simulation.Key] += Simulate(simulation.Value, playerId, maxDepth - 1).Value;
                }
            });

            // Get the option with the highest score
            return(scores.Aggregate((x, y) => x.Value > y.Value ? x : y));
        }
Beispiel #22
0
        private void Expand(Node node, POGame poGame)
        {
#if DEBUG
            Console.WriteLine($"basemana:{poGame.CurrentPlayer.BaseMana}, usedmana:{poGame.CurrentPlayer.UsedMana}, tempmana:{poGame.CurrentPlayer.TemporaryMana}");
            foreach (var minion in poGame.CurrentPlayer.BoardZone)
            {
                Console.WriteLine($"minion:{minion}, cost:{minion.Cost}");
            }
            foreach (var card in poGame.CurrentPlayer.HandZone)
            {
                Console.WriteLine($"hand:{card}, cost:{card.Cost}");
            }
            if (poGame.CurrentPlayer.Hero.Weapon != null)
            {
                Console.WriteLine($"weapon:{poGame.CurrentPlayer.Hero.Weapon.AttackDamage}");
            }
#endif
            var options = poGame.CurrentPlayer.Options();
            node.edges = new List <Edge>();
            foreach (PlayerTask task in options)
            {
                var code = GetActionHashCode(task);
#if DEBUG
                Console.WriteLine($"{task}, {code}");
#endif
                node.edges.Add(new Edge(code /*, task.ToString()*/));
            }
        }
Beispiel #23
0
 private void InitializeRoot(Node root, POGame poGame)
 {
     foreach (PlayerTask task in poGame.CurrentPlayer.Options())
     {
         root.children.Add(new Node(task, root, root.depth + 1));
     }
 }
        public override PlayerTask GetMove(POGame game)
        {
            int depth;
            int beamWidth;

            // Check how much time we have left on this turn. The hard limit is 75 seconds so we already stop
            // beam searching when 60 seconds have passed, just to be sure.
            if (_watch.ElapsedMilliseconds < 60 * 1000)
            {             // We still have ample time, proceed with beam search
                depth     = 15;
                beamWidth = 12;
            }
            else
            {             // Time is running out, just simulate one timestep now
                depth     = 1;
                beamWidth = 1;
                Console.WriteLine("Over 60s in turn already. Pausing beam search for this turn!");
            }

            _watch.Start();
            var move = BeamSearch(game, depth, playerbeamWidth: beamWidth, opponentBeamWidth: 1);

            _watch.Stop();

            if (move.PlayerTaskType == PlayerTaskType.END_TURN)
            {
                _watch.Reset();
            }

            return(move);
        }
Beispiel #25
0
 public SimGame(POGame _game, int _depth)
 {
     tasks = new PlayerTask[0];
     depth = _depth;
     step  = 0;
     game  = _game;
 }
Beispiel #26
0
        //public int AlternativeGameValue(int playerID) => new ScoreTest { Controller = playerID == game.CurrentPlayer.PlayerId ? game.CurrentPlayer : game.CurrentOpponent }.Rate();

        public SimGame(POGame _game, int _depth, PlayerTask[] _tasks, int _step)
        {
            game  = _game;
            depth = _depth;
            tasks = _tasks;
            step  = _step;
        }
        public PlayerTask MCTS(POGame state)
        {
            Controller player   = state.CurrentPlayer;
            var        timer    = Stopwatch.StartNew();
            Node       rootNode = new Node(parentNode: null, gameState: new KeyValuePair <PlayerTask, POGame>(null, state));

            // Do as long as we've got time
            while (timer.ElapsedMilliseconds < 10000 && rootNode.N < numSimulations)
            {
                //Selection
                Node currentNode = Selection(rootNode);

                //Expansion
                currentNode = Expansion(currentNode, 2);

                //Rollout
                int result = Rollout(currentNode, player);

                //Backpropagation
                Backpropagate(currentNode, result);
            }

            // Return the action that has accumulated the highest rewards
            return(rootNode.Children.Any() ?
                   rootNode.Children.OrderBy(x => SafeCalculateValue(x.T, x.N)).Last().State.Key :
                   rootNode.State.Value.CurrentPlayer.Options().First(x => x.PlayerTaskType == PlayerTaskType.END_TURN));
        }
        // Calculate different scores based on our hero's class
        private static int Score(POGame state, int playerId)
        {
            Controller cont = state.CurrentPlayer.PlayerId == playerId ? state.CurrentPlayer : state.CurrentOpponent;

            return(new My_Score_m {
                Controller = cont
            }.Rate());
        }
        // Calculate different scores based on our hero's class
        private static int Score(POGame state, int playerId)
        {
            var p = state.CurrentPlayer.PlayerId == playerId ? state.CurrentPlayer : state.CurrentOpponent;

            return(new ShroudedScore3 {
                Controller = p
            }.Rate());
        }
        private int Score(POGame state, int playerId)
        {
            var p = state.CurrentPlayer.PlayerId == playerId ? state.CurrentPlayer : state.CurrentOpponent;

            return(new CustomScore {
                Controller = p
            }.Rate(RuntimeScaling));
        }