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