1 // Vamos Automotive Simulator
2 // Copyright (C) 2001--2012 Sam Varner
3 //
4 // This program is free software; you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation; either version 2 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program; if not, write to the Free Software
16 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
18 #include "Timing_Info.h"
19 #include "World.h"
20
21 #include <algorithm>
22 #include <limits>
23
24 using namespace Vamos_World;
25
26 const double Timing_Info::NO_TIME = std::numeric_limits <double>::min ();
27
Timing_Info(size_t n_cars,size_t n_sectors,size_t n_laps)28 Timing_Info::Timing_Info (size_t n_cars, size_t n_sectors, size_t n_laps)
29 : m_sectors (n_sectors),
30 m_laps (n_laps),
31 m_total_time (0.0),
32 mp_fastest (0),
33 m_fastest_lap (NO_TIME),
34 m_finished (false)
35 {
36 assert (n_sectors > 0);
37
38 // Reserve space if we know the total number of sectors. n_laps may be 0 in
39 // which case these vectors will grow indefinitely.
40 ma_sector_position.reserve (n_sectors * n_laps);
41 ma_sector_time.reserve (n_sectors * n_laps);
42 for (size_t i = 0; i < n_cars; i++)
43 {
44 Car_Timing* p_car = new Car_Timing (i + 1, n_sectors, n_laps);
45 ma_car_timing.push_back (p_car);
46 ma_running_order.push_back (p_car);
47 if (i == 0)
48 mp_fastest = p_car;
49 }
50 }
51
~Timing_Info()52 Timing_Info::~Timing_Info ()
53 {
54 for (size_t i = 0; i < ma_car_timing.size (); i++)
55 delete ma_car_timing [i];
56 }
57
reset()58 void Timing_Info::reset ()
59 {
60 m_total_time = 0.0;
61 mp_fastest = 0;
62 m_fastest_lap = NO_TIME;
63
64 ma_running_order.clear ();
65 for (size_t i = 0; i < ma_car_timing.size (); i++)
66 {
67 Car_Timing* p_car = ma_car_timing [i];
68 p_car->reset ();
69 ma_running_order.push_back (p_car);
70 if (i == 0)
71 mp_fastest = p_car;
72 }
73 }
74
update(double current_time,size_t index,double distance,size_t sector)75 void Timing_Info::update (double current_time,
76 size_t index,
77 double distance,
78 size_t sector)
79 {
80 assert (index < ma_car_timing.size ());
81 assert (sector <= m_sectors);
82
83 m_total_time = current_time;
84 const bool new_sector = is_new_sector (index, sector);
85 Car_Timing* p_car = ma_car_timing [index];
86 bool already_finished = p_car->m_finished;
87 p_car->update (current_time, distance, sector, new_sector, m_finished);
88 if (new_sector)
89 update_position (p_car, current_time, sector, already_finished);
90
91 if (ma_car_timing [index]->current_lap () > m_laps)
92 m_finished = true;
93 }
94
update_position(Car_Timing * p_car,double current_time,size_t sector,bool finished)95 void Timing_Info::update_position (Car_Timing* p_car,
96 double current_time,
97 size_t sector,
98 bool finished)
99 {
100 assert ((sector > 0) && (sector <= m_sectors));
101 if ((p_car->previous_sector () == 0) || finished)
102 return;
103
104 const size_t nth_sector = m_sectors * (p_car->current_lap () - 1) + sector - 1;
105
106 Timing_Info::Running_Order::iterator new_position = ma_running_order.begin ();
107 double interval;
108 if (nth_sector > ma_sector_position.size ())
109 {
110 interval = NO_TIME;
111 ma_sector_position.push_back (1);
112 ma_sector_time.push_back (current_time);
113 }
114 else
115 {
116 size_t p = ma_sector_position [nth_sector - 1]++;
117 for (; p > 0; p--)
118 ++new_position;
119 interval = current_time - ma_sector_time [nth_sector - 1];
120 ma_sector_time [nth_sector - 1] = current_time;
121 }
122
123 // If this car has lost positions it will have been pushed down the running
124 // order by the cars that have already reached this sector. Thus, old_position
125 // is at or after new_position.
126 Timing_Info::Running_Order::iterator old_position
127 = std::find (new_position, ma_running_order.end (), p_car);
128 if (new_position != old_position)
129 {
130 ma_running_order.insert (new_position, *old_position);
131 ma_running_order.erase (old_position);
132 }
133 p_car->m_interval = interval;
134
135 const double best = p_car->best_lap_time ();
136 if ((best != NO_TIME)
137 && ((m_fastest_lap == NO_TIME) || (best < m_fastest_lap)))
138 {
139 mp_fastest = p_car;
140 m_fastest_lap = best;
141 }
142 }
143
is_new_sector(size_t index,size_t sector) const144 bool Timing_Info::is_new_sector (size_t index, size_t sector) const
145 {
146 const size_t current = ma_car_timing [index]->current_sector ();
147 // Do the % before + because sector is 1-based.
148 return (sector == (current % m_sectors) + 1);
149 }
150
Car_Timing(size_t position,size_t sectors,size_t laps)151 Timing_Info::Car_Timing::Car_Timing (size_t position, size_t sectors, size_t laps)
152 : m_grid_position (position),
153 m_total_laps (laps),
154 m_current_time (0.0),
155 m_distance (0.0),
156 m_interval (NO_TIME),
157 m_sectors (sectors),
158 m_sector (0),
159 m_last_sector (0),
160 m_lap (0),
161 m_best_lap_time (NO_TIME),
162 m_lap_delta (NO_TIME),
163 m_finished (false)
164 {
165 ma_lap_time.reserve (laps);
166 ma_sector_time.reserve (m_sectors * laps);
167 ma_best_sector_time.resize (m_sectors);
168 ma_sector_delta.resize (m_sectors);
169 for (size_t sector = 0; sector < m_sectors; sector++)
170 {
171 ma_best_sector_time [sector] = NO_TIME;
172 ma_sector_delta [sector] = NO_TIME;
173 }
174 }
175
reset()176 void Timing_Info::Car_Timing::reset ()
177 {
178 m_interval = NO_TIME;
179 m_sector = 0;
180 m_last_sector = 0;
181 m_lap = 0;
182 m_best_lap_time = NO_TIME;
183 m_lap_delta = NO_TIME;
184 m_finished = false;
185
186 ma_lap_time.clear ();
187 ma_sector_time.clear ();
188 for (size_t sector = 0; sector < m_sectors; sector++)
189 {
190 ma_best_sector_time [sector] = NO_TIME;
191 ma_sector_delta [sector] = NO_TIME;
192 }
193 }
194
update(double current_time,double distance,size_t sector,bool new_sector,bool finished)195 void Timing_Info::Car_Timing::update (double current_time,
196 double distance,
197 size_t sector,
198 bool new_sector,
199 bool finished)
200 {
201 m_current_time = current_time;
202 m_distance = distance;
203 if (!m_finished && new_sector)
204 {
205 if (is_start_of_lap (sector))
206 {
207 update_lap_data (current_time);
208 if ((m_lap > m_total_laps) || finished)
209 m_finished = true;
210 }
211 update_sector_data (current_time, sector);
212 }
213 }
214
is_start_of_lap(size_t sector) const215 bool Timing_Info::Car_Timing::is_start_of_lap (size_t sector) const
216 {
217 return (sector == 1);
218 }
219
update_lap_data(double current_time)220 void Timing_Info::Car_Timing::update_lap_data (double current_time)
221 {
222 m_lap++;
223 if (m_sector > 0)
224 {
225 ma_lap_time.push_back (current_time);
226 if (m_best_lap_time == NO_TIME)
227 m_best_lap_time = previous_lap_time ();
228 else
229 {
230 m_lap_delta = previous_lap_time () - m_best_lap_time;
231 if (m_lap_delta < 0.0)
232 m_best_lap_time = previous_lap_time ();
233 }
234 }
235 }
236
update_sector_data(double current_time,size_t sector)237 void Timing_Info::Car_Timing::update_sector_data (double current_time, size_t sector)
238 {
239 if (m_sector > 0)
240 ma_sector_time.push_back (current_time);
241
242 m_last_sector = m_sector;
243 m_sector = sector;
244
245 if (m_last_sector > 0)
246 {
247 const size_t index = m_last_sector - 1;
248 assert (index < m_sectors);
249 double& best = ma_best_sector_time [index];
250 if (best == NO_TIME)
251 best = ma_sector_time.back ()
252 - (ma_sector_time.size () > 1
253 ? *(ma_sector_time.end () - 2)
254 : 0);
255 else
256 {
257 ma_sector_delta [index] = previous_sector_time () - best;
258 if (ma_sector_delta [index] < 0.0)
259 best = previous_sector_time ();
260 }
261 }
262 }
263
lap_time() const264 double Timing_Info::Car_Timing::lap_time () const
265 {
266 if (m_finished)
267 return NO_TIME;
268 return m_current_time - (ma_lap_time.size () == 0 ? 0.0 : ma_lap_time.back ());
269 }
270
previous_lap_time() const271 double Timing_Info::Car_Timing::previous_lap_time () const
272 {
273 switch (ma_lap_time.size ())
274 {
275 case 0:
276 return NO_TIME;
277 case 1:
278 return ma_lap_time.back ();
279 default:
280 return ma_lap_time.back () - *(ma_lap_time.end () - 2);
281 }
282 }
283
sector_time() const284 double Timing_Info::Car_Timing::sector_time () const
285 {
286 if (m_finished)
287 return NO_TIME;
288 return m_current_time - (ma_sector_time.size () == 0 ? 0.0 : ma_sector_time.back ());
289 }
290
best_sector_time() const291 double Timing_Info::Car_Timing::best_sector_time () const
292 {
293 // Return the best time on the current sector.
294 return (m_sector == 0) ? NO_TIME : ma_best_sector_time [m_sector - 1];
295 }
296
previous_sector_time() const297 double Timing_Info::Car_Timing::previous_sector_time () const
298 {
299 // Return the time spent in the most recently completed sector.
300 switch (ma_sector_time.size ())
301 {
302 case 0:
303 return NO_TIME;
304 case 1:
305 return ma_sector_time.back ();
306 default:
307 return ma_sector_time.back () - *(ma_sector_time.end () - 2);
308 }
309 }
310
previous_sector_time_difference() const311 double Timing_Info::Car_Timing::previous_sector_time_difference () const
312 {
313 return (m_last_sector == 0) ? NO_TIME : ma_sector_delta [m_last_sector - 1];
314 }
315