1 //
2 //  SuperTuxKart - a fun racing game with go-kart
3 //  Copyright (C) 2009-2015 Joerg Henrichs
4 //
5 //  This program is free software; you can redistribute it and/or
6 //  modify it under the terms of the GNU General Public License
7 //  as published by the Free Software Foundation; either version 3
8 //  of the License, or (at your option) any later version.
9 //
10 //  This program is distributed in the hope that it will be useful,
11 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 //  GNU General Public License for more details.
14 //
15 //  You should have received a copy of the GNU General Public License
16 //  along with this program; if not, write to the Free Software
17 //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18 
19 #include "modes/profile_world.hpp"
20 
21 #include "main_loop.hpp"
22 #include "graphics/camera.hpp"
23 #include "graphics/irr_driver.hpp"
24 #include "karts/kart_with_stats.hpp"
25 #include "karts/controller/controller.hpp"
26 #include "tracks/track.hpp"
27 
28 #include <ISceneManager.h>
29 
30 #include <iomanip>
31 #include <iostream>
32 #include <sstream>
33 
34 ProfileWorld::ProfileType ProfileWorld::m_profile_mode=PROFILE_NONE;
35 int   ProfileWorld::m_num_laps    = 0;
36 float ProfileWorld::m_time        = 0.0f;
37 
38 //-----------------------------------------------------------------------------
39 /** The constructor sets the number of (local) players to 0, since only AI
40  *  karts are used.
41  */
ProfileWorld()42 ProfileWorld::ProfileWorld()
43 {
44     RaceManager::get()->setNumPlayers(0);
45     // Set number of laps so that the end of the race can be detected by
46     // quering the number of finished karts from the race manager (in laps
47     // based profiling) - in case of time based profiling, the number of
48     // laps is set to 99999.
49     RaceManager::get()->setNumLaps(m_num_laps);
50     setPhase(RACE_PHASE);
51     m_frame_count      = 0;
52     m_start_time       = irr_driver->getRealTime();
53     m_num_triangles    = 0;
54     m_num_culls        = 0;
55     m_num_solid        = 0;
56     m_num_transparent  = 0;
57     m_num_trans_effect = 0;
58     m_num_calls        = 0;
59 }   // ProfileWorld
60 
61 //-----------------------------------------------------------------------------
62 /** Sets profile mode off again.
63  *  Needed because demo mode's closing allows the player to continue playing
64  *  STK. If we didn't set it off, profile mode would stay activated.
65  */
~ProfileWorld()66 ProfileWorld::~ProfileWorld()
67 {
68     m_profile_mode = PROFILE_NONE;
69 }
70 
71 //-----------------------------------------------------------------------------
72 /** Enables profiling for a certain amount of time. It also sets the
73  *  number of laps to a high number (so that the lap count will not finish
74  *  a race before the time is over).
75  *  \param time Time to profile a race for.
76  */
setProfileModeTime(float time)77 void ProfileWorld::setProfileModeTime(float time)
78 {
79     m_profile_mode = PROFILE_TIME;
80     m_num_laps     = 99999;
81     m_time         = time;
82 }   // setProfileModeTime
83 
84 //-----------------------------------------------------------------------------
85 /** Enables profiling for a certain number of laps. The race will end when all
86  *  karts have done (at least) this number of laps.
87  *  \param laps The number of laps.
88  */
setProfileModeLaps(int laps)89 void ProfileWorld::setProfileModeLaps(int laps)
90 {
91     m_profile_mode = PROFILE_LAPS;
92     m_num_laps     = laps;
93 }   // setProfileModeLaps
94 
95 //-----------------------------------------------------------------------------
96 /** Creates a kart, having a certain position, starting location, and local
97  *  and global player id (if applicable).
98  *  \param kart_ident Identifier of the kart to create.
99  *  \param index Index of the kart.
100  *  \param local_player_id If the kart is a player kart this is the index of
101  *         this player on the local machine.
102  *  \param global_player_id If the akrt is a player kart this is the index of
103  *         this player globally (i.e. including network players).
104  *  \param init_pos The start XYZ coordinates.
105  */
createKart(const std::string & kart_ident,int index,int local_player_id,int global_player_id,RaceManager::KartType kart_type,HandicapLevel handicap)106 std::shared_ptr<AbstractKart> ProfileWorld::createKart
107     (const std::string &kart_ident, int index, int local_player_id,
108     int global_player_id, RaceManager::KartType kart_type,
109     HandicapLevel handicap)
110 {
111     btTransform init_pos   = getStartTransform(index);
112 
113     std::shared_ptr<KartWithStats> new_kart =
114         std::make_shared<KartWithStats>(kart_ident, /*world kart id*/ index,
115         /*position*/ index + 1, init_pos, handicap);
116     new_kart->init(RaceManager::KT_AI);
117     Controller *controller = loadAIController(new_kart.get());
118     new_kart->setController(controller);
119 
120     // Create a camera for the last kart (since this way more of the
121     // karts can be seen.
122     if (!GUIEngine::isNoGraphics() &&
123         index == (int)RaceManager::get()->getNumberOfKarts()-1)
124     {
125         // The camera keeps track of all cameras and will free them
126         Camera::createCamera(new_kart.get(), local_player_id);
127     }
128     return new_kart;
129 }   // createKart
130 
131 //-----------------------------------------------------------------------------
132 /** The race is over if either the requested number of laps have been done
133  *  or the requested time is over.
134  */
isRaceOver()135 bool ProfileWorld::isRaceOver()
136 {
137     if(m_profile_mode==PROFILE_TIME)
138         return getTime()>m_time;
139 
140     if(m_profile_mode == PROFILE_LAPS )
141     {
142         // Now it must be laps based profiling:
143         return RaceManager::get()->getFinishedKarts()==getNumKarts();
144     }
145     // Unknown profile mode
146     assert(false);
147     return false;  // keep compiler happy
148 }   // isRaceOver
149 
150 //-----------------------------------------------------------------------------
151 /** Counts the number of frames.
152  *  \param ticks number of physics time steps - should be 1.
153  */
update(int ticks)154 void ProfileWorld::update(int ticks)
155 {
156     StandardRace::update(ticks);
157 
158     m_frame_count++;
159     video::IVideoDriver *driver = irr_driver->getVideoDriver();
160     io::IAttributes   *attr = irr_driver->getSceneManager()->getParameters();
161     m_num_triangles    += (int)(driver->getPrimitiveCountDrawn( 0 )
162                         * ( 1.f / 1000.f ));
163     m_num_calls        += attr->getAttributeAsInt("calls");
164     m_num_culls        += attr->getAttributeAsInt("culled" );
165     m_num_solid        += attr->getAttributeAsInt("drawn_solid" );
166     m_num_transparent  += attr->getAttributeAsInt("drawn_transparent" );
167     m_num_trans_effect += attr->getAttributeAsInt("drawn_transparent_effect" );
168 
169 }   // update
170 
171 //-----------------------------------------------------------------------------
172 /** This function is called when the race is finished, but end-of-race
173  *  animations have still to be played. In the case of profiling,
174  *  we can just abort here without waiting for the animations.
175  */
enterRaceOverState()176 void ProfileWorld::enterRaceOverState()
177 {
178     // If in timing mode, the number of laps is way too high (which avoids
179     // aborting too early). So in this case determine the maximum number
180     // of laps and set this +1 as the number of laps to get more meaningful
181     // time estimations.
182     if(m_profile_mode==PROFILE_TIME)
183     {
184         int max_laps = -2;
185         for(unsigned int i=0; i<RaceManager::get()->getNumberOfKarts(); i++)
186         {
187             if(m_kart_info[i].m_finished_laps>max_laps)
188                 max_laps = m_kart_info[i].m_finished_laps;
189         }   // for i<getNumberOfKarts
190         RaceManager::get()->setNumLaps(max_laps+1);
191     }
192 
193     StandardRace::enterRaceOverState();
194     // Estimate finish time and set all karts to be finished.
195     for (unsigned int i=0; i<RaceManager::get()->getNumberOfKarts(); i++)
196     {
197         // ---------- update rank ------
198         if (m_karts[i]->hasFinishedRace() || m_karts[i]->isEliminated())
199             continue;
200         m_karts[i]->finishedRace(estimateFinishTimeForKart(m_karts[i].get()));
201     }
202 
203     // Print framerate statistics
204     float runtime = (irr_driver->getRealTime()-m_start_time)*0.001f;
205     Log::verbose("profile", "Number of frames: %d time %f, Average FPS: %f",
206                  m_frame_count, runtime, (float)m_frame_count/runtime);
207 
208     // Print geometry statistics if we're not in no-graphics mode
209     if(!GUIEngine::isNoGraphics())
210     {
211         Log::verbose("profile", "Average # drawn nodes           %f k",
212                      (float)m_num_triangles/m_frame_count);
213         Log::verbose("profile", "Average # culled nodes:         %f k",
214                      (float)m_num_culls/m_frame_count);
215         Log::verbose("profile", "Average # solid nodes:          %f k",
216                      (float)m_num_solid/m_frame_count);
217         Log::verbose("profile", "Average # transparent nodes:    %f",
218                      (float)m_num_transparent/m_frame_count);
219         Log::verbose("profile", "Average # transp. effect nodes: %f",
220                      (float)m_num_trans_effect/m_frame_count);
221     }
222 
223     // Print race statistics for each individual kart
224     float min_t=999999.9f, max_t=0.0, av_t=0.0;
225     Log::verbose("profile", "name start_position end_position time average_speed top_speed "
226            "skid_time rescue_time rescue_count brake_count "
227            "explosion_time explosion_count bonus_count banana_count "
228            "small_nitro_count large_nitro_count bubblegum_count");
229 
230     std::set<std::string> all_groups;
231 
232     for (KartList::size_type i = 0; i < m_karts.size(); ++i)
233     {
234         auto kart = std::dynamic_pointer_cast<KartWithStats>(m_karts[i]);
235 
236         max_t = std::max(max_t, kart->getFinishTime());
237         min_t = std::min(min_t, kart->getFinishTime());
238         av_t += kart->getFinishTime();
239         std::ostringstream ss;
240         ss << kart->getIdent() << " "
241            << kart->getController()->getControllerName() << " ";
242         ss << 1+(int)i << " " << kart->getPosition() << " "
243            << kart->getFinishTime() << " ";
244 
245         all_groups.insert(kart->getController()->getControllerName());
246         float distance = (float)(m_profile_mode==PROFILE_LAPS
247                                  ? RaceManager::get()->getNumLaps() : 1);
248         distance *= Track::getCurrentTrack()->getTrackLength();
249         ss << distance/kart->getFinishTime() << " " << kart->getTopSpeed() << " ";
250         ss << kart->getSkiddingTime() << " " << kart->getRescueTime() << " ";
251         ss << kart->getRescueCount() << " " << kart->getBrakeCount() << " ";
252         ss << kart->getExplosionTime() << " " << kart->getExplosionCount() << " ";
253         ss << kart->getBonusCount() << " " << kart->getBananaCount() << " ";
254         ss << kart->getSmallNitroCount() << " " << kart->getLargeNitroCount() << " ";
255         ss << kart->getBubblegumCount() << " " << kart->getOffTrackCount() << " ";
256         Log::verbose("profile", ss.str().c_str());
257     }
258 
259     // Print group statistics of all karts
260     Log::verbose("profile", "min %f  max %f  av %f\n",
261                   min_t, max_t, av_t/m_karts.size());
262 
263     // Determine maximum length of group name
264     unsigned int max_len=4;   // for 'name' heading
265     for(std::set<std::string>::iterator it = all_groups.begin();
266         it !=all_groups.end(); it++)
267     {
268         if(it->size()>max_len) max_len = (unsigned int) it->size();
269     }
270     max_len++;  // increase by 1 for one additional space after the name
271 
272     std::ostringstream ss;
273     Log::verbose("profile", "");
274     ss << "name" << std::setw(max_len-4) << " "
275        << "Strt End  Time    AvSp  Top   Skid  Resc Rsc Brake Expl Exp Itm Ban SNitLNit Bub Off Energy";
276     Log::verbose("profile", ss.str().c_str());
277     for(std::set<std::string>::iterator it = all_groups.begin();
278         it !=all_groups.end(); it++)
279     {
280         int   count         = 0,    position_gain   = 0,    rescue_count = 0;
281         int   brake_count   = 0,    bonus_count     = 0,    banana_count = 0;
282         int   l_nitro_count = 0,    s_nitro_count   = 0,    bubble_count = 0;
283         int   expl_count    = 0,    off_track_count = 0;
284         float skidding_time = 0.0f, rescue_time     = 0.0f, expl_time    = 0.0f;
285         float av_time       = 0.0f, energy          = 0;
286         for ( unsigned int i = 0; i < (unsigned int)m_karts.size(); ++i)
287         {
288             auto kart = std::dynamic_pointer_cast<KartWithStats>(m_karts[i]);
289             const std::string &name=kart->getController()->getControllerName();
290             if(name!=*it)
291                 continue;
292             count ++;
293             std::ostringstream ss;
294             ss.setf(std::ios::fixed, std::ios::floatfield);
295             ss.precision(2);
296             ss << name << std::setw(max_len-name.size()) << " ";
297             ss << std::setw(4) <<  1 + i
298                << std::setw(4) << kart->getPosition()
299                << std::setw(7) << kart->getFinishTime();
300             position_gain += 1+i - kart->getPosition();
301 
302             float distance = (float)(m_profile_mode==PROFILE_LAPS
303                                      ? RaceManager::get()->getNumLaps() : 1);
304             distance *= Track::getCurrentTrack()->getTrackLength();
305 
306             Log::verbose("profile",
307                    "%s %4.2f %3.2f %6.2f %4.2f %3d %5d %4.2f %3d %3d %3d %3d %3d %3d %5d %4.2f",
308                    ss.str().c_str(), distance/kart->getFinishTime(),
309                    kart->getTopSpeed(),
310                    kart->getSkiddingTime(),        kart->getRescueTime(),
311                    kart->getRescueCount(),         kart->getBrakeCount(),
312                    kart->getExplosionTime(),       kart->getExplosionCount(),
313                    kart->getBonusCount(),          kart->getBananaCount(),
314                    kart->getSmallNitroCount(),     kart->getLargeNitroCount(),
315                    kart->getBubblegumCount(),      kart->getOffTrackCount(),
316                    kart->getEnergy()
317                    );
318             av_time += kart->getFinishTime();
319             skidding_time   += kart->getSkiddingTime();
320             rescue_time     += kart->getRescueTime();
321             rescue_count    += kart->getRescueCount();
322             brake_count     += kart->getBrakeCount();
323             bonus_count     += kart->getBonusCount();
324             banana_count    += kart->getBananaCount();
325             l_nitro_count   += kart->getLargeNitroCount();
326             s_nitro_count   += kart->getSmallNitroCount();
327             bubble_count    += kart->getBubblegumCount();
328             expl_time       += kart->getExplosionTime();
329             expl_count      += kart->getExplosionCount();
330             off_track_count += kart->getOffTrackCount();
331             energy          += kart->getEnergy();
332         }    // for i < m_karts.size
333 
334         Log::verbose("profile", std::string(max_len+90, '-').c_str());
335         ss.clear();
336         ss.str("");
337         ss << *it << std::string(max_len-it->size(),' ');
338         ss << std::showpos << std::setw(4) << position_gain
339            << std::noshowpos << std::setw(13) << av_time/count
340            << std::string(11,' ');
341 
342         Log::verbose("profile", "%s%6.2f %4.2f %3d %5d %4.2f %3d %3d %3d %3d %3d %3d %5d %4.2f",
343                ss.str().c_str(), skidding_time/count, rescue_time/count,
344                rescue_count,brake_count, expl_time, expl_count, bonus_count,
345                banana_count, s_nitro_count, l_nitro_count, bubble_count,
346                off_track_count, energy);
347         Log::verbose("profile", "");
348     }   // for it !=all_groups.end
349     delete this;
350     main_loop->abort();
351 }   // enterRaceOverState
352