public Schedule FindBestSchedule(SortedDictionary <int, List <Job> > jobs, int level = 20, int populationSize = 100, int topPercentToKeep = 80, OpenShopGASchedulerSettings currentBestFit = null)
        {
            //first, define a population
            var populationSet = new List <Schedule>();
            var rand          = new Random();

            for (int i = 0; i < populationSize; i++)
            {
                var chromosome = ScheduleCourses(jobs, true);
                var settings   = new OpenShopGASchedulerSettings()
                {
                    Chromosome = chromosome
                };
                var sched = new Schedule()
                {
                    Courses          = this.Schedule,
                    SchedulerName    = nameof(OpenShopGAScheduler),
                    ScheduleSettings = settings,
                };
                var rating = GetRating(sched);
                sched.Rating = rating;
                populationSet.Add(sched);
            }

            var fittest = SelectFittest(populationSet, level, topPercentToKeep, currentBestFit);

            return(fittest.OrderByDescending(s => s.Rating).First());
        }
        //------------------------------------------------------------------------------
        //
        // Constructors
        //
        //------------------------------------------------------------------------------

        public OpenShopGAScheduler(int paramID, Models.Preferences preferences, bool preferShortest = true, OpenShopGASchedulerSettings currentBestFit = null)
        {
            this.CurrentBestFit = currentBestFit;
            this.Preferences    = preferences;
            SetUp(paramID);
            MakeStartingPoint();
            InitDegreePlan();
        }
        private List <OpenShopGASchedulerSettings> GetCrossOvers(OpenShopGASchedulerSettings parent1, OpenShopGASchedulerSettings parent2, int count)
        {
            var random = new Random();
            var lowest = parent1.Chromosome.Count < parent2.Chromosome.Count ? parent1.Chromosome.Count : parent2.Chromosome.Count;

            if (parent1.Chromosome.Count <= 0)
            {
                return new List <OpenShopGASchedulerSettings>()
                       {
                           parent1
                       }
            }
            ;
            var crossOvers = new List <OpenShopGASchedulerSettings>();

            for (int i = 0; i < count; i++)
            {
                var crossOver = new OpenShopGASchedulerSettings()
                {
                    Chromosome = new List <Job>()
                };
                foreach (var job in parent1.Chromosome)
                {
                    crossOver.Chromosome.Add(job);
                }
                var randIndex = random.Next(lowest - 1);

                //swap at this index
                var old    = crossOver.Chromosome[randIndex];
                var newVal = crossOver.Chromosome[randIndex];

                crossOver.Chromosome[randIndex] = newVal;
                int jobToReplace = -1;
                for (int j = 0; i < parent1.Chromosome.Count; i++)
                {
                    if (crossOver.Chromosome[i] == newVal)
                    {
                        jobToReplace = i;
                    }
                }

                if (jobToReplace != -1)
                {
                    crossOver.Chromosome[jobToReplace] = old;
                }
                crossOvers.Add(crossOver);
            }


            return(crossOvers);
        }
        public List <Schedule> SelectFittest(List <Schedule> populationSet, int level = 20, int topPercentToKeep = 95, OpenShopGASchedulerSettings currentBestFit = null)
        {
            if (level == 0 || populationSet.Count <= 2)
            {
                return(populationSet);
            }
            var      rand        = new Random();
            Schedule topSchedule = null;

            //select best from population
            if (currentBestFit == null)
            {
                topSchedule = populationSet.OrderByDescending(s => s.Rating).First();
            }
            else
            {
                ScheduleCourse(currentBestFit.Chromosome);
                topSchedule = new Schedule()
                {
                    Courses          = this.Schedule,
                    SchedulerName    = nameof(OpenShopGAScheduler),
                    ScheduleSettings = currentBestFit,
                };
                var rating = GetRating(topSchedule);
                topSchedule.Rating = rating;
            }

            var cutoff = populationSet.Count * topPercentToKeep / 100;

            populationSet = populationSet.OrderByDescending(s => s.Rating).Take(cutoff).ToList();
            if (populationSet.Contains(topSchedule))
            {
                populationSet.Remove(topSchedule);
            }
            List <Schedule> offSprings = new List <Schedule>();

            while (populationSet.Count > 0)
            {
                var mate = populationSet.First();
                //cross over
                var crossOvers = GetCrossOvers(topSchedule.ScheduleSettings, mate.ScheduleSettings, 2);
                foreach (var crossOver in crossOvers)
                {
                    //mutate (swap with some other schedule in the population)
                    var randomPop      = rand.Next(populationSet.Count - 1);
                    var randomToMutate = populationSet[randomPop];
                    var mutation       = GetCrossOvers(crossOver, randomToMutate.ScheduleSettings, 1);

                    ScheduleCourse(mutation.First().Chromosome);
                    var offspring = new Schedule()
                    {
                        Courses          = this.Schedule,
                        SchedulerName    = nameof(OpenShopGAScheduler),
                        ScheduleSettings = crossOver,
                    };
                    var rating = GetRating(offspring);
                    offspring.Rating = rating;
                    offSprings.Add(offspring);
                }

                populationSet.Remove(mate);
            }

            return(SelectFittest(offSprings, level - 1));
        }