/// <summary>
        /// Checks, whether one of the existing matches contains any of the two or three teams of a certain match.
        /// </summary>
        /// <param name="match">The match for which the check will be performed.</param>
        /// <param name="matchGroup">The group of matches to search.</param>
        /// <returns>Returns true, one of the existing matches contains any of the two or three teams of a certain match, false otherwise.</returns>
        private bool AnyTeamExistsInGroup(TeamCombination <T> combination, TeamCombinationGroup <T> group)
        {
            var teams = new Stack <T>(30);

            foreach (var t in group)
            {
                teams.Push(t.HomeTeam);
                teams.Push(t.GuestTeam);
                teams.Push(t.Referee);
            }
            while (teams.Count > 0)
            {
                T team = teams.Pop();
                if (Comparer <T> .Default.Compare(team, combination.HomeTeam) == 0 ||
                    Comparer <T> .Default.Compare(team, combination.GuestTeam) == 0 ||
                    Comparer <T> .Default.Compare(team, combination.Referee) == 0)
                {
                    return(true);
                }
            }
            return(false);
        }
        /// <summary>
        /// Groups the calculated team combinations for matches.  in a way, that most matches
        /// can be played in parallel.
        /// </summary>
        /// <param name="legType">First leg or return leg.</param>
        /// <param name="optiType">Optimization type for groups. Differences can be seen be with an uneven number of teams.</param>
        /// <returns>Return a collection containing collections of team combinations.</returns>
        internal Collection <TeamCombinationGroup <T> > GetBundledGroups(LegType legType, CombinationGroupOptimization optiType)
        {
            var combinationsQueue = new Queue <TeamCombination <T> >(_group.Count);
            TeamCombinationGroup <T> group;
            var bundledGroups = new Collection <TeamCombinationGroup <T> >();

            // create the FIFO queue
            foreach (var combination in _group)
            {
                combinationsQueue.Enqueue(combination);
            }

            switch (optiType)
            {
            case CombinationGroupOptimization.NoGrouping:
                // every group contains a collection with only 1 match
                while (combinationsQueue.Count > 0)
                {
                    group = new TeamCombinationGroup <T>();
                    group.Add(combinationsQueue.Dequeue());
                    bundledGroups.Add(group);
                }
                break;

            case CombinationGroupOptimization.GroupWithAlternatingHomeGuest:
                group = new TeamCombinationGroup <T>();
                while (combinationsQueue.Count > 0)
                {
                    TeamCombination <T> combination = combinationsQueue.Dequeue();
                    if (AnyTeamExistsInGroup(combination, group))
                    {
                        bundledGroups.Add(group);
                        group = new TeamCombinationGroup <T>();
                    }
                    group.Add(combination);
                }
                if (group.Count > 0)
                {
                    bundledGroups.Add(group);
                }
                break;

            case CombinationGroupOptimization.LeastGroupsPossible:
                while (combinationsQueue.Count > 0)
                {
                    var tmpGroup = new List <TeamCombination <T> >();
                    tmpGroup.AddRange(combinationsQueue);

                    group = new TeamCombinationGroup <T>();
                    foreach (var combination in tmpGroup)
                    {
                        if (!AnyTeamExistsInGroup(combination, group))
                        {
                            group.Add(combinationsQueue.Dequeue());
                        }
                    }
                    bundledGroups.Add(group);
                }
                break;
            }
            return(bundledGroups);
        }
 /// <summary>
 /// Constructor.
 /// </summary>
 /// <param name="matches">A collection of team  combinations with type T objects.</param>
 internal CombinationGroupOptimizer(TeamCombinationGroup <T> group)
 {
     _group = group;
 }
Пример #4
0
        private List <AvailableMatchDateEntity?> GetMatchDates(RoundLegEntity roundLeg,
                                                               TeamCombinationGroup <long> teamCombinationGroup, EntityCollection <MatchEntity> groupMatches)
        {
            // here the resulting match dates are stored:
            var matchDatePerCombination = new List <AvailableMatchDateEntity?>();

            // these are possible date alternatives per combination:
            var matchDates = new List <List <AvailableMatchDateEntity> >();

            for (var index = 0; index < teamCombinationGroup.Count; index++)
            {
                var combination = teamCombinationGroup[index];

                var availableDates = _availableMatchDates.GetGeneratedAndManualAvailableMatchDates(combination.HomeTeam,
                                                                                                   teamCombinationGroup.DateTimePeriod, GetOccupiedMatchDates(combination, groupMatches));
                // initialize MinTimeDiff for the whole list
                availableDates.ForEach(amd => amd.MinTimeDiff = TimeSpan.MaxValue);
                if (availableDates.Count == 0)
                {
                    availableDates = _availableMatchDates.GetGeneratedAndManualAvailableMatchDates(combination.HomeTeam,
                                                                                                   new DateTimePeriod(roundLeg.StartDateTime, roundLeg.EndDateTime),
                                                                                                   GetOccupiedMatchDates(combination, groupMatches));
                }

                matchDates.Add(availableDates);

                #if DEBUG
                // Check whether there is a match of this combination
                var lastMatchOfCombination = groupMatches.OrderBy(gm => gm.PlannedStart).LastOrDefault(gm =>
                                                                                                       gm.HomeTeamId == combination.HomeTeam || gm.GuestTeamId == combination.GuestTeam);
                if (lastMatchOfCombination != null)
                {
                    _logger.LogTrace("Last match date found for home team '{0}' and guest team '{1}' is '{2}'", combination.HomeTeam, combination.GuestTeam, lastMatchOfCombination.PlannedStart?.ToShortDateString() ?? "none");
                }
                else
                {
                    _logger.LogTrace("No last match found for home team '{0}' and guest team '{1}'", combination.HomeTeam, combination.GuestTeam);
                }
                #endif
            }

            // we can't proceed without and match dates found
            if (matchDates.Count == 0)
            {
                return(matchDatePerCombination);
            }

            // only 1 match date found, so optimization is not possible
            // and the following "i-loop" will be skipped
            if (matchDates.Count == 1)
            {
                matchDatePerCombination.Add(matchDates[0][0]);
                return(matchDatePerCombination);
            }

            // cross-compute the number of dates between between group pairs.
            // goal: found match dates should be as close together as possible

            // start with 1st dates, end with last but one dates
            for (var i = 0; i < matchDates.Count - 1; i++)
            {
                // start with 2nd dates, end with last dates
                for (var j = 1; j < matchDates.Count; j++)
                {
                    // compare each date in the first list...
                    foreach (var dates1 in matchDates[i])
                    {
                        // ... with the dates in the second list
                        foreach (var dates2 in matchDates[j])
                        {
                            var daysDiff = Math.Abs((dates1.MatchStartTime.Date - dates2.MatchStartTime.Date).Days);

                            // save minimum dates found for later reference
                            if (daysDiff < dates1.MinTimeDiff.Days)
                            {
                                dates1.MinTimeDiff = new TimeSpan(daysDiff, 0, 0, 0);
                            }

                            if (daysDiff < dates2.MinTimeDiff.Days)
                            {
                                dates2.MinTimeDiff = new TimeSpan(daysDiff, 0, 0, 0);
                            }
                        } // end dates2
                    }     // end dates1
                }         // end j

                // get the date that has least distance to smallest date in other group(s)
                // Note: If no match dates could be determined for a team, bestDate will be null.
                var bestDate = matchDates[i].Where(md => md.MinTimeDiff == matchDates[i].Min(d => d.MinTimeDiff))
                               .OrderBy(md => md.MinTimeDiff).FirstOrDefault();
                matchDatePerCombination.Add(bestDate);

                // process the last combination

                // in case comparisons took place,
                // now the "j-loop" group is not processed yet:
                if (i + 1 >= matchDates.Count - 1)
                {
                    bestDate = matchDates[^ 1].Where(md => md.MinTimeDiff == matchDates[^ 1].Min(d => d.MinTimeDiff))