use crate::types::Sector;
use nalgebra as na;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Circuit {
pub id: String,
pub name: String,
pub country: String,
pub length: f32,
pub num_turns: u8,
pub lap_record: f32,
pub characteristics: TrackCharacteristics,
pub sectors: Vec<SectorInfo>,
pub drs_zones: Vec<DrsZone>,
pub typical_race_laps: u16,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TrackCharacteristics {
pub tire_severity: f32,
pub fuel_consumption: f32,
pub overtaking_difficulty: f32,
pub downforce_level: f32,
pub average_speed: f32,
pub maximum_speed: f32,
pub elevation_change: f32,
pub weather_variability: f32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SectorInfo {
pub sector: Sector,
pub length: f32,
pub average_time: f32,
pub sector_type: SectorType,
pub key_corners: Vec<Corner>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum SectorType {
Straights, Technical, HighSpeed, MixedSpeed, }
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Corner {
pub number: u8,
pub name: Option<String>,
pub apex_speed: f32, pub entry_speed: f32, pub exit_speed: f32, pub corner_type: CornerType,
pub gear: u8,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum CornerType {
Slow, Medium, Fast, VeryFast, Hairpin,
Chicane,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DrsZone {
pub zone_id: u8,
pub detection_point: f32,
pub activation_point: f32,
pub end_point: f32,
pub expected_time_gain: f32,
}
impl Circuit {
pub fn total_length(&self) -> f32 {
self.length
}
pub fn race_distance_km(&self) -> f32 {
(self.length * self.typical_race_laps as f32) / 1000.0
}
pub fn sector(&self, sector: Sector) -> Option<&SectorInfo> {
self.sectors.iter().find(|s| s.sector == sector)
}
pub fn has_drs(&self) -> bool {
!self.drs_zones.is_empty()
}
pub fn famous_circuits() -> Vec<Circuit> {
vec![
Self::monaco(),
Self::spa(),
Self::silverstone(),
Self::monza(),
Self::suzuka(),
]
}
pub fn monaco() -> Self {
Circuit {
id: "monaco".to_string(),
name: "Circuit de Monaco".to_string(),
country: "Monaco".to_string(),
length: 3337.0,
num_turns: 19,
lap_record: 70.246,
characteristics: TrackCharacteristics {
tire_severity: 0.8,
fuel_consumption: 0.85,
overtaking_difficulty: 0.95,
downforce_level: 0.95,
average_speed: 160.0,
maximum_speed: 290.0,
elevation_change: 42.0,
weather_variability: 0.3,
},
sectors: vec![],
drs_zones: vec![],
typical_race_laps: 78,
}
}
pub fn spa() -> Self {
Circuit {
id: "spa".to_string(),
name: "Circuit de Spa-Francorchamps".to_string(),
country: "Belgium".to_string(),
length: 7004.0,
num_turns: 19,
lap_record: 103.458,
characteristics: TrackCharacteristics {
tire_severity: 1.2,
fuel_consumption: 1.3,
overtaking_difficulty: 0.4,
downforce_level: 0.6,
average_speed: 237.0,
maximum_speed: 340.0,
elevation_change: 105.0,
weather_variability: 0.9,
},
sectors: vec![],
drs_zones: vec![],
typical_race_laps: 44,
}
}
pub fn silverstone() -> Self {
Circuit {
id: "silverstone".to_string(),
name: "Silverstone Circuit".to_string(),
country: "United Kingdom".to_string(),
length: 5891.0,
num_turns: 18,
lap_record: 86.089,
characteristics: TrackCharacteristics {
tire_severity: 1.1,
fuel_consumption: 1.1,
overtaking_difficulty: 0.5,
downforce_level: 0.7,
average_speed: 230.0,
maximum_speed: 330.0,
elevation_change: 30.0,
weather_variability: 0.7,
},
sectors: vec![],
drs_zones: vec![],
typical_race_laps: 52,
}
}
pub fn monza() -> Self {
Circuit {
id: "monza".to_string(),
name: "Autodromo Nazionale di Monza".to_string(),
country: "Italy".to_string(),
length: 5793.0,
num_turns: 11,
lap_record: 81.046,
characteristics: TrackCharacteristics {
tire_severity: 0.9,
fuel_consumption: 1.4,
overtaking_difficulty: 0.3,
downforce_level: 0.3,
average_speed: 264.0,
maximum_speed: 360.0,
elevation_change: 28.0,
weather_variability: 0.4,
},
sectors: vec![],
drs_zones: vec![],
typical_race_laps: 53,
}
}
pub fn suzuka() -> Self {
Circuit {
id: "suzuka".to_string(),
name: "Suzuka International Racing Course".to_string(),
country: "Japan".to_string(),
length: 5807.0,
num_turns: 18,
lap_record: 87.435,
characteristics: TrackCharacteristics {
tire_severity: 1.3,
fuel_consumption: 1.2,
overtaking_difficulty: 0.7,
downforce_level: 0.8,
average_speed: 226.0,
maximum_speed: 315.0,
elevation_change: 43.0,
weather_variability: 0.6,
},
sectors: vec![],
drs_zones: vec![],
typical_race_laps: 53,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_circuit() {
let monaco = Circuit::monaco();
assert_eq!(monaco.id, "monaco");
assert!(monaco.race_distance_km() > 250.0);
assert!(monaco.characteristics.overtaking_difficulty > 0.9);
}
#[test]
fn test_famous_circuits() {
let circuits = Circuit::famous_circuits();
assert_eq!(circuits.len(), 5);
assert!(circuits.iter().any(|c| c.id == "spa"));
}
}