public IActionResult Powerball()
        {
            // get drawing records
            var sodaHost     = _appSettings.Powerball.HostUrl;
            var soda4x4      = _appSettings.Powerball.TableId;
            var sodaAppToken = _appSettings.SODA.AppToken;

            var client = new SodaClient(sodaHost, sodaAppToken);

            // go max one year back to look at data
            var soql = new SoqlQuery().Select("draw_date", "winning_numbers", "multiplier")
                       .Where($"draw_date > '{DateTime.Now.AddYears(-1).ToString("yyyy-MM-dd")}'").Order(SoqlOrderDirection.DESC, "draw_date");

            var dataset = client.GetResource <PowerballResults>(soda4x4);
            var results = dataset.Query(soql);

            // create drawing collection
            var drawings = new List <Drawing>();
            //var ballStatList = new List<BallsStats>();

            // set drawing limits
            var maxBallValue      = _appSettings.Powerball.MaxBallValue;
            var maxPowerBallValue = _appSettings.Powerball.MaxPowerBallValue;
            var ballCount         = _appSettings.Powerball.BallCount;

            // calculate next drawing date
            var nextDrawDate = CalculateNextDrawDate(new List <DayOfWeek> {
                DayOfWeek.Wednesday, DayOfWeek.Saturday
            });

            // build base drawing collection
            foreach (var draw in results)
            {
                var winningNumbers = draw.winning_numbers.Split(' ');
                var drawing        = new Drawing {
                    DrawDate = DateTime.Parse(draw.draw_date)
                };
                foreach (var nbr in winningNumbers)
                {
                    var idx  = Array.IndexOf(winningNumbers, nbr) + 1;
                    var ball = new DrawnBall
                    {
                        Id  = Convert.ToInt32(nbr),
                        Seq = idx
                    };

                    drawing.Balls.Add(ball);
                }

                drawings.Add(drawing);
            }

            var ballStatsList = BuildStats(maxBallValue, ballCount, drawings, nextDrawDate, maxPowerBallValue);

            var autoPcikList = GetAutoPicks(nextDrawDate, ballStatsList, maxBallValue, true);

            var model = BuildViewModel(nextDrawDate, autoPcikList, ballStatsList);

            return(View(model));
        }
        /// <summary>
        /// Balls fall into zones (ie: ball 1 is balls 1-10 ball, 2 11-20, ...)
        /// </summary>
        private List <DrawnBall> ZoneBall(List <BallStat>[] ballStatLists, int maxBallId)
        {
            var picks     = new List <DrawnBall>();
            var count     = ballStatLists.Count();
            var rangeSize = maxBallId / count;
            var maxScore  = 99999D;
            var minId     = 0;
            var maxId     = rangeSize;

            foreach (var stat in ballStatLists)
            {
                var balls = stat.Where(b => b.Score < 99999D && b.Id >= minId && b.Id <= maxId).ToList();
                maxScore = balls.Max(b => b.Score);
                var ball = balls.Where(b => b.Score == maxScore).First();
                var draw = new DrawnBall {
                    Id = ball.Id, Seq = ball.Position
                };
                minId = draw.Id;
                picks.Add(draw);
                minId = maxId + 1;
                maxId = minId + rangeSize;
            }

            return(picks);
        }
        /// <summary>
        /// Start with Ball 1 and everyone after must be bigger
        /// </summary>
        private List <DrawnBall> AscendingBall(List <BallStat>[] ballStatLists)
        {
            var picks     = new List <DrawnBall>();
            var maxScore  = 99999D;
            var minBallId = 0;

            foreach (var stat in ballStatLists)
            {
                var balls = stat.Where(b => b.Score <99999D && b.Id> minBallId).ToList();
                maxScore = balls.Max(b => b.Score);
                var ball = balls.Where(b => b.Score == maxScore).First();
                var draw = new DrawnBall {
                    Id = ball.Id, Seq = ball.Position
                };
                minBallId = draw.Id;
                picks.Add(draw);
            }

            return(picks);
        }
        /// <summary>
        /// Best Ball in each list no order no repeat
        /// </summary>
        private List <DrawnBall> BestBall(List <BallStat>[] ballStatLists)
        {
            var picks = new List <DrawnBall>();

            foreach (var stat in ballStatLists)
            {
                DrawnBall draw     = null;
                var       fail     = false;
                var       maxScore = 99999D;

                do
                {
                    fail = false;

                    var balls = stat.Where(b => b.Score < maxScore).ToList();
                    maxScore = balls.Max(b => b.Score);
                    var ball = balls.Where(b => b.Score == maxScore).First();
                    draw = new DrawnBall {
                        Id = ball.Id, Seq = ball.Position
                    };

                    foreach (var checkStat in ballStatLists)
                    {
                        var checkBalls  = checkStat.Where(b => b.Position != ball.Position && b.Score < 99999D).ToList();
                        var checkedBall = checkBalls.Where(b => b.Id == ball.Id && b.Score > ball.Score).FirstOrDefault();
                        if (checkedBall != null)
                        {
                            fail      = true;
                            maxScore -= 1;
                            break;
                        }
                    }
                } while (fail);

                picks.Add(draw);
            }

            return(picks);
        }
        /// <summary>
        /// Reverse Logic of Ascending Picks
        /// </summary>
        private List <DrawnBall> DesendingBall(List <BallStat>[] ballStatLists, int maxBallId)
        {
            var picks    = new List <DrawnBall>();
            var maxScore = 99999D;
            var maxId    = maxBallId;

            var idx = ballStatLists.Length - 1;

            for (int i = idx; i >= 0; i--)
            {
                var balls = ballStatLists[i].Where(b => b.Score < 99999D && b.Id < maxId).ToList();
                maxScore = balls.Max(b => b.Score);
                var ball = balls.Where(b => b.Score == maxScore).First();
                var draw = new DrawnBall {
                    Id = ball.Id, Seq = ball.Position
                };
                maxId = draw.Id;
                picks.Add(draw);
            }

            picks.Reverse();
            return(picks);
        }
        private List <Drawing> GetAutoPicks(DateTime nextDrawDate, List <BallStat>[] ballStatLists, int maxBallId, bool hasSpecialBall)
        {
            var picks             = new List <Drawing>();
            var specialBallFactor = Convert.ToInt32(hasSpecialBall);

            var ballList = ballStatLists[ballStatLists.Length - 1];

            List <BallStat>[] mainBallList = new List <BallStat> [ballStatLists.Length - specialBallFactor];
            Array.Copy(ballStatLists, mainBallList, ballStatLists.Length - specialBallFactor);

            // calculate ball draws
            var ballMaxScore = ballList.Max(b => b.Score);
            var ball         = ballList.Where(b => b.Score == ballMaxScore).First();
            var ballBestDraw = new DrawnBall {
                Id = ball.Id, Seq = ball.Position
            };

            // Balls fall into zones (ie: ball 1 is balls 1-10 ball, 2 11-20, ...)
            var zoneDraw = new Drawing();

            zoneDraw.DrawDate = nextDrawDate;
            zoneDraw.Balls    = ZoneBall(mainBallList, maxBallId);
            if (hasSpecialBall)
            {
                zoneDraw.Balls.Add(ballBestDraw);
            }
            if (!IsPickInList(picks, zoneDraw))
            {
                picks.Add(zoneDraw);
            }

            if (hasSpecialBall)
            {
                // Start with Ball 1 and everyone after must be bigger
                var ascendingDraw = new Drawing();
                ascendingDraw.DrawDate = nextDrawDate;
                ascendingDraw.Balls    = AscendingBall(mainBallList);
                ascendingDraw.Balls.Add(ballBestDraw);
                if (!IsPickInList(picks, ascendingDraw))
                {
                    picks.Add(ascendingDraw);
                }

                // Reverse Logic of Ascending Picks
                var desendingDraw = new Drawing();
                desendingDraw.DrawDate = nextDrawDate;
                desendingDraw.Balls    = DesendingBall(mainBallList, maxBallId);
                desendingDraw.Balls.Add(ballBestDraw);
                if (!IsPickInList(picks, desendingDraw))
                {
                    picks.Add(desendingDraw);
                }
            }

            // Best Ball in each list no order no repeat
            var bestBallDraw = new Drawing();

            bestBallDraw.DrawDate = nextDrawDate;
            bestBallDraw.Balls    = BestBall(mainBallList);
            if (hasSpecialBall)
            {
                bestBallDraw.Balls.Add(ballBestDraw);
            }
            if (!IsPickInList(picks, bestBallDraw))
            {
                picks.Add(bestBallDraw);
            }

            return(picks);
        }
        public IActionResult Pick3()
        {
            // get drawing records
            XmlReader       reader = XmlReader.Create(_appSettings.Pick3.HostUrl);
            SyndicationFeed feed   = SyndicationFeed.Load(reader);

            var results = new List <Pick3Results>();

            foreach (SyndicationItem item in feed.Items.Where(x => x.PublishDate > DateTime.Now.AddYears(-1)).OrderByDescending(x => x.PublishDate))
            {
                var result = new Pick3Results();

                // get draw date
                var dateLineValues = item.Title.Text.Split(':');
                if (DateTime.TryParse(dateLineValues[1].Trim(), out DateTime tmpDate))
                {
                    result.draw_date = tmpDate;
                }

                // get winning balls
                var contentLine      = ((TextSyndicationContent)item.Content).Text;
                var contentLineItems = contentLine.Split("<br />", StringSplitOptions.RemoveEmptyEntries);

                var winningNumbersValues = contentLineItems[0].Split(':');
                result.midday_numbers = winningNumbersValues[1].Trim();

                if (contentLineItems.Length > 1)
                {
                    winningNumbersValues   = contentLineItems[1].Split(':');
                    result.evening_numbers = winningNumbersValues[1].Trim();
                }

                results.Add(result);
            }

            // create drawing collection
            var drawings = new List <Drawing>();

            // set drawing limits
            var maxBallValue = _appSettings.Pick3.MaxBallValue;
            var ballCount    = _appSettings.Pick3.BallCount;

            // calculate next drawing date
            var nextDrawDate = DateTime.Today + new TimeSpan(12, 29, 00);

            if (DateTime.Now > nextDrawDate.AddHours(7))
            {
                nextDrawDate = nextDrawDate.AddDays(1);
            }
            if (DateTime.Now > nextDrawDate)
            {
                nextDrawDate = nextDrawDate.AddHours(7);
            }

            // build base drawing collection
            foreach (var draw in results)
            {
                var drawDateTime = draw.draw_date.Value;

                var middayNumbers = draw.midday_numbers.Split(',');
                var middayDrawing = new Drawing {
                    DrawDate = drawDateTime + new TimeSpan(12, 29, 0)
                };
                foreach (var nbr in middayNumbers)
                {
                    var idx  = Array.IndexOf(middayNumbers, nbr) + 1;
                    var ball = new DrawnBall
                    {
                        Id  = Convert.ToInt32(nbr),
                        Seq = idx
                    };
                    middayDrawing.Balls.Add(ball);
                }
                drawings.Add(middayDrawing);

                if (!string.IsNullOrWhiteSpace(draw.evening_numbers))
                {
                    var eveningNumbers = draw.evening_numbers.Split(',');
                    var eveningDrawing = new Drawing {
                        DrawDate = drawDateTime + new TimeSpan(19, 29, 0)
                    };
                    foreach (var nbr in eveningNumbers)
                    {
                        var idx  = Array.IndexOf(eveningNumbers, nbr) + 1;
                        var ball = new DrawnBall
                        {
                            Id  = Convert.ToInt32(nbr),
                            Seq = idx
                        };
                        eveningDrawing.Balls.Add(ball);
                    }
                    drawings.Add(eveningDrawing);
                }
            }

            var ballStatsList = BuildStats(maxBallValue, ballCount, drawings, nextDrawDate);

            var autoPicks = GetAutoPicks(nextDrawDate, ballStatsList, maxBallValue, false);

            var model = BuildViewModel(nextDrawDate, autoPicks, ballStatsList);

            return(View(model));
        }
        public IActionResult LuckyForLife()
        {
            // get drawing records
            XmlReader       reader = XmlReader.Create(_appSettings.LuckyForLife.HostUrl);
            SyndicationFeed feed   = SyndicationFeed.Load(reader);

            var results = new List <LuckyForLifeResults>();

            foreach (SyndicationItem item in feed.Items.Where(x => x.PublishDate > DateTime.Now.AddYears(-1)).OrderByDescending(x => x.PublishDate))
            {
                var result = new LuckyForLifeResults();

                // get draw date
                var dateLineValues = item.Title.Text.Split(':');
                if (DateTime.TryParse(dateLineValues[1].Trim(), out DateTime tmpDate))
                {
                    result.draw_date = tmpDate;
                }

                // get winning balls
                var contentLine      = ((TextSyndicationContent)item.Content).Text;
                var contentLineItems = contentLine.Split("<br />");

                var winningNumbersValues = contentLineItems[0].Split(':');
                result.winning_numbers = winningNumbersValues[1].Trim();

                var luckyBallValues = contentLineItems[1].Split(':');
                result.lucky_ball = luckyBallValues[1].Trim();

                results.Add(result);
            }

            // create drawing collection
            var drawings = new List <Drawing>();

            // set drawing limits
            var maxBallValue      = _appSettings.LuckyForLife.MaxBallValue;
            var maxLuckyBallValue = _appSettings.LuckyForLife.MaxLuckyBallValue;
            var ballCount         = _appSettings.LuckyForLife.BallCount;

            // calculate next drawing date
            var nextDrawDate = CalculateNextDrawDate(new List <DayOfWeek> {
                DayOfWeek.Monday, DayOfWeek.Thursday
            });

            // build base drawing collection
            foreach (var draw in results)
            {
                var winningNumbers = draw.winning_numbers.Split(',');
                var drawing        = new Drawing {
                    DrawDate = draw.draw_date.Value
                };
                foreach (var nbr in winningNumbers)
                {
                    var idx  = Array.IndexOf(winningNumbers, nbr) + 1;
                    var ball = new DrawnBall
                    {
                        Id  = Convert.ToInt32(nbr),
                        Seq = idx
                    };

                    drawing.Balls.Add(ball);
                }
                var luckyBall = new DrawnBall {
                    Id = Convert.ToInt32(draw.lucky_ball), Seq = 6
                };
                drawing.Balls.Add(luckyBall);

                drawings.Add(drawing);
            }

            var ballStatsList = BuildStats(maxBallValue, ballCount, drawings, nextDrawDate, maxLuckyBallValue);

            var autoPcikList = GetAutoPicks(nextDrawDate, ballStatsList, maxBallValue, true);

            var model = BuildViewModel(nextDrawDate, autoPcikList, ballStatsList);

            return(View(model));
        }