racedelta

    Deterministic race strategy simulation and crossover analysis in MoonBit.

    motorsport
    race-strategy
    simulation
    data-analysis
    cli
    Download zip
    Author
    Version
    0.1.0
    License
    MIT
    Last updated
    2 days ago
    Downloads
    3

    Dependencies

    #youyong5/racedelta

    CliError

    pub struct CliError {
    code : CliErrorCode
    field : String
    message : String
    } derive(Eq)

    A deterministic, user-facing CLI parsing error.

    field identifies the option or logical input field; code is stable for programmatic handling and message is suitable for stderr output.

    CliError::equal

    fn CliError::equal(CliError, CliError) -> Bool

    CliError::not_equal

    fn CliError::not_equal(x : CliError, y : CliError) -> Bool

    CliErrorCode

    pub(all) enum CliErrorCode {
    MissingInputPath
    MissingRequiredOption
    MissingOptionValue
    DuplicateOption
    UnknownArgument
    UnexpectedPositional
    InvalidPlanFormat
    InvalidPlanLap
    InvalidPlanCompound
    DuplicatePlanLap
    } derive(Eq)

    Stable categories for command-line argument and PLAN parsing errors.

    Codes distinguish missing, duplicate, and unknown arguments from malformed or duplicate LAP:COMPOUND plan entries.

    CliErrorCode::equal

    CliErrorCode::not_equal

    fn CliErrorCode::not_equal(x : CliErrorCode, y : CliErrorCode) -> Bool

    CliOptions

    pub struct CliOptions {
    input_path : String
    target_driver : String
    opponent_driver : String
    stops : Array[PlannedPitStop]
    output_path : String?
    } derive(Eq)

    Parsed CLI input. File paths remain at the command boundary; simulation stays pure.

    target_driver receives the replacement plan in stops; opponent_driver is used for signed-gap comparison. output_path selects file output when present.

    CliOptions::equal

    fn CliOptions::equal(CliOptions, CliOptions) -> Bool

    CliOptions::not_equal

    fn CliOptions::not_equal(x : CliOptions, y : CliOptions) -> Bool

    CliPipelineError

    pub struct CliPipelineError {
    code : CliPipelineErrorCode
    message : String
    } derive(Eq)

    A user-facing error from the pure CSV-to-Markdown pipeline.

    This keeps file I/O out of the core pipeline while exposing a stable failure phase through code and a concise diagnostic through message.

    CliPipelineError::equal

    CliPipelineError::not_equal

    fn CliPipelineError::not_equal(x : CliPipelineError, y : CliPipelineError) -> Bool

    CliPipelineErrorCode

    pub(all) enum CliPipelineErrorCode {
    CsvValidationFailed
    StrategySimulationFailed
    ExplanationFailed
    } derive(Eq)

    Stable categories for the pure CSV-to-report pipeline.

    Codes identify whether CSV validation, strategy simulation, or explanation failed while producing a Markdown report.

    CliPipelineErrorCode::equal

    CliPipelineErrorCode::not_equal

    Compound

    pub(all) enum Compound {
    Soft
    Medium
    Hard
    Intermediate
    Wet
    } derive(Eq,
    Debug
    )

    CSV tyre compounds supported by RaceDelta CSV v1.

    The value is used by parsed records, pace profiles, and planned pit stops.

    Compound::equal

    fn Compound::equal(Compound, Compound) -> Bool

    Compound::not_equal

    fn Compound::not_equal(x : Compound, y : Compound) -> Bool

    Compound::to_repr

    CrossoverDirection

    pub(all) enum CrossoverDirection {
    AlternativeBecomesFaster
    ActualBecomesFaster
    AlternativeBecomesBetter
    ActualBecomesBetter
    TargetMovesAhead
    TargetFallsBehind
    } derive(Eq,
    Debug
    )

    The direction in which a crossover changes its relationship.

    Each value names which side becomes favorable after a non-zero sign change.

    CrossoverDirection::equal

    CrossoverDirection::not_equal

    CrossoverKind

    pub(all) enum CrossoverKind {
    Performance
    Strategic
    Opponent
    } derive(Eq,
    Debug
    )

    The independent relationship represented by a crossover point.

    Performance excludes pit loss, Strategic uses cumulative time gain, and Opponent uses the simulated signed gap to the unchanged opponent.

    CrossoverKind::equal

    CrossoverKind::not_equal

    fn CrossoverKind::not_equal(x : CrossoverKind, y : CrossoverKind) -> Bool

    CrossoverPoint

    pub(all) struct CrossoverPoint {
    lap : Int
    kind : CrossoverKind
    direction : CrossoverDirection
    previous_value_ms : Int
    current_value_ms : Int
    } derive(Eq,
    Debug
    )

    A deterministic sign-change point in a strategy comparison.

    A crossover is emitted when a later non-zero relationship has the opposite sign from the previous non-zero one; zero values neither emit nor reset it. previous_value_ms and current_value_ms use the sign convention of kind.

    CrossoverPoint::equal

    CrossoverPoint::not_equal

    fn CrossoverPoint::not_equal(x : CrossoverPoint, y : CrossoverPoint) -> Bool

    DriverLapState

    pub struct DriverLapState {
    lap : Int
    driver : String
    lap_time_ms : Int
    cumulative_time_ms : Int
    position : Int
    gap_to_leader_ms : Int
    interval_to_ahead_ms : Int?
    compound : Compound
    tyre_age_laps : Int
    pit : Bool
    track_status : TrackStatus
    weather : Weather
    } derive(Eq,
    Debug
    )

    A driver's complete race state at the end of one lap.

    All time and gap fields are integer milliseconds. gap_to_leader_ms and interval_to_ahead_ms are non-negative; the leader has no interval to ahead.

    DriverLapState::equal

    DriverLapState::not_equal

    fn DriverLapState::not_equal(x : DriverLapState, y : DriverLapState) -> Bool

    ExplanationError

    pub(all) struct ExplanationError {
    code : ExplanationErrorCode
    lap : Int?
    message : String
    } derive(Eq,
    Debug
    )

    A user-facing explanation error; lap identifies a lap-specific failure.

    code is stable for callers and message describes the rejected model or simulation invariant.

    ExplanationError::equal

    ExplanationError::not_equal

    fn ExplanationError::not_equal(x : ExplanationError, y : ExplanationError) -> Bool

    ExplanationErrorCode

    pub(all) enum ExplanationErrorCode {
    InvalidModel
    TyreDeltaFailure
    EmptySimulation
    InconsistentSimulation
    } derive(Eq,
    Debug
    )

    Stable categories for explanation and report-generation failures.

    They distinguish invalid model input, tyre-delta failures, empty replay data, and inconsistencies between a simulation's lap details and summary.

    ExplanationErrorCode::equal

    ExplanationErrorCode::not_equal

    LapImpact

    pub(all) struct LapImpact {
    lap : Int
    lap_time_gain_ms : Int
    cumulative_time_gain_ms : Int
    } derive(Eq,
    Debug
    )

    The actual-versus-simulated effect measured on one lap.

    Positive lap_time_gain_ms means the simulated lap is faster. Positive cumulative_time_gain_ms means the alternative is faster overall at that lap.

    LapImpact::equal

    fn LapImpact::equal(LapImpact, LapImpact) -> Bool

    LapImpact::not_equal

    fn LapImpact::not_equal(x : LapImpact, y : LapImpact) -> Bool

    LapRecord

    pub struct LapRecord {
    lap : Int
    driver : String
    lap_time_ms : Int
    compound : Compound
    tyre_age_laps : Int
    pit : Bool
    track_status : TrackStatus
    weather : Weather
    } derive(Eq,
    Debug
    )

    One validated row in CSV v1.

    lap_time_ms is the complete positive lap duration in milliseconds; pit means an end-of-lap stop whose loss is already included in that duration.

    LapRecord::equal

    fn LapRecord::equal(LapRecord, LapRecord) -> Bool

    LapRecord::not_equal

    fn LapRecord::not_equal(x : LapRecord, y : LapRecord) -> Bool

    ModelError

    pub(all) struct ModelError {
    code : ModelErrorCode
    message : String
    } derive(Eq,
    Debug
    )

    A deterministic, user-facing model error suitable for future CLI output.

    code is stable for programmatic handling and message explains the invalid configuration or calculation input.

    ModelError::equal

    fn ModelError::equal(ModelError, ModelError) -> Bool

    ModelError::not_equal

    fn ModelError::not_equal(x : ModelError, y : ModelError) -> Bool

    ModelErrorCode

    pub(all) enum ModelErrorCode {
    MissingTyreProfile
    DuplicateTyreProfile
    NegativeLinearDegradation
    InvalidCliffAge
    NegativeCliffExtraDegradation
    NegativeGreenPitLoss
    NegativeSafetyCarPitLoss
    InvalidTyreAge
    InvalidLapTime
    NonPositiveResult
    } derive(Eq,
    Debug
    )

    Stable error categories for model configuration and calculations.

    These cover invalid profiles, tyre ages, observed/neutral times, and results.

    ModelErrorCode::equal

    ModelErrorCode::not_equal

    fn ModelErrorCode::not_equal(x : ModelErrorCode, y : ModelErrorCode) -> Bool

    PaceModelConfig

    pub(all) struct PaceModelConfig {
    tyre_profiles : Array[TyreProfile]
    pit_loss_profile : PitLossProfile
    } derive(Eq,
    Debug
    )

    The complete replaceable configuration for the explanatory pace model.

    It must contain one valid profile for every Compound; validate custom values with validate_pace_model_config before simulating or explaining.

    PaceModelConfig::equal

    PaceModelConfig::not_equal

    fn PaceModelConfig::not_equal(x : PaceModelConfig, y : PaceModelConfig) -> Bool

    PitLossProfile

    pub(all) struct PitLossProfile {
    green_pit_loss_ms : Int
    safety_car_pit_loss_ms : Int
    } derive(Eq,
    Debug
    )

    Configurable modeled pit loss for each supported track status.

    Values are non-negative integer milliseconds added only on a modeled pit lap.

    PitLossProfile::equal

    PitLossProfile::not_equal

    fn PitLossProfile::not_equal(x : PitLossProfile, y : PitLossProfile) -> Bool

    PitStopEvent

    pub struct PitStopEvent {
    lap : Int
    driver : String
    outgoing_compound : Compound
    next_compound : Compound?
    } derive(Eq,
    Debug
    )

    Details of an end-of-lap pit stop.

    outgoing_compound was used on lap; next_compound is absent when no following lap exists in the input.

    PitStopEvent::equal

    PitStopEvent::not_equal

    fn PitStopEvent::not_equal(x : PitStopEvent, y : PitStopEvent) -> Bool

    PlannedPitStop

    pub(all) struct PlannedPitStop {
    pit_lap : Int
    next_compound : Compound
    } derive(Eq,
    Debug
    )

    A planned end-of-lap pit stop and the compound used from the following lap.

    pit_lap is one-based and the selected next_compound starts on the next lap.

    PlannedPitStop::equal

    PlannedPitStop::not_equal

    fn PlannedPitStop::not_equal(x : PlannedPitStop, y : PlannedPitStop) -> Bool

    PositionChangeEvent

    pub struct PositionChangeEvent {
    lap : Int
    driver : String
    previous_position : Int
    current_position : Int
    } derive(Eq,
    Debug
    )

    A driver's end-of-lap position change.

    lap is the first completed lap with current_position.

    PositionChangeEvent::equal

    PositionChangeEvent::not_equal

    RaceAnalysis

    pub struct RaceAnalysis {
    drivers : Array[String]
    lap_count : Int
    states : Array[DriverLapState]
    stints : Array[Stint]
    events : Array[RaceEvent]
    } derive(Eq,
    Debug
    )

    The complete deterministic reconstruction produced from validated CSV data.

    drivers is lexicographically sorted; states, stints, and events preserve documented stable order and use integer-millisecond timing.

    RaceAnalysis::equal

    RaceAnalysis::not_equal

    fn RaceAnalysis::not_equal(x : RaceAnalysis, y : RaceAnalysis) -> Bool

    RaceData

    pub struct RaceData {
    records : Array[LapRecord]
    } derive(Eq,
    Debug
    )

    Canonically ordered and validated race input.

    records are sorted by ascending lap and then lexicographic driver name; construct this through parse_race_csv to retain CSV v1 guarantees.

    RaceData::equal

    fn RaceData::equal(RaceData, RaceData) -> Bool

    RaceData::not_equal

    fn RaceData::not_equal(x : RaceData, y : RaceData) -> Bool

    RaceData::to_repr

    RaceError

    pub struct RaceError {
    code : RaceErrorCode
    line : Int
    field : String
    message : String
    } derive(Eq,
    Debug
    )

    A user-facing CSV parse or validation error.

    line is one-based; zero denotes a global input error. field and message identify the rejected CSV value or cross-row constraint, while code is stable.

    RaceError::equal

    fn RaceError::equal(RaceError, RaceError) -> Bool

    RaceError::not_equal

    fn RaceError::not_equal(x : RaceError, y : RaceError) -> Bool

    RaceErrorCode

    pub(all) enum RaceErrorCode {
    EmptyInput
    InvalidHeader
    FieldCount
    UnsupportedCsvSyntax
    InvalidInteger
    InvalidValue
    DuplicateDriverLap
    MissingLap
    InconsistentLaps
    InconsistentTrackStatus
    InconsistentWeather
    InvalidTyreTransition
    } derive(Eq,
    Debug
    )

    Stable categories for CSV v1 parse and validation errors.

    Consumers can branch on these codes instead of parsing error messages.

    RaceErrorCode::equal

    RaceErrorCode::not_equal

    fn RaceErrorCode::not_equal(x : RaceErrorCode, y : RaceErrorCode) -> Bool

    RaceEvent

    pub(all) enum RaceEvent {
    PitStop(PitStopEvent)
    TrackStatusChange(TrackStatusChangeEvent)
    WeatherChange(WeatherChangeEvent)
    PositionChange(PositionChangeEvent)
    } derive(Eq,
    Debug
    )

    A deterministically ordered race event.

    Events are produced by analyze_race in a stable order suitable for reports and deterministic tests.

    RaceEvent::equal

    fn RaceEvent::equal(RaceEvent, RaceEvent) -> Bool

    RaceEvent::not_equal

    fn RaceEvent::not_equal(x : RaceEvent, y : RaceEvent) -> Bool

    SimulatedTyreState

    pub(all) struct SimulatedTyreState {
    compound : Compound
    tyre_age_laps : Int
    pit : Bool
    } derive(Eq,
    Debug
    )

    The target driver's simulated tyre state for one completed lap.

    pit is true when the simulated stop occurs at this lap's end.

    SimulatedTyreState::equal

    SimulatedTyreState::not_equal

    Stint

    pub struct Stint {
    driver : String
    stint_number : Int
    start_lap : Int
    end_lap : Int
    compound : Compound
    start_tyre_age_laps : Int
    end_tyre_age_laps : Int
    lap_count : Int
    total_time_ms : Int
    ended_by_pit : Bool
    } derive(Eq,
    Debug
    )

    A consecutive tyre run, closed by a pit stop or by the final lap.

    Ages describe completed laps; ended_by_pit distinguishes a stop from the end of the input race, and total_time_ms is the stint's observed duration.

    Stint::equal

    fn Stint::equal(Stint, Stint) -> Bool

    Stint::not_equal

    fn Stint::not_equal(x : Stint, y : Stint) -> Bool

    Stint::to_repr

    StrategyError

    pub(all) struct StrategyError {
    code : StrategyErrorCode
    lap : Int?
    message : String
    } derive(Eq,
    Debug
    )

    A user-facing replay error; lap is populated for lap-specific failures.

    code is stable and message provides a diagnostic without requiring callers to inspect internal implementation details.

    StrategyError::equal

    StrategyError::not_equal

    fn StrategyError::not_equal(x : StrategyError, y : StrategyError) -> Bool

    StrategyErrorCode

    pub(all) enum StrategyErrorCode {
    UnknownTargetDriver
    UnknownOpponentDriver
    SameDriver
    InvalidPitLap
    DuplicatePitLap
    InvalidModel
    NeutralLapFailure
    EstimatedLapFailure
    MissingLapState
    } derive(Eq,
    Debug
    )

    Stable categories for invalid replay requests and model failures.

    These distinguish request validation, model errors, neutral-anchor failures, estimation failures, and unexpectedly missing reconstructed states.

    StrategyErrorCode::equal

    StrategyErrorCode::not_equal

    fn StrategyErrorCode::not_equal(x : StrategyErrorCode, y : StrategyErrorCode) -> Bool

    StrategyExplanation

    pub(all) struct StrategyExplanation {
    verdict : StrategyVerdict
    target_driver : String
    opponent_driver : String
    actual_stops : Array[StrategyPitStop]
    simulated_stops : Array[StrategyPitStop]
    performance_crossovers : Array[CrossoverPoint]
    strategic_crossovers : Array[CrossoverPoint]
    opponent_crossovers : Array[CrossoverPoint]
    turning_points : Array[TurningPoint]
    best_lap_gain : LapImpact?
    worst_lap_loss : LapImpact?
    summary : StrategySummary
    laps : Array[StrategyLapComparison]
    } derive(Eq,
    Debug
    )

    A deterministic explanation derived from, but not mutating, a simulation.

    It groups three crossover kinds, stable turning points, lap impacts, and the original summary. Model conclusions are explanatory and use illustrative parameters rather than official F1 data.

    StrategyExplanation::equal

    StrategyExplanation::not_equal

    StrategyLapComparison

    pub(all) struct StrategyLapComparison {
    lap : Int
    neutral_lap_time_ms : Int
    weather : Weather
    track_status : TrackStatus
    actual_lap_time_ms : Int
    actual_cumulative_time_ms : Int
    actual_compound : Compound
    actual_tyre_age_laps : Int
    actual_pit : Bool
    actual_position : Int
    actual_signed_gap_to_opponent_ms : Int
    simulated_lap_time_ms : Int
    simulated_cumulative_time_ms : Int
    simulated_compound : Compound
    simulated_tyre_age_laps : Int
    simulated_pit : Bool
    simulated_position : Int
    simulated_signed_gap_to_opponent_ms : Int
    cumulative_time_gain_ms : Int
    } derive(Eq,
    Debug
    )

    Actual and simulated end-of-lap facts for a target-driver strategy replay.

    All time values are milliseconds. Signed gaps are target cumulative time minus unchanged opponent cumulative time: positive means the target trails. Positive cumulative_time_gain_ms means the simulated alternative is cumulatively faster.

    StrategyLapComparison::equal

    StrategyLapComparison::not_equal

    StrategyPitStop

    pub(all) struct StrategyPitStop {
    lap : Int
    next_compound : Compound?
    } derive(Eq,
    Debug
    )

    A pit stop reconstructed from a completed strategy comparison.

    next_compound is absent only when the stop occurs on the final available lap.

    StrategyPitStop::equal

    StrategyPitStop::not_equal

    fn StrategyPitStop::not_equal(x : StrategyPitStop, y : StrategyPitStop) -> Bool

    StrategyRequest

    pub(all) struct StrategyRequest {
    target_driver : String
    opponent_driver : String
    stops : Array[PlannedPitStop]
    } derive(Eq,
    Debug
    )

    The target, opponent, and complete replacement pit plan for a replay.

    The stop list replaces the target driver's observed stops; it does not append to them. Both driver names must exist and refer to different drivers.

    StrategyRequest::equal

    StrategyRequest::not_equal

    fn StrategyRequest::not_equal(x : StrategyRequest, y : StrategyRequest) -> Bool

    StrategySimulation

    pub(all) struct StrategySimulation {
    request : StrategyRequest
    laps : Array[StrategyLapComparison]
    summary : StrategySummary
    } derive(Eq,
    Debug
    )

    A complete deterministic counterfactual replay result.

    request is the normalized replacement plan, laps are chronological, and summary duplicates final-lap totals for convenient stable consumption.

    StrategySimulation::equal

    StrategySimulation::not_equal

    StrategySummary

    pub(all) struct StrategySummary {
    target_driver : String
    opponent_driver : String
    lap_count : Int
    actual_stop_count : Int
    simulated_stop_count : Int
    actual_total_time_ms : Int
    simulated_total_time_ms : Int
    time_gain_ms : Int
    actual_finish_position : Int
    simulated_finish_position : Int
    positions_gained : Int
    actual_final_signed_gap_to_opponent_ms : Int
    simulated_final_signed_gap_to_opponent_ms : Int
    opponent_gap_gain_ms : Int
    } derive(Eq,
    Debug
    )

    Final comparison between actual and simulated target-driver outcomes.

    Positive time_gain_ms, positions_gained, and opponent_gap_gain_ms mean the alternative improved the target result; signed opponent gaps are positive when the target trails the opponent.

    StrategySummary::equal

    StrategySummary::not_equal

    fn StrategySummary::not_equal(x : StrategySummary, y : StrategySummary) -> Bool

    StrategyVerdict

    pub(all) enum StrategyVerdict {
    Improved
    Worsened
    Unchanged
    } derive(Eq,
    Debug
    )

    Final time-based assessment of a counterfactual strategy.

    Improved corresponds to positive final time gain, Worsened to negative, and Unchanged to exactly zero.

    StrategyVerdict::equal

    StrategyVerdict::not_equal

    fn StrategyVerdict::not_equal(x : StrategyVerdict, y : StrategyVerdict) -> Bool

    TrackStatus

    pub(all) enum TrackStatus {
    Green
    SafetyCar
    } derive(Eq,
    Debug
    )

    Race-control state shared by every driver on a lap.

    CSV v1 requires one shared status per lap; SafetyCar changes the modeled pit-loss selection but does not compress observed input gaps.

    TrackStatus::equal

    fn TrackStatus::equal(TrackStatus, TrackStatus) -> Bool

    TrackStatus::not_equal

    fn TrackStatus::not_equal(x : TrackStatus, y : TrackStatus) -> Bool

    TrackStatusChangeEvent

    pub struct TrackStatusChangeEvent {
    lap : Int
    previous : TrackStatus
    current : TrackStatus
    } derive(Eq,
    Debug
    )

    A track-status change detected between two completed laps.

    lap is the first completed lap using current.

    TrackStatusChangeEvent::equal

    TrackStatusChangeEvent::not_equal

    TurningPoint

    pub(all) struct TurningPoint {
    lap : Int
    kind : TurningPointKind
    impact_ms : Int?
    message : String
    } derive(Eq,
    Debug
    )

    A concise, deterministic event used by the report and future CLI output.

    impact_ms is optional and follows the originating gain or crossover sign convention; points sort deterministically by lap then documented kind order.

    TurningPoint::equal

    TurningPoint::not_equal

    fn TurningPoint::not_equal(x : TurningPoint, y : TurningPoint) -> Bool

    TurningPointKind

    pub(all) enum TurningPointKind {
    TrackStatusChange
    WeatherChange
    ActualPitStop
    SimulatedPitStop
    SafetyCarPitOpportunity
    PerformanceCrossover
    StrategicCrossover
    OpponentCrossover
    RelativePositionGain
    RelativePositionLoss
    BestLapGain
    WorstLapLoss
    } derive(Eq,
    Debug
    )

    Stable categories for explainable strategy turning points.

    Values cover input-state changes, actual/simulated stops, computed Safety Car opportunities, crossover points, relative positions, and lap-time extremes.

    TurningPointKind::equal

    TurningPointKind::not_equal

    fn TurningPointKind::not_equal(x : TurningPointKind, y : TurningPointKind) -> Bool

    TyreProfile

    pub(all) struct TyreProfile {
    compound : Compound
    fresh_deltas : WeatherPaceDeltas
    degradation_ms_per_lap : Int
    cliff_age_laps : Int
    cliff_extra_ms_per_lap : Int
    } derive(Eq,
    Debug
    )

    Configurable pace and degradation parameters for one tyre compound.

    Degradation applies per completed lap after fresh age; cliff extra loss starts after cliff_age_laps. Values are integer milliseconds and demonstration-only.

    TyreProfile::equal

    fn TyreProfile::equal(TyreProfile, TyreProfile) -> Bool

    TyreProfile::not_equal

    fn TyreProfile::not_equal(x : TyreProfile, y : TyreProfile) -> Bool

    Weather

    pub(all) enum Weather {
    Dry
    Damp
    Wet
    } derive(Eq,
    Debug
    )

    Track weather condition shared by every driver on a lap.

    CSV v1 requires one shared weather value per lap and the pace model selects its fresh-tyre delta from this value.

    Weather::equal

    fn Weather::equal(Weather, Weather) -> Bool

    Weather::not_equal

    fn Weather::not_equal(x : Weather, y : Weather) -> Bool

    Weather::to_repr

    WeatherChangeEvent

    pub struct WeatherChangeEvent {
    lap : Int
    previous : Weather
    current : Weather
    } derive(Eq,
    Debug
    )

    A weather change detected between two completed laps.

    lap is the first completed lap using current.

    WeatherChangeEvent::equal

    WeatherChangeEvent::not_equal

    WeatherPaceDeltas

    pub(all) struct WeatherPaceDeltas {
    dry_ms : Int
    damp_ms : Int
    wet_ms : Int
    } derive(Eq,
    Debug
    )

    Demonstration fresh-tyre pace deltas by weather, in integer milliseconds.

    Positive values make a lap slower than its neutral anchor; negative values make it faster. These replaceable values are illustrative, not official F1 data.

    WeatherPaceDeltas::equal

    WeatherPaceDeltas::not_equal

    fn WeatherPaceDeltas::not_equal(x : WeatherPaceDeltas, y : WeatherPaceDeltas) -> Bool

    analyze_race

    fn analyze_race(data : RaceData) -> RaceAnalysis

    Reconstruct a deterministic end-of-lap timeline from validated CSV v1 data.

    data must come from parse_race_csv; the result contains cumulative milliseconds, positions, leader gaps, intervals, stints, and stable events. A positive signed driver-to-driver gap means the named driver trails.

    cli_help

    fn cli_help() -> String

    Return the stable, human-readable CLI usage text.

    This has no side effects and describes the executable syntax, PLAN grammar, supported compounds, output option, and help option.

    default_pace_model_config

    fn default_pace_model_config() -> PaceModelConfig

    Return the centralized, replaceable demonstration configuration for M3.

    Values are integer milliseconds and are illustrative teaching assumptions, not official F1 data for any circuit, tyre, team, or driver.

    derive_neutral_lap_time_ms

    fn derive_neutral_lap_time_ms(config : PaceModelConfig, observed_lap_time_ms : Int, compound : Compound, tyre_age_laps : Int, weather : Weather, track_status : TrackStatus, pit : Bool) -> Result[Int, ModelError]

    Derive a positive neutral anchor by removing modeled pace and pit effects.

    This is the inverse of estimate_lap_time_ms for the same model inputs. It preserves unmodeled observed effects in the anchor and returns ModelError for invalid input or a non-positive derived time.

    estimate_lap_time_ms

    fn estimate_lap_time_ms(config : PaceModelConfig, neutral_lap_time_ms : Int, compound : Compound, tyre_age_laps : Int, weather : Weather, track_status : TrackStatus, pit : Bool) -> Result[Int, ModelError]

    Estimate observed lap time from a positive neutral anchor and model conditions.

    Adds the tyre pace delta and, when pit is true, the configured track-status pit loss. Returns a positive integer-millisecond lap time or ModelError for invalid model input or a non-positive result.

    explain_strategy

    fn explain_strategy(config : PaceModelConfig, simulation : StrategySimulation) -> Result[StrategyExplanation, ExplanationError]

    Explain one completed M4 simulation without replaying or mutating it.

    Performance crossovers compare modeled non-pit lap pace, strategic crossovers compare cumulative time gain, and opponent crossovers compare the simulated signed gap to the unchanged opponent. Positive time gain means the alternative improves the target result. Returns ExplanationError for an invalid model or inconsistent simulation; model values remain illustrative, not official F1 data.

    find_lap_state

    fn find_lap_state(analysis : RaceAnalysis, driver : String, lap : Int) -> DriverLapState?

    Find one driver's end-of-lap state, or None when the driver or lap is absent.

    analysis is not mutated. The returned state is the end-of-lap state for the exact driver and one-based lap query.

    format_duration_ms

    fn format_duration_ms(milliseconds : Int) -> String

    Format an integer-millisecond duration without a leading positive sign.

    Negative input keeps a leading minus sign; non-negative input has no plus sign. The stable output always uses seconds with three decimal places.

    format_signed_ms

    fn format_signed_ms(milliseconds : Int) -> String

    Format an integer-millisecond signed gain or gap with an explicit sign.

    Positive values render with +, negative values with -, and zero as 0.000 s; callers supply the domain-specific gain or gap semantics.

    generate_strategy_report

    fn generate_strategy_report(csv : String, target_driver : String, opponent_driver : String, stops : Array[PlannedPitStop]) -> Result[String, CliPipelineError]

    Convert validated CSV text into the stable M5 Markdown strategy report.

    The pure pipeline parses CSV, applies the default illustrative pace model, simulates stops for target_driver, explains the result against opponent_driver, and renders Markdown. Returns CliPipelineError with a stable phase code for CSV, simulation, or explanation failure.

    parse_cli_arguments

    fn parse_cli_arguments(arguments : Array[String]) -> Result[CliOptions, CliError]

    Parse program arguments after the executable prefix in any option order.

    Requires one CSV path plus --driver, --opponent, and --stops; -o or --output is optional. Returns CliOptions without file I/O or a structured CliError for missing, duplicate, unknown, or malformed input.

    parse_race_csv

    fn parse_race_csv(input : String) -> Result[RaceData, RaceError]

    Parse, validate, and canonically sort a CSV v1 race input string.

    Accepts CRLF or LF, ignores blank lines, trims ordinary cell whitespace, and returns records sorted by lap then driver. It rejects unsupported quoted CSV syntax, malformed fields, enum values, and cross-row race constraints with a structured RaceError containing a CSV line and field.

    parse_stop_plan

    fn parse_stop_plan(plan : String) -> Result[Array[PlannedPitStop], CliError]

    Parse none or a comma-separated sequence of LAP:COMPOUND pit stops.

    Each lap must be a positive decimal integer and each compound is one of the CSV v1 uppercase values. Duplicate laps and malformed items return a structured CliError; lap range and ordering remain M4 validation concerns.

    pit_loss_ms

    fn pit_loss_ms(config : PaceModelConfig, track_status : TrackStatus) -> Result[Int, ModelError]

    Return the configured pit loss for a track status after validating the config.

    The returned non-negative integer is milliseconds added on a pit lap. Returns ModelError when config is invalid.

    render_strategy_markdown

    fn render_strategy_markdown(explanation : StrategyExplanation) -> String

    Render a stable English Markdown report from an already-derived explanation.

    The output is deterministic and contains verdict, strategy comparison, crossover sections, turning points, a lap trace, and model limitations. Signed gains are positive when the simulated alternative improves time.

    signed_gap_ms

    fn signed_gap_ms(analysis : RaceAnalysis, driver : String, opponent : String, lap : Int) -> Int?

    Return driver time minus opponent time at a lap, or None for an unknown query.

    The result is in milliseconds: positive means driver trails opponent, negative means driver leads, and zero means equal cumulative time.

    simulate_strategy

    fn simulate_strategy(data : RaceData, config : PaceModelConfig, request : StrategyRequest) -> Result[StrategySimulation, StrategyError]

    Replay a complete replacement pit plan for one target driver against real rivals.

    request.stops replaces, rather than augments, the target driver's observed stops; other drivers retain their observed timeline. The returned comparisons use milliseconds where positive cumulative_time_gain_ms means the simulated alternative is faster. Returns a structured StrategyError for invalid model configuration, unknown drivers, illegal stops, or unreconstructable laps.

    stints_for_driver

    fn stints_for_driver(analysis : RaceAnalysis, driver : String) -> Array[Stint]

    Return stints in number order for one driver; an unknown driver returns an empty array.

    Each stint is reconstructed from validated end-of-lap states and is returned in chronological stint-number order without mutating analysis.

    tyre_pace_delta_ms

    fn tyre_pace_delta_ms(config : PaceModelConfig, compound : Compound, tyre_age_laps : Int, weather : Weather) -> Result[Int, ModelError]

    Calculate fresh pace plus linear and cliff degradation in integer milliseconds.

    tyre_age_laps is the completed-lap age and must be at least one. A positive result makes the lap slower than the neutral anchor; a negative result makes it faster. Returns ModelError for an invalid configuration, missing profile, or invalid tyre age.

    validate_pace_model_config

    fn validate_pace_model_config(config : PaceModelConfig) -> Result[Unit, ModelError]

    Validate that the model configuration has exactly one valid profile per compound.

    Returns Ok(()) only when every supported compound has exactly one profile, degradation and cliff values are valid, and both pit losses are non-negative. Otherwise returns a structured ModelError with a stable error code.