private (string option, StateOption optionData)? PickOption(DrawState state)
        {
            var options = _gameProcessor.GetCurrentOptionsFullDrawData(gameState, game).Where(a => !game.Metadata.PermanentButtons.Any(b => b.ButtonText == a.option)).ToList();

            if (options == null || options.Count == 0)
            {
                return(null);
            }

            options.ForEach(a =>
            {
                if (options.Count(b => b.option.ToLower() == a.option.ToLower()) > 1)
                {
                    data.WarningMessage($"Found a state with two or more options with the same text '{a}' at state with text '{new string(state.StateText.Take(150).ToArray())}'", gameState.Clone());
                }
            });

            // Select the option with the fewest times chosen
            //(StateOption option, string message, int timesChosen) minValue = (options[0].optionData, options[0].option, data.GetTimesChosen(options[0].optionData.Id));
            //foreach (var opt in options)
            //{
            //    var optionTimesChosen = data.GetTimesChosen(opt.optionData.Id);
            //    if (optionTimesChosen < minValue.timesChosen)
            //        minValue = (opt.optionData, opt.option, optionTimesChosen);
            //    else if (optionTimesChosen == minValue.timesChosen)
            //    {
            //        if (rand.Next(1) == 0)
            //            minValue = (opt.optionData, opt.option, optionTimesChosen);
            //    }
            //}

            // Select an option, preferring those that have been visited fewer times
            var chosen = WeightedRandom(options.Select(a => new WeightedRandomItem <(string option, StateOption optionData)> {
                Data = a, Weight = data.GetTimesChosen(a.optionData.Id)
            }).ToList());

            return(chosen);

            // Full random
            //return options[rand.Next(options.Count)];
        }
        public (List <string> errors, List <string> warnings, int totalActionsDone) RunTest(DrawGame game, DateTime runUntil)
        {
            data = new GameTestData();
            Random rand      = new Random();
            var    gameState = new PlayerGameSave();

            gameState.GameName = game.GameName;
            gameState.StateId  = game.startState.Id;

            int totalActionsDone = 0;

            while (DateTime.Now <= runUntil)
            {
                try
                {
                    data.StateVisited(gameState.StateId);

                    var options = _gameProcessor.GetCurrentOptionsFullDrawData(gameState, game);

                    if (options == null || options.Count == 0)
                    {
                        ReportWarning("No options found - Data:" + PrettifyData(gameState), gameState, game);
                        gameState          = new PlayerGameSave();
                        gameState.GameName = game.GameName;
                        gameState.StateId  = game.startState.Id;
                        continue;
                    }
                    options.ForEach(a =>
                    {
                        if (options.Count(b => b.option.ToLower() == a.option.ToLower()) > 1)
                        {
                            ReportError("Found a state with two or more options with the same text: " + a, gameState, game);
                        }
                    });

                    (StateOption option, string message, int timesChosen)minValue = (options[0].optionData, options[0].option, data.GetTimesChosen(options[0].optionData.Id));
                    foreach (var opt in options)
                    {
                        var optionTimesChosen = data.GetTimesChosen(opt.optionData.Id);
                        if (optionTimesChosen < minValue.timesChosen)
                        {
                            minValue = (opt.optionData, opt.option, optionTimesChosen);
                        }
                        else if (optionTimesChosen == minValue.timesChosen)
                        {
                            if (rand.Next(1) == 0)
                            {
                                minValue = (opt.optionData, opt.option, optionTimesChosen);
                            }
                        }
                    }
                    var optionToExecute = options[rand.Next(options.Count)];

                    var execResult = _gameProcessor.ProcessMessage(optionToExecute.option, gameState, game);
                    execResult.StatesVisited.ForEach(a => data.StateVisited(a));
                    data.OptionChosen(optionToExecute.optionData.Id);
                    totalActionsDone++;
                }
                catch (Exception e)
                {
                    ReportError("ERROR: " + e.Message + "\nSTACK: " + e.StackTrace + "\nDATA: " + PrettifyData(gameState), gameState, game);
                    gameState          = new PlayerGameSave();
                    gameState.GameName = game.GameName;
                    gameState.StateId  = game.startState.Id;
                    continue;
                }
            }
            var statesNeverVisited = game.Stats.states.Select(a => a.Id).Except(data.GetAllStatesVisited());

            statesNeverVisited.ToList().ForEach(a =>
            {
                var state = game.FindStateById(a);
                errors.Add($"State never visited with text: '{(!string.IsNullOrWhiteSpace(state.StateText) && state.StateText.Length >= 300 ? state.StateText.Substring(0, 300) : state.StateText)}'");
            });

            return(errors.ToList(), warnings.ToList(), totalActionsDone);
        }