/// <summary> /// Computes the transitions for a player. /// </summary> /// <param name="player">The player.</param> private void ComputeTransitionsForPlayer(MatchPlayer player) { var transitions = new SparseMatrix(StrokeLimit + 2, StrokeLimit + 2); var points = new DenseVector(StrokeLimit + 2); var errors = new DenseVector(StrokeLimit + 2); // Fill the transitions for (int i = 1; i < transitions.ColumnCount - 1; ++i) { // Determine which player has to serve for our player to // make this stroke. For odd numbers, it's the player herself, // but for even numbers, she plays the return. var server = i % 2 == 0 ? player.Other() : player; // Get all rallies that are actually long enough for our player // to get to stroke i+1 var rallies = this.Match.FinishedRallies .Where(r => r.Length >= i && r.Server == server); // Now determine how many rallies end here var endingRallies = rallies.Where(r => r.Length == i); // The number of transitions to the next stroke by the other // player is then obviously the number of all rallies long enough, // minus the number of ending rallies. transitions[i, i + 1] = rallies.Count() - endingRallies.Count(); // And now let's just find out quickly, how the ending rallies // ended: With a point or an error of our player points[i] = endingRallies.Count(r => r.Winner == player); errors[i] = endingRallies.Count(r => r.Winner == player.Other()); } // Now compute the catch all state. var allRemainingRallies = this.Match.FinishedRallies .Where(r => r.Length > StrokeLimit); var remainingRalliesEnding = allRemainingRallies .Where(r => r.Server == (r.Length % 2 == 0 ? player.Other() : player)); transitions[StrokeLimit + 1, StrokeLimit + 1] = allRemainingRallies .Sum(r => { // Calculate the number of transitions from our player to // the other player, this rally as gone through above the // stroke limit. var no = (r.Length - StrokeLimit) / 2.0; return((int)(r.Server == player ? Math.Floor(no) : Math.Ceiling(no))); }) - remainingRalliesEnding.Count(); points[StrokeLimit + 1] = remainingRalliesEnding.Count(r => r.Winner == player); errors[StrokeLimit + 1] = remainingRalliesEnding.Count(r => r.Winner == player.Other()); this.PointsAtStrokeByPlayer[player] = points; this.ErrorsAtStrokeByPlayer[player] = errors; this.TransitionsByPlayer[player] = transitions; }
private double Performance(Rally rally, MatchPlayer mp) { var score = rally.FinalRallyScore; var winningScore = score.Highest; double bias = rally.Winner == mp ? +1 : -1; var @base = winningScore == 11 ? score.Of(mp) - score.Of(mp.Other()) - 11 : -11 + (bias / (double)(winningScore - 11)); return(Math.Pow(@base, 2)); }
/// <summary> /// Gets the number of scores of <paramref name="player"/> in rallies /// of length <paramref name="n"/>. /// </summary> /// <param name="player">The player.</param> /// <param name="n">The rally length.</param> /// <returns>The scores of <paramref name="player"/> in rallies of length <paramref name="n"/></returns> public double ScoresAtLength(MatchPlayer player, int n) { var points = this.Transitions.PointsAtStrokeByPlayer[player]; if (n < 0 || n >= points.Count) { throw new ArgumentOutOfRangeException("Cannot compute errors for stroke " + n); } var otherErrors = this.Transitions.ErrorsAtStrokeByPlayer[player.Other()]; if (n == points.Count - 1) { return(points[n] + otherErrors[n] + points[n - 1]); } else if (n == points.Count - 2) { return(points[n]); } else { return(points[n] + otherErrors[n + 1]); } }
/// <summary> /// Gets the number of errors of <paramref name="player"/> in rallies /// of length <paramref name="n"/>. /// </summary> /// <param name="player">The player</param> /// <param name="n">The rally length</param> /// <returns>The errors of <paramref name="player"/> in rallies of length <paramref name="n"/></returns> public double ErrorsAtLength(MatchPlayer player, int n) { var errors = this.Transitions.ErrorsAtStrokeByPlayer[player]; if (n < 1 || n >= errors.Count) { throw new ArgumentOutOfRangeException("Cannot compute errors for stroke " + n); } var otherPoints = this.Transitions.PointsAtStrokeByPlayer[player.Other()]; if (n == 0) { return(errors[n]); } else if (n == errors.Count - 1) { return(errors[n] + otherPoints[n - 1] + otherPoints[n] + this.ErrorsAtLength(player, n - 1)); } else { return(errors[n] + otherPoints[n - 1]); } }