private static void ProcessJamPlayer(Dictionary <int, List <PenaltyGroup> > pgMap, Dictionary <int, List <PenaltyGroup> > jamBoxTimeMap, Dictionary <int, int> boxTimeEstimates, int jamID, JamTeamEffectiveness jte1, List <JamPlayerEffectiveness> pjeList, int jamTime, JamPlayer player) { // handle penalties var playerPenaltyGroups = pgMap.ContainsKey(player.PlayerID) ? pgMap[player.PlayerID] : null; ProcessPlayerJamPenalties(jamBoxTimeMap, jamID, playerPenaltyGroups); // try to estimate what portion of a jam someone missed via time in the box int timeInBox = 0; if (jamBoxTimeMap.ContainsKey(jamID)) { foreach (PenaltyGroup group in jamBoxTimeMap[jamID]) { foreach (BoxTime bt in group.BoxTimes) { if (bt.PlayerID == player.PlayerID && bt.JamID == jamID) { // factor in estimated box time timeInBox += boxTimeEstimates[bt.BoxTimeID]; } } } } JamPlayerEffectiveness pje = new JamPlayerEffectiveness { PlayerID = player.PlayerID, TeamID = player.TeamID, JamPortion = ((double)jamTime - timeInBox) / jamTime, BaseQuality = jte1.Percentile, JamID = jamID, IsJammer = player.IsJammer, PenaltyCost = 0 }; pjeList.Add(pje); }
private void CalculatePlayerEffectiveness() { if (_jamTeamEffectiveness == null) { CalculateJamTeamEffectiveness(); } SqlConnection connection = new SqlConnection(_connectionString); connection.Open(); SqlTransaction transaction = connection.BeginTransaction(); _jamPlayers = new JamPlayerGateway(connection, transaction).GetJamPlayers(); _penaltyGroups = new PenaltyGroupGateway(connection, transaction).GetAllPenaltyGroups(); // these three get mapped by jam var jteMap = _jamTeamEffectiveness.GroupBy(jte => jte.JamID).ToDictionary(g => g.Key); var jamDataMap = _jamTeamData.GroupBy(jf => jf.JamID).ToDictionary(g => g.Key); var jpJamMap = _jamPlayers.GroupBy(jp => jp.JamID).ToDictionary(g => g.Key); var jamTimeMap = new JamTimeLimitGateway(connection, transaction).GetAllJamTimeEstimates().ToDictionary(jte => jte.JamID); //var jpPlayerMap = players.GroupBy(jp => jp.PlayerID).ToDictionary(g => g.Key); // the best we can do with this is map it by player var pgMap = _penaltyGroups.GroupBy(pg => pg.PlayerID).ToDictionary(g => g.Key, g => g.ToList()); Dictionary <int, List <JamPlayerEffectiveness> > pjeMap = new Dictionary <int, List <JamPlayerEffectiveness> >(); Dictionary <int, List <PenaltyGroup> > jamBoxTimeMap1 = new Dictionary <int, List <PenaltyGroup> >(); Dictionary <int, List <PenaltyGroup> > jamBoxTimeMap2 = new Dictionary <int, List <PenaltyGroup> >(); _boxTimeEstimates = new BoxTimeEstimateGateway(connection, transaction).GetAllBoxTimeEstimates(); int maxJamID = jteMap.Keys.Max(); int jamID = 1; while (jamID <= maxJamID) { if (!jteMap.ContainsKey(jamID)) { jamID++; continue; } var tempJte = jteMap[jamID].OrderBy(jte => jte.TeamID); JamTeamEffectiveness jte1 = tempJte.First(); JamTeamEffectiveness jte2 = tempJte.Last(); var team1Players = jpJamMap[jamID].Where(jp => jp.TeamID == jte1.TeamID); var team2Players = jpJamMap[jamID].Where(jp => jp.TeamID == jte2.TeamID); List <JamPlayerEffectiveness> pjeList = new List <JamPlayerEffectiveness>(5); int jamTime = jamTimeMap[jamID].Estimate; // handle each player foreach (JamPlayer player in team1Players) { ProcessJamPlayer(pgMap, jamBoxTimeMap1, _boxTimeEstimates, jamID, jte1, pjeList, jamTime, player); } List <JamPlayerEffectiveness> pjeList2 = new List <JamPlayerEffectiveness>(5); // handle each player foreach (JamPlayer player in team2Players) { ProcessJamPlayer(pgMap, jamBoxTimeMap2, _boxTimeEstimates, jamID, jte2, pjeList, jamTime, player); } pjeMap[jamID] = pjeList; pjeMap[jamID].AddRange(pjeList2); jamID++; } jamID = 1; while (jamID <= maxJamID) { if (!jteMap.ContainsKey(jamID)) { jamID++; continue; } var tempJte = jteMap[jamID].OrderBy(jte => jte.TeamID); JamTeamEffectiveness jte1 = tempJte.First(); JamTeamEffectiveness jte2 = tempJte.Last(); // apply box time costs to players with correlated penalties if (jamBoxTimeMap1.ContainsKey(jamID)) { AssignPenaltyCosts(jamDataMap, jamTimeMap, pjeMap, jamBoxTimeMap1, _boxTimeEstimates, jamID, jte1); } // apply box time costs to players with correlated penalties if (jamBoxTimeMap2.ContainsKey(jamID)) { AssignPenaltyCosts(jamDataMap, jamTimeMap, pjeMap, jamBoxTimeMap2, _boxTimeEstimates, jamID, jte2); } jamID++; } new JamPlayerEffectivenessGateway(connection, transaction).InsertJamPlayerEffectiveness(pjeMap); transaction.Commit(); connection.Close(); }
private void AssignPenaltyCosts(Dictionary <int, IGrouping <int, JamTeamData> > jamDataMap, Dictionary <int, JamTimeEstimate> jamTimeMap, Dictionary <int, List <JamPlayerEffectiveness> > pjeMap, Dictionary <int, List <PenaltyGroup> > jamBoxTimeMap, Dictionary <int, int> boxTimeEstimates, int jamID, JamTeamEffectiveness jte) { List <PenaltyGroup> jamPenaltyGroups = jamBoxTimeMap[jamID]; JamTeamData thisJamData = jamDataMap[jamID].First(jd => jd.TeamID == jte.TeamID); foreach (PenaltyGroup group in jamPenaltyGroups) { int penaltyPlayerID = group.Penalties[0].PlayerID; foreach (BoxTime boxTime in group.BoxTimes) { if (boxTime.JamID == jamID) { // determine how big the swing would be if there had been no box time served double qualityDifference = DetermineQualityWithoutPenalty(thisJamData, boxTime.IsJammer) - jte.Percentile; // the cost of the box time is how bad that player would have to be in that jam to have an equivalent effect // we estimate this based on how important the player is in the jam, by position double penaltyCost = boxTime.IsJammer ? qualityDifference * 2 : qualityDifference * 8; double totalPenaltyTime = boxTime.IsJammer ? thisJamData.JammerBoxTime : thisJamData.BlockerBoxTime; // modify this by how much of the total penalty time this player contributed // doing the rough estimate version for now double penaltyPortion = boxTimeEstimates[boxTime.BoxTimeID] / totalPenaltyTime; // a portion of that cost goes to each penalty that factors into this box time int penaltyCount = group.Penalties.Count; foreach (Penalty penalty in group.Penalties) { var penaltyPJE = pjeMap[penalty.JamID].First(pje => pje.PlayerID == penalty.PlayerID); penaltyPJE.PenaltyCost += penaltyPortion * penaltyCost / penaltyCount; if (double.IsNaN(penaltyPJE.PenaltyCost)) { throw new Exception(penalty.JamID + ": bad penalty data"); } } // for a given penalty group, there should only ever be // a single box time in a given jam break; } } } }