void ReCarsManageCar(tCarElt *car, bool& bestLapChanged) { char msg[64]; int i; int xx; tTrackSeg *sseg; tdble wseg; static const float ctrlMsgColor[] = {0.0, 0.0, 1.0, 1.0}; tSituation *s = ReInfo->s; tReCarInfo *info = &(ReInfo->_reCarInfo[car->index]); // Update top speeds. if (car->_speed_x > car->_topSpeed) car->_topSpeed = car->_speed_x; // (practice and qualification only). if (car->_speed_x > info->topSpd) info->topSpd = car->_speed_x; if (car->_speed_x < info->botSpd) info->botSpd = car->_speed_x; // Pitstop management. if (car->_pit) { // If the driver can ask for a pit, update control messages whether slot occupied or not. if (car->ctrl.raceCmd & RM_CMD_PIT_ASKED) { // Pit already occupied? if (car->_pit->pitCarIndex == TR_PIT_STATE_FREE) snprintf(car->ctrl.msg[2], RM_CMD_MAX_MSG_SIZE, "Can Pit"); else snprintf(car->ctrl.msg[2], RM_CMD_MAX_MSG_SIZE, "Pit Occupied"); car->ctrl.msg[2][RM_CMD_MAX_MSG_SIZE-1] = 0; // Some snprintf implementations fail to do so. memcpy(car->ctrl.msgColor, ctrlMsgColor, sizeof(car->ctrl.msgColor)); } // If pitting, check if pitting delay over, and end up with pitting process if so. if (car->_state & RM_CAR_STATE_PIT) { car->ctrl.raceCmd &= ~RM_CMD_PIT_ASKED; // clear the flag. // Note: Due to asynchronous behaviour of the main updater and the situation updater, // we have to wait for car->_scheduledEventTime being set to smthg > 0. if (car->_scheduledEventTime > 0.0) { if (car->_scheduledEventTime < s->currentTime) { car->_state &= ~RM_CAR_STATE_PIT; car->_pit->pitCarIndex = TR_PIT_STATE_FREE; snprintf(msg, sizeof(msg), "%s pit stop %.1f s", car->_name, info->totalPitTime); msg[sizeof(msg)-1] = 0; // Some snprintf implementations fail to do so. ReSituation::self().setRaceMessage(msg, 5); GfLogInfo("%s exiting pit (%.1f s elapsed).\n", car->_name, info->totalPitTime); } else { snprintf(car->ctrl.msg[2], RM_CMD_MAX_MSG_SIZE, "In pits %.1f s", s->currentTime - info->startPitTime); car->ctrl.msg[2][RM_CMD_MAX_MSG_SIZE-1] = 0; // Some snprintf implementations fail to do so. } } // If the driver asks for a pit, check if the car is in the right conditions // (position, speed, ...) and start up pitting process if so. } else if ((car->ctrl.raceCmd & RM_CMD_PIT_ASKED) && car->_pit->pitCarIndex == TR_PIT_STATE_FREE && (s->_maxDammage == 0 || car->_dammage <= s->_maxDammage)) { snprintf(car->ctrl.msg[2], RM_CMD_MAX_MSG_SIZE, "Pit request"); car->ctrl.msg[2][RM_CMD_MAX_MSG_SIZE-1] = 0; // Some snprintf implementations fail to do so. tdble lgFromStart = car->_trkPos.seg->lgfromstart; switch (car->_trkPos.seg->type) { case TR_STR: lgFromStart += car->_trkPos.toStart; break; default: lgFromStart += car->_trkPos.toStart * car->_trkPos.seg->radius; break; } if ((lgFromStart > car->_pit->lmin) && (lgFromStart < car->_pit->lmax)) { int side; tdble toBorder; if (ReInfo->track->pits.side == TR_RGT) { side = TR_SIDE_RGT; toBorder = car->_trkPos.toRight; } else { side = TR_SIDE_LFT; toBorder = car->_trkPos.toLeft; } sseg = car->_trkPos.seg->side[side]; wseg = RtTrackGetWidth(sseg, car->_trkPos.toStart); if (sseg->side[side]) { sseg = sseg->side[side]; wseg += RtTrackGetWidth(sseg, car->_trkPos.toStart); } if (((toBorder + wseg) < (ReInfo->track->pits.width - car->_dimension_y / 2.0)) && (fabs(car->_speed_x) < 1.0) && (fabs(car->_speed_y) < 1.0)) { // All conditions fullfilled => enter pitting process car->_state |= RM_CAR_STATE_PIT; car->_scheduledEventTime = 0.0; // Pit will really start when set to smthg > 0. car->_nbPitStops++; for (i = 0; i < car->_pit->freeCarIndex; i++) { if (car->_pit->car[i] == car) { car->_pit->pitCarIndex = i; break; } } info->startPitTime = s->currentTime; snprintf(msg, sizeof(msg), "%s in pits", car->_name); msg[sizeof(msg)-1] = 0; // Some snprintf implementations fail to do so. ReSituation::self().setRaceMessage(msg, 5); GfLogInfo("%s entering in pit slot.\n", car->_name); if (car->robot->rbPitCmd(car->robot->index, car, s) == ROB_PIT_MENU) { // the pit cmd is modified by menu. reCarsSchedulePitMenu(car); } else { ReCarsUpdateCarPitTime(car); } } else { // The cars speed or offset is out of accepted range // Show the user/developer/robot the reason of the issue tTeamDriver* TeamDriver = RtTeamDriverByCar(car); if (TeamDriver) { TeamDriver->StillToGo = 0.0; TeamDriver->MoreOffset = 0.0; TeamDriver->TooFastBy = 0.0; } float Offset = (float) ((toBorder + wseg) - (ReInfo->track->pits.width - car->_dimension_y / 2.0)); if (Offset >= 0.0) { // The car's position across the track is out of accepted range snprintf(car->ctrl.msg[2], RM_CMD_MAX_MSG_SIZE, "Offset: %.02f",Offset); car->ctrl.msg[2][RM_CMD_MAX_MSG_SIZE-1] = 0; // Some snprintf implementations fail to do so. if (TeamDriver) TeamDriver->MoreOffset = Offset; } float TooFastBy = MAX(fabs(car->_speed_x),fabs(car->_speed_y)); if (TooFastBy >= 1.0) { // The car's speed is out of accepted range snprintf(car->ctrl.msg[2], RM_CMD_MAX_MSG_SIZE, "Speed: %.02f",TooFastBy); car->ctrl.msg[2][RM_CMD_MAX_MSG_SIZE-1] = 0; // Some snprintf implementations fail to do so. if (TeamDriver) TeamDriver->TooFastBy = TooFastBy; } } } else { // The car's position along the track is out of accepted range // Show the user/developer/robot the reason of the issue tTeamDriver* TeamDriver = RtTeamDriverByCar(car); if (TeamDriver) { TeamDriver->StillToGo = 0.0; TeamDriver->MoreOffset = 0.0; TeamDriver->TooFastBy = 0.0; } if (car->_pit->lmin > lgFromStart) { float StillToGo = car->_pit->lmin - lgFromStart; snprintf(car->ctrl.msg[2], RM_CMD_MAX_MSG_SIZE, "Still to go: %0.2f m" ,StillToGo); car->ctrl.msg[2][RM_CMD_MAX_MSG_SIZE-1] = 0; // Some snprintf implementations fail to do so. if (TeamDriver) TeamDriver->StillToGo = StillToGo; } else if (car->_pit->lmax < lgFromStart) { float StillToGo = lgFromStart - car->_pit->lmax; snprintf(car->ctrl.msg[2], RM_CMD_MAX_MSG_SIZE, "Overrun: %0.2f m" ,StillToGo); car->ctrl.msg[2][RM_CMD_MAX_MSG_SIZE-1] = 0; // Some snprintf implementations fail to do so. if (TeamDriver) TeamDriver->StillToGo = -StillToGo; } } } } /* Check if it is in a new sector */ while (true) { if (car->_currentSector < ReInfo->track->numberOfSectors - 1 && car->_laps > 0 && info->lapFlag == 0) { /* Must pass at least one sector before the finish */ if (RtGetDistFromStart(car) > ReInfo->track->sectors[car->_currentSector]) { /* It is in a new sector : update split time */ car->_curSplitTime[car->_currentSector] = car->_curLapTime; ++car->_currentSector; continue; } } break; } /* Start Line Crossing */ if (info->prevTrkPos.seg != car->_trkPos.seg) { if ((info->prevTrkPos.seg->raceInfo & TR_LAST) && (car->_trkPos.seg->raceInfo & TR_START)) { if (info->lapFlag == 0) { // If the car has not yet finished the race : if (!(car->_state & RM_CAR_STATE_FINISH)) { // 1 more lap completed // (Note: lap with index 0 finishes when the car crosses the start line the 1st time, // and is thus considered a real lap, whereas it is not). car->_laps++; /*if (NetGetNetwork()) NetGetNetwork()->SendLapStatusPacket(car);*/ car->_remainingLaps--; if (car->_pos == 1 && s->currentTime < s->_totTime && s->_raceType == RM_TYPE_RACE) { /* First car passed finish time before the time ends: increase the number of laps for everyone */ for (xx = 0; xx < s->_ncars; ++xx) ++ReInfo->s->cars[xx]->_remainingLaps; ++s->_totLaps; } car->_currentSector = 0; if (car->_laps > 1) { car->_lastLapTime = s->currentTime - info->sTime; if (car->_bestLapTime != 0) { car->_deltaBestLapTime = car->_lastLapTime - car->_bestLapTime; } if ((car->_lastLapTime < car->_bestLapTime) || (car->_bestLapTime == 0)) { car->_bestLapTime = car->_lastLapTime; memcpy(car->_bestSplitTime, car->_curSplitTime, sizeof(double)*(ReInfo->track->numberOfSectors - 1) ); if (s->_raceType != RM_TYPE_RACE && s->_ncars > 1) { /* Best lap time is made better : update times behind leader */ bestLapChanged = true; car->_timeBehindLeader = car->_bestLapTime - s->cars[0]->_bestLapTime; if (car->_pos > 1) { car->_timeBehindPrev = car->_bestLapTime - s->cars[car->_pos - 1]->_bestLapTime; } else { /* New best time for the leader : update the differences */ for (xx = 1; xx < s->_ncars; ++xx) { if (s->cars[xx]->_bestLapTime > 0.0f) s->cars[xx]->_timeBehindLeader = s->cars[xx]->_bestLapTime - car->_bestLapTime; } } if (car->_pos + 1 < s->_ncars && s->cars[car->_pos+1]->_bestLapTime > 0.0f) car->_timeBeforeNext = s->cars[car->_pos + 1]->_bestLapTime - car->_bestLapTime; else car->_timeBeforeNext = 0; } } } if (car->_laps > 0) { car->_curTime += s->currentTime - info->sTime; if (car->_pos != 1 && s->_raceType == RM_TYPE_RACE) { car->_timeBehindLeader = car->_curTime - s->cars[0]->_curTime; car->_lapsBehindLeader = s->cars[0]->_laps - car->_laps; car->_timeBehindPrev = car->_curTime - s->cars[car->_pos - 2]->_curTime; s->cars[car->_pos - 2]->_timeBeforeNext = car->_timeBehindPrev; } else if (s->_raceType == RM_TYPE_RACE) { car->_timeBehindLeader = 0; car->_lapsBehindLeader = 0; car->_timeBehindPrev = 0; } info->sTime = (tdble)s->currentTime; if (ReInfo->s->_raceType == RM_TYPE_PRACTICE && (car->_laps > 1 || s->_totLaps == 0)) ReSavePracticeLap(car); } if (ReInfo->_displayMode == RM_DISP_MODE_NONE) { switch(s->_raceType) { case RM_TYPE_PRACTICE: ReUpdatePracticeCurRes(car); break; case RM_TYPE_QUALIF: ReUpdateQualifCurRes(car); break; case RM_TYPE_RACE: ReUpdateRaceCurRes(); break; default: break; } } info->topSpd = car->_speed_x; info->botSpd = car->_speed_x; if ((car->_remainingLaps < 0 && s->currentTime > s->_totTime) || (s->_raceState == RM_RACE_FINISHING)) { car->_state |= RM_CAR_STATE_FINISH; s->_raceState = RM_RACE_FINISHING; if (ReInfo->s->_raceType == RM_TYPE_RACE) { if (car->_pos == 1) { snprintf(msg, sizeof(msg), "Winner %s", car->_name); msg[sizeof(msg)-1] = 0; // Some snprintf implementations fail to do so. ReSituation::self().setRaceMessage(msg, 10, /*big=*/true); if (NetGetServer()) { NetGetServer()->SetFinishTime(s->currentTime+FINISHDELAY); } } else { const char *numSuffix = "th"; if (abs(12 - car->_pos) > 1) { /* leave suffix as 'th' for 11 to 13 */ switch (car->_pos % 10) { case 1: numSuffix = "st"; break; case 2: numSuffix = "nd"; break; case 3: numSuffix = "rd"; break; default: break; } } snprintf(msg, sizeof(msg), "%s finished %d%s", car->_name, car->_pos, numSuffix); msg[sizeof(msg)-1] = 0; // Some snprintf implementations fail to do so. ReSituation::self().setRaceMessage(msg, 5); } } } // Notify the UI when a lap is completed (by the leader) // and race results have been updated. if (car->_pos == 1) ReUI().onLapCompleted(car->_laps - 1); } else { // Prevent infinite looping of cars around track, // allowing one lap after finish for the first car, but no more for (i = 0; i < s->_ncars; i++) { s->cars[i]->_state |= RM_CAR_STATE_FINISH; } return; } } else { info->lapFlag--; } } if ((info->prevTrkPos.seg->raceInfo & TR_START) && (car->_trkPos.seg->raceInfo & TR_LAST)) { /* going backward through the start line */ info->lapFlag++; } } // Start Line Crossing // Apply race rules (penalties if enabled). reCarsApplyRaceRules(car); // Update misc car info. info->prevTrkPos = car->_trkPos; car->_curLapTime = s->currentTime - info->sTime; car->_distFromStartLine = car->_trkPos.seg->lgfromstart + (car->_trkPos.seg->type == TR_STR ? car->_trkPos.toStart : car->_trkPos.toStart * car->_trkPos.seg->radius); car->_distRaced = (car->_laps - 1) * ReInfo->track->length + car->_distFromStartLine; }
//==========================================================================* // State (Sequential logic system) //--------------------------------------------------------------------------* void TSimpleStrategy::CheckPitState(float PitScaleBrake) { if (oPit == NULL) // No Pit no service return; if (!oPit->HasPits()) return; double TrackPos = RtGetDistFromStart(oCar); // Distance to pit switch(oState) // Check state { case PIT_NONE: // We are somewhere on the track, nothing has happend yet if ((!oPit->oPitLane[0].InPitSection(TrackPos)) && oGoToPit) { // if we are not parallel to the pits and get the flag, // let's stop in the pits. oState = PIT_BEFORE; } break; case PIT_BEFORE: // We are somewhere on the track and got the flag to go to pit if (oFuelChecked && oGoToPit) { // If we reache pit entry and flag is still set // switch to the pitlane oState = PIT_PREPARE; } break; case PIT_PREPARE: // We are near the pit entry on the track and got the flag to go to pit if (oPit->oPitLane[0].InPitSection(TrackPos) && oGoToPit) { // If we reache pit entry and flag is still set // switch to the pitlane oState = PIT_ENTER; } break; case PIT_ENTER: // We are on the pitlane and drive to our pit if (!oPit->oPitLane[0].CanStop(TrackPos)) { // We have to wait, till we reached the point to stop if (oDriver->CurrSpeed() < 3) { CarAccelCmd = // a little throttle MAX(0.05f,CarAccelCmd); CarBrakeCmd = 0.0; // Start braking LogSimplix.debug("#PIT_ENTER: Wait %g (%g)\n",TrackPos,oDriver->CurrSpeed()); } else LogSimplix.debug("#PIT_ENTER: Wait %g\n",TrackPos); break; } // We reached the poit to stopp oState = PIT_ASKED; LogSimplix.debug("#PIT_ENTER: %g\n",TrackPos); // falls through... case PIT_ASKED: // We are still going to the pit if (oPit->oPitLane[0].CanStop(TrackPos)) { // If we can stop a this position we start pit procedure LogSimplix.debug("#PIT_ASKED: CanStop %g (%g)\n",TrackPos,oDriver->CurrSpeed()); oDriver->oStanding = true; // For motion survey! oPitTicker = 0; // Start service timer CarAccelCmd = 0; // release throttle CarBrakeCmd = 1.0; // Start braking CarRaceCmd = RM_CMD_PIT_ASKED; // Tell TORCS to service us! To test oPitTicker comment out oState = PIT_SERVICE; } else { // We can't stop here (to early or to late) if (oPit->oPitLane[0].Overrun(TrackPos)) { // if to late LogSimplix.debug("#Overrun 1: %g\n",TrackPos); PitRelease(); oState = PIT_EXIT_WAIT; // pit stop finished, need to exit pits now. } else { LogSimplix.debug("#ToShort 1: %g\n",TrackPos); if (oDriver->CurrSpeed() < 3) { CarAccelCmd = // a little throttle MAX(0.05f,CarAccelCmd); CarBrakeCmd = 0.0; // Start braking } } } break; case PIT_SERVICE: // Wait to reach standstill to get service from TORCS oDriver->oStanding = true; // Keep motion survey quiet oPitTicker++; // Check time to start service if (oPitTicker > 10) // Check Timer { // If we have to wait LogSimplix.debug("#oPitTicker: %d\n",oPitTicker); tTeamDriver* TeamDriver = RtTeamDriverByCar(oCar); short int Major = RtTeamManagerGetMajorVersion(); short int Minor = RtTeamManagerGetMinorVersion(); if ((TeamDriver) && ((Major > NEEDED_MAJOR_VERSION) || ((Major = NEEDED_MAJOR_VERSION) && (Minor >= NEEDED_MINOR_VERSION)))) { LogSimplix.debug("#Pitting issues %s\n",oDriver->GetBotName()); LogSimplix.debug("#StillToGo : %.2f m\n",TeamDriver->StillToGo); LogSimplix.debug("#MoreOffset: %.2f m\n",TeamDriver->MoreOffset); LogSimplix.debug("#TooFastBy : %.2f m/s\n",TeamDriver->TooFastBy); // Learn from the response if (fabs(TeamDriver->StillToGo) > 0.0) { //CarSteerCmd = 0.0; // Straight on if (fabs(CarSpeedLong) < 1.0) { CarAccelCmd = // a little throttle MAX(0.005f,CarAccelCmd); CarBrakeCmd = 0.0; // No braking LogSimplix.debug("#Accel : %.2f\n",CarAccelCmd); } else { CarBrakeCmd = 0.1f; // Braking LogSimplix.debug("#Brake : %.2f\n",CarBrakeCmd); } CarClutchCmd = 0.5; // Press clutch if (TeamDriver->StillToGo > 0) CarGearCmd = 1; // First gear else CarGearCmd = -1; // reverse gear } else { // LogSimplix.debug("#Stopped\n"); CarAccelCmd = 0.0; // Stop throttle CarBrakeCmd = 1.0; // Lock brake CarClutchCmd = 0.0; // Release clutch CarGearCmd = 1; // First gear } } if (oPitTicker > 300) // Check Timer { // If we have to wait too long PitRelease(); // Something went wrong, we have oState = PIT_EXIT_WAIT; // to leave and release pit for teammate } } else if (oPit->oPitLane[0].Overrun(TrackPos)) { // If we couldn't stop in place LogSimplix.debug("#Overrun 2: %g\n",TrackPos); PitRelease(); // We have to release the pit oState = PIT_EXIT_WAIT; // for teammate } else { // There is nothing that hampers TORCS to service us LogSimplix.debug("#PIT_SERVICE: %g (%g)\n",TrackPos,oDriver->CurrSpeed()); CarLightCmd = 0; // No lights on CarAccelCmd = 0; // No throttle CarBrakeCmd = 1.0; // Still braking CarRaceCmd = RM_CMD_PIT_ASKED; // Tell TORCS to service us! To test oPitTicker comment out // oState is set to next state in PitRepair()! // If TORCS doesn't service us, no call to PitRepair() is done! // We run into timeout! (oPitTicker) oPitStartTicker = 600; } break; case PIT_EXIT_WAIT: // We are still in the box oDriver->oStanding = true; // Keep motion survey quiet if ((oMinTimeSlot < 7) // If start slot to short || ((oMinDistBack > -7) // or cars aside && (oMinDistBack < 5))) // we have to wait { oPitStartTicker--; if (oPitStartTicker < 0) { LogSimplix.debug("#PIT_EXIT: mts%g (mdb%gm)\n",oMinTimeSlot,oMinDistBack); oState = PIT_EXIT; } CarLightCmd = RM_LIGHT_HEAD2; // Only small lights on CarAccelCmd = 0.0; CarBrakeCmd = 1.0; } else { CarLightCmd = RM_LIGHT_HEAD1; // Only big lights on oState = PIT_EXIT; } break; case PIT_EXIT: // We are still in the box oDriver->oStanding = true; // Keep motion survey quiet oGoToPit = false; // Service is finished, lets go CarAccelCmd = 0.5; // Press throttle CarBrakeCmd = 0; // Release brake PitRelease(); // Release pit for teammate if (oDriver->CurrSpeed() > 5) oState = PIT_GONE; break; case PIT_GONE: // We are on our path back to the track if (!oPit->oPitLane[0].InPitSection(TrackPos)) { // If we reached the end of the pitlane CarLightCmd = RM_LIGHT_HEAD1 | // All lights on RM_LIGHT_HEAD2; oState = PIT_NONE; // Switch to default mode } break; } }