Skip to main content

subtr_actor/stats/accumulators/
ball_third.rs

1use super::*;
2
3#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
4pub struct BallThirdStats {
5    pub tracked_time: f32,
6    pub team_zero_third_time: f32,
7    pub neutral_third_time: f32,
8    pub team_one_third_time: f32,
9    #[serde(default, skip_serializing_if = "LabeledFloatSums::is_empty")]
10    pub labeled_time: LabeledFloatSums,
11}
12
13impl BallThirdStats {
14    pub fn team_zero_third_pct(&self) -> f32 {
15        if self.tracked_time == 0.0 {
16            0.0
17        } else {
18            self.team_zero_third_time * 100.0 / self.tracked_time
19        }
20    }
21
22    pub fn team_one_third_pct(&self) -> f32 {
23        if self.tracked_time == 0.0 {
24            0.0
25        } else {
26            self.team_one_third_time * 100.0 / self.tracked_time
27        }
28    }
29
30    pub fn neutral_third_pct(&self) -> f32 {
31        if self.tracked_time == 0.0 {
32            0.0
33        } else {
34            self.neutral_third_time * 100.0 / self.tracked_time
35        }
36    }
37
38    pub fn time_with_labels(&self, labels: &[StatLabel]) -> f32 {
39        self.labeled_time.sum_matching(labels)
40    }
41
42    pub fn for_team(&self, is_team_zero: bool) -> BallThirdTeamStats {
43        let (defensive_third_time, offensive_third_time) = if is_team_zero {
44            (self.team_zero_third_time, self.team_one_third_time)
45        } else {
46            (self.team_one_third_time, self.team_zero_third_time)
47        };
48
49        let mut labeled_time = LabeledFloatSums::default();
50        for entry in &self.labeled_time.entries {
51            labeled_time.add(
52                entry
53                    .labels
54                    .iter()
55                    .map(|label| team_relative_ball_third_label(label, is_team_zero)),
56                entry.value,
57            );
58        }
59
60        BallThirdTeamStats {
61            tracked_time: self.tracked_time,
62            defensive_third_time,
63            neutral_third_time: self.neutral_third_time,
64            offensive_third_time,
65            labeled_time,
66        }
67    }
68}
69
70#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, ts_rs::TS)]
71#[ts(export)]
72pub struct BallThirdTeamStats {
73    pub tracked_time: f32,
74    pub defensive_third_time: f32,
75    pub neutral_third_time: f32,
76    pub offensive_third_time: f32,
77    #[serde(default, skip_serializing_if = "LabeledFloatSums::is_empty")]
78    pub labeled_time: LabeledFloatSums,
79}
80
81#[derive(Debug, Clone, Default, PartialEq)]
82pub struct BallThirdStatsAccumulator {
83    stats: BallThirdStats,
84}
85
86impl BallThirdStatsAccumulator {
87    pub fn new() -> Self {
88        Self::default()
89    }
90
91    pub fn stats(&self) -> &BallThirdStats {
92        &self.stats
93    }
94
95    pub fn apply_event(&mut self, event: &BallThirdEvent) {
96        if !event.active {
97            return;
98        }
99
100        self.stats.tracked_time += event.duration;
101        let field_third = match event.field_third.as_str() {
102            "team_zero_third" => {
103                self.stats.team_zero_third_time += event.duration;
104                "team_zero_third"
105            }
106            "team_one_third" => {
107                self.stats.team_one_third_time += event.duration;
108                "team_one_third"
109            }
110            "neutral_third" => {
111                self.stats.neutral_third_time += event.duration;
112                "neutral_third"
113            }
114            _ => return,
115        };
116        self.stats
117            .labeled_time
118            .add([StatLabel::new("field_third", field_third)], event.duration);
119    }
120}
121
122fn team_relative_ball_third_label(label: &StatLabel, is_team_zero: bool) -> StatLabel {
123    match (label.key, label.value) {
124        ("field_third", "team_zero_third") => StatLabel::new(
125            "field_third",
126            if is_team_zero {
127                "defensive_third"
128            } else {
129                "offensive_third"
130            },
131        ),
132        ("field_third", "team_one_third") => StatLabel::new(
133            "field_third",
134            if is_team_zero {
135                "offensive_third"
136            } else {
137                "defensive_third"
138            },
139        ),
140        _ => label.clone(),
141    }
142}