/// <summary> /// Gets the specified contest scores for all rounds from the file. /// </summary> /// <param name="contestId">The contest identifier.</param> /// <returns>Dictionary of timesheets. Key is the round ordinal, value is an enumeration of timesheets for that round.</returns> public async Task <Result <ContestScoresCollection> > ReadAsync(string contestId) { if (string.IsNullOrEmpty(contestId)) { return(Error <ContestScoresCollection>(null, $"{nameof(contestId)} cannot be null or empty.")); } var allScores = (await GetAll <TimeSheet>()) ?? new List <TimeSheet>(); if (allScores == null) { allScores = new List <TimeSheet>(); } var scoresToReturn = allScores.Where(p => p.ContestId == contestId).GroupBy(score => score.RoundOrdinal); if (scoresToReturn == null) { return(Error <ContestScoresCollection>(null, new Exception($"A pilot with the id {contestId} could not be found"))); } var returnObj = new ContestScoresCollection(); foreach (var scoreGroup in scoresToReturn) { var pilotTimeSheets = new List <TimeSheet>(); foreach (var score in scoreGroup) { pilotTimeSheets.Add(score); } var roundOrdinal = scoreGroup.First()?.RoundOrdinal ?? 0; if (!returnObj.Rounds.Contains(roundOrdinal)) { returnObj.Add(new ContestRoundScoresCollection(pilotTimeSheets, roundOrdinal)); } } return(Success(returnObj, nameof(ReadAsync))); }
/// <summary> /// Generates the pilot scores. /// </summary> /// <param name="numberOfPilots">The number of pilots.</param> /// <param name="numberOfRounds">The number of rounds.</param> /// <param name="timeSheets">The time sheets.</param> /// <returns></returns> private Dictionary <string, PilotContestScoreCollection> GeneratePilotScores(int numberOfPilots, int numberOfRounds, ContestScoresCollection scores) { // This is really basic, we aren't testing the algo, we are testing the validation and handling of the non-algo code in this test class var returnCollection = new Dictionary <string, PilotContestScoreCollection>(); var rnd = new Random(); for (var i = 0; i < numberOfPilots; ++i) { returnCollection.Add($"pilot{i}", new PilotContestScoreCollection($"pilot{i}", rnd.Next(0, 1000))); for (var j = 0; j < numberOfRounds; ++j) { returnCollection[$"pilot{i}"].Add(j, 10); } } return(returnCollection); }
/// <summary> /// Generates the contest scores by pilot. /// </summary> /// <param name="allContestRoundScores">All contest round scores.</param> /// <returns></returns> public Dictionary <string, PilotContestScoreCollection> GenerateContestScores(ContestScoresCollection allContestRoundScores) { // Init the output collection. var totals = new Dictionary </*PilotId*/ string, PilotContestScoreCollection>(); // Flatten the rounds and // group all of the scores by pilot id var pilotTimeSheets = new List <TimeSheet>(); foreach (var round in allContestRoundScores) { pilotTimeSheets.AddRange(round.Select(r => r)); } var pilotScoreGroups = pilotTimeSheets.GroupBy(pts => pts.PilotId); // Take each pilot and their scores and place each round score in the output collection. foreach (var individualPilotScoreSet in pilotScoreGroups) { foreach (var roundScore in individualPilotScoreSet) { // Check to see if we have a pilot entry in the output collection, if not, create one. if (!totals.ContainsKey(roundScore.PilotId)) { totals.Add(roundScore.PilotId, new PilotContestScoreCollection(roundScore.PilotId, individualPilotScoreSet.Sum(scores => scores.Score), individualPilotScoreSet.ToDictionary(k => k.RoundOrdinal, v => v.Score))); } // If there are multiple scores for the same round for a pilot, only take the first if (!totals[roundScore.PilotId].ContainsKey(roundScore.RoundOrdinal)) { totals[roundScore.PilotId].Add(roundScore.RoundOrdinal, roundScore.Score); } } } // Remove the lowest score for each, if four rounds have been completed. foreach (var individualPilotScoreSet in totals) { double lowestScore = 0.0; int droppedRoundOrdinal = -1; // Only do this if the pilot has completed 4 rounds if (individualPilotScoreSet.Value.Count > 4) { // Loop through and find the lowest round score. for (var i = 0; i < individualPilotScoreSet.Value.Count; ++i) { if (individualPilotScoreSet.Value[i] < lowestScore) { lowestScore = individualPilotScoreSet.Value[i]; droppedRoundOrdinal = i; } } } // Total the score and remove the lowest if applicable. individualPilotScoreSet.Value.TotalScore = individualPilotScoreSet.Value.Sum(x => x.Value) - lowestScore; // Mark the dropped round individualPilotScoreSet.Value.DroppedRoundOrdinal = droppedRoundOrdinal; } // Total the scores for each round, for each pilot and put it in the last "column" / entry (int.MaxValue will be the key) for the pilot foreach (var pilotScoreSet in totals) { } return(totals); }
public static ContestScoresCollection GenerateValidContestRoundScores(int numberOfPilots, string contestId = "SASS August 2017", int numberOfRounds = 1, int maxTimeGateTimeInSeconds = 120, bool multipleFlightGroups = false, bool generateScores = false) { var validScoresForRound = new ContestRoundScoresCollection(); var TaskA = new TaskA_LastFlightSevenMin(); var result = new ContestScoresCollection(); if (numberOfPilots == 0) { return(result); } var rnd = new Random(); // Set up a mock task to validate and score the rounds if required. var mockTask = new Mock <TaskBase>(); mockTask.Setup(x => x.ValidateTask(It.IsAny <RoundScoreBase>())).Returns(true); mockTask.Setup(x => x.ScoreTask(It.IsAny <RoundScoreBase>())).Returns <RoundScoreBase>(y => y.TimeGates.Sum(x => x.Time.TotalSeconds)); var f3kRoundScoreAlgo = new RoundScoringAlgo(); for (var i = 0; i < numberOfRounds; ++i) { // Reset validScoresForRound = new ContestRoundScoresCollection(); for (var j = 0; j < numberOfPilots; ++j) { var flightGroup = FlightGroup.A; // Generate 2 flight groups if (multipleFlightGroups) { flightGroup = (j % 2) != 0 ? FlightGroup.A : FlightGroup.B; } var timeGates = new List <TimeGate> { new TimeGate(TimeSpan.FromSeconds(rnd.Next(45, maxTimeGateTimeInSeconds)), j, TimeGateType.Task) }; var roundScore = new TimeSheet { RoundOrdinal = i, TimeGates = timeGates, PilotId = $"pilot{j}", ContestId = contestId, TaskId = TaskA.Id, FlightGroup = FlightGroup.A }; // Generate the scores if needed. if (generateScores) { } validScoresForRound.Add(roundScore); } if (generateScores) { f3kRoundScoreAlgo.ScoreRound(validScoresForRound, mockTask.Object); } result.Add(validScoresForRound); } return(result); }
/// <summary> /// Generates the contest scores by pilot. /// </summary> /// <param name="allContestRoundScores">All contest round scores.</param> /// <returns></returns> public Dictionary <string, PilotContestScoreCollection> GenerateContestScores(ContestScoresCollection allContestRoundScores) { // Init the output collection. var totals = new Dictionary </*PilotId*/ string, PilotContestScoreCollection>(); // Flatten the rounds and // group all of the scores by pilot id var pilotTimeSheets = new List <TimeSheet>(); foreach (var round in allContestRoundScores) { pilotTimeSheets.AddRange(round.Select(r => r)); } var pilotScoreGroups = pilotTimeSheets.GroupBy(pts => pts.PilotId); // Take each pilot and their scores and place each round score in the output collection. foreach (var individualPilotScoreSet in pilotScoreGroups) { foreach (var roundScore in individualPilotScoreSet) { // Check to see if we have a pilot entry in the output collection, if not, create one. if (!totals.ContainsKey(roundScore.PilotId)) { totals.Add(roundScore.PilotId, new PilotContestScoreCollection(roundScore.PilotId, individualPilotScoreSet.Sum(scores => scores.Score), individualPilotScoreSet.ToDictionary(k => k.RoundOrdinal, v => v.Score))); } // If there are multiple scores for the same round for a pilot, only take the first if (!totals[roundScore.PilotId].ContainsKey(roundScore.RoundOrdinal)) { totals[roundScore.PilotId].Add(roundScore.RoundOrdinal, roundScore.Score); } } } return(totals); }
/// <summary> /// Scores a group of rounds. /// </summary> /// <param name="allRoundScores">All contest scores.</param> /// <returns> /// A collection with the PilotId as the key and a collection of scores for each round as the value. /// In the collection of scores, the round number is the key and the score for that round is value. /// The last entry (int.MaxValue) in the scores collection is the total for the set of rounds passed in. /// </returns> public Result <Dictionary <string, PilotContestScoreCollection> > GetAggregateRoundScoresForPilots(ContestScoresCollection allRoundScores) { if (allRoundScores == null || allRoundScores.Count() < 1) { return(Error <Dictionary <string, PilotContestScoreCollection> >(null, $"{nameof(allRoundScores)} cannot be null or empty")); } var result = contestScoreAggregator.GenerateContestScores(allRoundScores); return(Success(result, nameof(GetAggregateRoundScoresForPilots))); }