private static FsTaskScoreParams CalcScoreParams(Task t, ScoreFormulaGAP sfg, Participant[] ps) { FsTaskScoreParams sp = new FsTaskScoreParams(); double min_dist = sfg.Min_dist; double task_distance = t.DistanceExact; // http://fs.fai.org/ticket/103 sp.Task_distance = task_distance; sp.Ss_distance = t.Ss_distance; double[] times = new double[ps.Length]; int i = 0; foreach (Participant p in ps) { // present sp.No_of_pilots_present++; // flying if (p._flew) sp.No_of_pilots_flying++; // sum dist over task min dist if (p._distance > min_dist) sp.Sum_dist_over_min += p._distance - min_dist; // no pilots landing out if (p._flew && (p._distance < task_distance || task_distance == 0)) sp.No_of_pilots_LO++; //if (p._flew && p._finished_ss == null) sp.No_of_pilots_LO++; // no pilots reaching ES if (p._flew && p._finished_ss != null) sp.No_of_pilots_reaching_ES++; if (p._flew && p._distance >= task_distance && task_distance > 0) sp.No_of_pilots_reaching_goal++; // not used? // best distance if (p._distance > sp.Best_dist) { // ERDAL20090907 in response to http://fs.fai.org/ticket/198 if (t.DistanceExact > 0 && p._distance > t.DistanceExact) sp.Best_dist = t.DistanceExact; else sp.Best_dist = p._distance; //if (t.Distance > 0 && p._distance > t.Distance) sp.Best_dist = t.Distance; //else sp.Best_dist = p._distance; } // those reaching end of speedsection ... double time = p.Result.Ss_Time_Dec_Hours; if (time > 0) { // http://fs.fai.org/ticket/35 if (sp.First_start_time == null || p.Result.Started_ss.Utc_time < sp.First_start_time.Utc_time) sp.First_start_time = p.Result.Started_ss; // http://fs.fai.org/ticket/16 // store time (will sort ascending to use later for time validity calculations) // (not using Best_time anymore) times[i++] = time; // best time (given time) if (time < sp.Best_time || sp.Best_time == 0) sp.Best_time = time; // first finish time Time finishedTaskSs = p.Result.Finished_ss; if (sp.First_finish_time == null || finishedTaskSs.Utc_time < sp.First_finish_time.Utc_time) sp.First_finish_time = finishedTaskSs; } } // http://fs.fai.org/ticket/16 // time_validity_based_on_pilot_with_speed_rank // default is time of first pilot at ES // time_validity_based_on_pilot_with_speed_rank = 1 if not specified in the scoring formula params. Array.Sort(times); i = 0; while (i < times.Length && times[i] == 0) i++; i += sfg.Time_validity_based_on_pilot_no - 1; double time_validity_time = 0; // if no one at ES time used is 0 if (i < times.Length) time_validity_time = times[i]; // pilot no x reached ES so we use that time else if (times.Length > 0) time_validity_time = times[times.Length - 1]; // pilot no x did not reach ES so we use the last one to reach ES. double time_validity = CalcTimeValidity(time_validity_time, sfg.Nom_time, sp.Best_dist, sfg.Nom_dist); double launch_validity = CalcLaunchValidity(sp.No_of_pilots_flying, sp.No_of_pilots_present); double distance_validity = CalcDistanceValidity(sp.Sum_dist_over_min, sp.No_of_pilots_flying, sfg.Nom_goal, sfg.Nom_dist, sfg.Min_dist, sp.Best_dist); double day_quality = CalcDayQuality(time_validity, launch_validity, distance_validity); sp.Time_validity = time_validity; sp.Launch_validity = launch_validity; sp.Distance_validity = distance_validity; sp.Day_quality = day_quality; //ERDAL20090517 if (sfg.Day_quality > 0) { //sp.Time_validity = 0; //sp.Launch_validity = 0; //sp.Distance_validity = 0; sp.Day_quality = sfg.Day_quality; } // Distance points (depends, among other things, on ratio of pilots at ES and flying) if (sfg.Use_distance_points) { sp.K = 0; // if not using time points: K=0 giving Distance_weight=0.9 if (sp.Best_time > 0 && sfg.Use_time_points) { sp.Max_time_to_get_time_points = sp.Best_time + Math.Sqrt(sp.Best_time); foreach (Participant p in ps) { double time = p.Result.Ss_Time_Dec_Hours; if (time > 0 && time < sp.Max_time_to_get_time_points) sp.No_of_pilots_with_time_points++; } sp.K = sp.No_of_pilots_with_time_points * 1.0 / sp.No_of_pilots_flying; } sp.Distance_weight = 0.9 - 1.665 * sp.K + 1.713 * Math.Pow(sp.K, 2) - 0.587 * Math.Pow(sp.K, 3); } double dummy_arr_weight = 0; // Arrival time points (OzGAP2005) if (sfg.Use_arrival_time_points) { // a quart of what's left after distance got it's share dummy_arr_weight = (1.0 - sp.Distance_weight) / 4.0; if (sfg.If_use_arrival_time_points_div_by != 0) // ERDAL20090529 adapted to PWC2009. Note probably an error by Ulric ... probably missed the diff. btw. modOzGap2005.bas and modGAP.bas dummy_arr_weight = dummy_arr_weight / sfg.If_use_arrival_time_points_div_by; } // or Arrival position points (Normal <= GAP2002) // ERDAL20090513 http://fs.fai.org/ticket/179 else // if (sfg.Use_arrival_position_points) { // an eight of what's left after distance got it's share dummy_arr_weight = (1.0 - sp.Distance_weight) / 8.0; } // Arrival (time or pos) and Departure points only if someone reached ES if (sp.No_of_pilots_reaching_ES > 0) { if (sfg.Use_arrival_time_points || sfg.Use_arrival_position_points) { sp.Arrival_weight = dummy_arr_weight; } // Departure points based on time enroute if (sfg.Use_departure_points) { sp.Departure_weight = dummy_arr_weight * 1.4; } // or Leading Out points based on area of time/distance graph else if (sfg.Use_leading_points) { sp.Leading_weight = dummy_arr_weight * 1.4; // ERDAL20110417 PWC2010... http://fs.fai.org/ticket/219 if not using arrival points put them into leading points instead of time points if (!sfg.Use_arrival_time_points && !sfg.Use_arrival_position_points) sp.Leading_weight += dummy_arr_weight; } // Time points if (sfg.Use_time_points) { sp.Time_weight = (1 - sp.Distance_weight) - sp.Arrival_weight - sp.Departure_weight - sp.Leading_weight; } } else // no one reaching end of speedsection, // hence no allocation of time, arrival, nor departure points (based on time enroute) // Only distance points and leading points make sense here. { // Leading points if (sfg.Use_leading_points) { sp.Leading_weight = dummy_arr_weight * 1.4; // http://fs.fai.org/ticket/88 } } // now sum of all weights should be 1. // if it is less than 1 we add to distance weight // ERDAL20090701 restored this again but set default value to true so it is not in user interface (yet) but can be set directly in fsdb // ERDAL20090518 http://fs.fai.org/ticket/180 TODO: remove option all together (unless reaction is that it is needed) if (sfg.Use_1000_points_for_max_day_quality) sp.Distance_weight = 1 - sp.Time_weight - sp.Leading_weight - sp.Departure_weight - sp.Arrival_weight; // adjust for day quality sp.Available_points_distance = 1000 * sp.Day_quality * sp.Distance_weight; sp.Available_points_arrival = 1000 * sp.Day_quality * sp.Arrival_weight; sp.Available_points_departure = 1000 * sp.Day_quality * sp.Departure_weight; sp.Available_points_leading = 1000 * sp.Day_quality * sp.Leading_weight; sp.Available_points_time = 1000 * sp.Day_quality * sp.Time_weight; // smallest_leading_coefficient if (sfg.Use_departure_points || sfg.Use_leading_points) { // smallest leading coefficient sp.Smallest_leading_coefficient = 0; double smallest_leading_coefficient = double.MaxValue; if (sfg.Use_departure_points && sp.No_of_pilots_reaching_ES > 0) { foreach (Participant p in ps) { double lc = Gap.LcUsingDeparturePoints(p, t, sp); if (lc > 0 && lc < smallest_leading_coefficient) smallest_leading_coefficient = lc; } } else if (sfg.Use_leading_points) { foreach (Participant p in ps) { if (p._lc > 0 && (p._lc < smallest_leading_coefficient || smallest_leading_coefficient == 0)) { smallest_leading_coefficient = p._lc; } } } if (smallest_leading_coefficient != double.MaxValue) sp.Smallest_leading_coefficient = smallest_leading_coefficient; } // -- smallest_leading_coefficient return sp; } private static DistanseScoreData100m[] CreateDistanceScoreData100mArray(Task t, FsTaskScoreParams sp, ScoreFormulaGAP sfg, Participant[] ps) { // Save info for every 100 meters of the task DistanseScoreData100m[] da = null; double best_dist = sp.Best_dist; if (best_dist < sfg.Min_dist) best_dist = sfg.Min_dist; if (best_dist == 0) return da; int best_dist_kmx10 = (int)(best_dist * 10); int best_dist_kmx10r = (best_dist_kmx10 + 10) / 10 * 10; // eg if kmx10 = 517, best_dist_kmx10r = 520 da = new DistanseScoreData100m[best_dist_kmx10r + 1]; // For each 100m of task count no of landed pilots foreach (Participant p in ps) { // ERDAL20090907 in response to http://fs.fai.org/ticket/198 //double dist = p._distance; //if (dist < sfg.Min_dist) dist = sfg.Min_dist; //else if (t.Distance > 0 && dist > t.Distance) dist = t.Distance; //// if landed out (ERDAL20080303-1940: not counting those in goal!!) http://fs.fai.org/ticket/60 //if (p._flew && dist < t.Distance) da[(int)(dist * 10)]._landed++; //// TODO: check ... for open dist. tasks it is vital that t.Distance is set to distance of the best pilot + 1 meter.!!! double dist = p._distance; if (dist < sfg.Min_dist) dist = sfg.Min_dist; else if (t.DistanceExact > 0 && dist > t.DistanceExact) dist = t.DistanceExact; if (p._flew && dist < t.DistanceExact) da[(int)(dist * 10)]._landed++; } // For each kilometer of the task find no of pilots having landed in the past kilometer int j = 0; for (int i = 0; i <= best_dist_kmx10r; i++) { // add up pilots having landed for each 100 meters j += da[i]._landed; // if a whole km is reached if (Math.IEEERemainder(i, 10) == 0) { // pilots having landed in the past kilometer da[i]._landed_full_km = j; // start over for the next kilometer j = 0; } } // Compute difficulty for each 100 meters of the task: // find no of landed pilots up ahead ... // 100 pilots landing out in 100km -> we look at the next 3 km. // 10 pilots landing out in 100km -> we look at the next 30 km. int look_ahead_dist_kmx10 = 30; // look at least 3km ahead if (sp.No_of_pilots_LO > 0 && best_dist > sp.No_of_pilots_LO) look_ahead_dist_kmx10 = (int)Math.Round(look_ahead_dist_kmx10 * best_dist / sp.No_of_pilots_LO, MidpointRounding.AwayFromZero); for (int i = 0; i <= best_dist_kmx10; i++) for (j = i; j < i + look_ahead_dist_kmx10 && j <= best_dist_kmx10; j++) da[i]._difficulty += da[j]._landed; // sum of all difficulty double sum_difficulty = 0; for (int i = 0; i <= best_dist_kmx10; i++) sum_difficulty += da[i]._difficulty; // Difficulty relative to sum of all difficulty if (sum_difficulty > 0) { for (int i = 0; i <= best_dist_kmx10; i++) { da[i]._rel_diff = da[i]._difficulty / (2.0 * sum_difficulty); } } // Compute distance score % // accumulated relative difficulty per 100 meters. double sum_rel_difficulty = 0; for (int i = 0; i <= best_dist_kmx10; i++) { sum_rel_difficulty += da[i]._rel_diff; da[i]._score_percent = sum_rel_difficulty; } // ERDAL20090518 making sure we 0.5 (sum of _rel_diff can end up at 0.49999999999999978 or similar, should not make visible diff but...) for (int i = best_dist_kmx10; i <= best_dist_kmx10r; i++) { da[i]._score_percent = 0.5; } // set score to all distances below or equal minimum distance int min_dist_kmx10 = (int)(sfg.Min_dist * 10); if (min_dist_kmx10 < da.Length) { for (int i = 0; i <= min_dist_kmx10; i++) { da[i]._score_percent = da[min_dist_kmx10]._score_percent; } } return da; }