use std::collections::HashMap; pub struct ScoringConfig { pub r_double: f32, pub r_treble: f32, pub r_outer_bull: f32, pub r_inner_bull: f32, pub w_double_treble: f32, } impl Default for ScoringConfig { fn default() -> Self { Self { r_double: 0.170, r_treble: 0.1074, r_outer_bull: 0.0159, r_inner_bull: 0.00635, w_double_treble: 0.01, } } } pub fn get_board_dict() -> HashMap { let mut m = HashMap::new(); // BDO standard mapping based on degrees let slices = [ "6", "13", "4", "18", "1", "20", "5", "12", "9", "14", "11", "8", "16", "7", "19", "3", "17", "2", "15", "10", ]; for (i, &s) in slices.iter().enumerate() { m.insert(i as i32, s); } m } pub fn calculate_dart_score( cal_pts: &[[f32; 2]], dart_pt: &[f32; 2], config: &ScoringConfig, ) -> (i32, String) { // 1. Calculate Center (Average of 4 calibration points) let cx = cal_pts.iter().map(|p| p[0]).sum::() / 4.0; let cy = cal_pts.iter().map(|p| p[1]).sum::() / 4.0; // 2. Calculate average radius to boundary (doubles wire) let avg_r_px = cal_pts .iter() .map(|p| ((p[0] - cx).powi(2) + (p[1] - cy).powi(2)).sqrt()) .sum::() / 4.0; // 3. Relative distance of dart from center let dx = dart_pt[0] - cx; let dy = dart_pt[1] - cy; let dist_px = (dx.powi(2) + dy.powi(2)).sqrt(); // Scale distance relative to BDO double radius let dist_scaled = (dist_px / avg_r_px) * config.r_double; // 4. Calculate Angle (0 is 3 o'clock, CCW) let mut angle_deg = (-dy).atan2(dx).to_degrees(); if angle_deg < 0.0 { angle_deg += 360.0; } // Center sectors by adding 9 degrees (half-sector width) let board_dict = get_board_dict(); let sector_idx = (((angle_deg + 9.0) / 18.0).floor() as i32) % 20; let sector_num = board_dict.get(§or_idx).unwrap_or(&"0"); // 5. Determine multipliers based on scaled distance let r_t = config.r_treble; let r_d = config.r_double; let w = config.w_double_treble; let r_ib = config.r_inner_bull; let r_ob = config.r_outer_bull; if dist_scaled > r_d { (0, "Miss".to_string()) } else if dist_scaled <= r_ib { (50, "DB".to_string()) } else if dist_scaled <= r_ob { (25, "B".to_string()) } else if dist_scaled <= r_d && dist_scaled > (r_d - w) { let val = sector_num.parse::().unwrap_or(0); (val * 2, format!("D{}", sector_num)) } else if dist_scaled <= r_t && dist_scaled > (r_t - w) { let val = sector_num.parse::().unwrap_or(0); (val * 3, format!("T{}", sector_num)) } else { let val = sector_num.parse::().unwrap_or(0); (val, sector_num.to_string()) } }