00001 00005 /* Copyright © 2010 James Legg. 00006 This program is free software: you can redistribute it and/or modify 00007 it under the terms of the GNU General Public License as published by 00008 the Free Software Foundation, either version 3 of the License, or 00009 (at your option) any later version. 00010 */ 00011 #include "AITrainer.h" 00012 00013 #include <libtrack/StartingPosition.h> 00014 #include <iostream> 00015 #include <Debug.h> 00016 00021 std::string read_line(std::istream & input) 00022 { 00023 std::string line; 00024 std::getline(input, line); 00025 return line; 00026 } 00027 00028 namespace Engine 00029 { 00030 00031 AITrainer::AITrainer(std::string track_filename) 00032 : number_of_cars(30) 00033 , ticks_simulated(2000) 00034 , max_iterations(150) 00035 , track_file(track_filename.c_str()) 00036 , theme_filename(read_line(track_file)) 00037 , theme_file(theme_filename.c_str()) 00038 , theme(theme_file) 00039 , track (track_file, theme) 00040 { 00041 // make the AI graph for the track. 00042 track.update_ai_mesh(); 00043 btDefaultMotionState motion_state(btTransform(btQuaternion(0, 0, 0, 1), 00044 btVector3(0, 0, 0))); 00045 track_shape = track.get_collision_shape(); 00046 btRigidBody::btRigidBodyConstructionInfo 00047 rigid_body_CI(btScalar(0), 00048 &motion_state, 00049 &(*track_shape), 00050 btVector3(0, 0, 0)); 00051 rigid_body_CI.m_restitution = 1.0; 00052 track_body = boost::shared_ptr<btRigidBody>(new btRigidBody(rigid_body_CI)); 00053 00054 // now do the floor shape in the floor world. 00055 floor_shape = track.get_floor_shape(); 00056 rigid_body_CI.m_collisionShape = &(*floor_shape); 00057 floor_body = boost::shared_ptr<btRigidBody>(new btRigidBody(rigid_body_CI)); 00058 00059 starting_scores.resize(number_of_cars); 00060 cars.reserve(number_of_cars); 00061 m_ai_devices.reserve(number_of_cars); 00062 00063 // Add some AI cars 00064 for (unsigned int ai = 0; ai < number_of_cars; ai++) 00065 { 00066 m_ai_devices.push_back(boost::shared_ptr<InputDeviceAI>(new InputDeviceAI())); 00067 } 00068 00069 // Do the simulation 00070 for (iteration = 0; iteration < max_iterations; iteration++) 00071 { 00072 world = new Physics::World(track); 00073 world->get_dynamics_world().addRigidBody(&(*track_body)); 00074 world->get_floor_world().addCollisionObject(&(*floor_body)); 00075 reset_cars(); 00077 simulate(); 00078 rank_cars(); 00079 mutate(); 00080 delete world; 00081 } 00082 // save the ai net for the best car. 00083 m_ai_devices[car_ranks[0]]->save(); 00084 } 00085 00086 void AITrainer::reset_cars() 00087 { 00088 cars.clear(); 00089 const Track::Path & path = track.get_path(); 00090 const Track::PathEdge & edge = path.get_edge(path.get_starting_edge()); 00091 start_point = path.find_start_position(); 00092 path.find_start_plane(start_plane_normal, start_plane_distance); 00093 for (unsigned int ai = 0; ai < number_of_cars; ai++) 00094 { 00095 // Find the starting position for this car. 00096 btTransform initial_transform = 00097 // guess transformation when starting position cannot be found. 00098 edge.get_transform(float(ai) / float(number_of_cars)); 00099 // find the starting position track attachment for this car's rank. 00100 for (std::vector<boost::shared_ptr<Track::TrackAttachment> >::const_iterator it = edge.get_attachments().begin(); 00101 it != edge.get_attachments().end(); 00102 it++) 00103 { 00104 const Track::StartingPosition * pos = dynamic_cast<const Track::StartingPosition *>(&(**it)); 00105 if (pos) 00106 { 00107 if (pos->get_rank() == ai) 00108 { 00109 initial_transform = pos->get_global_transform(); 00110 } 00111 } 00112 } 00113 // raise the car above the track slightly. 00114 initial_transform.setOrigin(initial_transform(btVector3(0.0, 0.0, 0.2))); 00115 cars.push_back(new GameObjects::Car(*world, 00116 initial_transform, 00117 &(*m_ai_devices[ai]), 00118 ai%2,// ai alterenatly uses both cars 00119 start_plane_normal, 00120 start_plane_distance, 00121 start_point)); 00122 starting_scores[ai] = car_score(ai); 00123 } 00124 } 00125 00126 void AITrainer::simulate() 00127 { 00128 // simulate some game time 00129 world->update(ticks_simulated * 10); 00130 } 00131 00132 void AITrainer::rank_cars() 00133 { 00134 std::map<btScalar, unsigned int> scores; 00135 for (unsigned int i = 0; i < number_of_cars; i++) 00136 { 00137 btScalar score = car_score(i) - starting_scores[i]; 00138 // don't let two vechicles with the same score mess ordering up. 00139 while (scores.find(score) != scores.end()) 00140 { 00141 if (score == 0) score = -1; else score *= (31.0/32.0); 00142 } 00143 // DEBUG_MESSAGE("Car " << i << " scored " << score); 00144 scores.insert(std::pair<btScalar, unsigned int>(score, i)); 00145 } 00146 // was insertion sorted, but we want highest score first. 00147 car_ranks.resize(number_of_cars - 1); 00148 unsigned int rank = number_of_cars - 1; 00149 for (std::map<btScalar, unsigned int>::iterator it = scores.begin(); 00150 it != scores.end(); 00151 it++) 00152 { 00153 car_ranks[rank] = it->second; 00154 rank--; 00155 } 00156 assert(rank == (unsigned int) -1); 00157 if (!(iteration % 5)) 00158 { 00159 std::cout << "Iteration " << iteration << " score " << scores.rbegin()->first << '\n'; 00160 } 00161 } 00162 00163 btScalar AITrainer::car_score(unsigned int i) 00164 { 00165 // score first based on distance traveled around the course. 00166 btScalar score = cars[i]->get_lap_distance() + 00167 (cars[i]->get_lap() * track.get_lap_length() + 100.0); 00168 if (cars[i]->get_finished()) 00169 { 00170 // bonus meter per tick for finishing all 3 laps before the end of 00171 // the simulation. 00172 score += ticks_simulated - cars[i]->get_finish_time(); 00173 DEBUG_MESSAGE("Car " << i << " finished 3 laps in " << cars[i]->get_finish_time() << " ticks."); 00174 } else if (cars[i]->get_disqualified()) 00175 { 00176 // Penalty: deduct a meter per tick for falling off before the end 00177 // of the simulation. 00178 score -= ticks_simulated - cars[i]->get_finish_time(); 00179 DEBUG_MESSAGE("Car " << i << " was disqualified in " << cars[i]->get_finish_time() << " ticks."); 00180 } 00181 return score; 00186 } 00187 00188 void AITrainer::mutate() 00189 { 00190 // Leave cars ranked 0-4 alone. 00191 // cars ranked 5-9 get a little variation. 00192 for (unsigned int r = 5; r < 10; r++) 00193 { 00194 m_ai_devices[car_ranks[r]]->mutate(); 00195 } 00196 // the rest gets reconstructed from two of the top 5 00197 for (unsigned int r = 10; r < number_of_cars; r++) 00198 { 00199 // pick two different classes to cross 00200 /*int p1 = rand() / (RAND_MAX / 5); 00201 int p2 = p1; 00202 while (p2 == p1) 00203 { 00204 p2 = rand() / (RAND_MAX / 5); 00205 }*/ 00206 00207 // cross a unique pair from the top 5 00208 int p1 = (r-10)%5; 00209 int p2 = (r-10)/4; 00210 if (p2 >= p1) p2++; 00211 assert(number_of_cars == 30); 00212 m_ai_devices[car_ranks[r]]->cross(*m_ai_devices[car_ranks[p1]], 00213 *m_ai_devices[car_ranks[p2]]); 00214 } 00215 } 00216 00217 AITrainer::~AITrainer() 00218 { 00219 } 00220 00221 } 00222
Generated at Mon Sep 6 00:41:12 2010 by Doxygen version 1.4.7 for Racer version svn335.