internal JamTimeEstimate ReadEstimateData(SqlDataReader reader) { JamTimeEstimate jam = new JamTimeEstimate(); jam.JamID = reader.GetInt32(reader.GetOrdinal("JamID")); jam.Estimate = reader.GetInt32(reader.GetOrdinal("Seconds")); jam.Minimum = reader.GetInt32(reader.GetOrdinal("Minimum")); jam.Maximum = reader.GetInt32(reader.GetOrdinal("Maximum")); return(jam); }
private Dictionary <int, int> CorrectJamLengthsForBoxes(Dictionary <int, JamTimeEstimate> estimateMap) { Dictionary <int, int> groupDurationMap = new Dictionary <int, int>(); Dictionary <int, bool> durationIssues = new Dictionary <int, bool>(); var crazyJammerBoxTimes = _crazyJammerBoxTimes.Select(cj => cj.BoxTime); bool exceptions = false; foreach (PenaltyGroup penaltyGroup in _penaltyGroups) { int penaltyDuration; if (penaltyGroup.BoxTimes.Count == 1 && crazyJammerBoxTimes.Contains(penaltyGroup.BoxTimes.First())) { penaltyDuration = 10; groupDurationMap[penaltyGroup.GroupID] = penaltyDuration; } else if (penaltyGroup.BoxTimes.Last().EndedJamInBox&& _endJams.Contains(penaltyGroup.BoxTimes.Last().JamID)) { // if this was just the end jam, assume 10 seconds; otherwise, assume 20 penaltyDuration = penaltyGroup.BoxTimes.Count == 1 ? 10 : 20; groupDurationMap[penaltyGroup.GroupID] = penaltyDuration; } else { penaltyDuration = penaltyGroup.Penalties.Count * 30; groupDurationMap[penaltyGroup.GroupID] = penaltyDuration; // we want to make sure there are a few seconds on either side of the penalty penaltyDuration += 10; } List <JamTimeEstimate> groupJams = new List <JamTimeEstimate>(); foreach (BoxTime boxTime in penaltyGroup.BoxTimes) { groupJams.Add(estimateMap[boxTime.JamID]); } int currentDuration = groupJams.Select(gj => gj.Estimate).Sum(); int minimumDuration = groupJams.Select(gj => gj.Minimum).Sum(); int maximumDuration = groupJams.Select(gj => gj.Maximum).Sum(); foreach (BoxTime boxTime in penaltyGroup.BoxTimes) { var others = _singleJamMultiPenalty.Where(sjmp => sjmp.Key.JamID == boxTime.JamID && sjmp.Key.PlayerID == boxTime.PlayerID); if (others.Any()) { currentDuration -= estimateMap[boxTime.JamID].Estimate / 2; } } if (maximumDuration < penaltyDuration) { durationIssues[penaltyGroup.BoxTimes.First().JamID] = true; exceptions = true; } else if (currentDuration < penaltyDuration) { // determine where we can extend int timeNeeded = penaltyDuration - currentDuration; int timeAvailable = maximumDuration - currentDuration; int boxCount = penaltyGroup.BoxTimes.Count; float ratio = ((float)timeNeeded) / timeAvailable; foreach (BoxTime boxTime in penaltyGroup.BoxTimes) { JamTimeEstimate thisJam = estimateMap[boxTime.JamID]; if (thisJam.Maximum > thisJam.Estimate) { // this was a partial service jam, so we can potentially manipulate its length int addition = (int)(ratio * (thisJam.Maximum - thisJam.Estimate)); thisJam.Estimate += addition; timeNeeded -= addition; } } // any leftover seconds can just pack wherever is first available if (timeNeeded > 0) { foreach (BoxTime boxTime in penaltyGroup.BoxTimes) { JamTimeEstimate thisJam = estimateMap[boxTime.JamID]; if (thisJam.Maximum > thisJam.Estimate) { if (thisJam.Maximum - thisJam.Estimate > timeNeeded) { thisJam.Estimate += timeNeeded; break; } else { int difference = thisJam.Maximum - thisJam.Estimate; thisJam.Estimate = thisJam.Maximum; timeNeeded -= difference; } } } } } } if (exceptions) { Console.WriteLine("Duration issues in:"); foreach (int foo in durationIssues.Keys.OrderBy(f => f)) { Console.WriteLine(foo); } throw new InvalidDataException("duration issues"); } return(groupDurationMap); }
private Dictionary <int, int> CalculateBoxTimeEstimates(Dictionary <int, JamTimeEstimate> estimateMap) { Dictionary <int, int> boxTimeEstimateMap = new Dictionary <int, int>(); Dictionary <int, int> groupDurationMap = CorrectJamLengthsForBoxes(estimateMap); if (_singleJamMultiPenalty == null) { _singleJamMultiPenalty = _connectedBoxList.GroupBy(cbl => new JamPlayerPair { JamID = cbl.BoxTime.JamID, PlayerID = cbl.BoxTime.PlayerID }).Where(g => g.Count() > 1).ToList(); } foreach (PenaltyGroup penaltyGroup in _penaltyGroups) { int penaltyDuration = groupDurationMap[penaltyGroup.GroupID]; var fullJamBoxes = penaltyGroup.BoxTimes.Where(bt => bt.StartedJamInBox == true && bt.EndedJamInBox); var partialJamBoxes = penaltyGroup.BoxTimes.Except(fullJamBoxes); foreach (BoxTime boxTime in fullJamBoxes) { int jamTime = estimateMap[boxTime.JamID].Estimate; boxTimeEstimateMap[boxTime.BoxTimeID] = estimateMap[boxTime.JamID].Estimate; penaltyDuration -= estimateMap[boxTime.JamID].Estimate; } // apply remaining penalty time proportionally among jams List <JamTimeEstimate> boxEstimates = new List <JamTimeEstimate>(); int availableJamDuration = 0; foreach (BoxTime boxTime in partialJamBoxes) { JamTimeEstimate estimate = estimateMap[boxTime.JamID]; boxEstimates.Add(estimate); var others = _singleJamMultiPenalty.Where(sjmp => sjmp.Key.JamID == boxTime.JamID && sjmp.Key.PlayerID == boxTime.PlayerID); if (others.Any()) { availableJamDuration += (estimate.Estimate / 2) - 1; } else { availableJamDuration += estimate.Estimate; } } float ratio = ((float)penaltyDuration) / availableJamDuration; foreach (BoxTime boxTime in partialJamBoxes) { JamTimeEstimate thisJam = estimateMap[boxTime.JamID]; int time; var others = _singleJamMultiPenalty.Where(sjmp => sjmp.Key.JamID == boxTime.JamID && sjmp.Key.PlayerID == boxTime.PlayerID); if (others.Any()) { time = (int)(ratio * estimateMap[boxTime.JamID].Estimate / 2) - 1; } else { time = (int)(ratio * estimateMap[boxTime.JamID].Estimate); } boxTimeEstimateMap[boxTime.BoxTimeID] = time; penaltyDuration -= time; } // any leftover seconds can just pack wherever is first available if (penaltyDuration > 0) { foreach (BoxTime boxTime in partialJamBoxes) { JamTimeEstimate thisJam = estimateMap[boxTime.JamID]; int boxTimeEstimate = boxTimeEstimateMap[boxTime.BoxTimeID]; if (thisJam.Estimate > boxTimeEstimate + 1) { if (thisJam.Estimate - boxTimeEstimate - 1 > penaltyDuration) { boxTimeEstimateMap[boxTime.BoxTimeID] += penaltyDuration; penaltyDuration = 0; break; } else { int difference = thisJam.Estimate - boxTimeEstimate - 1; boxTimeEstimateMap[boxTime.BoxTimeID] += difference; penaltyDuration -= difference; } } } } if (penaltyDuration > 0) { throw new InvalidDataException("Penalty group " + penaltyGroup.GroupID + " has duration issues"); } } return(boxTimeEstimateMap); }
private Dictionary <int, JamTimeEstimate> CalculateJamDurationLimits(SqlConnection connection, SqlTransaction transaction, Dictionary <int, Jam> jamMap) { if (_penaltyGroups == null) { _penaltyGroups = new PenaltyGroupGateway(connection, transaction).GetAllPenaltyGroups(); } if (_jammers == null) { _jammers = new JammerGateway(connection, transaction).GetAllJammers().Where(j => jamMap.Keys.Contains(j.JamID)).ToList(); } if (_connectedBoxList == null) { _connectedBoxList = GenerateConnectedBoxList(jamMap); } var jamEstimateMap = jamMap.ToDictionary(jm => jm.Key, jm => new JamTimeEstimate { JamID = jm.Key, Minimum = 5, Maximum = 120 }); _endJams = jamMap.Values.GroupBy(j => j.BoutID).Select(g => g.Max(j => j.ID)); if (_matchedStartingJammerBoxTimes == null) { _matchedStartingJammerBoxTimes = (from cbl1 in _connectedBoxList join cbl2 in _connectedBoxList on cbl1.BoxTime.JamID equals cbl2.BoxTime.JamID where cbl1.BoxTime.IsJammer && cbl2.BoxTime.IsJammer && cbl1.BoxTime.PlayerID != cbl2.BoxTime.PlayerID && cbl1.BoxTime.StartedJamInBox == true && cbl2.BoxTime.StartedJamInBox == true select cbl1).ToList(); } if (_crazyJammerBoxTimes == null) { _crazyJammerBoxTimes = (from cbl1 in _matchedStartingJammerBoxTimes join cbl2 in _matchedStartingJammerBoxTimes on cbl1.BoxTime.JamID equals cbl2.BoxTime.JamID where cbl1.PenaltyGroup.BoxTimes.Count == 1 && cbl2.PenaltyGroup.BoxTimes.Count == 1 select cbl1).ToList(); } var fullJamService = _connectedBoxList.Where(cbl => cbl.BoxTime.EndedJamInBox && cbl.BoxTime.StartedJamInBox == true && !cbl.BoxTime.Finished); foreach (ConnectedBoxTime cbt in fullJamService) { if (cbt.PenaltyGroup.BoxTimes.Count != 1) { // the penalty was not completely served in this jam int newMax = (30 * cbt.PenaltyGroup.Penalties.Count) - (3 * (cbt.PenaltyGroup.BoxTimes.Count - 1)); if (jamEstimateMap[cbt.BoxTime.JamID].Maximum > newMax) { jamEstimateMap[cbt.BoxTime.JamID].Maximum = newMax; } } } var singleJamService = _connectedBoxList.Where(cbl => cbl.PenaltyGroup.BoxTimes.Count == 1); foreach (ConnectedBoxTime cbt in singleJamService) { int newMin; if (_matchedStartingJammerBoxTimes.Contains(cbt)) { // when both jammers start a jam in the box and neither ended the previous jam in the box // then they are both released after 10 seconds, or something like that newMin = 10; } else if (cbt.BoxTime.EndedJamInBox && _endJams.Contains(cbt.BoxTime.JamID)) { // if someone did not come back onto the track after a penalty // they started serving in the final jam, // assume they did not serve the whole penalty newMin = 10; } else { // leave enough time for the player to have gotten into and out of the box newMin = cbt.PenaltyGroup.Penalties.Count * 30 + 2; } JamTimeEstimate limit = jamEstimateMap[cbt.BoxTime.JamID]; if (newMin > limit.Minimum) { limit.Minimum = newMin; } } if (_singleJamMultiPenalty == null) { _singleJamMultiPenalty = _connectedBoxList.GroupBy(cbl => new JamPlayerPair { JamID = cbl.BoxTime.JamID, PlayerID = cbl.BoxTime.PlayerID }).Where(g => g.Count() > 1).ToList(); } foreach (IGrouping <JamPlayerPair, ConnectedBoxTime> group in _singleJamMultiPenalty) { float penaltyCount = 0; JamTimeEstimate limit = jamEstimateMap[group.Key.JamID]; foreach (ConnectedBoxTime boxTime in group) { if (!boxTime.BoxTime.StartedJamInBox == true && !boxTime.BoxTime.EndedJamInBox) { penaltyCount += boxTime.PenaltyGroup.Penalties.Count; } else { penaltyCount += 0.5f; } } int totalSeconds = (int)(penaltyCount * 32); if (totalSeconds < limit.Maximum && totalSeconds > limit.Minimum) { limit.Minimum = totalSeconds; } } var jammers = _jammers.GroupBy(j => j.JamID); bool exceptions = false; foreach (IGrouping <int, Jammer> jamGroup in jammers) { // Jams that do not end by call or injury must have gone the full two minutes if (!jamGroup.Where(j => j.Called || j.Injury).Any()) { jamEstimateMap[jamGroup.Key].Minimum = 120; if (jamEstimateMap[jamGroup.Key].Maximum != 120) { Console.WriteLine("JamID " + jamGroup.Key + ": conflicting maximums"); exceptions = true; } } else { // jams must have gone at least long enough for people to get around the track int highestScore = jamGroup.GroupBy(jg => jg.TeamID).Max(g => g.Sum(j => j.Score)); int newMin = (highestScore + 4 / 5) * 6 + 5; JamTimeEstimate limit = jamEstimateMap[jamGroup.Key]; if (newMin > limit.Minimum) { limit.Minimum = newMin; } } } if (exceptions) { throw new InvalidDataException("Conflicting maximums"); } return(jamEstimateMap); }