8.5 Pit Strategy Functions

Introduction

In this section we will finish the pit class. What is still missing is the strategy functionality. I will show you a very simple implementation, which will work quite well for endurance races, but not for short ones. It works exactly the same way like in the berniw robot.

The update Method

The update method is responsible for the housekeeping of some variables and to decide if we need a pit stop.

/* update pit data and strategy */
void Pit::update()
{
    if (mypit != NULL) {
        if (isBetween(car->_distFromStartLine)) {
            if (getPitstop()) {
                setInPit(true);
            }
        } else {
            setInPit(false);
        }

If we are between pitentry and pitexit and pitstop is set, set inpit to true. We need that to remember after the pit stop that we have to follow the pit path to leave the pit lane. That is necessary because the pitstop variable is set to false immediately after the pit stop, when we still need to drive out of the pit lane and back to the track. If we are not between pitentry and pitexit we set inpit to false.

        /* check for damage */
        if (car->_dammage > PIT_DAMMAGE) {
            setPitstop(true);
        }

The first very simple strategy rule. If the damage of the car is greater than PIT_DAMMAGE, we set pitstop to true. Here is very much room for improvement, you could make that dependent on the remaining laps and the situation in the race.

        /* fuel update */
        int id = car->_trkPos.seg->id;
        if (id >= 0 && id < 5 && !fuelchecked) {

We update the fuel values once per lap when we cross the track segment zero. We check the range of 5 segments that we catch it for sure.

            if (car->race.laps > 0) {
                fuelperlap = MAX(fuelperlap, (lastfuel+lastpitfuel-car->priv.fuel));
            }
            lastfuel = car->priv.fuel;
            lastpitfuel = 0.0;
            fuelchecked = true;
        } else if (id > 5) {
            fuelchecked = false;
        }

We compute the amount of fuel which the car has consumed till the check one lap before. The amount of the consumed fuel on the last lap is usually the difference between the available amount of fuel one lap ago (lastfuel) and the current amount of fuel available (car->priv.fuel). If we have performed a pit stop, this is not true. To make the computation work we have to add to the lastfuel value the amount of new fuel (lastpitfuel).

We take the maximum because if we get some damage or have to brake and accelarate more than usual the car consumes more fuel. If you take the average you may run out of fuel.

        int laps = car->_remainingLaps-car->_lapsBehindLeader;
        if (!getPitstop() && laps > 0) {
            if (car->_fuel < 1.5*fuelperlap &&
                car->_fuel < laps*fuelperlap) {
                setPitstop(true);
            }
        }

Here comes the second strategy rule, feel also free to improve that. If there are some laps remaining for our robot check if we have fuel for the next one and a half laps and if we need to refuel at all. If that is the case set pitstop to true.

        if (getPitstop()) car->_raceCmd = RM_CMD_PIT_ASKED;
    }
}

If pitstop is true set the race command accordingly (here we let TORCS know that we want to pit). Our car is captured by the pit if the car is slow enough, near enough to the center of our pit and if the race command is set to RM_CMD_PIT_ASKED.

The getFuel Method

This method computes the amount of fuel we request on the pit stop. It is called from the drivers Driver::pitCommand callback function (I will show you that soon). It is also part of the strategy.

/* Computes the amount of fuel */
float Pit::getFuel()
{
    float fuel;
    fuel = MAX(MIN((car->_remainingLaps+1.0)*fuelperlap - car->_fuel,
                    car->_tank - car->_fuel),
               0.0);
    lastpitfuel = fuel;
    return fuel;
}

The fuel we need to finish the race is the difference between the remaining laps times the fuel we need per lap and the fuel in the tank. To play safe we request fuel for an additional lap and take the remaining laps of our car (perhaps the leader will have an accident). If the amount of fuel we need is bigger than the tank, we cut the amount with the second expression in the MIN statement. Because we perhaps stopped in the pit to repair damage, the needed amount of fuel can become negative. For that is the surrounding MAX statement.

The getRepair Method

Computes the damage points to repair. A the moment we simply repair the whole damage. This is on short races really braindead... so improve that.

/* Computes how much damage to repair */
int Pit::getRepair()
{
    return car->_dammage;
}

The Makefile

We have now finished pit.cpp. To check if everything is in place, we add it in the Makefile. Of course it will still do nothing, because we do not instantiate the pit in our driver yet. This will be the last step. Now change in the Makefile the line

SOURCES     = ${ROBOT}.cpp driver.cpp opponent.cpp spline.cpp

to

SOURCES     = ${ROBOT}.cpp driver.cpp opponent.cpp spline.cpp pit.cpp

Downloads

In case you got lost, you can download my robot for TORCS 1.2.0 or later.

Summary

  • You have understood and implemented the above methods.
  • You know that you can improve the strategy a lot.