File size: 2,900 Bytes
9874885
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a6578e7
 
 
 
9874885
 
 
 
 
 
a6578e7
 
 
 
 
9874885
 
 
 
 
a6578e7
 
9874885
a6578e7
 
9874885
 
 
 
 
a6578e7
9874885
 
 
 
 
a6578e7
 
 
 
 
9874885
a6578e7
9874885
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
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<i32, &'static str> {
    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::<f32>() / 4.0;
    let cy = cal_pts.iter().map(|p| p[1]).sum::<f32>() / 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::<f32>()
        / 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(&sector_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::<i32>().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::<i32>().unwrap_or(0);
        (val * 3, format!("T{}", sector_num))
    } else {
        let val = sector_num.parse::<i32>().unwrap_or(0);
        (val, sector_num.to_string())
    }
}