AI improvements: better (level dependent) skidding, and using nitro.

git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/trunk/supertuxkart@2582 178a84e3-b1eb-0310-8ba1-8eac791a3b58
This commit is contained in:
hikerstk 2008-12-08 23:23:33 +00:00
parent 582bd86cdd
commit d6ccd9cd1f
5 changed files with 177 additions and 54 deletions

View File

@ -135,10 +135,18 @@ void LinearWorld::update(float delta)
// ------------- do stuff specific to this subtype of race -----
for(unsigned int n=0; n<kart_amount; n++)
for(unsigned int i=0; i<kart_amount; i++)
{
// ---------- update rank ------
if(!m_kart[n]->hasFinishedRace()) updateRacePosition(m_kart[n], m_kart_info[n]);
if(!m_kart[i]->hasFinishedRace())
{
updateRacePosition(m_kart[i], m_kart_info[i]);
// During the last lap update the estimated finish time.
// This is used to play the faster music, and by the AI
if(m_kart_info[i].m_race_lap == race_manager->getNumLaps()-1)
m_kart_info[i].m_estimated_finish =
estimateFinishTimeForKart(m_kart[i]);
}
}
for(unsigned int n=0; n<kart_amount; n++)
{
@ -320,6 +328,16 @@ void LinearWorld::setTimeAtLapForKart(float t, const int kart_id)
{
m_kart_info[kart_id].m_time_at_last_lap=t;
}
//-----------------------------------------------------------------------------
/** Returns the estimated finishing time. Only valid during the last lap!
* \param kart_id Id of the kart.
*/
float LinearWorld::getEstimatedFinishTime(const int kart_id) const
{
assert(m_kart_info[kart_id].m_race_lap == race_manager->getNumLaps()-1);
return m_kart_info[kart_id].m_estimated_finish;
} // getEstimatedFinishTime
//-----------------------------------------------------------------------------
float LinearWorld::getTimeAtLapForKart(const int kart_id) const
{
@ -423,7 +441,7 @@ void LinearWorld::terminateRace()
{
if(!m_kart[i]->hasFinishedRace())
{
const float est_finish_time = estimateFinishTimeForKart(m_kart[i], m_kart_info[i]);
const float est_finish_time = m_kart_info[i].m_estimated_finish;
m_kart[i]->raceFinished(est_finish_time);
} // if !hasFinishedRace
} // for i
@ -437,7 +455,7 @@ void LinearWorld::raceResultOrder( int* order )
}
}
//-----------------------------------------------------------------------------
float LinearWorld::estimateFinishTimeForKart (Kart* kart, KartInfo& kart_info)
float LinearWorld::estimateFinishTimeForKart(Kart* kart)
{
// Estimate the arrival time of any karts that haven't arrived
// yet by using their average speed up to now and the distance
@ -446,6 +464,7 @@ float LinearWorld::estimateFinishTimeForKart (Kart* kart, KartInfo& kart_info)
// higher average speed and therefore finish the race earlier
// than karts further behind), so the position doesn't have to
// be updated to get the correct scoring.
const KartInfo &kart_info = m_kart_info[kart->getWorldKartId()];
float distance_covered = kart_info.m_race_lap * RaceManager::getTrack()->getTrackLength()
+ getDistanceDownTrackForKart(kart->getWorldKartId());
// In case that a kart is rescued behind start line, or ...
@ -535,11 +554,11 @@ void LinearWorld::updateRacePosition ( Kart* kart, KartInfo& kart_info )
// Switch on faster music if not already done so, if the
// first kart is doing its last lap, and if the estimated
// remaining time is less than 30 seconds.
if(!m_faster_music_active &&
if(!m_faster_music_active &&
kart_info.m_race_lap == race_manager->getNumLaps()-1 &&
p==1 &&
useFastMusicNearEnd() &&
estimateFinishTimeForKart( kart, m_kart_info[kart->getWorldKartId()] )-getTime()<30.0f )
p==1 &&
useFastMusicNearEnd() &&
kart_info.m_estimated_finish - getTime() < 30.0f )
{
sound_manager->switchToFastMusic();
m_faster_music_active=true;

View File

@ -29,11 +29,13 @@ class RaceGUI;
*/
struct KartInfo
{
int m_race_lap; // number of finished(!) laps
float m_time_at_last_lap; // time at finishing last lap
float m_lap_start_time; // Time at start of a new lap
int m_track_sector; // index in driveline, special values
// e.g. UNKNOWN_SECTOR can be negative!
int m_race_lap; /**<Number of finished(!) laps. */
float m_time_at_last_lap; /**<Time at finishing last lap. */
float m_lap_start_time; /**<Time at start of a new lap. */
float m_estimated_finish; /**<During last lap only:
* estimated finishing time! */
int m_track_sector; /**<Index in driveline, special values
* e.g. UNKNOWN_SECTOR can be negative!*/
int m_last_valid_sector;
Vec3 m_curr_track_coords;
Vec3 m_last_track_coords;
@ -58,7 +60,7 @@ protected:
void forceRescue(Kart* kart, KartInfo& kart_info, bool shortcut);
void doLapCounting ( KartInfo& kart_info, Kart* kart );
float estimateFinishTimeForKart (Kart* kart, KartInfo& kart_info);
float estimateFinishTimeForKart(Kart* kart);
void updateRacePosition ( Kart* kart, KartInfo& kart_info );
public:
LinearWorld();
@ -79,6 +81,7 @@ public:
int getSectorForKart(const int kart_id) const;
float getDistanceDownTrackForKart(const int kart_id) const;
float getDistanceToCenterForKart(const int kart_id) const;
float getEstimatedFinishTime(const int kart_id) const;
int getLapForKart(const int kart_id) const;
void setTimeAtLapForKart(float t, const int kart_id);
float getTimeAtLapForKart(const int kart_id) const;

View File

@ -28,6 +28,7 @@
//won't be erased the next time the function is called.
#define SHOW_NON_CRASHING_POINT //If defined, draws a green sphere where the
//n farthest non-crashing point is.
#define _WINSOCKAPI_
#include <plib/ssgAux.h>
#endif
@ -72,24 +73,31 @@ DefaultRobot::DefaultRobot(const std::string& kart_name,
m_item_tactic = IT_TEN_SECONDS;
m_max_start_delay = 0.5f;
m_min_steps = 0;
m_skidding_threshold = 4.0f;
break;
case RaceManager::RD_MEDIUM:
m_wait_for_players = true;
m_wait_for_players = true;
m_max_handicap_accel = 0.95f;
m_fallback_tactic = FT_PARALLEL;
m_item_tactic = IT_CALCULATE;
m_max_start_delay = 0.4f;
m_min_steps = 1;
m_fallback_tactic = FT_PARALLEL;
m_item_tactic = IT_CALCULATE;
m_max_start_delay = 0.4f;
m_min_steps = 1;
m_skidding_threshold = 2.0f;
break;
case RaceManager::RD_HARD:
m_wait_for_players = false;
m_wait_for_players = false;
m_max_handicap_accel = 1.0f;
m_fallback_tactic = FT_FAREST_POINT;
m_item_tactic = IT_CALCULATE;
m_max_start_delay = 0.1f;
m_min_steps = 2;
m_fallback_tactic = FT_FAREST_POINT;
m_item_tactic = IT_CALCULATE;
m_max_start_delay = 0.1f;
m_min_steps = 2;
m_skidding_threshold = 1.3f;
break;
}
static int count =0;
m_skidding_threshold = count==0?4.0f: 1.3f;
count = 1-count;
} // DefaultRobot
//-----------------------------------------------------------------------------
@ -155,6 +163,7 @@ void DefaultRobot::update( float delta )
handleItems( delta, steps );
handleRescue( delta );
handleBraking();
handleNitro();
/*And obviously general kart stuff*/
AutoKart::update( delta );
@ -293,6 +302,7 @@ void DefaultRobot::handleSteering()
}
else
{
m_start_kart_crash_direction = 0;
switch( m_fallback_tactic )
{
case FT_FAREST_POINT:
@ -326,8 +336,7 @@ void DefaultRobot::handleSteering()
if (fabsf(steer_angle) < 2.0f*3.1415/180.0f)
steer_angle = 0.f;
m_controls.lr = angleToControl( steer_angle );
m_controls.jump = fabsf(m_controls.lr)>0.99;
setSteering(steer_angle);
} // handleSteering
//-----------------------------------------------------------------------------
@ -462,7 +471,7 @@ void DefaultRobot::handleRescue(const float DELTA)
//TODO: check if we collided against a dynamic object (ej.:kart) or
//against the track's static object.
//The m_crash_time measures if a kart has been crashing for too long
#ifdef RESCUE_IF_CRASHES_WITH_KARTS
m_crash_time += (m_collided && isOnGround()) ? 3.0f * DELTA : -0.25f * DELTA;
if( m_crash_time < 0.0f ) m_crash_time = 0.0f;
@ -472,7 +481,7 @@ void DefaultRobot::handleRescue(const float DELTA)
forceRescue();
m_crash_time = 0.0f;
}
#endif
// check if kart is stuck
if(getSpeed()<2.0f && !isRescue() && !RaceManager::getWorld()->isStartPhase())
@ -490,6 +499,92 @@ void DefaultRobot::handleRescue(const float DELTA)
}
} // handleRescue
//-----------------------------------------------------------------------------
/** Decides wether to use nitro or not.
*/
void DefaultRobot::handleNitro()
{
m_controls.wheelie = false;
// Don't use nitro if the kart doesn't have any, is not on ground,
if(getEnergy()==0 || !isOnGround() || hasFinishedRace() ) return;
// If a parachute or anvil is attached, the nitro doesn't give much
// benefit. Better wait till later.
const bool has_slowdown_attachment =
m_attachment.getType()==ATTACH_PARACHUTE ||
m_attachment.getType()==ATTACH_ANVIL;
if(has_slowdown_attachment) return;
// If the kart is very slow (e.g. after rescue), use nitro
if(getSpeed()<5)
{
m_controls.wheelie = true;
return;
}
// If this kart is the last kart, and we have enough
// (i.e. more than 2) nitro, use it.
// -------------------------------------------------
const unsigned int num_karts = race_manager->getNumKarts();
if(getPosition()== num_karts && getEnergy()>2.0f)
{
m_controls.wheelie = true;
return;
}
// On the last track shortly before the finishing line, use nitro
// anyway. Since the kart is faster with nitro, estimate a 30% time
// decrease.
if(m_world->getLapForKart(getWorldKartId())==race_manager->getNumLaps()-1)
{
float finish = m_world->getEstimatedFinishTime(getWorldKartId());
if( 1.3f*getEnergy() >= finish - m_world->getTime() )
{
m_controls.wheelie = true;
printf("lasp lap --> nitro.\n");
return;
}
}
const float my_dist = m_world->getDistanceDownTrackForKart(getWorldKartId());
// A kart within this distance is considered to be overtaking (or to be
// overtaken).
const float overtake_distance = 10.0f;
for(unsigned int i=0; i<num_karts; i++)
{
Kart *kart = RaceManager::getKart(i);
if(kart==this||kart->isEliminated()) continue; // ignore eliminated karts
float dist = m_world->getDistanceDownTrackForKart(i);
// Kart too far behind to be a risk
if(dist+overtake_distance<my_dist) continue;
// Kart too far ahead to try overtake
if(dist-overtake_distance>my_dist) continue;
// Kart behind might overtake this kart
// ------------------------------------
if(dist<my_dist)
{
// Kart behind is slower than this kart - no need to use nitro
if(kart->getSpeed() < getSpeed()) continue;
// Nitro doesn't give much benefit - better wait and
// see if we can re-overtake once the attachment is gone
m_controls.wheelie = true;
return;
}
// Now there is a kart close ahead, try overtaking
// -----------------------------------------------
if(kart->getSpeed()+5.0f > getSpeed())
{
m_controls.wheelie = true;
}
}
} // handleNitro
//-----------------------------------------------------------------------------
float DefaultRobot::steerToAngle (const size_t SECTOR, const float ANGLE)
{
@ -541,14 +636,12 @@ void DefaultRobot::checkCrashes( const int STEPS, const Vec3& pos )
const Vec3 &VEL = getVelocity();
vel_normal.setValue(VEL.getX(), VEL.getY(), 0.0);
float len=vel_normal.length();
if(len>0.0f)
{
vel_normal/=len;
}
else
{
vel_normal.setValue(0.0, 0.0, 0.0);
}
// If the velocity is zero, no sense in checking for crashes in time
if(len==0) return;
// Time it takes to drive for m_kart_length units.
float dt = m_kart_length / len;
vel_normal/=len;
for(int i = 1; STEPS > i; ++i)
{
@ -563,12 +656,13 @@ void DefaultRobot::checkCrashes( const int STEPS, const Vec3& pos )
{
const Kart* kart = RaceManager::getKart(j);
if(kart==this||kart->isEliminated()) continue; // ignore eliminated karts
const Kart *other_kart = RaceManager::getKart(j);
Vec3 other_kart_xyz = other_kart->getXYZ() + other_kart->getVelocity()*(i*dt);
kart_distance = (step_coord - other_kart_xyz).length_2d();
kart_distance = (step_coord - RaceManager::getKart(j)->getXYZ()).length_2d();
if( kart_distance < m_kart_length + 0.125f * i )
if( getVelocityLC().getY() > RaceManager::getKart(j)->
getVelocityLC().getY() * 0.75f ) m_crashes.m_kart = j;
if( kart_distance < m_kart_length &&
getVelocityLC().getY() > other_kart->getVelocityLC().getY())
m_crashes.m_kart = j;
}
}
@ -592,7 +686,7 @@ void DefaultRobot::checkCrashes( const int STEPS, const Vec3& pos )
center[1] = step_coord[1];
center[2] = pos[2];
sphere->setCenter( center );
sphere->setSize( m_kart_properties->getKartLength() );
sphere->setSize( m_kart_properties->getKartModel()->getLength() );
if( m_sector == Track::UNKNOWN_SECTOR )
{
sgVec4 colour;
@ -696,7 +790,7 @@ void DefaultRobot::findNonCrashingPoint( sgVec2 result )
sgVec3 center;
center[0] = result[0];
center[1] = result[1];
center[2] = m_curr_pos.xyz[2];
center[2] = getXYZ().getZ();
sphere->setCenter( center );
sphere->setSize( 0.5f );
@ -777,18 +871,18 @@ int DefaultRobot::calcSteps()
} // calcSteps
//-----------------------------------------------------------------------------
/** Translates coordinates from an angle(in degrees) to values within the range
* of -1.0 to 1.0 to use the same format as the KartControl::lr variable.
/** Converts the steering angle to a lr steering in the range of -1 to 1.
* If the steering angle is too great, it will also trigger skidding.
*/
float DefaultRobot::angleToControl( float angle ) const
void DefaultRobot::setSteering( float angle )
{
angle = angle / getMaxSteerAngle();
m_controls.jump = fabsf(angle)>m_skidding_threshold;
if(angle > 1.0f) return 1.0f;
else if(angle < -1.0f) return -1.0f;
return angle;
} // angleToControl
if (angle > 1.0f) m_controls.lr = 1.0f;
else if(angle < -1.0f) m_controls.lr = -1.0f;
else m_controls.lr = angle;
} // setSteering
//-----------------------------------------------------------------------------
/** Finds the approximate radius of a track's curve. It needs two arguments,

View File

@ -122,6 +122,12 @@ private:
* deallocated. */
static int m_num_of_track_info_instances;
/** The minimum steering angle at which the AI adds skidding. Lower values
* tend to improve the line the AI is driving. This is used to adjust for
* different AI levels.
*/
float m_skidding_threshold;
int m_sector;
/*Functions called directly from update(). They all represent an action
@ -135,6 +141,7 @@ private:
void handleItems(const float DELTA, const int STEPS);
void handleRescue(const float DELTA);
void handleBraking();
void handleNitro();
/*Lower level functions not called directly from update()*/
float steerToAngle(const size_t SECTOR, const float ANGLE);
@ -145,7 +152,7 @@ private:
float normalizeAngle(float angle);
int calcSteps();
float angleToControl(float angle) const;
void setSteering(float angle);
float getApproxRadius(const int START, const int END) const;
void findCurve();

View File

@ -59,8 +59,8 @@ TrackInfo::DirectionType TrackInfo::computeDirection(int i)
if( diff > M_PI ) diff -= 2*M_PI;
else if( diff < -M_PI ) diff+= 2*M_PI;
const float curve_degree = 15*M_PI/180.0f;
// Consider a difference of up to 5 degrees as 'straight'.
const float curve_degree = 5*M_PI/180.0f;
DirectionType t = DIR_STRAIGHT;
if (diff <-curve_degree)
t = DIR_LEFT;