/// <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; }
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))