ArenaReplay.cc

Go to the documentation of this file.
00001 /*
00002 RealTimeBattle, a robot programming game for Unix
00003 Copyright (C) 1998-2000  Erik Ouchterlony and Ragnar Ouchterlony
00004 
00005 This program is free software; you can redistribute it and/or modify
00006 it under the terms of the GNU General Public License as published by
00007 the Free Software Foundation; either version 2 of the License, or
00008 (at your option) any later version.
00009 
00010 This program is distributed in the hope that it will be useful,
00011 but WITHOUT ANY WARRANTY; without even the implied warranty of
00012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013 GNU General Public License for more details.
00014 
00015 You should have received a copy of the GNU General Public License
00016 along with this program; if not, write to the Free Software Foundation,
00017 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00018 */
00019 
00020 
00021 #ifdef HAVE_CONFIG_H
00022 #include <config.h>
00023 #endif
00024 
00025 #include <sys/stat.h>
00026 #include <math.h>
00027 #include <stdio.h>
00028 
00029 #include "ArenaReplay.h"
00030 #include "ArenaController.h"
00031 #include "IntlDefs.h"
00032 #include "Various.h"
00033 #include "Options.h"
00034 #include "Extras.h"
00035 #include "Shot.h"
00036 #include "MessageWindow.h"
00037 #include "ArenaWindow.h"
00038 #include "ScoreWindow.h"
00039 #include "ControlWindow.h"
00040 #include "Robot.h"
00041 
00042 const int max_time_infos = 16384;
00043 
00044 extern class ControlWindow* controlwindow_p;
00045 
00046 ArenaReplay::ArenaReplay()
00047 {
00048   reset_timer();
00049   //  speed = PLAY;
00050   fast_forward_factor = 1.0;
00051   state = NOT_STARTED;
00052   current_replay_time = 0.0;
00053   game_position_in_log = NULL;
00054   time_position_in_log = NULL;
00055 
00056   set_game_mode( (ArenaBase::game_mode_t)the_arena_controller.game_mode );
00057   set_filenames( the_arena_controller.replay_filename,
00058                  the_arena_controller.message_filename,
00059                  the_arena_controller.statistics_filename,
00060                  the_arena_controller.option_filename );
00061 }
00062 
00063 ArenaReplay::~ArenaReplay()
00064 {
00065   if( game_position_in_log != NULL )
00066     {
00067       for( int i=0; i<sequences_in_tournament; i++ )
00068         delete [] game_position_in_log[i];
00069       delete [] game_position_in_log;
00070     }
00071 
00072   if( time_position_in_log != NULL )
00073     delete [] time_position_in_log;
00074 
00075 }
00076 
00077 bool
00078 ArenaReplay::timeout_function()
00079 {
00080   if( ( state == GAME_IN_PROGRESS || state == BEFORE_GAME_START ) )
00081     update_timer( fast_forward_factor );
00082   else if( state == PAUSED )
00083     update_timer( 0.0 );
00084 
00085   switch( state )
00086     {
00087     case NOT_STARTED:
00088       start_tournament();
00089       break;
00090     case GAME_IN_PROGRESS:
00091       {
00092 #ifndef NO_GRAPHICS
00093         int old_total = (int)current_replay_time;
00094 #endif 
00095 
00096         parse_this_interval();
00097 
00098 #ifndef NO_GRAPHICS
00099         if((int)current_replay_time - old_total != 0 && !no_graphics)
00100           {
00101             the_gui.get_scorewindow_p()->set_window_title();
00102             if( !log_from_stdin )
00103               controlwindow_p->set_progress_time( current_replay_time );
00104           }
00105 #endif
00106       }
00107       break;
00108 
00109     case BEFORE_GAME_START:
00110       parse_this_interval();
00111 #ifndef NO_GRAPHICS
00112       if( !no_graphics )
00113         {
00114           if( controlwindow_p->get_displayed() != ControlWindow::REPLAY_WIDGETS &&
00115               !log_from_stdin )
00116             controlwindow_p->display_replay_widgets();
00117           the_gui.get_arenawindow_p()->drawing_area_scale_changed();      
00118           the_gui.get_arenawindow_p()->draw_everything();      
00119           the_gui.get_scorewindow_p()->update_robots();
00120           char msg[64];
00121           snprintf(msg, 63, _("Game %d of sequence %d"), game_nr, sequence_nr);
00122           print_message( "RealTimeBattle", (String)msg );
00123         }
00124 #endif 
00125       set_state( GAME_IN_PROGRESS );
00126       break;
00127 
00128     case FINISHED:
00129       if( the_arena_controller.auto_start_and_end )
00130         {
00131           if( statistics_file_name != "" )
00132             save_statistics_to_file( statistics_file_name );
00133 
00134           Quit();
00135         }
00136       break;
00137 
00138     default:
00139       break;
00140     }
00141   return true;
00142 }
00143 
00144 void
00145 ArenaReplay::parse_this_interval()
00146 {
00147 #ifndef NO_GRAPHICS
00148   if( state == GAME_IN_PROGRESS && !no_graphics)
00149     the_gui.get_messagewindow_p()->freeze_clist();
00150 #endif
00151   if( fast_forward_factor > 0.0 )
00152     while( !log_file.eof() && total_time >= current_replay_time )
00153       {
00154         parse_this_time_index();
00155       }
00156   else
00157     {
00158     while( !log_file.eof() && total_time <= current_replay_time && 
00159            step_forward(-1, false) )
00160       {
00161         parse_this_time_index();
00162       }
00163     }
00164 #ifndef NO_GRAPHICS
00165   if( state == GAME_IN_PROGRESS && !no_graphics)
00166     the_gui.get_messagewindow_p()->thaw_clist();
00167 #endif
00168   
00169   if( log_file.eof() )
00170     {
00171       set_state( FINISHED );
00172 #ifndef NO_GRAPHICS
00173       if( !no_graphics )
00174         {
00175           controlwindow_p->remove_replay_widgets();
00176           the_gui.close_arenawindow();
00177         }
00178 #endif
00179     }
00180 }
00181 
00182 void
00183 ArenaReplay::parse_this_time_index()
00184 {
00185   double last_replay_time = current_replay_time;
00186 
00187   if( log_file.peek() == 'T' )
00188     parse_log_line();
00189   else
00190     {
00191 //        cout << "'T' not first in log_file for parse_this_time_index()" << endl;
00192 //        cout << "log_file.peek(): " << (char)log_file.peek() << endl;
00193 //        cout << "current_replay_time: " << current_replay_time << endl;
00194     }
00195 
00196   double next_replay_time = current_replay_time;
00197 
00198   while( log_file.peek() != 'T' || current_replay_time == 0.0 )
00199     {
00200       if( parse_log_line() == '?' ) return;
00201       if ( current_replay_time == 0.0 ) next_replay_time = 0.0; // A new game has started
00202     }
00203 
00204 
00205   current_replay_time = next_replay_time;
00206   move_shots_no_check( current_replay_time - last_replay_time );
00207 
00208 #ifndef NO_GRAPHICS
00209         if( !no_graphics )
00210           the_gui.get_arenawindow_p()->draw_moving_objects( true );
00211 #endif 
00212 
00213   // check if robots have died and set their points
00214   if( robots_killed_this_round != 0 )
00215     {
00216       robots_left -= robots_killed_this_round; 
00217 
00218       ListIterator<Shape> li;
00219       Robot* robotp;
00220       for( object_lists[ROBOT].first(li); li.ok(); li++ )
00221         {                
00222           robotp = (Robot*)li();
00223           if( robotp->is_alive() )
00224             {
00225               //              robotp->add_points(robots_killed_this_round);
00226 #ifndef NO_GRAPHICS
00227               if( robots_left < 15 && !no_graphics ) 
00228                 robotp->display_score();
00229 #endif
00230             }
00231         }
00232 
00233       robots_killed_this_round = 0;
00234     }
00235 }
00236   
00237 void 
00238 ArenaReplay::start_tournament()
00239 {
00240 #ifndef NO_GRAPHICS
00241   if( !no_graphics )
00242     {
00243       if( the_gui.is_messagewindow_up() )
00244         MessageWindow::clear_clist( NULL, the_gui.get_messagewindow_p() );
00245       else //if( !use_message_file )
00246         the_gui.open_messagewindow();
00247 
00248       if( !the_gui.is_scorewindow_up() ) the_gui.open_scorewindow();
00249       if( !the_gui.is_arenawindow_up() ) the_gui.open_arenawindow();
00250     }
00251 #endif
00252 
00253   if( !log_from_stdin )
00254     make_statistics_from_file();
00255 
00256   set_state( BEFORE_GAME_START );
00257 }
00258   
00259 void 
00260 ArenaReplay::end_game()
00261 {
00262 }
00263 
00264 void 
00265 ArenaReplay::update()
00266 {
00267 }
00268 
00269 void 
00270 ArenaReplay::start_game()
00271 {
00272 }
00273 
00274 void 
00275 ArenaReplay::start_sequence()
00276 {
00277 }
00278 
00279 void 
00280 ArenaReplay::end_sequence()
00281 {
00282 }
00283 
00284 void 
00285 ArenaReplay::end_tournament()
00286 {
00287 }
00288 
00289 char
00290 ArenaReplay::parse_log_line()
00291 {
00292   char first_letter = '?';
00293 
00294   log_file >> ws;
00295   log_file.get( first_letter );
00296 
00297   if( log_file.eof() ) return '?';
00298   //  cerr << first_letter;
00299   switch( first_letter )
00300     {
00301     case 'R':
00302     case 'S':
00303     case 'M':
00304     case 'C': 
00305     case 'D': // these five are dependent on which direction we are replaying
00306       if ( fast_forward_factor > 0.0 )
00307         parse_log_line_forward( first_letter );
00308       else
00309         parse_log_line_rewind( first_letter );
00310       break;
00311 
00312     case 'T': // Time
00313       {
00314         log_file >> current_replay_time;
00315       }
00316       break;
00317     case 'P': // Print a robot message
00318       {
00319         int robot_id;
00320         char message[200];
00321         log_file >> robot_id;
00322         log_file.get( message, 200, '\n' );
00323         ListIterator<Shape> li;
00324         find_object_by_id( object_lists[ROBOT], li, robot_id );
00325         if( li.ok() ) 
00326           {
00327             Robot* robotp = (Robot*)li();
00328             print_message( robotp->get_robot_name(), (String)message );
00329           }
00330         // else: robot sent a message before first game of sequences.
00331         // Robot data is not yet known to us, thus ignore!
00332       }
00333       break;
00334     case 'G': // Game begins
00335       {
00336         delete_lists(false, false, false, false);
00337         reset_timer();
00338         current_replay_time = 0;
00339         robots_killed_this_round = 0;
00340         robots_left = robots_per_game;
00341 
00342         log_file >> sequence_nr >> game_nr;
00343 
00344         if( !log_from_stdin )
00345           get_time_positions_in_game();
00346 
00347         ListIterator<Robot> li;
00348         for( all_robots_in_tournament.first(li); li.ok(); li++ )
00349           li()->set_values_before_game(Vector2D(infinity,infinity), 0.0);
00350 
00351         arena_scale = the_opts.get_d(OPTION_ARENA_SCALE);
00352         arena_angle_factor = 1.0;
00353         arena_succession = 1;
00354         set_state( BEFORE_GAME_START );
00355 
00356 #ifndef NO_GRAPHICS
00357         controlwindow_p->change_time_limitations();
00358 #endif
00359       }
00360       break;
00361     case 'H': // Header
00362       {
00363         if( log_from_stdin )
00364           log_file >> games_per_sequence >> robots_per_game 
00365                    >> sequences_in_tournament >> number_of_robots;        
00366         else
00367           {
00368             char buffer[400];
00369             log_file.get( buffer, 400, '\n' );
00370             log_file >> ws;
00371           }          
00372       }
00373       break;
00374     case 'L': // List of robot properties
00375       {
00376         if( log_from_stdin )
00377           {
00378             int robot_id;
00379             char robot_colour[7];
00380             char name[200];
00381             log_file >> robot_id >> ws;
00382             log_file.get( robot_colour, 7, ' ');
00383             long int col = str2hex( (String)robot_colour );
00384             log_file.get( name, 200, '\n' );
00385             Robot* robotp = new Robot( robot_id, col, (String)name );
00386             all_robots_in_tournament.insert_last(robotp); // used by statistics
00387           }
00388         else
00389           {
00390             char buffer[400];
00391             log_file.get( buffer, 400, '\n' );
00392             log_file >> ws;
00393           }
00394       }
00395       break;
00396     case 'A': // Arena file line
00397       {
00398         parse_arena_line( log_file, arena_scale, arena_succession, arena_angle_factor );
00399       }
00400       break;
00401     case 'O': // Option
00402       {
00403         char temp;
00404         char label[200];
00405         log_file.get( label, 200, ':');
00406         log_file.get( temp );
00407         option_return_t opt = the_opts.get_option_from_string( String( label ) );
00408         switch( opt.datatype )
00409           {
00410           case ENTRY_INT:
00411           case ENTRY_HEX:
00412             {
00413               long val;
00414               log_file >> val;
00415               //              cerr << label << ": " << val << endl;
00416               the_opts.get_all_long_options()[opt.option_number].value = val;
00417             }
00418             break;
00419           case ENTRY_DOUBLE:
00420             {
00421               double val;
00422               log_file >> val;
00423               //              cerr << label << ": " << val << endl;
00424               the_opts.get_all_double_options()[opt.option_number].value = val;
00425             }
00426             break;
00427           case ENTRY_CHAR:
00428             {
00429               char val[400];
00430               log_file.get( val, 400, '\n' );
00431               //              cerr << label << ": " << val << endl;
00432               the_opts.get_all_string_options()[opt.option_number].value = val;
00433             }
00434             break;
00435           case ENTRY_BOOL:
00436             break;
00437           default:
00438             Error( true, "Unknown datatype", "ArenaReplay::parse_log_line" );
00439             break;
00440           }
00441       }
00442       break;
00443 
00444     case '?':
00445     default:
00446       Error( false, "Unrecognized first letter in logfile: " + 
00447              String(first_letter), "ArenaReplay::parse_log_line" );
00448       char buffer[400];
00449       log_file.get( buffer, 400, '\n' );
00450       log_file >> ws;
00451       log_file.clear();
00452       break;
00453     }
00454 
00455   log_file >> ws;
00456 
00457   return first_letter;
00458 }
00459 
00460 void 
00461 ArenaReplay::parse_log_line_forward( const char first_letter )
00462 {
00463   switch( first_letter )
00464     {
00465     case 'R': // Robot pos
00466       {
00467         int robot_id;
00468         double x, y, robot_angle, cannon_angle, radar_angle, energy; 
00469         Robot* robotp = NULL;
00470         log_file >> robot_id >> x >> y 
00471                  >> robot_angle >> cannon_angle >> radar_angle >> energy;
00472         ListIterator<Shape> li;
00473         if( find_object_by_id( object_lists[ROBOT], li, robot_id ) )
00474           {
00475             robotp = (Robot*)li();
00476           }
00477         else
00478           {
00479             ListIterator<Robot> li2;
00480             if( log_from_stdin &&
00481                 find_object_by_id( all_robots_in_tournament, li2, robot_id ) )
00482               {
00483                 object_lists[ROBOT].insert_last( li2() ); 
00484                 robotp = li2();
00485               }
00486             else
00487               Error(true, "Robot not in list", "ArenaReplay::parse_log_line_forward");
00488           }
00489 
00490         robotp->change_position( x, y, robot_angle, cannon_angle, radar_angle, energy );
00491         robotp->live();
00492       }
00493       break;
00494     case 'C': // Cookie
00495       {
00496         int cookie_id;
00497         double x, y;
00498         log_file >> cookie_id >> x >> y;
00499         Cookie* cookiep = new Cookie( Vector2D(x,y), 0.0, cookie_id);
00500         object_lists[COOKIE].insert_last( cookiep );
00501       }
00502       break;
00503     case 'M': // Mine
00504       {
00505         int mine_id;
00506         double x, y;
00507         log_file >> mine_id >> x >> y;
00508         Mine* minep = new Mine( Vector2D(x,y), 0.0, mine_id);
00509         object_lists[MINE].insert_last( minep );
00510       }
00511       break;
00512     case 'S': // Shot
00513       {
00514         int shot_id;
00515         double x, y, dx, dy;
00516         log_file >> shot_id >> x >> y >> dx >> dy;
00517         Shot* shotp = new Shot( Vector2D(x,y), Vector2D(dx,dy), 0, shot_id );
00518         object_lists[SHOT].insert_last( shotp );
00519       }
00520       break;
00521 
00522 
00523     case 'D': // Die
00524       {
00525         char object_type = '?';
00526         int object_id;
00527         log_file.get( object_type );
00528         log_file >> object_id;
00529         switch( object_type )
00530           {
00531           case 'R':
00532             {
00533               double points_received;
00534               int pos_this_game;
00535               log_file >> points_received >> pos_this_game;
00536               ListIterator<Shape> li;             
00537               find_object_by_id( object_lists[ROBOT], li, object_id );
00538               if( !li.ok() ) 
00539                 Error(true, "Dying robot not in list", "ArenaReplay::parse_log_line_forward");
00540               Robot* robotp = (Robot*) li();
00541               if( robotp->is_alive() )
00542                 {
00543                   robotp->die();
00544                   robots_killed_this_round++;
00545                   robotp->set_stats( points_received, pos_this_game, 
00546                                      current_replay_time, log_from_stdin);
00547                   robotp->change_energy( -robotp->get_energy() );
00548                 }
00549             }
00550             break;
00551           case 'C':
00552             {
00553               ListIterator<Shape> li;             
00554               find_object_by_id( object_lists[COOKIE], li, object_id );
00555               if( !li.ok() ) 
00556                 Error(false, "Dying cookie not in list", "ArenaReplay::parse_log_line_forward");
00557               else
00558                 {
00559                   ((Cookie*)li())->die();
00560                   object_lists[COOKIE].remove(li);
00561                 }
00562             }
00563             break;
00564           case 'M':
00565             {
00566               ListIterator<Shape> li;
00567               find_object_by_id( object_lists[MINE], li, object_id );
00568               if( !li.ok() ) 
00569                 Error(false, "Dying mine not in list", "ArenaReplay::parse_log_line_forward");
00570               else
00571                 {
00572                   ((Mine*)li())->die();
00573                   object_lists[MINE].remove(li);
00574                 }
00575             }
00576             break;
00577           case 'S':
00578             {
00579               ListIterator<Shape> li;
00580               find_object_by_id( object_lists[SHOT], li, object_id );
00581               if( !li.ok() )
00582                 Error(false, "Dying shot not in list", "ArenaReplay::parse_log_line_forward");
00583               else
00584                 {
00585                   ((Shot*)li())->die();
00586                   object_lists[SHOT].remove(li);
00587                 }
00588             }
00589             break;
00590           case '?':
00591           default:
00592             Error( false, "Unknown object type is dead", "ArenaReplay::parse_log_line_forward" );
00593             break;
00594           }
00595       }
00596       break;
00597     }
00598 }
00599 
00600 void 
00601 ArenaReplay::parse_log_line_rewind( const char first_letter )
00602 {
00603   switch( first_letter )
00604     {
00605     case 'R':
00606       {
00607         int robot_id;
00608         double x, y, robot_angle, cannon_angle, radar_angle, energy;
00609         log_file >> robot_id >> x >> y >> 
00610           robot_angle >> cannon_angle >> radar_angle >> energy;
00611         ListIterator<Shape> li;
00612         find_object_by_id( object_lists[ROBOT], li, robot_id );
00613         if( !li.ok() ) Error(true, "Robot not in list", "ArenaReplay::parse_log_line_rewind");
00614         
00615         Robot* robotp = (Robot*)li();
00616         
00617         robotp->change_position( x, y, robot_angle, cannon_angle, radar_angle, energy );
00618         if( !robotp->is_alive() )
00619           {
00620             robotp->live();
00621             robots_killed_this_round--;
00622             robotp->set_stats(0.0, 0, 0.0, false); // clear position_this_game
00623           }
00624       }
00625       break;
00626 
00627         
00628     case 'C':
00629       {
00630         int id;
00631         double x,y;
00632         log_file >> id >> x >> y;
00633         ListIterator<Shape> li;             
00634         find_object_by_id( object_lists[COOKIE], li, id );
00635         if( !li.ok() ) 
00636           {
00637             // This happens if it dies the round as it is created
00638             //Error(false, "Dying cookie not in list", "ArenaReplay::parse_log_line_rewind");
00639           }
00640         else
00641           {
00642             ((Cookie*)li())->die();
00643             object_lists[COOKIE].remove(li);
00644           }
00645       }
00646       break;
00647     case 'M':
00648       {
00649         int id;
00650         double x,y;
00651         log_file >> id >> x >> y;
00652         ListIterator<Shape> li;
00653         find_object_by_id( object_lists[MINE], li, id );
00654         if( !li.ok() ) 
00655           {
00656             // This happens if it dies the round as it is created
00657             //Error(false, "Dying mine not in list", "ArenaReplay::parse_log_line_rewind");
00658           }
00659         else
00660           {
00661             ((Mine*)li())->die();
00662             object_lists[MINE].remove(li);
00663           }
00664       }
00665       break;
00666     case 'S':
00667       {
00668         int id;
00669         double x,y,vx,vy;
00670         log_file >> id >> x >> y >> vx >> vy;
00671         //        cout << "Shot rewind_dying: " << id << endl;
00672         ListIterator<Shape> li;
00673         find_object_by_id( object_lists[SHOT], li, id );
00674         if( !li.ok() )
00675           {
00676             // This happens if it dies the round as it is created
00677             //Error(false, "Dying shot not in list", "ArenaReplay::parse_log_line_rewind");
00678           }
00679         else
00680           {
00681             ((Shot*)li())->die();
00682             object_lists[SHOT].remove(li);
00683           }
00684       }
00685       break;
00686 
00687     case 'D':
00688       char object_type = '?';
00689       int object_id;
00690       log_file.get( object_type );
00691       log_file >> object_id;
00692       switch( object_type )
00693         {
00694         case 'R':
00695           {
00696             double pnts;
00697             int pos;
00698             log_file >> pnts >> pos;
00699             // Robot will be revived at the next 'R' line.
00700           }
00701           break;
00702         case 'C': // Cookie
00703           {
00704             object_pos_info_t* info = find_object_in_log( COOKIE, object_id );
00705             if( info->end_time > info->start_time )              
00706               object_lists[COOKIE].insert_last
00707                 ( new Cookie( info->pos, 0.0, info->id ) );
00708           }
00709           break;
00710         case 'M': // Mine
00711           {
00712             object_pos_info_t* info = find_object_in_log( MINE, object_id );
00713             if( info->end_time > info->start_time )              
00714               object_lists[MINE].insert_last
00715                 ( new Mine( info->pos, 0.0, info->id ) );
00716           }
00717           break;
00718         case 'S': // Shot
00719           {
00720             object_pos_info_t* info = find_object_in_log( SHOT, object_id );
00721 
00722             if( info->end_time > info->start_time )              
00723               object_lists[SHOT].insert_last
00724                 ( new Shot( info->pos + (current_replay_time - info->start_time)
00725                             * info->vel, info->vel, 0, info->id ) );
00726           }
00727           break;
00728   
00729         }
00730       break;
00731     }
00732 }
00733 
00734 void
00735 ArenaReplay::set_filenames( String& replay_fname, String& message_fname,
00736                             const String& statistics_fname,
00737                             const String& option_fname )
00738 {
00739         
00740  /*
00741   if( replay_fname != "-" )
00742     {
00743       log_file.open( replay_fname.chars() );
00744       if( !log_file )
00745         Error( true, "Couldn't open replay file",
00746                "ArenaReplay::set_filenames" );
00747       log_from_stdin = false;
00748     }
00749   else
00750     {
00751       log_file.attach( STDIN_FILENO );
00752       if( !log_file )
00753         Error( true, "Couldn't attach to STDIN",
00754                "ArenaRealTime::set_filenames" );
00755       log_from_stdin = true;
00756     }
00757 
00758   if( message_fname == "" )
00759     use_message_file = false;
00760   else if( message_fname == "-" || message_fname == "STDOUT" )
00761     {
00762       use_message_file = true;
00763       message_file.attach( STDOUT_FILENO );
00764     }
00765   else
00766     {
00767       use_message_file = true;
00768       message_file.open( message_fname.chars(), ios::out, S_IRUSR | S_IWUSR );
00769       if( !message_file )
00770         {
00771           Error( false, "Couldn't open message file. Message file disabled",
00772                  "ArenaRealTime::set_filenames" );
00773           use_message_file = false;
00774         }
00775       
00776     }
00777 
00778   statistics_file_name = statistics_fname;
00779   option_file_name = option_fname;
00780   */
00781 }
00782 
00783 // Changes game when replaying to fast forward, rewind or normal speed
00784 void
00785 ArenaReplay::change_speed( const bool forward, const bool fast )
00786 {
00787   if( fast_forward_factor < 0 )
00788     {  // Parse current time_index in forward direction
00789       fast_forward_factor = 1.0;  
00790       step_forward( 0, true );
00791     }
00792 
00793   if( !fast || log_from_stdin )
00794     {
00795       fast_forward_factor = 1.0;
00796     }
00797   else if( forward )
00798     {
00799       fast_forward_factor = the_opts.get_d( OPTION_FAST_FORWARD_FACTOR );
00800     }
00801   else
00802     {
00803       fast_forward_factor = -the_opts.get_d( OPTION_FAST_FORWARD_FACTOR );
00804       step_forward( 0, true );
00805     }
00806 }
00807 
00808 // Changes the game or sequence number. Mainly after pressing one of
00809 // the replay buttons in the control panel.
00810 void
00811 ArenaReplay::change_game( const int inc_game, const int inc_seq )
00812 {
00813   int temp_game = game_nr;
00814   int temp_seq = sequence_nr;
00815 
00816   temp_game += inc_game;
00817   temp_seq += inc_seq;
00818 
00819   if( inc_seq == 0 )
00820     {
00821     if( temp_game >= games_per_sequence + 1 )
00822       {
00823         temp_game -= games_per_sequence;
00824         temp_seq++;
00825       }
00826     else if( temp_game <= 0 )
00827       {
00828         temp_game += games_per_sequence;
00829         temp_seq--;
00830       }
00831     }
00832   else if( inc_seq < 0 )
00833     temp_game = games_per_sequence;
00834   else if( inc_seq > 0 )
00835     temp_game = 1;
00836 
00837   if( temp_seq < 1 || temp_seq > sequences_in_tournament )
00838     return;
00839 
00840   //  cout << "Seq: " << temp_seq << "  Game: " << temp_game
00841   //       << "  game_pos: " << game_position_in_log[temp_seq-1][temp_game-1] << endl;
00842 
00843   log_file.seekg( game_position_in_log[temp_seq-1][temp_game-1] );
00844 
00845   //  cout << log_file.tellg() << " " << (char)log_file.peek() << endl;
00846 }
00847 
00848 
00849 // returns true if the step forward succeded
00850 bool   
00851 ArenaReplay::step_forward( const int n_o_steps, const bool clear_time )
00852 {
00853   if( !log_from_stdin )
00854     {
00855       int index = find_streampos_for_time( current_replay_time );
00856       
00857       index += n_o_steps;
00858 
00859       //      cout << "Stepping to index: " << index << endl;
00860       
00861       if( index >= 0 && index <= max_time_infos && time_position_in_log[index].pos > 0 )
00862         {
00863           double last_replay_time = current_replay_time;
00864           current_replay_time = time_position_in_log[index].time;
00865           log_file.seekg( time_position_in_log[index].pos );
00866 
00867           if( clear_time )
00868             {
00869               total_time = time_position_in_log[index].time + 0.00001;
00870               update_timer(0.0);
00871               parse_this_time_index();
00872             }  
00873           if( n_o_steps != 0 )
00874             move_shots_no_check( current_replay_time - last_replay_time );
00875 
00876 #ifndef NO_GRAPHICS
00877           if( !no_graphics && n_o_steps != 0 )
00878             {
00879               the_gui.get_arenawindow_p()->draw_moving_objects( true );
00880               the_gui.get_scorewindow_p()->set_window_title();
00881               if( !log_from_stdin )
00882                 controlwindow_p->set_progress_time( current_replay_time );
00883             }
00884 #endif
00885 
00886           return true;
00887         }
00888       else
00889         return false;
00890     }
00891   return true;
00892 }
00893 
00894 void
00895 ArenaReplay::step_forward_or_backward( const bool forward )
00896 {
00897   double fff_tmp = fast_forward_factor;
00898   if( forward )
00899     step_forward( 1, true );
00900   else
00901     {
00902       fast_forward_factor = -1.0;
00903       step_forward( 0, true );
00904       step_forward( -1, true);
00905       fast_forward_factor = 1.0;
00906       step_forward( 0, true );
00907     }
00908 
00909   fast_forward_factor = fff_tmp;
00910 }
00911 
00912 void
00913 ArenaReplay::change_replay_time( const double time )
00914 {
00915   if( !log_from_stdin && abs(time - current_replay_time) > 0.001 )
00916     {
00917       int index = find_streampos_for_time( time );
00918       
00919       index -= 1; // ??
00920 
00921       //      cout << "Changed to index: " << index << endl;
00922       
00923       if( index >= 0 && index <= max_time_infos && time_position_in_log[index].pos > 0 )
00924         {
00925           current_replay_time = time_position_in_log[index].time;
00926           total_time = time_position_in_log[index].time + 0.00001;
00927           update_timer(0.0);
00928           log_file.seekg( time_position_in_log[index].pos );
00929           recreate_lists();
00930         }
00931     }  
00932 }
00933 
00934 // Recreates the object_list[] array to hold what is correct for the current
00935 // time, not the old time.
00936 void
00937 ArenaReplay::recreate_lists()
00938 {
00939   // Kill och awaken robots that have died or began to live again.
00940   {
00941     ListIterator<Shape> li;
00942     for( object_lists[ROBOT].first(li); li.ok(); li++ )
00943       {
00944         Robot* robotp = (Robot*) li();
00945         int id = robotp->get_id();
00946         object_pos_info_t* info;
00947         if( NULL != (info = find_object_in_log( ROBOT,id ) ) )
00948         {
00949           if( info->end_time < current_replay_time && robotp->is_alive() )
00950             {
00951               robotp->die(); // TODO: Something more here?
00952               robotp->change_energy( -robotp->get_energy() );
00953             }
00954           else if( info->end_time > current_replay_time && !(robotp->is_alive()) )
00955             {
00956               robotp->live(); // TODO: Something more here?
00957             }
00958         }
00959         else
00960           Error(false, "Robot in object_list is not in object_positions_in_log",
00961                 "ArenaReplay::recreate_lists");
00962       }
00963   }
00964 
00965   // Kill all Shots, mines and cookies that are in the object_list
00966   // Create new shots for the new time.
00967   {
00968     for( int type = SHOT; type <= MINE; (type == SHOT ? type+=2 : type++)  )
00969       {
00970         ListIterator<Shape> li;
00971         for( object_lists[type].first(li); li.ok(); li++ )
00972           switch( type )
00973             {
00974             case SHOT:
00975               ((Shot*)li())->die();
00976               object_lists[SHOT].remove(li);
00977               break;
00978             case COOKIE:
00979               ((Cookie*)li())->die();
00980               object_lists[COOKIE].remove(li);
00981               break;
00982             case MINE:
00983               ((Mine*)li())->die();
00984               object_lists[MINE].remove(li);
00985               break;
00986             }
00987       }
00988 
00989     ListIterator<object_pos_info_t> li;
00990     for( object_positions_in_log.first(li); li.ok(); li++ )
00991       {
00992         object_pos_info_t* info = li();
00993         if( current_replay_time > info->start_time &&
00994             current_replay_time < info->end_time )
00995           {
00996             switch( info->obj )
00997               {
00998               case SHOT:
00999                 object_lists[SHOT].insert_last
01000                   ( new Shot( info->pos + (current_replay_time - info->start_time)
01001                               * info->vel, info->vel, 0, info->id ) );
01002                 break;
01003               case COOKIE:
01004                 object_lists[COOKIE].insert_last
01005                   ( new Cookie( info->pos, 0.0, info->id ) );
01006                 break;
01007               case MINE:
01008                 object_lists[MINE].insert_last
01009                   ( new Mine( info->pos, 0.0, info->id ) );
01010                 break;
01011               case ROBOT:
01012                 break;
01013               default:
01014                 Error(true,
01015                       "Variable 'type' has a forbidden value. Should never happen",
01016                       "ArenaReplay::recreate_lists");
01017               }
01018           }
01019       }
01020   }
01021 }
01022 
01023 // Searches the log_file for a line beginning with any of the 
01024 // letters in 'search_letters'.
01025 // The file pointer will be directly after the found letter.
01026 // Returns the letter found, or '?' if none found.
01027 char
01028 ArenaReplay::search_forward( const String& search_letters )
01029 {
01030   if( log_from_stdin )
01031     return '?';
01032 
01033   char letter;
01034   char buffer[400];
01035   
01036 
01037   while( !log_file.eof() )
01038     {
01039       log_file.clear();
01040 
01041       log_file >> letter;   // check first letter of line
01042       if( search_letters.find( letter ) != -1 )
01043         {
01044           return letter;
01045         }
01046 
01047       log_file >> ws;
01048       log_file.get( buffer, 400, '\n' );  // go to next line
01049     }
01050   
01051   return '?';
01052 }
01053 
01054 // Similar to the previous function, but the argument is a list
01055 // of strings (string lengths between 1 and 16) to serach for.
01056 // Returns the string found, or the empty string if none found.
01057 String
01058 ArenaReplay::search_forward( const List<String>& search_strings )
01059 {
01060   if( log_from_stdin ) return "";
01061 
01062   bool found = false;
01063   char buffer[400];
01064   ListIterator<String> li;
01065   int i;
01066   int read_letters;
01067   char letter[16];
01068 
01069  while( !log_file.eof() )
01070     {
01071       log_file.clear();
01072 
01073       read_letters = 0;
01074       for( search_strings.first(li); li.ok() && !found; li++ )
01075         {
01076           found = true; 
01077           for( i=0; i < li()->get_length() && found; i++ )
01078             {
01079               if( read_letters < i+1 )
01080                 {
01081                   log_file >> letter[read_letters];
01082                   read_letters++;
01083                 }
01084               if( (*li())[i] != letter[i] ) found = false;
01085             }
01086 
01087           if( found ) return *li();
01088         }      
01089       
01090       log_file.get( buffer, 400, '\n' );  // go to next line
01091     }
01092   
01093   return "";
01094 }
01095 
01096 
01097 String
01098 ArenaReplay::search_backwards( const String& search_letters )
01099 {
01100  /* if( log_from_stdin )
01101     return "";
01102 
01103   char letter='?';
01104   while( search_letters.find( letter ) == -1 && log_file.tellg() != 0 )
01105     {
01106       beginning_of_prev_line();
01107       letter = log_file.peek();
01108     }
01109 
01110   if( log_file.tellg() == 0 )
01111     return "";
01112   else
01113     {
01114       char buffer[400];
01115       log_file.get( buffer, 400, '\n' );
01116       beginning_of_current_line();
01117       return (String) buffer;
01118     }
01119 
01120  
01121   */
01122   return "";
01123 }
01124 
01125 void
01126 ArenaReplay::beginning_of_prev_line()
01127 {
01128  /* char letter='?';
01129   bool already_found_one_line = false;
01130   while( ( letter != '\n' || !already_found_one_line ) &&
01131          log_file.tellg() != 0 )
01132     {
01133       log_file.seekg( -1, ios::cur );
01134       letter = log_file.peek();
01135       if( letter == '\n' && !already_found_one_line )
01136         {
01137           already_found_one_line = true;
01138           letter = '?';
01139         }
01140     }
01141   log_file.get( letter ); */
01142 }
01143 
01144 void
01145 ArenaReplay::beginning_of_current_line()
01146 {
01147   /*
01148   char letter='?';
01149   while( letter != '\n' && log_file.tellg() != 0 )
01150     {
01151       log_file.seekg( -1, ios::cur );
01152       letter = log_file.peek();
01153     }
01154   log_file.get( letter );
01155   */
01156 }
01157 
01158 void
01159 ArenaReplay::make_statistics_from_file()
01160 {
01161  /*
01162   List<String> str_list;
01163   str_list.insert_last( new String("DR") );
01164   str_list.insert_last( new String("L") );
01165   str_list.insert_last( new String("G") );
01166   str_list.insert_last( new String("T") );
01167   str_list.insert_last( new String("H") );
01168 
01169   ListIterator<Shape> li;             
01170   double points_received;
01171   int pos_this_game, object_id;
01172   char buffer[400];
01173   String letters;
01174 
01175   streampos old_pos = log_file.tellg();
01176 
01177   while(  (letters = search_forward( str_list ))  != "" )
01178     {
01179       switch( letters[0] )
01180         {
01181         case 'D':
01182           log_file >> object_id >> points_received >> pos_this_game;
01183             
01184           find_object_by_id( object_lists[ROBOT], li, object_id );
01185 
01186           if( !li.ok() ) Error(true, "Dying robot not in list", 
01187                                "ArenaReplay::make_statistics_from_file");
01188           ((Robot*)li())->set_stats( points_received, pos_this_game, 
01189                                      current_replay_time, true);
01190           break;
01191             
01192         case 'L':
01193           {
01194             int robot_id;
01195             char robot_colour[7];
01196             char name[200];
01197             log_file >> robot_id >> ws;
01198             log_file.get( robot_colour, 7, ' ');
01199             long int col = str2hex( (String)robot_colour );
01200             log_file >> ws;
01201             log_file.get( name, 200, '\n' );
01202             Robot* robotp = new Robot( robot_id, col, (String)name );
01203             object_lists[ROBOT].insert_last(robotp); // array better?
01204             all_robots_in_tournament.insert_last(robotp); // used by statistics
01205           }
01206           break;
01207             
01208         case 'G':
01209           {
01210             streampos temp_pos = log_file.tellg() - 1;
01211             log_file >> sequence_nr >> game_nr;
01212             game_position_in_log[sequence_nr-1][game_nr-1] = temp_pos;
01213           }
01214           break;
01215             
01216         case 'T':
01217           log_file >> current_replay_time;
01218           break;
01219 
01220         case 'H':
01221           if( game_position_in_log != NULL )
01222             {
01223               log_file.seekg( old_pos );
01224               return;
01225             }
01226               
01227           log_file >> games_per_sequence >> robots_per_game 
01228                    >> sequences_in_tournament >> number_of_robots;
01229               
01230           game_position_in_log = new streampos*[sequences_in_tournament];
01231 
01232           for( int i=0; i<sequences_in_tournament; i++ )
01233               game_position_in_log[i] = new streampos[games_per_sequence];
01234               
01235           break;
01236           
01237         default:
01238           Error(true, "Wrong log line found", 
01239                 "ArenaReplay::make_statistics_from_file");
01240           break;
01241         }             
01242       log_file.get( buffer, 400, '\n' );
01243     }
01244 
01245   log_file.seekg( old_pos );
01246   log_file.clear();
01247 
01248   current_replay_time = 0;
01249 */
01250 }
01251 
01252 void
01253 ArenaReplay::get_time_positions_in_game()
01254 {  
01255   /*
01256   String letter_list = "TGHSMCDR";
01257   char letter;
01258   char buffer[400];
01259   int time_pos_index = 0;
01260   float cur_time;
01261   if( time_position_in_log != NULL ) delete [] time_position_in_log;
01262 
01263   time_position_in_log = new time_pos_info_t[max_time_infos];
01264   object_positions_in_log.delete_list();
01265 
01266   for(int i=0; i<max_time_infos; i++) 
01267     {
01268       time_position_in_log[i].pos = -1;
01269       time_position_in_log[i].time = 0.0;
01270     }
01271 
01272   streampos old_pos = log_file.tellg();
01273 
01274   //  cout << "get_time_positions_in_game" << endl;
01275 
01276   while(  (letter = search_forward( letter_list ))  != '?' )
01277     {
01278       switch( letter )
01279         {
01280         case 'T':
01281           time_position_in_log[time_pos_index].pos = log_file.tellg() - 1;
01282           log_file >> cur_time;
01283           time_position_in_log[time_pos_index].time = cur_time;
01284               
01285           time_pos_index++;
01286           if( time_pos_index >= max_time_infos )
01287             Error(false, "Too many time-info entries", 
01288                   "ArenaReplay::get_time_positions_in_game");
01289 
01290           break;
01291 
01292         case 'S':
01293           {
01294             int shot_id;
01295             double x, y, dx, dy;
01296             log_file >> shot_id >> x >> y >> dx >> dy;
01297             object_positions_in_log.insert_last
01298               ( new object_pos_info_t( SHOT, shot_id, Vector2D( x,y ),
01299                                        cur_time, the_opts.get_d( OPTION_TIMEOUT ),
01300                                        Vector2D( dx,dy ) ) );
01301           }
01302           break;
01303 
01304         case 'M':
01305           {
01306             int mine_id;
01307             double x, y;
01308             log_file >> mine_id >> x >> y;
01309             object_positions_in_log.insert_last
01310               ( new object_pos_info_t( MINE, mine_id, Vector2D( x,y ),
01311                                        cur_time, the_opts.get_d( OPTION_TIMEOUT ) ) );
01312           }
01313           break;
01314         case 'C':
01315           {
01316             int cookie_id;
01317             double x, y;
01318             log_file >> cookie_id >> x >> y;
01319             
01320             object_positions_in_log.insert_last
01321               ( new object_pos_info_t( COOKIE, cookie_id, Vector2D( x,y ),
01322                                        cur_time, the_opts.get_d( OPTION_TIMEOUT ) ) );
01323           }
01324           break;
01325 
01326         case 'R':
01327           {
01328             int robot_id;
01329             double x, y, temp;
01330             log_file >> robot_id >> x >> y >> temp >> temp >> temp >> temp;
01331 
01332             if( NULL == ( find_object_in_log( ROBOT, robot_id ) ) )
01333               {
01334                 object_positions_in_log.insert_last
01335                   ( new object_pos_info_t( ROBOT, robot_id, Vector2D( x,y ),
01336                                            0, the_opts.get_d( OPTION_TIMEOUT ) ) );
01337                 ListIterator<Robot> li;
01338                 find_object_by_id( all_robots_in_tournament, li, robot_id );
01339                 object_lists[ROBOT].insert_last( li() );
01340               }
01341           }
01342           break;
01343 
01344         case 'D':
01345           {
01346             char obj = '?';
01347             int object_id;
01348             log_file.get( obj );
01349             log_file >> object_id;
01350             object_type object = NOOBJECT;
01351             switch( obj )
01352               {
01353               case 'S':
01354                 object = SHOT;
01355                 break;
01356 
01357               case 'M':
01358                 object = MINE;
01359                 break;
01360               case 'C':
01361                 object = COOKIE;
01362                 break;
01363 
01364               case 'R':
01365                 object = ROBOT;
01366                 break;
01367 
01368               default:
01369                 break;
01370               }
01371             
01372             if( object != NOOBJECT )
01373               {
01374                 object_pos_info_t* obj_info;
01375                 if( NULL != ( obj_info = find_object_in_log( object, object_id ) ) )
01376                   {
01377                     obj_info->end_time = cur_time;
01378                   }
01379                 else
01380                   Error(false, "Dying object not in list: " + String(obj) + " "
01381                         + String(object_id), "ArenaReplay::get_time_positions_in_game");
01382               }
01383           }
01384           break;
01385 
01386         case 'G':
01387         case 'H':
01388           last_time_info = time_pos_index - 1;
01389           log_file.seekg( old_pos );
01390           log_file.clear();
01391           return;
01392           break;
01393           
01394         default:
01395           Error(true, "Wrong log line found", 
01396                 "ArenaReplay::get_time_positions_in_game");
01397           break;
01398               
01399         }
01400       log_file.get( buffer, 400, '\n' );
01401     }
01402 
01403   log_file.seekg( old_pos );
01404   log_file.clear();
01405 
01406   last_time_info = time_pos_index - 1;
01407   */
01408 }
01409 
01410 // Finds the index of time_position_in_log which has the largest time
01411 // smaller than cur_time
01412 int
01413 ArenaReplay::find_streampos_for_time(const float cur_time)
01414 {
01415   // Can be optimized if necessary
01416   for(int i=1; i<max_time_infos && time_position_in_log[i].pos > -1; i++)
01417     if( cur_time < time_position_in_log[i].time )
01418       return i-1;
01419 
01420   Error(false, "Time not found",  "ArenaReplay::find_streampos_for_time");
01421 
01422   return -1;
01423 }
01424 
01425 double
01426 ArenaReplay::get_length_of_current_game()
01427 {
01428   if( time_position_in_log != NULL )
01429     if( last_time_info != 0 )
01430       {
01431         //        cout << "Game length: " << time_position_in_log[last_time_info].time << endl;
01432         return time_position_in_log[last_time_info].time;
01433       }
01434 
01435   //  cout << the_opts.get_d( OPTION_TIMEOUT ) + 1.0 << endl;
01436   return the_opts.get_d( OPTION_TIMEOUT ) + 1.0;
01437 }
01438 
01439 // Returns NULL if the specified object is not found.
01440 ArenaReplay::object_pos_info_t*
01441 ArenaReplay::find_object_in_log( object_type obj, int id )
01442 {
01443   ListIterator<object_pos_info_t> li;
01444   for( object_positions_in_log.first(li); li.ok(); li++ )
01445     {
01446       if( obj == li()->obj && id == li()->id )
01447         return li();
01448     }
01449 
01450   return NULL;
01451 }

Generated on Fri Oct 15 15:47:35 2004 for Real Time Battle by  doxygen 1.3.9.1