예제 #1
0
        /*
         * LEAGUE
         *
         *  input
         *      - list of teams (ids)
         *      - start date
         *      - weekdays available
         *          - with range of hours
         *      - days forbidden
         *      - list of play fields
         *          - with their availability
         *      - game duration (minutes)
         *      - is preview?
         *
         *  output:
         *      - a collection of days, each containing:
         *          - date
         *          - list of matches, each containing:
         *              - teams
         *              - hour
         *              - play field
         *              - status: scheduled
         *
         *  Notes:
         *      - First create a list of matches per day, then spread it in the available slots.
         *      - From the number of teams, the algorithm can predict how many slots are needed in each day
         *          (it's the same as the number of teams). This should be used in the UI to set up the
         *          calendar parameters.
         *      - To create the list of matches per day, start with the list of teams.
         *          - In the UI, allow the organizer to order them any way the want (display a message for this).
         *          - Then do every possible combination. If odd number of teams, one will be left out each round, in order.
         *      - To spread the matches in slots, randomize the list and assign to slots.
         *      - Have to consider the "home" setting of each team. Only one team should play at home, but then,
         *          what if two have then same home? no matter what, they will have to play, and their home won't
         *          change.
         *
         *  Should this be implemented in the server instead? Having this not validated on the client implies an
         *  organizer can mess the calendar. Is it possible that it can ultimately generate payments to users?
         *  In the server this could be implemented as a simple API call that calculates the preview. An argument
         *  can decide whether the generated result is just a preview or has to be commited to the DB.
         *  Since the game field availability is a lot of data that has to be queried to the database, it's
         *  benefitial to do it on the server side.
         *
         *  Test environment for this.
         */

        /// <summary>
        ///
        /// </summary>
        /// <param name="input"></param>
        /// <param name="fields">List of fields, including matches already scheduled to consider in the plan</param>
        /// <returns></returns>
        public static CalendarResult Calculate(CalendarGenInput input, IList <Field> fields, string locale, TeamLocationPreferenceProviderDelegate preferenceProvider, TeamNameProvider nameProvider)
        {
            //-First create a list of matches per round, then spread it in the available slots.

            //      - From the number of teams, the algorithm can predict how many slots are needed in each round
            //          (it's the same as the number of teams). This should be used in the UI to set up the
            //            calendar parameters.
            //        -To create the list of matches per round, start with the list of teams.
            //            - In the UI, allow the organizer to order them any way the want(display a message for this).
            //            - Then apply the round robin algorithm, preserving the first entry in the list.
            //     - To spread the matches in slots, randomize each round list and assign to slots in sequence.
            //     - Have to consider the "home" setting of each team. Only one team should play at home, but then,
            //       what if two have the same home? no matter what, they will have to play, and their home won't
            //          change.

            Assert.IsNotNull(input);
            Assert.IsNotNull(input.TeamIds);
            Assert.IsNotNull(input.WeekdaySlots);
            Assert.IsNotNull(input.FieldIds);

            var result = new CalendarResult();

            if (input.TeamIds.Length <= 2)
            {
                throw new PlannerException("Error.Calendar.NotEnoughTeams");
            }

            var teams = input.TeamIds;

            var oddNumTeams = teams.Length % 2 == 1;

            if (oddNumTeams)
            {
                // Add a "free day" team (id = -1)
                Array.Resize(ref teams, teams.Length + 1);
                teams[teams.Length - 1] = -1;
            }

            var numSlotsNeededPerRound = teams.Length / 2;
            //var numFields = input.FieldIds.Length;
            //var availableTimeSlots = PlannerScheduler.GetTimeSlots(input.WeekdaySlots, input.GameDuration);
            //var availableSlots = availableTimeSlots * numFields;
            //if (availableSlots < numSlotsNeededPerRound) throw new PlannerException("Error.Calendar.NotEnoughHours");

            var matchRounds = CreateRoundRobinMatches(teams, input.NumRounds);

            string roundNameCallback(int index) => Localization.Get("Jornada {0}", locale, index);

            var locationPreferences = preferenceProvider?.Invoke(input.TeamIds);

            // Now assign matches to days. Consider field availability.
            PlannerScheduler.SpreadMatchesInCalendar(
                result,
                matchRounds,
                input.WeekdaySlots,
                input.StartDate,
                input.FieldIds,
                numSlotsNeededPerRound,
                input.GameDuration,
                input.RandomizeFields,
                input.Group,
                input.ForbiddenDays,
                locationPreferences,
                roundNameCallback);

            return(result);
        }
예제 #2
0
        public static CalendarResult Calculate(CalendarGenInput input, IList <Field> fields, string locale, TeamLocationPreferenceProviderDelegate preferenceProvider, IDbConnection c)
        {
            Assert.IsNotNull(input);
            Assert.IsNotNull(input.TeamIds);
            Assert.IsNotNull(input.WeekdaySlots);
            Assert.IsNotNull(input.FieldIds);

            var result = new CalendarResult();

            var  teams          = input.TeamIds;
            bool noTeamsDefined = teams.Length == 0;
            int  numberOfTeams  = 0;

            if (noTeamsDefined) // No teams defined yet.
            {
                long idGroup = input.Group.IdGroup;
                numberOfTeams = c.QueryFirst <int>($"SELECT numteams FROM stageGroups WHERE id = {idGroup};");
            }
            else
            {
                numberOfTeams = teams.Length;
            }

            if (!IsPowerOfTwo(numberOfTeams))
            {
                throw new PlannerException("Error.NotPowerOfTwo");
            }
            // Should also check that teams are actually valid (!= -1)

            var matchRounds = CreateRounds(teams, input.Group, numberOfTeams);

            string roundNameCallback(int index) => GetRoundName(index, matchRounds.Count, locale);

            var numSlotsNeededPerRound = numberOfTeams / 2;
            //var numFields = input.FieldIds.Length;
            //var availableTimeSlots = PlannerScheduler.GetTimeSlots(input.WeekdaySlots, input.GameDuration);
            //var availableSlots = availableTimeSlots * numFields;
            //if (availableSlots < numSlotsNeededPerRound) throw new PlannerException("Error.Calendar.NotEnoughHours");

            var teamPrefs = noTeamsDefined ? null : preferenceProvider?.Invoke(input.TeamIds);

            PlannerScheduler.SpreadMatchesInCalendar(
                result,
                matchRounds,
                input.WeekdaySlots,
                input.StartDate,
                input.FieldIds,
                numSlotsNeededPerRound,
                input.GameDuration,
                false,
                input.Group,
                input.ForbiddenDays,
                teamPrefs,
                roundNameCallback);

            return(result);
        }