1 | private static FsTaskScoreParams CalcScoreParams(Task t, ScoreFormulaGAP sfg, Participant[] ps) |
---|
2 | { |
---|
3 | FsTaskScoreParams sp = new FsTaskScoreParams(); |
---|
4 | double min_dist = sfg.Min_dist; |
---|
5 | double task_distance = t.DistanceExact; // http://fs.fai.org/ticket/103 |
---|
6 | sp.Task_distance = task_distance; |
---|
7 | sp.Ss_distance = t.Ss_distance; |
---|
8 | |
---|
9 | double[] times = new double[ps.Length]; |
---|
10 | int i = 0; |
---|
11 | foreach (Participant p in ps) |
---|
12 | { |
---|
13 | // present |
---|
14 | sp.No_of_pilots_present++; |
---|
15 | |
---|
16 | // flying |
---|
17 | if (p._flew) sp.No_of_pilots_flying++; |
---|
18 | |
---|
19 | // sum dist over task min dist |
---|
20 | if (p._distance > min_dist) sp.Sum_dist_over_min += p._distance - min_dist; |
---|
21 | |
---|
22 | // no pilots landing out |
---|
23 | if (p._flew && (p._distance < task_distance || task_distance == 0)) sp.No_of_pilots_LO++; |
---|
24 | //if (p._flew && p._finished_ss == null) sp.No_of_pilots_LO++; |
---|
25 | |
---|
26 | // no pilots reaching ES |
---|
27 | if (p._flew && p._finished_ss != null) sp.No_of_pilots_reaching_ES++; |
---|
28 | |
---|
29 | if (p._flew && p._distance >= task_distance && task_distance > 0) sp.No_of_pilots_reaching_goal++; // not used? |
---|
30 | |
---|
31 | // best distance |
---|
32 | if (p._distance > sp.Best_dist) |
---|
33 | { |
---|
34 | // ERDAL20090907 in response to http://fs.fai.org/ticket/198 |
---|
35 | if (t.DistanceExact > 0 && p._distance > t.DistanceExact) sp.Best_dist = t.DistanceExact; |
---|
36 | else sp.Best_dist = p._distance; |
---|
37 | //if (t.Distance > 0 && p._distance > t.Distance) sp.Best_dist = t.Distance; |
---|
38 | //else sp.Best_dist = p._distance; |
---|
39 | } |
---|
40 | |
---|
41 | // those reaching end of speedsection ... |
---|
42 | double time = p.Result.Ss_Time_Dec_Hours; |
---|
43 | if (time > 0) |
---|
44 | { |
---|
45 | // http://fs.fai.org/ticket/35 |
---|
46 | if (sp.First_start_time == null || p.Result.Started_ss.Utc_time < sp.First_start_time.Utc_time) |
---|
47 | sp.First_start_time = p.Result.Started_ss; |
---|
48 | |
---|
49 | // http://fs.fai.org/ticket/16 |
---|
50 | // store time (will sort ascending to use later for time validity calculations) |
---|
51 | // (not using Best_time anymore) |
---|
52 | times[i++] = time; |
---|
53 | |
---|
54 | // best time (given time) |
---|
55 | if (time < sp.Best_time || sp.Best_time == 0) sp.Best_time = time; |
---|
56 | |
---|
57 | // first finish time |
---|
58 | Time finishedTaskSs = p.Result.Finished_ss; |
---|
59 | if (sp.First_finish_time == null || finishedTaskSs.Utc_time < sp.First_finish_time.Utc_time) |
---|
60 | sp.First_finish_time = finishedTaskSs; |
---|
61 | } |
---|
62 | } |
---|
63 | |
---|
64 | // http://fs.fai.org/ticket/16 |
---|
65 | // time_validity_based_on_pilot_with_speed_rank |
---|
66 | // default is time of first pilot at ES |
---|
67 | // time_validity_based_on_pilot_with_speed_rank = 1 if not specified in the scoring formula params. |
---|
68 | Array.Sort<double>(times); |
---|
69 | i = 0; |
---|
70 | while (i < times.Length && times[i] == 0) i++; |
---|
71 | i += sfg.Time_validity_based_on_pilot_no - 1; |
---|
72 | double time_validity_time = 0; // if no one at ES time used is 0 |
---|
73 | if (i < times.Length) time_validity_time = times[i]; // pilot no x reached ES so we use that time |
---|
74 | 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. |
---|
75 | |
---|
76 | double time_validity = CalcTimeValidity(time_validity_time, sfg.Nom_time, sp.Best_dist, sfg.Nom_dist); |
---|
77 | double launch_validity = CalcLaunchValidity(sp.No_of_pilots_flying, sp.No_of_pilots_present); |
---|
78 | 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); |
---|
79 | double day_quality = CalcDayQuality(time_validity, launch_validity, distance_validity); |
---|
80 | |
---|
81 | sp.Time_validity = time_validity; |
---|
82 | sp.Launch_validity = launch_validity; |
---|
83 | sp.Distance_validity = distance_validity; |
---|
84 | sp.Day_quality = day_quality; |
---|
85 | |
---|
86 | //ERDAL20090517 |
---|
87 | if (sfg.Day_quality > 0) |
---|
88 | { |
---|
89 | //sp.Time_validity = 0; |
---|
90 | //sp.Launch_validity = 0; |
---|
91 | //sp.Distance_validity = 0; |
---|
92 | sp.Day_quality = sfg.Day_quality; |
---|
93 | } |
---|
94 | |
---|
95 | // Distance points (depends, among other things, on ratio of pilots at ES and flying) |
---|
96 | if (sfg.Use_distance_points) |
---|
97 | { |
---|
98 | sp.K = 0; // if not using time points: K=0 giving Distance_weight=0.9 |
---|
99 | if (sp.Best_time > 0 && sfg.Use_time_points) |
---|
100 | { |
---|
101 | sp.Max_time_to_get_time_points = sp.Best_time + Math.Sqrt(sp.Best_time); |
---|
102 | foreach (Participant p in ps) |
---|
103 | { |
---|
104 | double time = p.Result.Ss_Time_Dec_Hours; |
---|
105 | if (time > 0 && time < sp.Max_time_to_get_time_points) |
---|
106 | sp.No_of_pilots_with_time_points++; |
---|
107 | } |
---|
108 | sp.K = sp.No_of_pilots_with_time_points * 1.0 / sp.No_of_pilots_flying; |
---|
109 | } |
---|
110 | sp.Distance_weight = 0.9 - 1.665 * sp.K + 1.713 * Math.Pow(sp.K, 2) - 0.587 * Math.Pow(sp.K, 3); |
---|
111 | } |
---|
112 | |
---|
113 | double dummy_arr_weight = 0; |
---|
114 | // Arrival time points (OzGAP2005) |
---|
115 | if (sfg.Use_arrival_time_points) |
---|
116 | { |
---|
117 | // a quart of what's left after distance got it's share |
---|
118 | dummy_arr_weight = (1.0 - sp.Distance_weight) / 4.0; |
---|
119 | 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 |
---|
120 | dummy_arr_weight = dummy_arr_weight / sfg.If_use_arrival_time_points_div_by; |
---|
121 | } |
---|
122 | // or Arrival position points (Normal <= GAP2002) |
---|
123 | // ERDAL20090513 http://fs.fai.org/ticket/179 |
---|
124 | else // if (sfg.Use_arrival_position_points) |
---|
125 | { |
---|
126 | // an eight of what's left after distance got it's share |
---|
127 | dummy_arr_weight = (1.0 - sp.Distance_weight) / 8.0; |
---|
128 | } |
---|
129 | |
---|
130 | // Arrival (time or pos) and Departure points only if someone reached ES |
---|
131 | if (sp.No_of_pilots_reaching_ES > 0) |
---|
132 | { |
---|
133 | if (sfg.Use_arrival_time_points || sfg.Use_arrival_position_points) |
---|
134 | { |
---|
135 | sp.Arrival_weight = dummy_arr_weight; |
---|
136 | } |
---|
137 | |
---|
138 | // Departure points based on time enroute |
---|
139 | if (sfg.Use_departure_points) |
---|
140 | { |
---|
141 | sp.Departure_weight = dummy_arr_weight * 1.4; |
---|
142 | } |
---|
143 | // or Leading Out points based on area of time/distance graph |
---|
144 | else if (sfg.Use_leading_points) |
---|
145 | { |
---|
146 | sp.Leading_weight = dummy_arr_weight * 1.4; |
---|
147 | // ERDAL20110417 PWC2010... http://fs.fai.org/ticket/219 if not using arrival points put them into leading points instead of time points |
---|
148 | if (!sfg.Use_arrival_time_points && !sfg.Use_arrival_position_points) sp.Leading_weight += dummy_arr_weight; |
---|
149 | } |
---|
150 | |
---|
151 | // Time points |
---|
152 | if (sfg.Use_time_points) |
---|
153 | { |
---|
154 | sp.Time_weight |
---|
155 | = (1 - sp.Distance_weight) |
---|
156 | - sp.Arrival_weight |
---|
157 | - sp.Departure_weight |
---|
158 | - sp.Leading_weight; |
---|
159 | } |
---|
160 | } |
---|
161 | else // no one reaching end of speedsection, |
---|
162 | // hence no allocation of time, arrival, nor departure points (based on time enroute) |
---|
163 | // Only distance points and leading points make sense here. |
---|
164 | { |
---|
165 | // Leading points |
---|
166 | if (sfg.Use_leading_points) |
---|
167 | { |
---|
168 | sp.Leading_weight = dummy_arr_weight * 1.4; // http://fs.fai.org/ticket/88 |
---|
169 | } |
---|
170 | } |
---|
171 | |
---|
172 | // now sum of all weights should be 1. |
---|
173 | // if it is less than 1 we add to distance weight |
---|
174 | // 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 |
---|
175 | // ERDAL20090518 http://fs.fai.org/ticket/180 TODO: remove option all together (unless reaction is that it is needed) |
---|
176 | if (sfg.Use_1000_points_for_max_day_quality) |
---|
177 | sp.Distance_weight = 1 - sp.Time_weight - sp.Leading_weight - sp.Departure_weight - sp.Arrival_weight; |
---|
178 | |
---|
179 | // adjust for day quality |
---|
180 | sp.Available_points_distance = 1000 * sp.Day_quality * sp.Distance_weight; |
---|
181 | sp.Available_points_arrival = 1000 * sp.Day_quality * sp.Arrival_weight; |
---|
182 | sp.Available_points_departure = 1000 * sp.Day_quality * sp.Departure_weight; |
---|
183 | sp.Available_points_leading = 1000 * sp.Day_quality * sp.Leading_weight; |
---|
184 | sp.Available_points_time = 1000 * sp.Day_quality * sp.Time_weight; |
---|
185 | |
---|
186 | // smallest_leading_coefficient |
---|
187 | if (sfg.Use_departure_points |
---|
188 | || sfg.Use_leading_points) |
---|
189 | { |
---|
190 | // smallest leading coefficient |
---|
191 | sp.Smallest_leading_coefficient = 0; |
---|
192 | double smallest_leading_coefficient = double.MaxValue; |
---|
193 | if (sfg.Use_departure_points && sp.No_of_pilots_reaching_ES > 0) |
---|
194 | { |
---|
195 | foreach (Participant p in ps) |
---|
196 | { |
---|
197 | double lc = Gap.LcUsingDeparturePoints(p, t, sp); |
---|
198 | if (lc > 0 && lc < smallest_leading_coefficient) smallest_leading_coefficient = lc; |
---|
199 | } |
---|
200 | } |
---|
201 | else if (sfg.Use_leading_points) |
---|
202 | { |
---|
203 | foreach (Participant p in ps) |
---|
204 | { |
---|
205 | if (p._lc > 0 && (p._lc < smallest_leading_coefficient || smallest_leading_coefficient == 0)) |
---|
206 | { |
---|
207 | smallest_leading_coefficient = p._lc; |
---|
208 | } |
---|
209 | } |
---|
210 | } |
---|
211 | if (smallest_leading_coefficient != double.MaxValue) |
---|
212 | sp.Smallest_leading_coefficient = smallest_leading_coefficient; |
---|
213 | } // -- smallest_leading_coefficient |
---|
214 | |
---|
215 | return sp; |
---|
216 | } |
---|
217 | |
---|
218 | |
---|
219 | |
---|
220 | private static DistanseScoreData100m[] CreateDistanceScoreData100mArray(Task t, FsTaskScoreParams sp, ScoreFormulaGAP sfg, Participant[] ps) |
---|
221 | { |
---|
222 | // Save info for every 100 meters of the task |
---|
223 | |
---|
224 | DistanseScoreData100m[] da = null; |
---|
225 | |
---|
226 | double best_dist = sp.Best_dist; |
---|
227 | if (best_dist < sfg.Min_dist) best_dist = sfg.Min_dist; |
---|
228 | if (best_dist == 0) return da; |
---|
229 | int best_dist_kmx10 = (int)(best_dist * 10); |
---|
230 | int best_dist_kmx10r = (best_dist_kmx10 + 10) / 10 * 10; // eg if kmx10 = 517, best_dist_kmx10r = 520 |
---|
231 | da = new DistanseScoreData100m[best_dist_kmx10r + 1]; |
---|
232 | |
---|
233 | // For each 100m of task count no of landed pilots |
---|
234 | foreach (Participant p in ps) |
---|
235 | { |
---|
236 | // ERDAL20090907 in response to http://fs.fai.org/ticket/198 |
---|
237 | //double dist = p._distance; |
---|
238 | //if (dist < sfg.Min_dist) dist = sfg.Min_dist; |
---|
239 | //else if (t.Distance > 0 && dist > t.Distance) dist = t.Distance; |
---|
240 | //// if landed out (ERDAL20080303-1940: not counting those in goal!!) http://fs.fai.org/ticket/60 |
---|
241 | //if (p._flew && dist < t.Distance) da[(int)(dist * 10)]._landed++; |
---|
242 | //// TODO: check ... for open dist. tasks it is vital that t.Distance is set to distance of the best pilot + 1 meter.!!! |
---|
243 | |
---|
244 | double dist = p._distance; |
---|
245 | if (dist < sfg.Min_dist) dist = sfg.Min_dist; |
---|
246 | else if (t.DistanceExact > 0 && dist > t.DistanceExact) dist = t.DistanceExact; |
---|
247 | if (p._flew && dist < t.DistanceExact) da[(int)(dist * 10)]._landed++; |
---|
248 | } |
---|
249 | |
---|
250 | // For each kilometer of the task find no of pilots having landed in the past kilometer |
---|
251 | int j = 0; |
---|
252 | for (int i = 0; i <= best_dist_kmx10r; i++) |
---|
253 | { |
---|
254 | // add up pilots having landed for each 100 meters |
---|
255 | j += da[i]._landed; |
---|
256 | // if a whole km is reached |
---|
257 | if (Math.IEEERemainder(i, 10) == 0) |
---|
258 | { |
---|
259 | // pilots having landed in the past kilometer |
---|
260 | da[i]._landed_full_km = j; |
---|
261 | // start over for the next kilometer |
---|
262 | j = 0; |
---|
263 | } |
---|
264 | } |
---|
265 | |
---|
266 | // Compute difficulty for each 100 meters of the task: |
---|
267 | // find no of landed pilots up ahead ... |
---|
268 | // 100 pilots landing out in 100km -> we look at the next 3 km. |
---|
269 | // 10 pilots landing out in 100km -> we look at the next 30 km. |
---|
270 | int look_ahead_dist_kmx10 = 30; // look at least 3km ahead |
---|
271 | if (sp.No_of_pilots_LO > 0 && best_dist > sp.No_of_pilots_LO) |
---|
272 | look_ahead_dist_kmx10 |
---|
273 | = (int)Math.Round(look_ahead_dist_kmx10 * best_dist / sp.No_of_pilots_LO, |
---|
274 | MidpointRounding.AwayFromZero); |
---|
275 | for (int i = 0; i <= best_dist_kmx10; i++) |
---|
276 | for (j = i; j < i + look_ahead_dist_kmx10 && j <= best_dist_kmx10; j++) |
---|
277 | da[i]._difficulty += da[j]._landed; |
---|
278 | |
---|
279 | // sum of all difficulty |
---|
280 | double sum_difficulty = 0; |
---|
281 | for (int i = 0; i <= best_dist_kmx10; i++) |
---|
282 | sum_difficulty += da[i]._difficulty; |
---|
283 | |
---|
284 | // Difficulty relative to sum of all difficulty |
---|
285 | if (sum_difficulty > 0) |
---|
286 | { |
---|
287 | for (int i = 0; i <= best_dist_kmx10; i++) |
---|
288 | { |
---|
289 | da[i]._rel_diff = da[i]._difficulty / (2.0 * sum_difficulty); |
---|
290 | } |
---|
291 | } |
---|
292 | |
---|
293 | // Compute distance score % |
---|
294 | // accumulated relative difficulty per 100 meters. |
---|
295 | double sum_rel_difficulty = 0; |
---|
296 | for (int i = 0; i <= best_dist_kmx10; i++) |
---|
297 | { |
---|
298 | sum_rel_difficulty += da[i]._rel_diff; |
---|
299 | da[i]._score_percent = sum_rel_difficulty; |
---|
300 | } |
---|
301 | |
---|
302 | // ERDAL20090518 making sure we 0.5 (sum of _rel_diff can end up at 0.49999999999999978 or similar, should not make visible diff but...) |
---|
303 | for (int i = best_dist_kmx10; i <= best_dist_kmx10r; i++) |
---|
304 | { |
---|
305 | da[i]._score_percent = 0.5; |
---|
306 | } |
---|
307 | |
---|
308 | // set score to all distances below or equal minimum distance |
---|
309 | int min_dist_kmx10 = (int)(sfg.Min_dist * 10); |
---|
310 | if (min_dist_kmx10 < da.Length) |
---|
311 | { |
---|
312 | for (int i = 0; i <= min_dist_kmx10; i++) |
---|
313 | { |
---|
314 | da[i]._score_percent = da[min_dist_kmx10]._score_percent; |
---|
315 | } |
---|
316 | } |
---|
317 | return da; |
---|
318 | } |
---|