Skip to main content

Overview

The pit stop optimization system manages pit stop timing, tire compound selection, and race strategy during simulations. It includes dynamic strategy adaptation for weather changes, safety car periods, and undercut/overcut opportunities. Source: race_engine.py

Pit Stop Constants

PIT_LOSS

Time penalty for making a pit stop (pit lane time loss). Source: race_engine.py:20
PIT_LOSS = 22.0  # seconds lost in pit lane
PIT_LOSS
float
default:"22.0"
Fixed time cost in seconds for pit stop execution
This includes:
  • Pit lane speed limit penalty
  • Deceleration and acceleration time
  • Actual stop time (~3-4 seconds)
  • Pit entry/exit time loss
Usage Example:
from race_engine import PIT_LOSS

race_time = 1500.0  # seconds
pit_stops = 1
total_time = race_time + (pit_stops * PIT_LOSS)

print(f"Race time with {pit_stops} stop(s): {total_time:.1f}s")
Output:
Race time with 1 stop(s): 1522.0s

Race Strategy Presets

STRATEGIES

Predefined pit stop strategies for dry conditions. Source: race_engine.py:84
STRATEGIES = {
    "S-M":  {"start": "SOFT",   "pits": [18],       "compounds": ["SOFT",   "MEDIUM"]},
    "M-H":  {"start": "MEDIUM", "pits": [26],       "compounds": ["MEDIUM", "HARD"]},
    "H-M":  {"start": "HARD",   "pits": [34],       "compounds": ["HARD",   "MEDIUM"]},
    "S-M-H":{"start": "SOFT",   "pits": [15, 32],   "compounds": ["SOFT",   "MEDIUM", "HARD"]},
    "M-M":  {"start": "MEDIUM", "pits": [28],       "compounds": ["MEDIUM", "MEDIUM"]},
}
start
string
Starting tire compound
pits
list
List of planned pit stop lap numbers
compounds
list
Ordered list of tire compounds to use (includes starting tire)
Strategy Details:
StrategyTypeStarting TirePit Lap(s)Compounds UsedBest For
S-MOne-stopSOFT18Soft → MediumQualifying pace, track position
M-HOne-stopMEDIUM26Medium → HardBalanced approach
H-MOne-stopHARD34Hard → MediumLong first stint
S-M-HTwo-stopSOFT15, 32Soft → Med → HardMaximum pace
M-MOne-stopMEDIUM28Medium → MediumConsistent pace

WET_STRATEGY

Strategy preset for wet weather races. Source: race_engine.py:93
WET_STRATEGY = {
    "start": "WET", 
    "pits": [], 
    "compounds": ["WET"]
}
start
string
Starts on full wet tires
pits
list
Empty list - no planned stops (until track dries)
compounds
list
Only wet tires used
Note: Strategy adapts dynamically if weather changes during race.

Strategy Assignment

assign_strategies

Assigns racing strategies to all drivers based on weather and grid position. Source: race_engine.py:168
def assign_strategies(weather):
    """Assign strategies to all 20 drivers"""
weather
string
Starting weather condition: “DRY”, “LIGHT_RAIN”, or “HEAVY_RAIN”
assignments
dict
Dictionary mapping driver codes to strategy objects
Strategy Assignment Logic:
# Heavy rain
if weather == "HEAVY_RAIN":
    strategy = WET_STRATEGY
    
# Light rain
elif weather == "LIGHT_RAIN":
    strategy = {
        "start": "INTER",
        "pits": [26],
        "compounds": ["INTER", "MEDIUM"]
    }
    
# Dry conditions
else:
    # Top teams (P1-P6)
    if grid_pos <= 6:
        strategy = random.choice(["S-M", "M-H", "S-M-H"])
    
    # Midfield (P7-P12)
    elif grid_pos <= 12:
        strategy = random.choice(["M-H", "H-M", "M-M"])
    
    # Backmarkers (P13-P20)
    else:
        strategy = random.choice(["H-M", "M-H"])
Example:
from race_engine import assign_strategies

strategies = assign_strategies(weather="DRY")

for driver_code, strategy in strategies.items():
    print(f"{driver_code}: {strategy['start']}{strategy['compounds']}")
Output:
VER: SOFT → ['SOFT', 'MEDIUM']
HAM: MEDIUM → ['MEDIUM', 'HARD']
LEC: SOFT → ['SOFT', 'MEDIUM', 'HARD']
...

Pit Stop Decision Logic

Pit Stop Trigger

Pit stops are triggered by strategic planning or opportunistic timing. Source: race_engine.py:272-281
# Planned pit stop
pitting = False
if car.pit_stops and lap == car.pit_stops[0]:
    pitting = True

# Opportunistic SC pit
elif sc_active and car.pit_stops and abs(lap - car.pit_stops[0]) <= 3:
    pitting = True
    car.events.append(f"L{lap}: Pit under Safety Car!")
Pit Stop Conditions:
  1. Planned Stop: Current lap matches next planned pit lap
  2. Safety Car Window: Within 3 laps of planned stop AND safety car is out
  3. Weather Change: Automatic tire change if current tire unsuitable

Pit Stop Execution

When a pit stop is executed, the following occurs: Source: race_engine.py:283-298
if pitting:
    car.pit_stops.pop(0)  # Remove executed pit from queue
    car.compound_idx += 1
    next_cpd = car.compounds[min(car.compound_idx, len(car.compounds)-1)]
    
    # Handle wet/dry transitions
    if current_weather in ["LIGHT_RAIN","HEAVY_RAIN"] and next_cpd in ["SOFT","MEDIUM","HARD"]:
        next_cpd = "INTER"
    elif current_weather == "DRY" and next_cpd == "WET":
        next_cpd = "MEDIUM"
    
    car.tire = next_cpd
    car.tire_age = 0
    car.pit_count += 1
    car.pit_laps.append(lap)
    car.total_time += PIT_LOSS
    car.events.append(f"L{lap}: PIT → {next_cpd} tires (pit #{car.pit_count})")
Pit Stop Effects:
  1. Time Penalty: +22 seconds to total race time
  2. Tire Reset: New compound, tire age reset to 0
  3. Strategy Update: Next compound from planned sequence
  4. Weather Adaptation: Automatic compound override if conditions changed

CarState Attributes

Source: race_engine.py:97-128
pit_count
int
Total number of pit stops completed
pit_stops
list
Remaining planned pit stop lap numbers (consumed as pits occur)
pit_laps
list
List of laps on which pit stops were actually taken
tire
string
Current tire compound on the car
tire_age
int
Number of laps completed on current tires
compounds
list
Full list of compounds in the strategy
compound_idx
int
Index of current compound in the compounds list
Example:
# After 2 pit stops
car.pit_count = 2
car.pit_laps = [15, 32]
car.tire = "HARD"
car.tire_age = 5
car.compounds = ["SOFT", "MEDIUM", "HARD"]
car.compound_idx = 2

Complete Pit Strategy Examples

from race_engine import simulate_race

# Run simulation with seed for reproducibility
result = simulate_race(weather="DRY", circuit="STANDARD", seed=42)

# Analyze pit strategies
for driver_result in result['results'][:5]:
    print(f"\nP{driver_result['position']}: {driver_result['name']}")
    print(f"  Pit stops: {driver_result['pit_count']}")
    print(f"  Pit laps: {driver_result['pit_laps']}")
    print(f"  Tires used: {' → '.join(driver_result['tires_used'])}")
    print(f"  Total time: {driver_result['total_time']:.1f}s")
    print(f"  Fastest lap: {driver_result['fastest_lap']:.3f}s")

Race Result Analysis

Pit Stop Statistics

After a race simulation, detailed pit stop data is available in results. Result Fields:
pit_count
int
Total number of pit stops completed
pit_laps
list
List of lap numbers when pits occurred
tires_used
list
Chronological list of tire compounds used
Example Analysis:
result = simulate_race(weather="DRY", seed=42)

print("PIT STOP ANALYSIS")
print("="*60)

# Analyze winner's strategy
winner = result['results'][0]
print(f"\nWinner: {winner['name']}")
print(f"Strategy: {' → '.join(winner['tires_used'])}")
print(f"Pit stops: {winner['pit_count']} (laps {winner['pit_laps']})")

# Compare strategies
stop_counts = {}
for driver in result['results']:
    stops = driver['pit_count']
    stop_counts[stops] = stop_counts.get(stops, 0) + 1

print("\nStrategy Distribution:")
for stops, count in sorted(stop_counts.items()):
    print(f"  {stops}-stop: {count} drivers")

# Average pit lap
all_pit_laps = [lap for d in result['results'] for lap in d['pit_laps']]
avg_pit_lap = sum(all_pit_laps) / len(all_pit_laps) if all_pit_laps else 0
print(f"\nAverage first pit: Lap {avg_pit_lap:.1f}")
Output:
PIT STOP ANALYSIS
============================================================

Winner: Max Verstappen
Strategy: SOFT → MEDIUM
Pit stops: 1 (laps [18])

Strategy Distribution:
  0-stop: 2 drivers
  1-stop: 14 drivers
  2-stop: 4 drivers

Average first pit: Lap 22.3

Dynamic Strategy Adaptation

Weather-Based Adaptation

Strategies automatically adapt when weather changes mid-race. Source: race_engine.py:287-291
# Wet weather adaptation
if current_weather in ["LIGHT_RAIN","HEAVY_RAIN"] and next_cpd in ["SOFT","MEDIUM","HARD"]:
    next_cpd = "INTER"

# Dry weather adaptation
elif current_weather == "DRY" and next_cpd == "WET":
    next_cpd = "MEDIUM"
Adaptation Rules:
Planned TireWeather ChangeActual Tire
SOFT/MED/HARD→ LIGHT_RAININTER
SOFT/MED/HARD→ HEAVY_RAININTER
WET→ DRYMEDIUM
INTER→ DRYUnchanged*
*INTER can handle drying conditions for several laps

Strategy Comparison

One-Stop Strategy:
  • Single pit stop (~22s penalty)
  • Longer stints with more degradation
  • Simpler execution, less risky
  • Best when tire deg is low
Two-Stop Strategy:
  • Two pit stops (~44s penalty)
  • Shorter stints, fresher tires
  • More complex, traffic risk
  • Best when tire deg is high
Typical Time Comparison (50 laps):
  • One-stop: Base + 22s + high deg
  • Two-stop: Base + 44s + low deg
  • Breakeven depends on deg rate

Pit Strategy Optimization Tips

Undercut vs Overcut

Undercut (Pit Earlier):
  • Pit before competitor to gain time on fresh tires
  • Requires ~2-3s lap time advantage with fresh tires
  • Works best when traffic is low
  • Risk: Gives up track position temporarily
Overcut (Pit Later):
  • Stay out longer while competitor pits
  • Gain time while running in clean air
  • Can work if your tires hold up
  • Risk: May lose too much time on old tires

Factors to Consider

  1. Tire Degradation: Higher deg → earlier pit more beneficial
  2. Traffic: Heavy traffic → overcut more attractive
  3. Safety Car Probability: High SC chance → delay pit if possible
  4. Weather Forecast: Rain expected → delay switching to slicks
  5. Gap to Competitor: Large gap → stick to planned strategy

Notes

  • Pit stop timing is one of the most critical strategic decisions in F1
  • Real-world pit stops average 2.5-4.0 seconds (not including pit lane time)
  • Optimal strategy depends heavily on circuit characteristics and tire degradation severity
  • Virtual Safety Car (VSC) also provides ~6s time saving for pitting
  • Teams run thousands of race simulations to optimize pit strategy
  • Undercuts typically gain 1-2 positions if executed within 3-lap window

Build docs developers (and LLMs) love