AITrainer.cpp

Go to the documentation of this file.
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 

Get Racer at SourceForge.net. Fast, secure and Free Open Source software downloads

Generated at Mon Sep 6 00:41:12 2010 by Doxygen version 1.4.7 for Racer version svn335.