speed-dreams/src/drivers/usr/src/driver.cpp

1728 lines
44 KiB
C++
Executable File

/***************************************************************************
file : driver.cpp
created : 2006-08-31 01:21:49 UTC
copyright : (C) Daniel Schellhammer
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include <teammanager.h>
#include "driver.h"
#include "MyParam.h"
#include "Utils.h"
#include <sstream>
#include <iostream>
#include <string>
#include <cstdio>
#include <portability.h>
//#define TIME_ANALYSIS
#ifdef TIME_ANALYSIS
#include "Timer.h"
#endif
#define RANDOM_SEED 0xfded
#define RANDOM_A 1664525
#define RANDOM_C 1013904223
#define SECT_SKILL "skill"
#define PRV_SKILL_LEVEL "level"
#define PRV_SKILL_AGGRO "aggression"
Driver::Driver(int index) :
INDEX(index),
mRain(0),
driver_aggression(1.0),
mGarage(false),
mFrontCollMargin(6.0 - driver_aggression),
mOvtMargin(2.0 - driver_aggression)
{
// Names assigned in constructor for VS 2013 compatibility
mFlagNames.push_back("STATE_CHANGE");
mFlagNames.push_back("DRIVING_FAST");
mFlagNames.push_back("FRICT_LR");
mFlagNames.push_back("COLL");
mFlagNames.push_back("WAIT");
mFlagNames.push_back("LET_PASS");
mFlagNames.push_back("CATCH");
mFlagNames.push_back("OVERTAKE");
mFlagNames.push_back("FAST_BEHIND");
}
//==========================================================================*
// Get Team info
//--------------------------------------------------------------------------*
void Driver::TeamInfo(tCarElt* car, tSituation* situation)
{
//RtTeamManagerShowInfo();
mTeamIndex = RtTeamManagerIndex(car, track, situation);
LogUSR.info("#Team index = %i\n", mTeamIndex);
RtTeamManagerDump();
}
void Driver::InitTrack(tTrack* Track, void* carHandle, void** carParmHandle, tSituation* situation)
{
LogUSR.debug(".......... %s Driver initrack .........\n", mDriverName);
track = Track;
const int BUFSIZE = 1024;
char buffer[BUFSIZE];
char carName[BUFSIZE];
/*------------------------------------------------------*/
/* Load a custom setup if one is available. */
/*------------------------------------------------------*/
// Get a pointer to the first char of the track filename.
char trackname[100];
std::string tName;
std::string cName;
std::string rName;
strncpy( trackname, strrchr(track->filename, '/') + 1, sizeof(trackname) - 1);
*strrchr(trackname, '.') = '\0';
LogUSR.info( " # USR trackName: '%s'\n", trackname );
tName = trackname;
if (strcmp(trackname, "garage") == 0)
mGarage = true;
// Setup for this robot
//void *newParmHandle;
*carParmHandle = NULL;
//newParmHandle = NULL;
const char *car_sect = SECT_GROBJECTS "/" LST_RANGES "/" "1";
strncpy(carName, GfParmGetStr(carHandle, car_sect, PRM_CAR, ""), sizeof(carName) - 1);
char *p = strrchr(carName, '.');
if (p)
*p = '\0';
cName = carName;
rName = mDriverName;
LogUSR.info( " # USR carName: '%s'\n", carName );
Meteorology(track);
std::string mData(std::string(GetDataDir()) + "drivers/" + rName + "/" + cName + "/");
mDataDir = mData;
// Assign to carParmHandle my parameters file handle, it will later be merged with carHandle by TORCS
MyParam param(carParmHandle, mDataDir, Track->internalname, weathercode);
LogUSR.info(" # Driver %s call param \n", mDriverName);
// Read the parameters
mTestPath = (PathType)((int)param.getNum("private", "test line")); // (int) for VS 2013 compatibility
//mMsgOn = (int)param.getNum("private", "message on"); // (int) for VS 2013 compatibility
mDataLogOn = (int)param.getNum("private", "data log on");
mPitDamage = (int)param.getNum("private", "pitdamage");
mPitGripFactor = param.getNum("private", "pitgripfactor");
mPitEntryMargin = param.getNum("private", "pitentrymargin");
mPitEntrySpeed = param.getNum("private", "pitentryspeed");
mPitExitSpeed = param.getNum("private", "pitexitspeed");
mPitTest = (int)param.getNum("private", "pittest");
mSegLen = param.getNum("private", "seglen");
mClothFactor = param.getNum("private", "clothoidfactor");
mVMaxK = param.getNum("private", "vmaxk");
mVMaxKFactor = param.getNum("private", "vmaxkfactor");
driver_aggromult = param.getNum("private", "Aggression Mult");
// Track and car-specific Aggression multiplier,
// to make certain car classes overtake more aggressively
if (driver_aggromult == NULL)
{
driver_aggromult = 0.0;
}
//get "angry" with increasing time
double anger_limit;
anger_limit = 0;
if (mVMaxK == 0.0)
mVMaxK = 0.0018;
if (mVMaxKFactor == 0.0)
mVMaxKFactor = 0.9;
mCar.readPrivateSection(param);
mCar.readVarSpecs(param);
mCar.readConstSpecs(carHandle);
// Init my track
mTrack.init(track, mSegLen);
// Set initial fuel
double distance = 0.0;
if (mPitTest > 0)
distance = mTrack.length() * 2 + 0.3;
else
distance = 1.05 * situation->_totLaps * mTrack.length();
LogUSR.info("distance fuel = %.3f\n", distance);
double startfuel = mCar.calcFuel(distance);
LogUSR.info("Start fuel : %.3f\n", startfuel);
double initFuel = param.getNum("private", "max fuel");
if (initFuel > 1.0)
startfuel = MIN(startfuel, initFuel);
param.setNum(SECT_CAR, PRM_FUEL, startfuel);
bool compound = mCar.HASCPD;
if (compound)
{
tdble temp = track->local.airtemperature;
if (temp < 13.0 || situation->_totLaps * (double)track->length < 57800.0)
{
param.setNum(SECT_TIRESET, PRM_COMPOUNDS_SET, 1);
mCar.setTireMu(1);
LogUSR.info("Compounds choice SOFT !!!\n");
}
else if (temp < 25.0 || situation->_totLaps * (double)track->length < 171000.0)
{
param.setNum(SECT_TIRESET, PRM_COMPOUNDS_SET, 2);
mCar.setTireMu(2);
LogUSR.info("Compounds choice MEDIUM !!!\n");
}
else
{
param.setNum(SECT_TIRESET, PRM_COMPOUNDS_SET, 3);
mCar.setTireMu(3);
LogUSR.info("Compounds choice HARD !!!\n");
}
mRain = track->local.rain;
if (mRain > 0 && mRain < 3)
{
param.setNum(SECT_TIRESET, PRM_COMPOUNDS_SET, 4);
mCar.setTireMu(4);
LogUSR.info("Compounds choice WET !!!\n");
}
else if (mRain > 2)
{
param.setNum(SECT_TIRESET, PRM_COMPOUNDS_SET, 5);
mCar.setTireMu(5);
LogUSR.info("Compounds choice EXTREM WET !!!\n");
}
}
// load the global skill level, range 0 - 10
void *skillHandle = GfParmReadFileLocal("config/raceman/extra/skill.xml", GFPARM_RMODE_REREAD);
if(!skillHandle)
{
snprintf(buffer, sizeof(buffer), "%sconfig/raceman/extra/skill.xml", GetDataDir());
skillHandle = GfParmReadFile(buffer, GFPARM_RMODE_REREAD);
}//if !skillHandle
if (skillHandle)
{
SkillGlobal = GfParmGetNum(skillHandle, SECT_SKILL, PRV_SKILL_LEVEL, (char *) NULL, 30.0f);
GfParmReleaseHandle(skillHandle);
}
SkillGlobal = MAX(0.7, 1.0 - 0.5 * SkillGlobal / 10.0);
LogUSR.info(" # Global Skill: %.3f\n", SkillGlobal);
//load the driver skill level, range 0 - 1
SkillDriver = 0.0f;
snprintf(buffer, sizeof(buffer), "%sdrivers/%s/%d/skill.xml", GetDataDir(), mDriverName, INDEX);
LogUSR.info("Path skill driver: %s\n", buffer);
skillHandle = GfParmReadFile(buffer, GFPARM_RMODE_STD);
if (skillHandle)
{
SkillDriver = GfParmGetNum(skillHandle, SECT_SKILL, PRV_SKILL_LEVEL, (char *) NULL, 0.0);
driver_aggression = (double)GfParmGetNum(skillHandle, SECT_SKILL, PRV_SKILL_AGGRO, (char *)NULL, 0.0);
GfParmReleaseHandle(skillHandle);
LogUSR.info( "# driver skill: %.2f - driver agression: %.3f\n", SkillDriver, driver_aggression);
SkillDriver = MAX(0.95, 1.0 - 0.05 * SkillDriver);
//driver_aggression /= driver_aggression;
driver_aggression = MIN(1.0, MAX(0.7, 0.99 + driver_aggression));
LogUSR.info(" # Global skill = %.2f - driver skill: %.2f - driver agression: %.3f\n", SkillGlobal, SkillDriver, driver_aggression);
}
else
LogUSR.info("Couldn't load : %s\n", buffer);
}
void Driver::NewRace(tCarElt* car, tSituation* situation)
{
LogUSR.info("********** %s : NewRace() **********\n", mDriverName);
mSituation = situation;
mSimTime = -1.0;
initVars();
TeamInfo(car, mSituation);
mCar.init(car, &mTrack);
mPit.init(mTrack.torcsTrack(), situation, &mCar, mPitDamage, mPitGripFactor, mPitEntryMargin, mRain);
// Create paths
mPath.clear();
for (unsigned i = 0; i < sizeof(PathNames)/sizeof(PathNames[0]); i++)
{
mPath.push_back(Path(&mTrack, mDataDir, mClothFactor, mVMaxK, mVMaxKFactor, (PathType)i));
}
mOpps.init(mTrack.torcsTrack(), situation, &mCar, &mPath[PATH_O]);
// Get mu factors
mMuFactors.read(mDataDir, mTrack.name());
// Create path states
mPathState.clear();
for (unsigned i = 0; i < sizeof(PathNames)/sizeof(PathNames[0]); i++)
{
mPathState.push_back(PathState(&mPath[i], &mCar, &mMuFactors));
}
// Messages
//mMsg.init(mLocalDir, &mCar);
// Data log
mDataLog.init(mLocalDir, mCar.car()->_name);
mDataLog.add("time", &mSimTime, 1.0);
mDataLog.add("fs", &mFromStart, 1.0);
mDataLog.add("10*LRtarget", &mLRTargetPortion, 10.0);
mDataLog.add("10*pathoffs", &mPathOffs, 10.0);
mDataLog.add("vmax", &mMaxspeed, 1.0);
mDataLog.add("v", &mCar.mSpeed, 1.0);
mDataLog.add("acc", &mAccelPedal, 100.0);
mDataLog.add("brake", &mBrakePedal, -100.0);
mDataLog.add("steer", &mSteer, 100.0);
mDataLog.add("steeryaw", &mYawSteer, 270.0);
mDataLog.add("steeryawrate", &mYawRateSteer, 270.0);
mDataLog.add("steercurv", &mCurvSteer, 270.0);
mDataLog.add("steeroffs", &mOffsSteer, 270.0);
mDataLog.add("steeroffsderiv", &mOffsDerivSteer, 270.0);
mDataLog.add("10*slip", &mCar.mSlip, 10.0);
mDataLog.add("sideslip", &mCar.mSideSlip, 1.0);
mDataLog.add("10*toMid", &mCar.mToMiddle, 10.0);
LogUSR.info("__________ %s initialized __________\n", mDriverName);
}
void Driver::Drive()
{
#ifdef TIME_ANALYSIS
Timer tmr;
#endif
if (mGarage)
return;
//mMsg.clearPanel();
updateTime();
updateBasics();
updateOpponents();
updatePathState(); // avg. 200usec on e-track-1 (debug build)
updateOvertakePath();
updateDrivingFast();
updateLetPass();
updateOnCollision();
calcStateAndPath();
calcOffsetAndYaw();
calcMaxspeed();
// std::cout << mFromStart << " vmax: " << 3.6*mMaxspeed << " offs: " << mPathOffs << std::endl;
setControls();
printInfos();
setPrevVars();
//std::cout << mCar.car()->_name << " dist=" << mCar.car()->_distFromStartLine << std::endl;
#ifdef TIME_ANALYSIS
double usec = 1000 * 1000 * tmr.elapsed();
std::cout << "usec " << usec << std::endl;
#endif
}
int Driver::PitCmd() // handle pitstop
{
mPit.pitCommand();
return ROB_PIT_IM; // ready to be serviced
}
void Driver::EndRace()
{
// This is never called by TORCS! Don't use it!
}
void Driver::Shutdown()
{
/*if (mMsgOn)
{
mMsg.write();
}*/
if (mDataLogOn)
{
mDataLog.write();
}
}
void Driver::updateTime()
{
mDeltaTime = mSituation->currentTime - mSimTime;
mSimTime = mSituation->currentTime;
}
void Driver::updateBasics()
{
// std::cout << mFromStart << " k " << mPath[mDrvPath].curvature(mFromStart) << " kz " << mPath[mDrvPath].curvZ(mFromStart) << std::endl;
mCar.update(mDeltaTime);
mFromStart = fromStart(mCar.car()->_distFromStartLine); // TORCS problem with negative _distFromStartLine
mPit.update();
mCurrMu = mMuFactors.muFactor(mFromStart) * mCar.segMu();
// Brake pedal
double brakeforce = mCar.brakeForce(mCar.v(), mPath[mDrvPath].curvature(mFromStart), mPath[mDrvPath].curvZ(mFromStart), mCurrMu * mCar.brakeMuFactor(), 0.0, 0.0, PATH_O);
brakeforce = std::max(0.15 * mCar.maxBrakeForce(), brakeforce);
mBrakePedalRace = brakeforce / mCar.maxBrakeForce() + 0.1;
mBrakePedalRace = Utils::clip(mBrakePedalRace, 0.0, 1.0);
// Accel pedal for 2 wheel drive
double accelpedal = 0.5 * brakeforce / mCar.maxAccelForce();
accelpedal = Utils::clip(accelpedal, 0.0, 1.0);
mAccelPedalRace = 0.8 * accelpedal;
// LR tire friction balance
m[FRICT_LR] = false;
if (fabs(mCar.tires()->frictionBalanceLR()) > 0.2)
{
m[FRICT_LR] = true;
}
}
void Driver::updateOpponents()
{
mOpps.update();
m[FAST_BEHIND] = mOpps.oppComingFastBehind();
// Distances
if (mOpps.oppNear() != NULL)
{
m[CATCH] = Utils::hysteresis(m[CATCH], 6.0 - mOpps.oppNear()->catchTime(), 3.0);
}
}
void Driver::updatePathState()
{
for (unsigned i = 0; i < sizeof(PathNames)/sizeof(PathNames[0]); i++)
{
mPathState[i].update(mDeltaTime);
}
}
void Driver::calcOffsetAndYaw()
{
// Set LR target portion
double maxStepLR = 0.01;
double stepdiff = 0.002;
if (mDrvPath == PATH_L && (m[OVERTAKE] || m[LET_PASS] || mDrvState == STATE_PITLANE || mPit.pitstop() || mTestPath != PATH_O))
{
if (mLRTargetPortion > 0.98 && mLRTargetStep >= stepdiff)
{
mLRTargetStep -= stepdiff;
}
else if (mLRTargetPortion <= 0.98)
{
mLRTargetStep += stepdiff;
}
}
else if (mDrvPath == PATH_R && (m[OVERTAKE] || m[LET_PASS] || mDrvState == STATE_PITLANE || mPit.pitstop() || mTestPath != PATH_O))
{
if (mLRTargetPortion < -0.98 && mLRTargetStep <= -stepdiff)
{
mLRTargetStep += stepdiff;
}
else if (mLRTargetPortion >= -0.98)
{
mLRTargetStep -= stepdiff;
}
}
else if (mDrvPath != PATH_O && fabs(mLRTargetPortion) > maxStepLR)
{
double sign = copysign(1.0, pathOffs(PATH_O));
mLRTargetStep += stepdiff * sign;
}
else
{
mLRTargetStep = 0.0;
mLRTargetPortion = 0.0;
}
mLRTargetStep = Utils::clip(mLRTargetStep, -maxStepLR, maxStepLR);
mLRTargetPortion += mLRTargetStep;
mLRTargetPortion = Utils::clip(mLRTargetPortion, -1.0, 1.0);
if (mLRTargetPortion > 0.0)
{
mPathOffs = pathOffs(PATH_O) + mLRTargetPortion * (pathOffs(PATH_L) - pathOffs(PATH_O));
mPathYaw = Utils::normPiPi(mPathState[PATH_O].yaw() + mLRTargetPortion * Utils::normPiPi(mPathState[PATH_L].yaw() - mPathState[PATH_O].yaw()));
mPathCurvature = mPathState[PATH_O].curvature() + mLRTargetPortion * (mPathState[PATH_L].curvature() - mPathState[PATH_O].curvature());
}
else
{
mPathOffs = pathOffs(PATH_O) - mLRTargetPortion * (pathOffs(PATH_R) - pathOffs(PATH_O));
mPathYaw = Utils::normPiPi(mPathState[PATH_O].yaw() - mLRTargetPortion * Utils::normPiPi(mPathState[PATH_R].yaw() - mPathState[PATH_O].yaw()));
mPathCurvature = mPathState[PATH_O].curvature() - mLRTargetPortion * (mPathState[PATH_R].curvature() - mPathState[PATH_O].curvature());
}
switch (mDrvState)
{
case STATE_RACE:
{
break;
}
case STATE_STUCK:
{
break;
}
case STATE_OFFTRACK:
{
if (mCar.wallDist() < 0.0)
{
// We are on the wrong side of the pit wall
double toMiddle = copysign(1.0, mCar.toMid()) * (mCar.wallToMiddleAbs() + 2.0);
mPathOffs = toMiddle - mCar.toMid();
}
break;
}
case STATE_PITLANE:
{
double pitToMiddle = mPit.pathToMiddle(mFromStart);
double pitToMiddleForward05 = mPit.pathToMiddle(fromStart(mFromStart + 0.5));
double pitToMiddleForward1 = mPit.pathToMiddle(fromStart(mFromStart + 1.0));
mPathOffs = pitToMiddle - mCar.toMid();
mPathYaw = mTrack.yaw(mFromStart) + 2.0 * (pitToMiddleForward05 - pitToMiddle);
double pathYawForward05 = mTrack.yaw(mFromStart) + 2.0 * (pitToMiddleForward1 - pitToMiddleForward05);
mPathCurvature = mTrack.curvature(mFromStart) + 2.0 * (pathYawForward05 - mPathYaw);
break;
}
case STATE_PITSTOP:
{
break;
}
}
mPathYaw_carSpeedYaw_diff = Utils::normPiPi(mPathYaw - mCar.speedYaw());
mPathOffsDeriv = (mPathOffsDeriv + (mPathOffs - mPrevPathOffs) / mDeltaTime) / 2.0;
if (m[STATE_CHANGE])
{
mPrevPathOffs = 0.0;
mPathOffsDeriv = 0.0;
}
}
void Driver::setControls()
{
mBrakePedal = mCar.filterABS(getBrake(mMaxspeed));
mAccelPedal = mCar.filterTCLSideSlip(mCar.filterTCL(getAccel(mMaxspeed))); // must be after brake
mSteer = getSteer(mCar.car()->_steerLock);
mCar.setControls(mAccelPedal, mBrakePedal, mSteer);
}
void Driver::printInfos()
{
if (mDataLogOn)
{
mDataLog.update();
}
/*if (!mMsgOn)
{
return;
}*/
// TORCS message panel lines 1 and 2 reserved for driver (max. 31 chars)
std::string flagdesc = "S P ff cw lco b FS";
std::stringstream flagss;
flagss << mDrvState << " " << mDrvPath << " "
<< m[1] << m[2] << " "
<< m[3] << m[4] << " "
<< m[5] << m[6] << m[7] << " "
<< m[8] << " "
<< (int)mFromStart;
std::string flagstring = flagss.str();
//mMsg.displayOnPanel(flagdesc, flagstring);
// Lap info
if (mFromStart < 3.0)
{
if (mSimTime - mLapSimTime > 1.0)
{
double lapsimtime = mSimTime;
if (mCar.v() > 0.001)
{
lapsimtime = mSimTime - mFromStart / mCar.v();
}
double laptime = lapsimtime - mLapSimTime;
LogUSR.debug("%.3f %s laptime %.3f\n", mSimTime, flagstring.c_str(), laptime);
mLapSimTime = lapsimtime;
LogUSR.debug("%.3f %s avgfuelperlap %.3f\n", mSimTime, flagstring.c_str(), mPit.avgFuelPerLap());
}
}
// Changed flags
if (m != mPrev)
{
for (unsigned i = 0; i < mFlagNames.size(); ++i)
{
if (m[i] != mPrev[i])
{
LogUSR.debug("%.3f %s %s \n", mSimTime, flagstring.c_str(), mFlagNames[i].c_str());
}
}
}
}
void Driver::initVars()
{
mDrvState = STATE_RACE;
mDrvPath = PATH_O;
mDrvPathOld = PATH_O;
mOvertakePath = PATH_L;
mAccelPedal = 0.0;
mSpeedController.mP = 0.5;
mSpeedController.mD = 0.09;
mLapSimTime = 0.0;
mStuckTime = 0.0;
mYawSteer = 0.0;
mOffsDerivSteer = 0.0;
mYawRateSteer = 0.0;
mPathOffs = 0.0;
mPrevPathOffs = 0.0;
mPathOffsDeriv = 0.0;
mLRTargetPortion = 0.0;
mLRTargetStep = 0.0;
m.clear();
for (unsigned i = 0; i < mFlagNames.size(); ++i)
{
m.push_back(false);
}
setPrevVars();
}
void Driver::setPrevVars()
{
mPrev = m;
mPrevPathOffs = mPathOffs;
}
double Driver::pitSpeed()
{
double pitlimitdist = fromStart(mPit.limitEntry() - mFromStart);
double pitdist = mPit.dist();
if (mPit.penalty() == RM_PENALTY_DRIVETHROUGH)
{
pitdist = 1000.0;
}
double speedEntryExit = mPit.pitstop() ? mPitEntrySpeed : mPitExitSpeed;
double pitspeed = speedEntryExit;
double mintrackspeed = std::min(pathSpeed(PATH_L), pathSpeed(PATH_R));
pitspeed = std::min(pitspeed, 0.8 * mintrackspeed);
if (pitlimitdist < brakeDist(PATH_O, mCar.v(), mPit.speedLimit()) || mPit.isPitLimit(mFromStart))
{
pitspeed = mPit.speedLimit();
}
if (pitdist < 2.0 * brakeDist(PATH_O, mCar.v(), 0.0))
{
pitspeed = 0.0;
}
return pitspeed;
}
double Driver::brakeDist(PathType path, double speed, double allowedspeed)
{
double speeddiff = speed - allowedspeed;
if (speeddiff <= 0.0)
{
return -1000.0;
}
double v1 = speed;
double v2;
double totaldist = 0.0;
int idx = mPath[path].segIdx(mFromStart);
idx++;
double seglen = fromStart(mPath[path].seg(idx).fromStart - mFromStart);
int maxidx = (int)(300.0 / mPath[path].segLenMid()); // (int) for VS 2013 compatibility
for (int i = 0; i < maxidx; i++)
{
double curv = mPath[path].seg(idx + i).k;
double curvz = mPath[path].seg(idx + i).kz;
double pitch = mPath[path].seg(idx + i).pitchAngle;
double roll = mPath[path].seg(idx + i).rollAngle;
double force = mCar.brakeForce(v1, curv, curvz, mCurrMu * mCar.brakeMuFactor(), pitch, roll, path) + mCar.cw() * v1 * v1;
double acc = -force / mCar.mass();
double vv1 = v1 * v1;
if (vv1 > -(2 * acc * seglen))
{
v2 = sqrt(vv1 + 2 * acc * seglen);
}
else
{
v2 = v1 + acc * seglen / v1;
}
if (v2 <= allowedspeed)
{
totaldist += seglen * (v1 - allowedspeed) / (v1 - v2);
return 1.1 * totaldist;
}
totaldist += seglen;
seglen = mPath[path].seg(idx + i).segLen;
v1 = v2;
}
return 300.0;
}
double Driver::getBrake(double maxspeed) const
{
double brakepedal = 0.0;
if (mCar.v() > maxspeed)
{
brakepedal = mBrakePedalRace;
}
switch (mDrvState)
{
case STATE_RACE:
{
break;
}
case STATE_STUCK:
{
break;
}
case STATE_OFFTRACK:
{
brakepedal *= 0.2;
break;
}
case STATE_PITLANE:
{
// Pit speed limiter
if (mCar.v() > maxspeed - 0.1 && mCar.v() <= maxspeed)
{
brakepedal = 0.05;
}
break;
}
case STATE_PITSTOP:
{
brakepedal = mBrakePedalRace;
break;
}
}
if (m[COLL] || (mCar.vX() < -1.0 && mDrvState != STATE_STUCK))
{
brakepedal = mBrakePedalRace;
}
// Side slip braking
if (fabs(mCar.sideSlip()) > 40.0)
{
brakepedal += 0.05;
}
else if (fabs(mCar.sideSlip()) > 45.0)
{
brakepedal += 0.1;
}
// LR tire friction unbalanced (grass on border)
if (m[FRICT_LR])
{
brakepedal = 0.0;
}
//brakepedal *= driver_aggression;
return brakepedal;
}
double Driver::getAccel(double maxspeed)
{
double accel = mAccelPedal;
if (mBrakePedal == 0.0)
{
accel = controlSpeed(accel, 0.98 * maxspeed);
}
// Save fuel when near opponent
if (mSimTime > 100.0 && mOpps.oppNear() != NULL)
{
if (mOpps.oppNear()->dist() > 5.0 && mOpps.oppNear()->dist() < 25.0)
{
if (!mOpps.oppNear()->backMarker() && !mOpps.oppNear()->damaged())
{
if (mCar.v() > 0.9 * maxspeed)
{
accel *= 0.5;
}
}
}
}
if (m[LET_PASS])
{
accel *= 0.5 + (driver_aggression * 0.25);
}
else if (mOpps.mateFrontAside())
{
accel *= 0.7 + (driver_aggression * 0.25);
}
// LR tire friction unbalanced (grass on border)
if (m[FRICT_LR] && mCar.v() > 25.0)
{
accel = 0.0;
}
if (mBrakePedal > 0.0 || (fabs(mPathYaw_carSpeedYaw_diff) > 0.11 && mCar.v() > 15.0) || mOppAsideAtPitEntry)
{
accel = 0.0;
}
if (mSimTime < 0.0)
{
if (mCar.car()->_enginerpm / mCar.car()->_enginerpmRedLine > 0.7) {
accel = 0.0;
}
}
accel *=SkillDriver;
return accel;
}
double Driver::getSteer(double steerlock)
{
double steer = getSteerAngle(steerlock);
// Reverse when stuck
if (mCar.vX() < 0.0)
{
if (fabs(mCar.angToTrack()) < 1.0)
{
steer = -mCar.angToTrack() / 4.0;
}
else
{
steer = -0.5 * copysign(1.0, mCar.angToTrack());
}
}
// Warm up tyres in qualifying
if (mSituation->_raceType == RM_TYPE_QUALIF && !m[DRIVING_FAST] && mCar.tires()->gripFactor() < 0.8)
{
if ((mCar.car()->_laps == 1) || (mCar.car()->_laps == 2 && 0.7 * mCar.car()->_lastLapTime > mCar.car()->_curLapTime))
{
steer += 0.2 * sin(30.0 * mSimTime);
}
}
steer = Utils::clip(steer, -steerlock, steerlock);
steer = steer / steerlock;
double rearSlip = (mCar.car()->_wheelSlipSide(REAR_LFT) + mCar.car()->_wheelSlipSide(REAR_RGT)) / 2;
double frntSlip = (mCar.car()->_wheelSlipSide(FRNT_LFT) + mCar.car()->_wheelSlipSide(FRNT_RGT)) / 2;
if (fabs(rearSlip) > fabs(frntSlip) + 2.0)
{
rearSlip -= frntSlip;
int sgn = (rearSlip > 0 ? 1 : -1);
rearSlip -= 2.0 * sgn;
rearSlip = rearSlip * (rearSlip / 3) * sgn;
rearSlip = MAX(-0.9, MIN(0.9, rearSlip / (900.0 + MIN(mCar.car()->_accel_x, mCar.car()->_accel_x * 1.1))));
rearSlip /= mCar.car()->_steerLock;
steer += rearSlip;
}
return steer;
}
bool Driver::stateStuck()
{
if (stuck())
{
mCar.driveBackwards();
return true;
}
mCar.driveForward();
return false;
}
bool Driver::stateOfftrack() const
{
if (mDrvState != STATE_PITLANE && mDrvState != STATE_PITSTOP)
{
double limit = 2.0;
if (mDrvState == STATE_OFFTRACK)
{
limit = 0.5;
}
if (mCar.borderDist() < -limit && mCar.borderFriction() < 1.0)
{
return true;
}
}
return false;
}
bool Driver::statePitstop() const
{
// Traffic in the way when leaving?
if (mDrvState == STATE_PITSTOP)
{
if (mOpps.oppNear() != NULL)
{
if (fabs(mOpps.oppNear()->dist()) < 10.0 && mOpps.oppNear()->v() > 3.0 )
{
return true;
}
}
if (mOpps.oppBack() != NULL)
{
if (mOpps.oppBack()->dist() > -20.0 && mOpps.oppBack()->v() > 5.0 && mOpps.oppBack()->v() < 25.0)
{
return true;
}
}
}
// Conditions for stop
if ((mDrvState == STATE_PITLANE || mDrvState == STATE_PITSTOP) && mPit.pitstop())
{
float dl, dw;
RtDistToPit((CarElt*)mCar.car(), (tTrack*)mTrack.torcsTrack(), &dl, &dw);
if (fabs(dw) < 1.6 && (dl < 0.5 || dl > mTrack.length() - 1.0))
{
return true;
}
}
return false;
}
bool Driver::statePitlane()
{
mOppAsideAtPitEntry = false;
double pitlanetomiddle = mPit.pathToMiddle(mFromStart);
if (pitlanetomiddle != 0.0)
{
if (mOpps.oppNear() != NULL && !(mDrvState == STATE_PITLANE || mDrvState == STATE_PITSTOP))
{
if (mOpps.oppNear()->aside())
{
mOppAsideAtPitEntry = true;
return false;
}
}
return true;
}
return false;
}
void Driver::updateLetPass()
{
if (mOpps.oppLetPass() == NULL)
{
m[LET_PASS] = false;
return;
}
if (mDrvState != STATE_RACE)
{
m[LET_PASS] = false;
return;
}
// Check range
double range = 15.0;
if (m[LET_PASS])
{
range = 25.0;
}
if (mOpps.oppLetPass()->dist() < -range || mOpps.oppLetPass()->dist() > 0.0)
{
m[LET_PASS] = false;
return;
}
// Check for other opponent between behind
if (mOpps.oppBack() != NULL)
{
if (mOpps.oppBack() != mOpps.oppLetPass() && mOpps.oppBack()->dist() > mOpps.oppLetPass()->dist())
{
m[LET_PASS] = false;
return;
}
}
// Check for other opponent aside
if (mOpps.oppNear() != NULL)
{
if (mOpps.oppNear() != mOpps.oppLetPass())
{
if (fabs(mOpps.oppNear()->dist()) < 3.0 - driver_aggression)
{
m[LET_PASS] = false;
return;
}
}
}
// Check for bad conditions
if (!m[LET_PASS])
{
if (m[DRIVING_FAST] || mCar.v() > mOpps.oppLetPass()->v() + 5.0 - driver_aggression)
{
return;
}
}
m[LET_PASS] = true;
}
void Driver::setDrvState(DriveState state)
{
// Update state changes
m[STATE_CHANGE] = false;
if (state != mDrvState)
{
mDrvState = state;
m[STATE_CHANGE] = true;
}
}
void Driver::setDrvPath(PathType path)
{
// Check the conditions
if (path != mDrvPath)
{
if (!m[OVERTAKE])
{
if (mCar.v() > pathSpeed(path) || (fabs(pathOffs(path)) > 0.1))
{
return;
}
}
else
{
if (mCar.v() > pathSpeed(path))
{
if (mOpps.oppNear() != NULL)
{
double dist = mOpps.oppNear()->dist();
if ((dist < 100.0 && dist > 10)
|| (dist <= 10 && dist >= 0.0 && fabs(mOpps.oppNear()->sideDist()) > 4.0 - driver_aggression)
|| (dist < 0.0))
{
return;
}
}
}
}
mDrvPathOld = mDrvPath;
mDrvPath = path;
}
}
void Driver::calcStateAndPath()
{
PathType path = PATH_O;
if (stateStuck())
{
setDrvState(STATE_STUCK);
}
else if (statePitstop())
{
setDrvState(STATE_PITSTOP);
}
else if (statePitlane())
{
setDrvState(STATE_PITLANE);
}
else if (stateOfftrack())
{
setDrvState(STATE_OFFTRACK);
}
else
{
setDrvState(STATE_RACE);
if (m[LET_PASS])
{
if (mCar.toMid() > 0.0)
{
path = PATH_L;
}
else
{
path = PATH_R;
}
}
if (overtakeOpponent())
{
path = mOvertakePath;
}
// Pre pit entry
if (mPit.pitstop())
{
if (mPit.pitOnLeftSide())
{
path = PATH_L;
}
else
{
path = PATH_R;
}
}
// Test paths
if (mTestPath != PATH_O)
{
path = mTestPath;
}
}
// Returning to track from excursion or pits
if (mDrvState == STATE_OFFTRACK)
{
if (fabs(pathOffs(PATH_L)) < fabs(pathOffs(PATH_R)))
{
path = PATH_L;
}
else
{
path = PATH_R;
}
}
if (mDrvState == STATE_PITLANE)
{
if (mPit.pitOnLeftSide())
{
path = PATH_L;
}
else
{
path = PATH_R;
}
}
// Set the path
setDrvPath(path);
}
bool Driver::overtakeOpponent()
{
if (mOpps.oppNear() == NULL)
{
m[OVERTAKE] = false;
return m[OVERTAKE];
}
// Normal overtake
double maxdist = std::min(50.0, mFrontCollMargin + 5.0 + mCar.v());
double dist = mOpps.oppNear()->dist();
if (dist < maxdist && dist > mOvtMargin / 2.0
&& (mOpps.oppNear()->borderDist() > -3.0 || (mOpps.oppNear()->borderDist() <= -3.0 && mOpps.oppNear()->v() > 25 && fabs(mOpps.oppNear()->sideDist()) < 2.0)))
{
if ((((m[CATCH] || (dist < mFrontCollMargin + 2.0 && mCar.accelFiltered() < 0.9 && mCar.v() > mOpps.oppNear()->v())))
|| (m[OVERTAKE] && dist < mFrontCollMargin + 8.0 && mCar.v() > mOpps.oppNear()->v() - 4.0 - (driver_aggression))
|| (mOpps.oppNear()->v() < 20.0 && dist < mFrontCollMargin + 20.0))
&& (!(!m[OVERTAKE] && m[DRIVING_FAST])))
{
m[OVERTAKE] = true;
} else {
m[OVERTAKE] = false;
}
}
else
{
m[OVERTAKE] = false;
}
// If aside always overtake
if (dist >= -mOvtMargin && dist <= mOvtMargin / 2.0 && mOpps.oppNear()->borderDist() > -4.0 - (driver_aggression)
&& (fabs(mOpps.oppNear()->sideDist()) < 4.0 + driver_aggression || mDrvPath != PATH_O))
{
m[OVERTAKE] = true;
}
// If in front and on raceline stay there
if (dist < -5.0 && mDrvPath == PATH_O)
{
m[OVERTAKE] = false;
}
return m[OVERTAKE];
}
void Driver::updateOvertakePath()
{
if (mOpps.oppNear() == NULL)
{
return;
}
// Normal overtaking
if ((mOpps.oppNear()->dist() > mOvtMargin && mOpps.oppNear()->catchTime() > 1.0 - (driver_aggression * 0.1))
|| (mOpps.oppNear()->dist() > 1.0 && mCar.v() < 2.5 - (driver_aggression)))
{
// Stay on your side when there is enough space
double righttomiddle = mPath[PATH_R].toMiddle(mOpps.oppNear()->fromStart());
double lefttomiddle = mPath[PATH_L].toMiddle(mOpps.oppNear()->fromStart());
bool rightfree = fabs(righttomiddle - mOpps.oppNear()->toMiddle()) > 4.0 + driver_aggression * 2;
bool leftfree = fabs(lefttomiddle - mOpps.oppNear()->toMiddle()) > 4.0 + driver_aggression * 2;
if (mOpps.oppNear()->leftOfMe())
{
if (!(rightfree || leftfree))
{
mOvertakePath = PATH_R;
}
else if (rightfree)
{
mOvertakePath = PATH_R;
}
else
{
mOvertakePath = PATH_L;
}
}
else
{
if (!(rightfree || leftfree))
{
mOvertakePath = PATH_L;
}
else if (leftfree)
{
mOvertakePath = PATH_L;
}
else
{
mOvertakePath = PATH_R;
}
}
}
else
{
// Always stay on your side if opponent aside
if (mOpps.oppNear()->leftOfMe())
{
mOvertakePath = PATH_R;
}
else
{
mOvertakePath = PATH_L;
}
}
}
bool Driver::stuck()
{
if (mSimTime > 0.0)
{
mStuckTime += mDeltaTime;
}
if (m[WAIT] || mDrvState == STATE_PITSTOP)
{
mStuckTime = 0.0;
}
if (mDrvState == STATE_STUCK)
{
if (mCar.v() > 8.0 || mStuckTime > 4.0)
{
mStuckTime = 0.0;
return false;
}
}
else if (mCar.v() < 1.0)
{
if (mStuckTime > 3.0)
{
mStuckTime = 0.0;
return true;
}
}
else
{
mStuckTime = 0.0;
}
return (mDrvState == STATE_STUCK);
}
bool Driver::updateOnCollision()
{
m[COLL] = false;
// Check opponents
for (int i = 0; i < mOpps.nrOpponents(); i++)
{
Opponent* opp = mOpps.opp(i);
// Conditions to ignore opponents
if (!opp->racing() || opp->dist() < -5.0 || opp->dist() > 200.0)
{
continue;
}
if (!oppInCollisionZone(opp))
{
continue;
}
// Collision is possible
double brakedist = 1.3 * brakeDist(mDrvPath, mCar.v(), opp->v());
if (mCar.vX() < 0.0)
{
brakedist = brakeDist(mDrvPath, -mCar.v(), 0.0);
}
double colldist = std::max(0.0, fabs(opp->dist()) - frontCollFactor(opp) * mFrontCollMargin);
// Simple way to account for the moving target
double opptraveldist = 0.0;
double factor = 0.0;
if (opp->v() > 15.0 && !m[DRIVING_FAST])
{
factor = 0.5;
}
else if (m[DRIVING_FAST])
{
factor = 0.2;
}
opptraveldist = factor * std::min(opp->catchTime() * opp->v(), 100.0);
double estcolldist = colldist + opptraveldist;
bool incollmargin = colldist == 0.0;
bool fasterthanopp = opp->v() > 15.0 && mCar.v() > fabs(opp->v()) - 0.5;
if (brakedist > estcolldist || (incollmargin && fasterthanopp) || ((fabs(opp->dist()) < 1.0 && opp->distToStraight() < diffSpeedMargin(opp))))
{
m[COLL] = true;
}
}
// Is track free to enter
m[WAIT] = false;
if (m[FAST_BEHIND] && mCar.borderDist() < -2.0 && mCar.borderDist() > -5.0 && mCar.v() < 9.0)
{
m[WAIT] = true;
m[COLL] = true;
}
// Check for wall
if (mCar.pointingToWall() && fabs(mCar.angToTrack()) > 1.0)
{
if (mCar.wallDist() - 2.5 < brakeDist(PATH_O, mCar.v(), 0.0) && mDrvState != STATE_STUCK)
{
m[COLL] = true;
}
}
return m[COLL];
}
bool Driver::oppInCollisionZone(Opponent* opp) const
{
if (opp->inDrivingDirection())
{
if (((opp->distToStraight() < diffSpeedMargin(opp))
|| (mCar.v() - opp->v() > 10.0 && opp->dist() < 50.0 && opp->dist() > 0.0 && opp->borderDist() > -3.0))
&& !(oppFast(opp) && opp->dist() > 50.0))
{
return true;
}
}
return false;
}
bool Driver::oppFast(Opponent* opp) const
{
double oppfs = fromStart(opp->fromStart());
if ((opp->v() > 0.8 * mPathState[PATH_O].maxSpeed(oppfs))
|| (opp->v() > 40.0))
{
return true;
}
return false;
}
double Driver::diffSpeedMargin(Opponent* opp) const
{
double speeddiff = std::max(0.0, mCar.v() - opp->v());
double oppangle = opp->angle();
double angle = std::min(0.3, fabs(oppangle));
double factor = std::max(0.15, 1.0 * angle + 0.1 * m[DRIVING_FAST]);
double diffspeedmargin = 2.5 + sin(fabs(oppangle)) + factor * speeddiff;
if (mCar.v() < 5.0 || oppNoDanger(opp))
{
diffspeedmargin = 2.2 + sin(fabs(oppangle));
}
double maxmargin = 15.0;
if (mPit.isBetween(opp->fromStart()))
{
maxmargin = 7.0;
}
return std::min(diffspeedmargin, maxmargin);
}
bool Driver::oppNoDanger(Opponent* opp) const
{
if ((opp->borderDist() < -3.0 && fabs(opp->v()) < 1.0 && mCar.borderDist() > 0.0 && fabs(opp->dist()) > 1.0))
{
return true;
}
return false;
}
double Driver::fromStart(double fromstart) const
{
double fs;
double length = mTrack.length();
if (fromstart > -length && fromstart < 2.0 * length)
{
if (fromstart > length)
{
fs = fromstart - length;
}
else if (fromstart < 0.0)
{
fs = fromstart + length;
}
else
{
fs = fromstart;
}
}
else
{
fs = 0.0;
}
return fs;
}
double Driver::pathOffs(PathType path) const
{
return mPathState[path].offset();
}
double Driver::pathSpeed(PathType path) const
{
return mPathState[path].maxSpeed();
}
double Driver::pathAcceleration(PathType path) const
{
return mPathState[path].acceleration();
}
void Driver::updateDrivingFast()
{
double factor;
if (m[DRIVING_FAST])
{
factor = 0.75;
}
else
{
factor = 1.50;
}
if (mDrvPath == PATH_O)
{
m[DRIVING_FAST] = mCar.v() * (1 + (driver_aggression*0.1)) > factor * pathSpeed(mOvertakePath);
}
else
{
m[DRIVING_FAST] = mCar.v() > factor * pathSpeed(mDrvPath);
}
// Going fast in curve
if (pathAcceleration(mDrvPath) < 0.0 && mCar.v() > 0.75 * mMaxspeed)
{
m[DRIVING_FAST] = true;
}
}
double Driver::frontCollFactor(Opponent* opp)
{
double factor = 1.0;
if (opp != NULL)
{
if ((m[OVERTAKE] || mCar.v() - opp->v() < 10.0) && !m[DRIVING_FAST])
{
factor = 0.5 + (driver_aggression * 0.1);
}
if (mCar.v() - opp->v() > 10.0 && m[DRIVING_FAST])
{
factor = 1.5 + (driver_aggression * 0.1);
}
if (fabs(opp->angle()) > 1.5 || fabs(opp->v()) < 2.0)
{
factor = 2.0 + (driver_aggression * 0.1);
}
if (mCar.v() < 2.0)
{
factor = 0.2;
}
}
return factor;
}
void Driver::calcMaxspeed()
{
switch (mDrvState)
{
case STATE_RACE:
{
if (mDrvPath != PATH_O)
{
mMaxspeed = pathSpeed(PATH_R) + 0.5 * (mLRTargetPortion + 1.0) * (pathSpeed(PATH_L) - pathSpeed(PATH_R));
mMaxspeed *= 1 + ((driver_aggression) * driver_aggromult);
}
else
{
mMaxspeed = pathSpeed(mDrvPath);
//mMaxspeed *= 1 + (driver_aggression * 0.01);
}
// Special cases
if (mPit.pitstop())
{
mMaxspeed = mPitEntrySpeed * 0.75; // pre pit entry
}
if (fabs(mCar.angToTrack()) > 1.0)
{
mMaxspeed = 10.0;
}
break;
}
case STATE_STUCK:
{
mMaxspeed = 10.0;
break;
}
case STATE_OFFTRACK:
{
mMaxspeed = 5.0;
break;
}
case STATE_PITLANE:
{
mMaxspeed = pitSpeed();
break;
}
default:
{
break;
}
}
mMaxspeed *= SkillGlobal;
}
double Driver::getSteerAngle(double steerlock)
{
double yaw = Utils::normPiPi(mCar.yaw() - mPathYaw);
double yawrate = mCar.yawRate() - mPathCurvature;
double curvature = mPathCurvature;
// Offset
double offs = Utils::clip(mPathOffs, -0.35, 0.35);
if (mSimTime > 10.0 && mCar.v() < 10.0 && !(fabs(yaw) > 0.75 && mCar.borderDist() > 0.0))
{
offs = mPathOffs;
}
// Offset derivative
double offsderiv = Utils::clip(mPathOffsDeriv, -5.0, 5.0);
// Factors
double yawfactor = 1.0 - Utils::clip(fabs(mPathOffs) - 2.0, 0.0, 4.0) / 16.0;
if (fabs(yaw) > 0.2)
{
yawfactor *= 1.5;
}
double yawratefactor = -3.0;
if (m[COLL])
{
yawratefactor = -20.0;
}
// Steer components
mYawSteer = (-yawfactor * yaw + mYawSteer) / 2.0;
mYawRateSteer = (yawratefactor * yawrate + mYawRateSteer) / 2.0;
mCurvSteer = 4.0 * curvature;
mOffsSteer = 0.35 * offs;
mOffsDerivSteer = 0.03 * offsderiv;
// Steer sum
double steer = mYawSteer + mYawRateSteer + mCurvSteer + mOffsSteer + mOffsDerivSteer;
// Driving in wrong direction
if (fabs(yaw) > PI / 2.0)
{
steer = -yaw;
}
return Utils::clip(steer, -steerlock, steerlock);
}
double Driver::controlSpeed(double accelerator, double maxspeed)
{
accelerator += mSpeedController.sample(maxspeed - mCar.v(), mDeltaTime);
return Utils::clip(accelerator, 0.0, 1.0);
}
// Meteorology
//--------------------------------------------------------------------------*
void Driver::Meteorology(const tTrack *t)
{
tTrackSeg *Seg;
tTrackSurface *Surf;
rainintensity = 0;
weathercode = GetWeather(t);
LogUSR.info("Meteoroly : %i\n", weathercode);
Seg = t->seg;
for ( int I = 0; I < t->nseg; I++)
{
Surf = Seg->surface;
rainintensity = MAX(rainintensity, Surf->kFrictionDry / Surf->kFriction);
LogUSR.debug("# %.4f, %.4f %s\n",Surf->kFriction, Surf->kRollRes, Surf->material);
Seg = Seg->next;
}
rainintensity -= 1;
if (rainintensity > 0)
{
rain = true;
}
else
rain = false;
}
//==========================================================================*
// Estimate weather
//--------------------------------------------------------------------------*
unsigned int Driver::GetWeather(const tTrack *t)
{
return (t->local.rain << 4) + t->local.water;
};
//==========================================================================*
// Check if pit sharing is activated
//--------------------------------------------------------------------------*
bool Driver::CheckPitSharing(tCarElt *car)
{
const tTrackOwnPit* OwnPit = car->_pit; // Get my pit
if (OwnPit == NULL) // If pit is NULL
{ // nothing to do
LogUSR.info(" #Pit = NULL\n\n"); // here
return false;
}
if (OwnPit->freeCarIndex > 1)
{
LogUSR.info(" #PitSharing = true\n\n");
return true;
}
else
{
LogUSR.info(" #PitSharing = false\n\n");
return false;
}
}