Code Breakdown

This section provides a detailed breakdown of the key components of the Race Simulation App's code. By understanding how the code is structured, you can gain insights into how the simulation works, and how different parts of the app contribute to generating realistic race outcomes.

1. Importing Libraries and Setting Up the App

The code begins by importing necessary libraries and setting up the Flask app:


from flask import Flask, render_template, request, jsonify
import numpy as np
import pandas as pd
import random
import matplotlib.pyplot as plt
import io
import base64
import matplotlib
matplotlib.use('Agg')  # Use the non-GUI Agg backend

app = Flask(__name__)

Explanation: This block of code imports various libraries that are essential for different functionalities in the app:

  • Flask: Used for setting up the web framework.
  • NumPy and Pandas: Used for handling numerical data and dataframes.
  • Random: Used for generating random numbers, which are essential for simulating race conditions.
  • Matplotlib: Used for generating visualizations like histograms and lap time graphs.

2. Driver Ratings and Information

Next, the code defines the driver ratings and information:


driver_ratings = {
    'VER': {'Pace': 95, 'Racecraft': 95, 'Awareness': 94, 'Experience': 92},
    'HAM': {'Pace': 93, 'Racecraft': 96, 'Awareness': 93, 'Experience': 97},
    'LEC': {'Pace': 92, 'Racecraft': 88, 'Awareness': 86, 'Experience': 72},
    ...
}

driver_info = {
    '1': 'VER',
    '44': 'HAM',
    '63': 'RUS',
    ...
}

Explanation: This section defines the drivers' ratings across four key attributes: Pace, Racecraft, Awareness, and Experience. These attributes are crucial for determining how each driver will perform during the race. The `driver_info` dictionary maps driver numbers to their respective codes (e.g., '1' for Verstappen as 'VER').

3. Simulation Logic

The heart of the app is the race simulation logic:


def run_simulation(driver, strategy, num_simulations, initial_position):
    driver_abbr = driver_info[driver]
    num_laps = 70
    ...
    for _ in range(num_simulations):
        current_tire_index = 0  # Start with the first tire in the sequence
        race_lap_times = {d: [] for d in driver_info.keys()}
        for lap in range(1, num_laps + 1):
            if lap == 1:
                sorted_drivers = list(driver_info.keys())
                sorted_drivers.remove(driver)
                sorted_drivers.insert(initial_position - 1, driver)
            else:
                sorted_drivers = sorted(race_lap_times.keys(), key=lambda d: race_lap_times[d][-1] if race_lap_times[d] else float('inf'))

            for idx, d in enumerate(sorted_drivers):
                ratings = driver_ratings[driver_info[d]]
                prime_time = driver_prime_times[driver_info[d]]
                ...

Explanation: The `run_simulation` function handles the core simulation logic. Here’s what happens:

  • It loops through a set number of simulations to get a range of possible outcomes.
  • Each lap is simulated by calculating lap times based on driver ratings, tire wear, and other factors.
  • The lap times are adjusted for pit stops, track evolution, and random events, creating a dynamic and realistic race environment.
  • Finally, drivers are sorted by their total race times to determine their finishing positions.

4. Plotting Results

After the simulations are run, the results are visualized using Matplotlib:


def plot_histogram(positions):
    plt.figure(figsize=(10, 6))
    plt.hist(positions, bins=range(1, 22), edgecolor='black', alpha=0.7)
    plt.xlabel('Finishing Position')
    plt.ylabel('Frequency')
    plt.title('Finishing Positions Histogram')

    img = io.BytesIO()
    plt.savefig(img, format='png')
    img.seek(0)
    img_url = base64.b64encode(img.getvalue()).decode('utf8')
    plt.close()

    return 'data:image/png;base64,{}'.format(img_url)

Explanation: This function creates histograms to display the distribution of finishing positions for the simulated races. The histogram provides a visual summary of where drivers are most likely to finish, which can be compared to actual race outcomes. The generated plot is then converted into a base64 string so that it can be easily rendered in the web application.

5. Serving the Application

The final part of the code is responsible for serving the Flask application:


@app.route('/')
def index():
    return render_template('index.html', drivers=driver_info)

@app.route('/simulate', methods=['POST'])
def simulate():
    data = request.json
    driver = data['driver']
    strategy = data['strategy']
    num_simulations = int(data['num_simulations'])
    initial_position = int(data['initial_position'])

    results = run_simulation(driver, strategy, num_simulations, initial_position)
    img = plot_histogram(results)

    return jsonify({'image': img})

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5005, debug=True, use_reloader=False)

Explanation: This section includes the Flask routes that handle requests:

  • The `index` route serves the main page where users can input their simulation parameters.
  • The `simulate` route processes the input data, runs the simulation, and returns the results as a JSON object, including the histogram image.
  • The application is then run on a specified host and port, making it accessible to users.
Back to Home