Пример #1
0
        /// <summary>
        /// This method will convert the current Splits
        /// to simple numered description.
        /// we just want numbers, not the list of cars
        /// because it is easier to make average on it.
        /// ---
        /// Exemple :
        //  {Split: 1, C7: 11 cars}
        //  {Split: 2, C7: 11 cars}
        //  {Split: 5, C7: 11 cars}
        //  {Split: 8, C7: 8 cars}
        /// </summary>
        /// <returns></returns>
        private List <MultiClassChanges> ConvertCurrentSplitsToSplitDescriptions()
        {
            List <MultiClassChanges> modes = new List <MultiClassChanges>();

            // foreach splits
            foreach (var item in Splits)
            {
                // split descrpition using the 'MultiClassChanges' class
                MultiClassChanges m = new MultiClassChanges();

                // set the split number.
                // (we will not use range here)
                // (or if you prefer, it will be ranges of 1) :D
                m.FromSplit = item.Number;
                m.ToSplit   = item.Number;

                // number of classes in the split
                m.ClassesCount    = item.GetClassesCount();
                m.ClassCarsTarget = new Dictionary <int, int>();

                // numbers of cars in each class
                // dictionnary ClassCarsTarget
                // KEY : class id
                // VALUE : cars count
                foreach (var classid in carClassesIds)
                {
                    int classIndex    = carClassesIds.IndexOf(classid);
                    int classCarCount = item.CountClassCars(classIndex);
                    if (classCarCount > 0)
                    {
                        m.ClassCarsTarget.Add(classid, classCarCount);
                    }
                }


                // add that description to the return list of that method
                modes.Add(m);
            }
            // foreach splits end

            // return the list
            return(modes);
        }
Пример #2
0
        /// <summary>
        /// If there is split with more cars than field Size, fix it
        /// </summary>
        /// <param name="splitsDescriptions"></param>
        private void SolveSplitsExceedFieldSize(List <MultiClassChanges> splitsDescriptions)
        {
            // foreach split
            foreach (var splitDescription in splitsDescriptions)
            {
                // while the split is in excess
                while (splitDescription.CountTotalTargets() > fieldSize)
                {
                    // get the class with more cars
                    var excess  = (from r in splitDescription.ClassCarsTarget orderby r.Value descending select r).FirstOrDefault();
                    int classid = excess.Key;

                    // find the uppest split possible containing the same class
                    // and a free avaiable slot
                    var splitWithSameClassAndSlotAvailable = (from r in splitsDescriptions
                                                              where
                                                              r.ClassCarsTarget.ContainsKey(excess.Key) &&
                                                              r.CountTotalTargets() < fieldSize
                                                              orderby r.ClassCarsTarget[excess.Key] ascending
                                                              select r).FirstOrDefault();

                    // yes, found it
                    if (splitWithSameClassAndSlotAvailable != null)
                    {
                        // move the car to it
                        splitWithSameClassAndSlotAvailable.ClassCarsTarget[classid]++;
                        splitDescription.ClassCarsTarget[classid]--;
                    }

                    // no, not any possible
                    else
                    {
                        // get the classes id, from most populated to less
                        List <int> mostpopulatedclassed = carClassesIds.ToList();
                        mostpopulatedclassed.Reverse();
                        mostpopulatedclassed.Remove(classid); // but remove the current class
                        // -->

                        // we will do a pool shot in two moves

                        // foreach most populated class
                        bool solutionfound = false;
                        foreach (int mostpopulatedclass in mostpopulatedclassed)
                        {
                            // find the split having the less cars possible, the lowest possible
                            // containing the mostpopulatedclass
                            // and thecontaining  current class 'classid'
                            var othersplit1 = (from r in splitsDescriptions
                                               where
                                               r.ClassCarsTarget.ContainsKey(classid) &&
                                               r.ClassCarsTarget.ContainsKey(mostpopulatedclass) &&
                                               r.ClassCarsTarget[mostpopulatedclass] > 0
                                               orderby r.CountTotalTargets() ascending, r.ToSplit descending
                                               select r).FirstOrDefault();

                            // find split containing the mostpopulatedclass, the uppest possible
                            // with the lowest car possible
                            var othersplit2 = (from r in splitsDescriptions
                                               where
                                               r.ClassCarsTarget.ContainsKey(mostpopulatedclass) &&
                                               r.CountTotalTargets() < fieldSize &&
                                               r.ClassCarsTarget[mostpopulatedclass] > 0
                                               orderby r.CountTotalTargets() ascending, r.ToSplit descending
                                               select r).FirstOrDefault();

                            // it these 2 splits are found
                            if (othersplit1 != null && othersplit2 != null)
                            {
                                // in otherslit1 : change a mostpopulatedclass slot with a classid slot
                                othersplit1.ClassCarsTarget[mostpopulatedclass]--;
                                othersplit1.ClassCarsTarget[classid]++;

                                // in otherslit1 : finaly add the missing car
                                othersplit2.ClassCarsTarget[mostpopulatedclass]++;

                                // to end solving the problem, we can now remove the car in excess to our current split
                                splitDescription.ClassCarsTarget[classid]--;

                                // break the 'foreach most populated class'
                                // we don't need to try with another class
                                // we solved the problem
                                solutionfound = true;
                                break;
                            }
                            else
                            {
                                // not found too...
                                // we will try with the next mostpopulatedclass
                            }
                        }

                        if (!solutionfound)
                        {
                            var newsplit = new MultiClassChanges()
                            {
                                FromSplit       = Splits.Count,
                                ToSplit         = Splits.Count,
                                ClassCarsTarget = new Dictionary <int, int>()
                            };

                            for (int i = 0; i < carClassesIds.Count; i++)
                            {
                                // algo.TakeCars(carClassesIds[i], carClassesIds, fieldSize)
                                //int cars = 0;
                                //if (carClassesIds[i] == excess.Key) cars = excess.Value;
                                newsplit.ClassCarsTarget.Add(carClassesIds[i], 0);
                            }


                            splitsDescriptions.Add(newsplit);
                            SolveSplitsExceedFieldSize(splitsDescriptions);
                            Split newSplit = new Split(Splits.Count);
                            Splits.Add(newSplit);

                            solutionfound = true;
                            return;
                        }
                    }
                }
            } // end of foreach split
        }
Пример #3
0
        public void Compute(List <Line> data, int fieldSize)
        {
            // Split cars per class
            var carsListPerClass = Tools.SplitCarsPerClass(data);

            // Create two dictionnary (KEY for both is the CarClass Id)
            // classRemainingCars : VALUE is the number of remaining cars in the class
            // classSplitsCount : VALUE is a list containing the number of car split per split
            Dictionary <int, int>         classRemainingCars = new Dictionary <int, int>();
            Dictionary <int, List <int> > classSplitsCount   = new Dictionary <int, List <int> >();

            foreach (var carClass in carsListPerClass)
            {
                classRemainingCars.Add(carClass.CarClassId, carClass.Cars.Count);
                classSplitsCount.Add(carClass.CarClassId, new List <int>());
            }

            // export classes id
            CarClassesId = new List <int>();
            foreach (var carClass in carsListPerClass)
            {
                CarClassesId.Add(carClass.CarClassId);
            }

            // MultiClassMode records describes changes on split car classes compositions
            // - FromSplit and ToSplit describes the range of splits
            // - ClassesCount describes how many car classes can be part of the splits
            //      (exemple: 3 first for LMP1/LMP2/GTE, then 2 when it become LMP1/GTE because not enought LMP2 are available, then 1 when single class)...
            List <MultiClassChanges> modes       = new List <MultiClassChanges>();
            MultiClassChanges        currentMode = new MultiClassChanges();

            currentMode.FromSplit    = 1;
            currentMode.ToSplit      = 1;
            currentMode.ClassesCount = classRemainingCars.Count;
            modes.Add(currentMode);

            int splitCounter = 1;


            while (SumValues(classRemainingCars) > 0) // when cars remaings
            {
                // count classes containing remaining cars
                int remCarClasses = (from r in classRemainingCars where r.Value > 0 select r).Count();

                foreach (var carClass in carsListPerClass)
                {
                    // count cars to take in this class
                    int takeCars = fieldSize;
                    takeCars = TakeClassCars(fieldSize, remCarClasses, classRemainingCars, carClass.CarClassId, carsListPerClass, splitCounter);

                    // if not enought remianing cars than wanted, take what is possible
                    int carClassSize = Math.Min(takeCars, classRemainingCars[carClass.CarClassId]);


                    classSplitsCount[carClass.CarClassId].Add(carClassSize); // save the number of car in the class for this split
                    classRemainingCars[carClass.CarClassId] -= carClassSize; // decrement reminaing cars in the class
                }

                var lastClass = carsListPerClass.LastOrDefault(); //get last class, which is the class with more cars then the other
                if (lastClass != null)
                {
                    // sum cars in this split
                    int carsInThisSplit = 0;
                    foreach (var carClass in carsListPerClass)
                    {
                        carsInThisSplit += classSplitsCount[carClass.CarClassId].Last();
                    }
                    // -->

                    // available slots ?
                    if (carsInThisSplit < fieldSize)
                    {
                        int availableSlots = fieldSize - carsInThisSplit;


                        // fill this availableSlots with last class cars to match the maximum field size..
                        var splitclasslist = classSplitsCount[lastClass.CarClassId];
                        splitclasslist[splitclasslist.Count - 1] += availableSlots;

                        // and decremement remaining cars if this last class
                        classRemainingCars[lastClass.CarClassId] -= availableSlots;
                    }
                }

                // is there always the same number of car class than the previous split ?
                if (remCarClasses == currentMode.ClassesCount)
                {
                    // yes, just update the ToSplit number
                    currentMode.ToSplit = splitCounter;
                }
                else
                {
                    // no, save a change starting from this split
                    currentMode              = new MultiClassChanges();
                    currentMode.FromSplit    = splitCounter;
                    currentMode.ToSplit      = splitCounter;
                    currentMode.ClassesCount = remCarClasses;
                    modes.Add(currentMode);
                }

                splitCounter++;
            }


            // create the array of splits
            Splits = new List <Split>();
            int maxsplit = (from r in modes select r.ToSplit).Max();

            for (int i = 1; i <= maxsplit; i++)
            {
                var split = new Split();
                split.Number = i;
                Splits.Add(split);
            }


            // reset the classRemainingCars counts
            classRemainingCars.Clear();
            foreach (var carClass in carsListPerClass)
            {
                classRemainingCars.Add(carClass.CarClassId, carClass.Cars.Count);
            }


            // for each car class
            foreach (var carClass in carsListPerClass)
            {
                // for each split
                for (int i = 1; i <= maxsplit; i++)
                {
                    var split = Splits[i - 1]; // get the split record in the array of splits

                    // get the MultiClassMode where this split is in, the target cars count for the classes
                    var mode = (from r in modes where i >= r.FromSplit orderby r.ToSplit descending select r).First();
                    int take = fieldSize / mode.ClassesCount;

                    // save the class target cars count in this class
                    split.SetClassTarget(carsListPerClass.IndexOf(carClass), take);
                    // .. and decrement the remaninng cars of this class
                    classRemainingCars[carClass.CarClassId] -= take;
                }
            }



            // AT THIS POINT :
            // on the Splits array, all Class{i}Target values are up to date with
            // number of cars we want
            // for each split, and each class.


            // Implement car lists
            foreach (var split in Splits)                           // foreach each split
            {
                for (int i = 0; i < 4; i++)                         // for each car class
                {
                    int carsToAddInClass = split.GetClassTarget(i); // get the cars count we want
                    if (carsListPerClass.Count > i)
                    {
                        var cars = carsListPerClass[i].PickCars(carsToAddInClass); // pick up the cars in the ordered list by iRating DESC
                        if (cars.Count > 0)
                        {
                            split.SetClass(i, cars, carsListPerClass[i].CarClassId); // set the class car list
                        }
                    }
                }
            }


            // manage the rest badly, very raw method
            var lastSplit = new Split();

            lastSplit.Number = Splits.Last().Number + 1;
            bool includeLastSplit = false;

            for (int i = 0; i < 4; i++)                                 // for each car class
            {
                int carsToAddInClass = Splits.Last().GetClassTarget(i); // get the cars count we want
                if (carsListPerClass.Count > i)
                {
                    var cars = carsListPerClass[i].PickCars(carsToAddInClass); // pick up the cars in the ordered list by iRating DESC
                    if (cars.Count > 0)
                    {
                        lastSplit.SetClass(i, cars, carsListPerClass[i].CarClassId); // set the class car list
                    }
                }
                if (lastSplit.TotalCarsCount > 0)
                {
                    includeLastSplit = true;
                }
            }
            if (includeLastSplit)
            {
                Splits.Add(lastSplit);
            }
            // done
            // :-)
        }
        public void Compute(List <Line> data, int fieldSize)
        {
            this.fieldSize = fieldSize;

            // Split cars per class
            var carsListPerClass = Tools.SplitCarsPerClass(data);



            // Create two dictionnary (KEY for both is the CarClass Id)
            // classRemainingCars : VALUE is the number of remaining cars in the class
            // classSplitsCount : VALUE is a list containing the number of car split per split
            Dictionary <int, int>         classRemainingCars = new Dictionary <int, int>();
            Dictionary <int, List <int> > classSplitsCount   = new Dictionary <int, List <int> >();

            foreach (var carClass in carsListPerClass)
            {
                classRemainingCars.Add(carClass.CarClassId, carClass.Cars.Count);
                classSplitsCount.Add(carClass.CarClassId, new List <int>());
            }

            // export classes id
            CarClassesId = new List <int>();
            foreach (var carClass in carsListPerClass)
            {
                CarClassesId.Add(carClass.CarClassId);
            }
            InitData(CarClassesId, data);

            // MultiClassMode records describes changes on split car classes compositions
            // - FromSplit and ToSplit describes the range of splits
            // - ClassesCount describes how many car classes can be part of the splits
            //      (exemple: 3 first for LMP1/LMP2/GTE, then 2 when it become LMP1/GTE because not enought LMP2 are available, then 1 when single class)...
            List <MultiClassChanges> modes       = new List <MultiClassChanges>();
            MultiClassChanges        currentMode = new MultiClassChanges();

            currentMode.FromSplit    = 1;
            currentMode.ToSplit      = 1;
            currentMode.ClassesCount = classRemainingCars.Count;
            modes.Add(currentMode);

            int splitCounter = 1;

            while (SumValues(classRemainingCars) > 0) // when cars remaings
            {
                // count classes containing remaining cars
                int remCarClasses = (from r in classRemainingCars where r.Value > 0 select r).Count();


                Dictionary <int, int> classRemainingCarsBeforeChange = new Dictionary <int, int>();
                foreach (var item in classRemainingCars)
                {
                    classRemainingCarsBeforeChange.Add(item.Key, item.Value);
                }


                foreach (var carClass in carsListPerClass)
                {
                    // count cars to take in this class
                    int takeCars = fieldSize;
                    takeCars = TakeClassCars(fieldSize, remCarClasses, classRemainingCarsBeforeChange, carClass.CarClassId, carsListPerClass, splitCounter);

                    // if not enought remianing cars than wanted, take what is possible
                    int carClassSize = Math.Min(takeCars, classRemainingCars[carClass.CarClassId]);


                    classSplitsCount[carClass.CarClassId].Add(carClassSize); // save the number of car in the class for this split
                    classRemainingCars[carClass.CarClassId] -= carClassSize; // decrement reminaing cars in the class
                }



                for (int i = 0; i < carsListPerClass.Count; i++) // do a pass per class
                {
                    // sum cars in this split
                    int carsInThisSplit = 0;
                    foreach (var carClass in carsListPerClass)
                    {
                        carsInThisSplit += classSplitsCount[carClass.CarClassId].Last();
                    }
                    int availableSlots = fieldSize - carsInThisSplit;
                    // -->

                    var lastClass = carsListPerClass.LastOrDefault(); //get last class, which is the class with more cars then the other

                    foreach (var item in classRemainingCars)          // if there is a better class, containing less remaning cars than available slots ?
                    {
                        if (item.Value < availableSlots && item.Value > 0 && item.Key != lastClass.CarClassId)
                        {
                            int classid = item.Key;
                            lastClass = (from r in carsListPerClass where r.CarClassId == classid select r).FirstOrDefault();
                            if (lastClass == null)
                            {
                                lastClass = carsListPerClass.LastOrDefault();
                            }
                            else
                            {
                                availableSlots = Math.Min(availableSlots, item.Value);
                                break;
                            }
                        }
                    }



                    if (lastClass != null)
                    {
                        // available slots ?
                        if (carsInThisSplit < fieldSize)
                        {
                            // fill this availableSlots with last class cars to match the maximum field size..
                            var splitclasslist = classSplitsCount[lastClass.CarClassId];
                            splitclasslist[splitclasslist.Count - 1] += availableSlots;

                            // and decremement remaining cars if this last class
                            classRemainingCars[lastClass.CarClassId] -= availableSlots;
                        }
                    }
                }



                // iis there always the same number of car class than the previous split ?
                if (remCarClasses == currentMode.ClassesCount)
                {
                    // yes, just update the ToSplit number
                    currentMode.ToSplit = splitCounter;
                }
                else
                {
                    // no, save a change starting from this split
                    currentMode              = new MultiClassChanges();
                    currentMode.FromSplit    = splitCounter;
                    currentMode.ToSplit      = splitCounter;
                    currentMode.ClassesCount = remCarClasses;
                    modes.Add(currentMode);
                }

                splitCounter++;
            }


            // for each MultiClassMode, for each change of simultaneous car classes if you prefer
            // ... (when 3 classes, when 2 classes, when 1 class)
            foreach (var field in modes)
            {
                // create a dictionnary where KEY is the car class id
                // and value is the number of cars
                field.ClassCarsTarget = new Dictionary <int, int>();

                foreach (var carClass in carsListPerClass)
                {
                    // sum all cars of this class in this "MultiClassMode"
                    int        sum    = 0;
                    List <int> splits = classSplitsCount[carClass.CarClassId];
                    for (int i = field.FromSplit - 1; i < field.ToSplit; i++)
                    {
                        sum += splits[i];
                    }

                    // count how many splits are in this "MultiClassMode"
                    int splitsCount = field.ToSplit - field.FromSplit + 1;

                    // calculate the average value, because we want all split having the same number of cars
                    // it will be our Target
                    int targetClassAverage = sum / splitsCount;

                    // save in the table the Target the this MultiClassMode:
                    // car class id -> target (avergate value)
                    field.ClassCarsTarget.Add(carClass.CarClassId, targetClassAverage);
                }
            }



            // create the array of splits
            Splits = new List <Split>();
            int maxsplit = (from r in modes select r.ToSplit).Max();

            for (int i = 1; i <= maxsplit; i++)
            {
                var split = new Split();
                split.Number = i;
                Splits.Add(split);
            }


            // reset the classRemainingCars counts
            classRemainingCars.Clear();
            foreach (var carClass in carsListPerClass)
            {
                classRemainingCars.Add(carClass.CarClassId, carClass.Cars.Count);
            }


            // for each car class
            foreach (var carClass in carsListPerClass)
            {
                // for each split
                for (int i = 1; i <= maxsplit; i++)
                {
                    var split = Splits[i - 1]; // get the split record in the array of splits

                    // get the MultiClassMode where this split is in, the target cars count for the classes
                    var mode = (from r in modes where i >= r.FromSplit orderby r.ToSplit descending select r).First();
                    int take = mode.ClassCarsTarget[carClass.CarClassId];

                    // save the class target cars count in this class
                    split.SetClassTarget(carsListPerClass.IndexOf(carClass), take);
                    // .. and decrement the remaninng cars of this class
                    classRemainingCars[carClass.CarClassId] -= take;
                }
            }



            // OPTIMIZATIONS
            // the important points of this algorithm
            Optimize(fieldSize, classRemainingCars);

            // AT THIS POINT :
            // on the Splits array, all Class{i}Target values are updated with
            // number of cars we want
            // for each split, and each class.


            // Implement car lists
            foreach (var split in Splits)                           // foreach each split
            {
                for (int i = 0; i < 4; i++)                         // for each car class
                {
                    int carsToAddInClass = split.GetClassTarget(i); // get the cars count we want
                    if (carsListPerClass.Count > i)
                    {
                        var cars = carsListPerClass[i].PickCars(carsToAddInClass); // pick up the cars in the ordered list by iRating DESC
                        if (cars.Count > 0)
                        {
                            split.SetClass(i, cars, carsListPerClass[i].CarClassId); // set the class car list
                        }
                    }
                }
            }

            // done
            // :-)
        }