/// <summary>
        /// Creates a SabberStoneAction from a collection of possible solutions by voting for separate tasks.
        /// </summary>
        /// <param name="solutions">The available solutions.</param>
        /// <param name="state">The game state.</param>
        /// <returns>SabberStoneAction.</returns>
        public SabberStoneAction VoteForSolution(List <SabberStoneAction> solutions, SabberStoneState state)
        {
            // Clone game so that we can process the selected tasks and get an updated options list.
            var clonedGame = state.Game.Clone();
            var action     = new SabberStoneAction();

            // Have all solutions vote on tasks
            var taskVotes = new Dictionary <int, int>();

            foreach (var solution in solutions)
            {
                foreach (var task in solution.Tasks)
                {
                    var taskHash = task.GetHashCode();
                    if (!taskVotes.ContainsKey(taskHash))
                    {
                        taskVotes.Add(taskHash, 0);
                    }
                    taskVotes[taskHash]++;
                }
            }

            // Keep selecting tasks until the action is complete or the game has ended
            while (!action.IsComplete() && clonedGame.State != State.COMPLETE)
            {
                // Make a dictionary of available tasks, indexed by their hashcode, but ignore the END-TURN task for now
                var availableTasks = clonedGame.CurrentPlayer.Options().Where(i => i.PlayerTaskType != PlayerTaskType.END_TURN).Select(i => (SabberStonePlayerTask)i).ToList();

                // Pick the one with most votes
                var votedOnTasks = availableTasks.Where(i => taskVotes.ContainsKey(i.GetHashCode())).ToList();
                var mostVoted    = votedOnTasks.OrderByDescending(i => taskVotes[i.GetHashCode()]).FirstOrDefault();

                // If this is null, it means none of the tasks that are available appear in any of the solutions
                if (mostVoted == null)
                {
                    // End the turn
                    action.AddTask((SabberStonePlayerTask)EndTurnTask.Any(clonedGame.CurrentPlayer));
                    break;
                }

                // Find any tasks tied for most votes
                var mostVotes = taskVotes[mostVoted.GetHashCode()];
                var ties      = votedOnTasks.Where(i => taskVotes[i.GetHashCode()] == mostVotes);

                // Add one of the tasks with the most votes to the action
                //TODO Ties during voting can be handled differently than random, but handling ties based on visit count would require extra information from the separate searches' solutions.
                var chosenTask = ties.RandomElementOrDefault();
                action.AddTask(chosenTask);

                // Process the task so we have an updated options list next iteration
                clonedGame.Process(chosenTask.Task);
            }

            return(action);
        }
        /// <summary>
        /// Determines the best tasks for the game state based on the provided statistics and creates a <see cref="SabberStoneAction"/> from them.
        /// </summary>
        /// <param name="state">The game state to create the best action for.</param>
        /// <returns><see cref="SabberStoneAction"/> created from the best individual tasks available in the provided state.</returns>
        public SabberStoneAction DetermineBestTasks(SabberStoneState state)
        {
            // Clone game so that we can process the selected tasks and get an updated options list.
            var clonedGame = state.Game.Clone();

            // We have to determine which tasks are the best to execute in this state, based on the provided values of the MCTS search.
            // So we'll check the statistics table for the highest value among tasks that are currently available in the state.
            // This continues until the end-turn task is selected.
            var action = new SabberStoneAction();

            while (!action.IsComplete() && clonedGame.State != State.COMPLETE)
            {
                // Get the available options in this state and find which tasks we have statistics on, but ignore the END-TURN task for now
                var availableTasks = clonedGame.CurrentPlayer.Options().Where(i => i.PlayerTaskType != PlayerTaskType.END_TURN).Select(i => ((SabberStonePlayerTask)i).GetHashCode());
                var stats          = TaskStatistics.Where(i => availableTasks.Contains(i.Key)).ToList();
                var bestTask       = stats.OrderByDescending(i => i.Value.AverageValue()).FirstOrDefault();

                // If we can't find any task, stop.
                if (bestTask.IsDefault())
                {
                    // End the turn
                    action.AddTask((SabberStonePlayerTask)EndTurnTask.Any(clonedGame.CurrentPlayer));
                    break;
                }

                // Handle the possibility of tasks with tied average value.
                var bestValue   = bestTask.Value.AverageValue();
                var tiedTasks   = stats.Where(i => Math.Abs(i.Value.AverageValue() - bestValue) < Constants.DOUBLE_EQUALITY_TOLERANCE);
                var orderedTies = tiedTasks.OrderByDescending(i => i.Value.Visits);
                bestTask = orderedTies.First();

                // If we found a task, add it to the Action and process it to progress the game.
                var task = bestTask.Value.Task;
                action.AddTask(task);
                clonedGame.Process(task.Task);
            }

            // Return the created action consisting of the best action available at each point.
            return(action);
        }