internal Dictionary <int, double> CalculateValueCostsForTeam(Dictionary <int, JamTeamData> jamData, IList <PenaltyGroup> pgs, Dictionary <int, int> boxTimeEstimates, Dictionary <FoulComparison, Dictionary <int, float> > sss, Dictionary <int, double> jamValueEstimates) { Dictionary <int, double> groupPenaltyCostMap = new Dictionary <int, double>(); foreach (PenaltyGroup group in pgs) { double totalCost = 0; // calculate the cost of each box time in the group foreach (BoxTime boxTime in group.BoxTimes) { int estimateTime = boxTimeEstimates[boxTime.BoxTimeID]; JamTeamData thisJamData = jamData[boxTime.JamID]; // determine how big the swing would be if there had been no box time served double valueDiff = GetEstimatedValueWithoutBoxTime(sss, thisJamData, boxTime.IsJammer) - jamValueEstimates[boxTime.JamID]; 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 totalCost += valueDiff * boxTimeEstimates[boxTime.BoxTimeID] / totalPenaltyTime; } groupPenaltyCostMap[group.GroupID] = totalCost / group.Penalties.Count; } return(groupPenaltyCostMap); }
private JamTeamData ReadJamTeamData(SqlDataReader reader) { JamTeamData jamFoul = new JamTeamData(); jamFoul.JamID = reader.GetInt32(reader.GetOrdinal("JamID")); jamFoul.TeamID = reader.GetInt32(reader.GetOrdinal("TeamID")); jamFoul.BlockerBoxTime = reader.GetInt32(reader.GetOrdinal("BlockerBoxTime")); jamFoul.JammerBoxTime = reader.GetInt32(reader.GetOrdinal("JammerBoxTime")); jamFoul.OppBlockerBoxTime = reader.GetInt32(reader.GetOrdinal("OppBlockerBoxTime")); jamFoul.OppJammerBoxTime = reader.GetInt32(reader.GetOrdinal("OppJammerBoxTime")); jamFoul.PointDelta = reader.GetInt32(reader.GetOrdinal("PointDelta")); return(jamFoul); }
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; } } } }
internal static Dictionary <int, double> CalculatePointCosts(Dictionary <int, Dictionary <int, JamTeamData> > jamData, Dictionary <int, Dictionary <int, JamPlayer> > jamPlayerMap, IList <PenaltyGroup> pgs, Dictionary <int, int> boxTimeEstimates, Dictionary <FoulComparison, Dictionary <int, float> > sss) { Dictionary <int, double> groupPenaltyCostMap = new Dictionary <int, double>(); foreach (PenaltyGroup group in pgs) { double totalCost = 0; int teamID = jamPlayerMap[group.Penalties[0].JamID][group.Penalties[0].PlayerID].TeamID; // calculate the cost of each box time in the group foreach (BoxTime boxTime in group.BoxTimes) { if (!jamData.ContainsKey(boxTime.JamID)) { continue; } int estimateTime = boxTimeEstimates[boxTime.BoxTimeID]; JamTeamData thisJamData = jamData[boxTime.JamID][teamID]; // determine how big the swing would be if there had been no box time served double pointDiff = GetEstimatedPointsWithoutBoxTime(sss, thisJamData, boxTime.IsJammer) - thisJamData.PointDelta; 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 totalCost += pointDiff * boxTimeEstimates[boxTime.BoxTimeID] / totalPenaltyTime; } groupPenaltyCostMap[group.GroupID] = totalCost / group.Penalties.Count; } return(groupPenaltyCostMap); }
internal static double GetEstimatedValueWithoutBoxTime(Dictionary <FoulComparison, Dictionary <int, float> > sss, JamTeamData jamData, bool isJammer) { if (!sss.ContainsKey(jamData.FoulComparison)) { throw new InvalidOperationException("This is bad data"); } // figure out the foul differential if this team did not commit fouls of this type this jam int jammerPenaltyDiff = (isJammer ? 0 : jamData.JammerBoxTime) - jamData.OppJammerBoxTime; int blockerPenaltyDiff = (isJammer ? jamData.BlockerBoxTime : 0) - jamData.OppBlockerBoxTime; double jammerBoxComp = Math.Round(jammerPenaltyDiff / 15.0, MidpointRounding.AwayFromZero) / 2.0; double blockerBoxComp = Math.Round(blockerPenaltyDiff / 15.0, MidpointRounding.AwayFromZero) / 2.0; FoulComparison foul = new FoulComparison { Year = jamData.Year, JammerBoxComparison = jammerBoxComp, BlockerBoxComparison = blockerBoxComp }; if (!sss.ContainsKey(foul)) { // lacking anything better, sum the distance of each factor from the percentile of the base JamTeamData baseJamData = new JamTeamData { Year = jamData.Year, BlockerBoxTime = 0, JammerBoxTime = 0, OppBlockerBoxTime = 0, OppJammerBoxTime = 0, PointDelta = jamData.PointDelta }; double baseDelta = GetPercentileForScore(sss[baseJamData.FoulComparison], jamData.PointDelta); // pull team 1 values baseJamData.BlockerBoxTime = isJammer ? jamData.BlockerBoxTime : 0; baseJamData.JammerBoxTime = isJammer ? 0 : jamData.JammerBoxTime; if (baseJamData.FoulComparison.Equals(foul)) { return(0); } double score1 = 0; if (!sss.ContainsKey(baseJamData.FoulComparison)) { while (baseJamData.Year > 2013) { baseJamData.Year--; if (sss.ContainsKey(baseJamData.FoulComparison)) { score1 = GetPercentileForScore(sss[baseJamData.FoulComparison], jamData.PointDelta); break; } } if (baseJamData.Year == 2013) { throw new InvalidOperationException("Bad data"); } } else { score1 = GetPercentileForScore(sss[baseJamData.FoulComparison], jamData.PointDelta); } // pull team 2 blocker baseJamData.BlockerBoxTime = 0; baseJamData.JammerBoxTime = 0; baseJamData.OppBlockerBoxTime = jamData.OppBlockerBoxTime; if (baseJamData.FoulComparison.Equals(foul)) { return(0); } double score2 = 0; if (!sss.ContainsKey(baseJamData.FoulComparison)) { while (baseJamData.Year > 2013) { baseJamData.Year--; if (sss.ContainsKey(baseJamData.FoulComparison)) { score1 = GetPercentileForScore(sss[baseJamData.FoulComparison], jamData.PointDelta); break; } } if (baseJamData.Year == 2013) { throw new InvalidOperationException("Bad data"); } } else { score2 = GetPercentileForScore(sss[baseJamData.FoulComparison], jamData.PointDelta); } // pull team 2 jammer baseJamData.OppBlockerBoxTime = 0; baseJamData.OppJammerBoxTime = jamData.OppJammerBoxTime; if (baseJamData.FoulComparison.Equals(foul)) { return(0); } double score3 = GetPercentileForScore(sss[baseJamData.FoulComparison], jamData.PointDelta); return(score1 + score2 + score3 - 2 * baseDelta); } else { return(GetPercentileForScore(sss[foul], jamData.PointDelta)); } }
private static Dictionary <int, PlayerPerformance> GenerateInitialPlayerPerformance(Dictionary <int, Dictionary <int, Player> > players, IOrderedEnumerable <Jam> jams, Dictionary <int, Dictionary <int, JamPlayerEffectiveness> > jpe, Dictionary <int, Dictionary <int, JamTeamData> > jamData, Dictionary <int, Team> teams, Dictionary <int, Bout> bouts, Dictionary <int, Dictionary <int, List <Penalty> > > penalties, IList <PenaltyGroup> pgs, AveragePenaltyCostPerJam avgPenCost, Dictionary <FoulComparison, float> medians, Dictionary <int, double> groupPenaltyCostMap, Dictionary <int, double> jamTotalPortionMap) { Dictionary <int, PlayerPerformance> pps = new Dictionary <int, PlayerPerformance>(); foreach (Jam jam in jams) { var pe = jpe[jam.ID]; foreach (var eff in pe.Values) { // get/set PlayerPerformance object if (!pps.ContainsKey(eff.PlayerID)) { pps[eff.PlayerID] = new PlayerPerformance { Player = players[eff.PlayerID][eff.TeamID], Bouts = new List <BoutPerformance>() }; } PlayerPerformance curPP = pps[eff.PlayerID]; // get/set BoutPerformance object Bout bout = bouts[jam.BoutID]; BoutPerformance bp = null; if (!curPP.Bouts.Any() || curPP.Bouts.Last().BoutDate != bout.BoutDate || curPP.Bouts.Last().HomeTeamName != teams[bout.HomeTeamID].Name || curPP.Bouts.Last().AwayTeamName != teams[bout.AwayTeamID].Name) { bp = new BoutPerformance { BoutID = bout.ID, AwayTeamName = teams[bout.AwayTeamID].Name, HomeTeamName = teams[bout.HomeTeamID].Name, BoutDate = bout.BoutDate, Jams = new List <JamPerformance>() }; curPP.Bouts.Add(bp); } else { bp = curPP.Bouts.Last(); } JamTeamData jd = jamData[jam.ID][eff.TeamID]; int penaltyCount = penalties.ContainsKey(jam.ID) && penalties[jam.ID].ContainsKey(eff.PlayerID) ? penalties[jam.ID][eff.PlayerID].Count() : 0; JamPerformance jp = new JamPerformance { BlockerJamPercentage = eff.IsJammer ? 0 : eff.JamPortion, DeltaPercentile = eff.BaseQuality, IsFirstHalf = jam.IsFirstHalf, JamID = jam.ID, JammerJamPercentage = eff.IsJammer ? eff.JamPortion : 0, JamNumber = jam.JamNumber, JamPenalties = penaltyCount, MedianDelta = medians[jd.FoulComparison], PenaltyCost = 0, PointDelta = jd.PointDelta }; if (jamTotalPortionMap.ContainsKey(jam.ID)) { jamTotalPortionMap[jam.ID] += eff.IsJammer ? eff.JamPortion * 4 : eff.JamPortion; } else { jamTotalPortionMap[jam.ID] = eff.IsJammer ? eff.JamPortion * 4 : eff.JamPortion; } bp.Jams.Add(jp); } ; } foreach (PenaltyGroup pg in pgs) { foreach (Penalty p in pg.Penalties) { JamPerformance jp = pps[p.PlayerID].Bouts.SelectMany(b => b.Jams).Where(j => j.JamID == p.JamID).First(); jp.PenaltyCost += groupPenaltyCostMap[pg.GroupID]; } } Parallel.ForEach(pps.Values, pp => { RollUpPlayerPerformance(avgPenCost, jamTotalPortionMap, pp); }); return(pps); }
private static Dictionary <int, PlayerPerformance> GeneratePlayerPerformanceIteration(Dictionary <int, PlayerPerformance> previousIteration, IOrderedEnumerable <Jam> jams, Dictionary <int, Dictionary <int, JamTeamData> > jamData, Dictionary <int, double> jamTotalPortionMap, Dictionary <int, Dictionary <int, JamPlayerEffectiveness> > jpe, AveragePenaltyCostPerJam avgPenCost) { Dictionary <int, PlayerPerformance> pps = new Dictionary <int, PlayerPerformance>(); foreach (Jam jam in jams) { var pe = jpe[jam.ID]; var grouped = pe.Values.GroupBy(p => p.TeamID); var team1Players = grouped.First().Select(p => previousIteration[p.PlayerID]); var team2Players = grouped.Last().Select(p => previousIteration[p.PlayerID]); IEnumerable <JamPlayerEffectiveness> jammers = pe.Values.Where(eff => eff.IsJammer); double team1Score = CalculateExpectedScore(team1Players, jammers, jam.BoutID); int team1ID = grouped.First().Key; double team2Score = CalculateExpectedScore(team2Players, jammers, jam.BoutID); foreach (var eff in pe.Values) { var previousPerformance = previousIteration[eff.PlayerID]; // get/set PlayerPerformance object if (!pps.ContainsKey(eff.PlayerID)) { pps[eff.PlayerID] = new PlayerPerformance { Player = previousPerformance.Player, Bouts = new List <BoutPerformance>() }; } PlayerPerformance curPP = pps[eff.PlayerID]; // get/set BoutPerformance object var prevBout = previousPerformance.Bouts.First(b => b.BoutID == jam.BoutID); BoutPerformance bp = null; if (!curPP.Bouts.Any() || curPP.Bouts.Last().BoutID != prevBout.BoutID) { bp = new BoutPerformance { BoutID = prevBout.BoutID, AwayTeamName = prevBout.AwayTeamName, HomeTeamName = prevBout.HomeTeamName, BoutDate = prevBout.BoutDate, Jams = new List <JamPerformance>() }; if (prevBout.BlockerPerformance != null) { bp.BlockerPerformance = new RolledUpPerformanceData { TotalJamPortions = prevBout.BlockerPerformance.TotalJamPortions, TotalPenalties = prevBout.BlockerPerformance.TotalPenalties, TotalPenaltyCost = prevBout.BlockerPerformance.TotalPenaltyCost }; } if (prevBout.JammerPerformance != null) { bp.JammerPerformance = new RolledUpPerformanceData { TotalJamPortions = prevBout.JammerPerformance.TotalJamPortions, TotalPenalties = prevBout.JammerPerformance.TotalPenalties, TotalPenaltyCost = prevBout.JammerPerformance.TotalPenaltyCost }; } curPP.Bouts.Add(bp); } else { bp = curPP.Bouts.Last(); } JamTeamData jd = jamData[jam.ID][eff.TeamID]; JamPerformance prevJamPerformance = prevBout.Jams.First(j => j.JamID == jam.ID); double pointDifferential = 0; var playerPerf = prevJamPerformance.JammerJamPercentage > 0 ? previousPerformance.JammerPerformance : previousPerformance.BlockerPerformance; double playerScore = playerPerf.TotalPointsVersusMedian / playerPerf.TotalJamPortions; if (curPP.Player.TeamID == team1ID) { pointDifferential = jd.PointDelta - team1Score + team2Score + playerScore; } else { pointDifferential = jd.PointDelta - team2Score + team1Score + playerScore; } JamPerformance jp = new JamPerformance { BlockerJamPercentage = prevJamPerformance.BlockerJamPercentage, DeltaPercentile = prevJamPerformance.DeltaPercentile, IsFirstHalf = prevJamPerformance.IsFirstHalf, JamID = prevJamPerformance.JamID, JammerJamPercentage = prevJamPerformance.JammerJamPercentage, JamNumber = prevJamPerformance.JamNumber, JamPenalties = prevJamPerformance.JamPenalties, MedianDelta = prevJamPerformance.MedianDelta, PenaltyCost = prevJamPerformance.PenaltyCost, PointDelta = jd.PointDelta, DeltaPortionVersusMedian = pointDifferential //PlayerValue }; bp.Jams.Add(jp); } } Parallel.ForEach(pps.Values, pp => { RollUpIteratedPlayerPerformance(avgPenCost, jamTotalPortionMap, pp); }); return(pps); }
public IList <PlayerPerformance> GetPlayerPointPerformancesForTeam(int teamID) { // pull data SqlConnection connection = new SqlConnection(_connectionString); connection.Open(); SqlTransaction transaction = connection.BeginTransaction(); var players = new PlayerGateway(connection, transaction).GetPlayersForTeam(teamID).ToDictionary(p => p.ID); var jams = new JamGateway(connection, transaction).GetJamsForTeamAfterDate(teamID, STATS_START_DATE).OrderBy(j => j.ID); var jamBoutMap = jams.ToDictionary(j => j.ID, j => j.BoutID); var jpe = new JamPlayerEffectivenessGateway(connection, transaction).GetJamPlayerEffectivenessForTeam(teamID); var jdg = new JamDataGateway(connection, transaction); var jamTeamData = jdg.GetJamDataForTeam(teamID).ToDictionary(jd => jd.JamID); var jamData = jdg.GetAllJamData().ToDictionary(jd => jd.JamID); foreach (JamTeamData jtd in jamTeamData.Values) { jtd.Year = jamData[jtd.JamID].PlayDate.Year; } var teams = new TeamGateway(connection, transaction).GetAllTeams().ToDictionary(t => t.ID); var bouts = new BoutGateway(connection, transaction).GetBouts().ToDictionary(t => t.ID); var penalties = new PenaltyGateway(connection, transaction).GetPenaltiesForTeam(teamID) .GroupBy(p => p.JamID) .ToDictionary( g => g.Key, g => g.GroupBy(g2 => g2.PlayerID).ToDictionary(g3 => g3.Key, g3 => g3.ToList())); var pgs = new PenaltyGroupGateway(connection, transaction).GetPenaltyGroupsForTeamAfterDate(teamID, STATS_START_DATE); Dictionary <int, int> boxTimeEstimates = new BoxTimeEstimateGateway(connection, transaction).GetAllBoxTimeEstimates(); Dictionary <FoulComparison, Dictionary <int, float> > sss = new SituationalScoreGateway(connection, transaction).GetAllSituationalScores(); AveragePenaltyCostPerJam avgPenCost = new AveragePenaltyCostGateway(connection, transaction).GetAveragePenaltyCost(); transaction.Commit(); connection.Close(); Dictionary <FoulComparison, float> medians = CalculateMedianScores(sss); PenaltyCostCalculator ppcCalc = new PenaltyCostCalculator(_connectionString); Dictionary <int, double> groupPenaltyCostMap = ppcCalc.CalculatePointCostsForTeam(jamTeamData, pgs, boxTimeEstimates, sss); Dictionary <int, PlayerPerformance> pps = new Dictionary <int, PlayerPerformance>(25); Dictionary <int, double> jamTotalPortionMap = new Dictionary <int, double>(300); // we use "jam portions" to divide up the total value of the jam among participating players // we give jammers an additional multiplier to their jam portions, based on year, to represent their greater impact on team success foreach (Jam jam in jams) { var pe = jpe[jam.ID]; foreach (JamPlayerEffectiveness eff in pe.Values) { // get/set PlayerPerformance object if (!pps.ContainsKey(eff.PlayerID)) { pps[eff.PlayerID] = new PlayerPerformance { Player = players[eff.PlayerID], Bouts = new List <BoutPerformance>() }; } PlayerPerformance curPP = pps[eff.PlayerID]; // get/set BoutPerformance object Bout bout = bouts[jam.BoutID]; BoutPerformance bp = null; if (!curPP.Bouts.Any() || curPP.Bouts.Last().BoutID != bout.ID) { bp = new BoutPerformance { BoutID = bout.ID, AwayTeamName = teams[bout.AwayTeamID].Name, HomeTeamName = teams[bout.HomeTeamID].Name, BoutDate = bout.BoutDate, Jams = new List <JamPerformance>() }; curPP.Bouts.Add(bp); } else { bp = curPP.Bouts.Last(); } JamTeamData jd = jamTeamData[jam.ID]; int penaltyCount = penalties.ContainsKey(jam.ID) && penalties[jam.ID].ContainsKey(eff.PlayerID) ? penalties[jam.ID][eff.PlayerID].Count() : 0; JamPerformance jp = new JamPerformance { BlockerJamPercentage = eff.IsJammer ? 0 : eff.JamPortion, DeltaPercentile = eff.BaseQuality, IsFirstHalf = jam.IsFirstHalf, JamID = jam.ID, JammerJamPercentage = eff.IsJammer ? eff.JamPortion : 0, JamNumber = jam.JamNumber, JamPenalties = penaltyCount, MedianDelta = medians[jd.FoulComparison], PenaltyCost = 0, PointDelta = jd.PointDelta }; double jammerRatio = bouts[jamBoutMap[jam.ID]].BoutDate.Year == 2019 ? 2.0 : 4.0; if (jamTotalPortionMap.ContainsKey(jam.ID)) { jamTotalPortionMap[jam.ID] += eff.IsJammer ? eff.JamPortion * jammerRatio : eff.JamPortion; } else { jamTotalPortionMap[jam.ID] = eff.IsJammer ? eff.JamPortion * jammerRatio : eff.JamPortion; } bp.Jams.Add(jp); } } foreach (PenaltyGroup pg in pgs) { foreach (Penalty p in pg.Penalties) { if (jams.Any(j => j.ID == p.JamID)) { JamPerformance jp = pps[p.PlayerID].Bouts.SelectMany(b => b.Jams).Where(j => j.JamID == p.JamID).First(); jp.PenaltyCost += groupPenaltyCostMap[pg.GroupID]; } } } foreach (PlayerPerformance pp in pps.Values) { RollUpPlayerPerformance(avgPenCost, jamTotalPortionMap, pp); } CalculateTeamAverages(pps, bouts); foreach (PlayerPerformance pp in pps.Values) { pp.BlockerPerformance.PlayerValueVersusTeamAverage = pp.Bouts.Sum(b => b.BlockerPerformance.PlayerValueVersusTeamAverage); pp.JammerPerformance.PlayerValueVersusTeamAverage = pp.Bouts.Sum(b => b.JammerPerformance.PlayerValueVersusTeamAverage); } return(pps.Values.ToList()); }
public IList <PlayerPerformance> GetPlayerValuePerformancesForTeam(int teamID) { // pull data SqlConnection connection = new SqlConnection(_connectionString); connection.Open(); SqlTransaction transaction = connection.BeginTransaction(); var players = new PlayerGateway(connection, transaction).GetPlayersForTeam(teamID).ToDictionary(p => p.ID); var jams = new JamGateway(connection, transaction).GetJamsForTeamAfterDate(teamID, new DateTime(2016, 1, 1)).OrderBy(j => j.ID); var jamBoutMap = jams.ToDictionary(j => j.ID, j => j.BoutID); var jpe = new JamPlayerEffectivenessGateway(connection, transaction).GetJamPlayerEffectivenessForTeam(teamID); var jamData = new JamDataGateway(connection, transaction).GetJamDataForTeam(teamID).ToDictionary(jd => jd.JamID); var teams = new TeamGateway(connection, transaction).GetAllTeams().ToDictionary(t => t.ID); var bouts = new BoutGateway(connection, transaction).GetBouts().ToDictionary(t => t.ID); var penalties = new PenaltyGateway(connection, transaction).GetPenaltiesForTeam(teamID) .GroupBy(p => p.JamID) .ToDictionary( g => g.Key, g => g.GroupBy(g2 => g2.PlayerID).ToDictionary(g3 => g3.Key, g3 => g3.ToList())); var pgs = new PenaltyGroupGateway(connection, transaction).GetPenaltyGroupsForTeam(teamID); Dictionary <int, int> boxTimeEstimates = new BoxTimeEstimateGateway(connection, transaction).GetAllBoxTimeEstimates(); Dictionary <FoulComparison, Dictionary <int, float> > sss = new SituationalScoreGateway(connection, transaction).GetAllSituationalScores(); Dictionary <int, double> jte = new JamTeamEffectivenessGateway(connection, transaction).GetJamTeamEffectivenessForTeam(teamID); AveragePenaltyCostPerJam avgPenCost = new AveragePenaltyCostGateway(connection, transaction).GetAveragePenaltyCost(); transaction.Commit(); connection.Close(); Dictionary <FoulComparison, float> medians = CalculateMedianScores(sss); PenaltyCostCalculator pcCalc = new PenaltyCostCalculator(_connectionString); Dictionary <int, double> groupPenaltyCostMap = pcCalc.CalculateValueCostsForTeam(jamData, pgs, boxTimeEstimates, sss, jte); Dictionary <int, PlayerPerformance> ppMap = new Dictionary <int, PlayerPerformance>(25); Dictionary <int, double> jamTotalPortionMap = new Dictionary <int, double>(300); foreach (Jam jam in jams) { var pe = jpe[jam.ID]; foreach (JamPlayerEffectiveness eff in pe.Values) { // get/set PlayerPerformance object if (!ppMap.ContainsKey(eff.PlayerID)) { ppMap[eff.PlayerID] = new PlayerPerformance { Player = players[eff.PlayerID], Bouts = new List <BoutPerformance>() }; } PlayerPerformance curPP = ppMap[eff.PlayerID]; // get/set BoutPerformance object Bout bout = bouts[jam.BoutID]; BoutPerformance bp = null; if (!curPP.Bouts.Any() || curPP.Bouts.Last().BoutDate != bout.BoutDate || curPP.Bouts.Last().HomeTeamName != teams[bout.HomeTeamID].Name || curPP.Bouts.Last().AwayTeamName != teams[bout.AwayTeamID].Name) { bp = new BoutPerformance { AwayTeamName = teams[bout.AwayTeamID].Name, HomeTeamName = teams[bout.HomeTeamID].Name, BoutDate = bout.BoutDate, Jams = new List <JamPerformance>() }; curPP.Bouts.Add(bp); } else { bp = curPP.Bouts.Last(); } JamTeamData jd = jamData[jam.ID]; int penaltyCount = penalties.ContainsKey(jam.ID) && penalties[jam.ID].ContainsKey(eff.PlayerID) ? penalties[jam.ID][eff.PlayerID].Count() : 0; JamPerformance jp = new JamPerformance { BlockerJamPercentage = eff.IsJammer ? 0 : eff.JamPortion, DeltaPercentile = eff.BaseQuality, IsFirstHalf = jam.IsFirstHalf, JamID = jam.ID, JammerJamPercentage = eff.IsJammer ? eff.JamPortion : 0, JamNumber = jam.JamNumber, JamPenalties = penaltyCount, MedianDelta = medians[jd.FoulComparison], PenaltyCost = 0, PointDelta = jd.PointDelta }; if (jamTotalPortionMap.ContainsKey(jam.ID)) { jamTotalPortionMap[jam.ID] += eff.IsJammer ? eff.JamPortion * 4 : eff.JamPortion; } else { jamTotalPortionMap[jam.ID] = eff.IsJammer ? eff.JamPortion * 4 : eff.JamPortion; } bp.Jams.Add(jp); } } foreach (PenaltyGroup pg in pgs) { foreach (Penalty p in pg.Penalties) { JamPerformance jp = ppMap[p.PlayerID].Bouts.SelectMany(b => b.Jams).Where(j => j.JamID == p.JamID).First(); jp.PenaltyCost += groupPenaltyCostMap[pg.GroupID]; } } foreach (PlayerPerformance pp in ppMap.Values) { pp.BlockerPerformance = new RolledUpPerformanceData(); pp.JammerPerformance = new RolledUpPerformanceData(); foreach (BoutPerformance bp in pp.Bouts) { bp.BlockerPerformance = new RolledUpPerformanceData(); bp.JammerPerformance = new RolledUpPerformanceData(); foreach (JamPerformance jp in bp.Jams) { double averagePenaltyCost = jp.JammerJamPercentage > 0 ? avgPenCost.JammerValueCost : avgPenCost.BlockerValueCost; double jamShare = (jp.JammerJamPercentage * 4 + jp.BlockerJamPercentage) / jamTotalPortionMap[jp.JamID]; jp.DeltaPortionVersusMedian = (jp.DeltaPercentile - 0.5) * jamShare; jp.PlayerValue = jp.DeltaPortionVersusMedian + jp.PenaltyCost - averagePenaltyCost; var rollUp = jp.JammerJamPercentage > 0 ? bp.JammerPerformance : bp.BlockerPerformance; rollUp.TotalJamPortions += jp.JammerJamPercentage + jp.BlockerJamPercentage; rollUp.TotalPenalties += jp.JamPenalties; rollUp.TotalPenaltyCost += jp.PenaltyCost; rollUp.TotalPointsVersusMedian += jp.DeltaPortionVersusMedian; rollUp.TotalPlayerValue += jp.PlayerValue; } pp.BlockerPerformance.TotalJamPortions += bp.BlockerPerformance.TotalJamPortions; pp.BlockerPerformance.TotalPenalties += bp.BlockerPerformance.TotalPenalties; pp.BlockerPerformance.TotalPenaltyCost += bp.BlockerPerformance.TotalPenaltyCost; pp.BlockerPerformance.TotalPointsVersusMedian += bp.BlockerPerformance.TotalPointsVersusMedian; pp.BlockerPerformance.TotalPlayerValue += bp.BlockerPerformance.TotalPlayerValue; pp.JammerPerformance.TotalJamPortions += bp.JammerPerformance.TotalJamPortions; pp.JammerPerformance.TotalPenalties += bp.JammerPerformance.TotalPenalties; pp.JammerPerformance.TotalPenaltyCost += bp.JammerPerformance.TotalPenaltyCost; pp.JammerPerformance.TotalPointsVersusMedian += bp.JammerPerformance.TotalPointsVersusMedian; pp.JammerPerformance.TotalPlayerValue += bp.JammerPerformance.TotalPlayerValue; } } return(ppMap.Values.ToList()); }
private double DetermineQualityWithoutPenalty(JamTeamData jamData, bool isJammer) { // figure out the foul differential if this team did not commit fouls of this time this jam int jammerPenaltyDiff = (isJammer ? 0 : jamData.JammerBoxTime) - jamData.OppJammerBoxTime; int blockerPenaltyDiff = (isJammer ? jamData.BlockerBoxTime : 0) - jamData.OppBlockerBoxTime; double jammerBoxComp = Math.Round(jammerPenaltyDiff / 15.0, MidpointRounding.AwayFromZero) / 2.0; double blockerBoxComp = Math.Round(blockerPenaltyDiff / 15.0, MidpointRounding.AwayFromZero) / 2.0; FoulComparison foul = new FoulComparison { Year = jamData.Year, JammerBoxComparison = jammerBoxComp, BlockerBoxComparison = blockerBoxComp }; if (!_sss.ContainsKey(foul)) { // lacking anything better, sum the distance of each factor from the percentile of the base JamTeamData baseJamData = new JamTeamData { Year = jamData.Year, BlockerBoxTime = 0, JammerBoxTime = 0, OppBlockerBoxTime = 0, OppJammerBoxTime = 0, PointDelta = jamData.PointDelta }; double baseQuality = DetermineQualityWithoutPenalty(baseJamData, isJammer); // pull team 1 values baseJamData.BlockerBoxTime = isJammer ? jamData.BlockerBoxTime : 0; baseJamData.JammerBoxTime = isJammer ? 0 : jamData.JammerBoxTime; if (baseJamData.FoulComparison.Equals(foul)) { return(0.5); } double quality1 = DetermineQualityWithoutPenalty(baseJamData, isJammer) - baseQuality; // pull team 2 blocker baseJamData.BlockerBoxTime = 0; baseJamData.JammerBoxTime = 0; baseJamData.OppBlockerBoxTime = jamData.OppBlockerBoxTime; if (baseJamData.FoulComparison.Equals(foul)) { return(0.5); } double quality2 = DetermineQualityWithoutPenalty(baseJamData, isJammer) - baseQuality; // pull team 2 jammer baseJamData.OppBlockerBoxTime = 0; baseJamData.OppJammerBoxTime = jamData.OppJammerBoxTime; if (baseJamData.FoulComparison.Equals(foul)) { return(0.5); } double quality3 = DetermineQualityWithoutPenalty(baseJamData, isJammer) - baseQuality; return(baseQuality + quality1 + quality2 + quality3); } else if (!_sss[foul].ContainsKey(jamData.PointDelta)) { // extrapolate from the points we do have var bottomList = _sss[foul].Keys.Where(k => k < jamData.PointDelta); int bottom = bottomList.Any() ? bottomList.Max() : -55; double bottomPercentile = bottomList.Any() ? _sss[foul][bottom] : 0; var topList = _sss[foul].Keys.Where(k => k > jamData.PointDelta); int top = topList.Any() ? topList.Min() : 55; double topPercentile = topList.Any() ? _sss[foul][top] : 1; int distance = top - bottom; int portion = jamData.PointDelta - bottom; double ratio = ((double)portion) / distance; return(bottomPercentile + ((topPercentile - bottomPercentile) * ratio)); } else { return(_sss[foul][jamData.PointDelta]); } }