Skip to main content

subtr_actor/stats/accumulators/
rush.rs

1use super::*;
2
3pub(crate) const RUSH_TEAM_LABELS: [StatLabel; 2] = [
4    StatLabel::new("team", "team_zero"),
5    StatLabel::new("team", "team_one"),
6];
7pub(crate) const RUSH_ATTACKER_LABELS: [StatLabel; 2] = [
8    StatLabel::new("attackers", "2"),
9    StatLabel::new("attackers", "3"),
10];
11pub(crate) const RUSH_DEFENDER_LABELS: [StatLabel; 3] = [
12    StatLabel::new("defenders", "1"),
13    StatLabel::new("defenders", "2"),
14    StatLabel::new("defenders", "3"),
15];
16
17/// Per-team accumulated rush counts by attacker/defender numbers.
18#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
19pub struct RushStats {
20    pub team_zero_count: u32,
21    pub team_zero_two_v_one_count: u32,
22    pub team_zero_two_v_two_count: u32,
23    pub team_zero_two_v_three_count: u32,
24    pub team_zero_three_v_one_count: u32,
25    pub team_zero_three_v_two_count: u32,
26    pub team_zero_three_v_three_count: u32,
27    pub team_one_count: u32,
28    pub team_one_two_v_one_count: u32,
29    pub team_one_two_v_two_count: u32,
30    pub team_one_two_v_three_count: u32,
31    pub team_one_three_v_one_count: u32,
32    pub team_one_three_v_two_count: u32,
33    pub team_one_three_v_three_count: u32,
34    #[serde(default, skip_serializing_if = "LabeledCounts::is_empty")]
35    pub labeled_rush_counts: LabeledCounts,
36}
37
38impl RushStats {
39    pub(crate) fn record(&mut self, event: &RushEvent) {
40        self.labeled_rush_counts.increment(event.labels());
41        self.sync_legacy_counts();
42    }
43
44    pub fn rush_count_with_labels(&self, labels: &[StatLabel]) -> u32 {
45        self.labeled_rush_counts.count_matching(labels)
46    }
47
48    pub fn complete_labeled_rush_counts(&self) -> LabeledCounts {
49        LabeledCounts::complete_from_label_sets(
50            &[
51                &RUSH_TEAM_LABELS,
52                &RUSH_ATTACKER_LABELS,
53                &RUSH_DEFENDER_LABELS,
54            ],
55            &self.labeled_rush_counts,
56        )
57    }
58
59    pub fn with_complete_labeled_rush_counts(mut self) -> Self {
60        self.labeled_rush_counts = self.complete_labeled_rush_counts();
61        self
62    }
63
64    fn team_count(&self, is_team_zero: bool) -> u32 {
65        self.rush_count_with_labels(&[rush_team_label(is_team_zero)])
66    }
67
68    fn matchup_count(&self, is_team_zero: bool, attackers: usize, defenders: usize) -> u32 {
69        self.rush_count_with_labels(&[
70            rush_team_label(is_team_zero),
71            rush_attackers_label(attackers),
72            rush_defenders_label(defenders),
73        ])
74    }
75
76    fn sync_legacy_counts(&mut self) {
77        self.team_zero_count = self.team_count(true);
78        self.team_zero_two_v_one_count = self.matchup_count(true, 2, 1);
79        self.team_zero_two_v_two_count = self.matchup_count(true, 2, 2);
80        self.team_zero_two_v_three_count = self.matchup_count(true, 2, 3);
81        self.team_zero_three_v_one_count = self.matchup_count(true, 3, 1);
82        self.team_zero_three_v_two_count = self.matchup_count(true, 3, 2);
83        self.team_zero_three_v_three_count = self.matchup_count(true, 3, 3);
84        self.team_one_count = self.team_count(false);
85        self.team_one_two_v_one_count = self.matchup_count(false, 2, 1);
86        self.team_one_two_v_two_count = self.matchup_count(false, 2, 2);
87        self.team_one_two_v_three_count = self.matchup_count(false, 2, 3);
88        self.team_one_three_v_one_count = self.matchup_count(false, 3, 1);
89        self.team_one_three_v_two_count = self.matchup_count(false, 3, 2);
90        self.team_one_three_v_three_count = self.matchup_count(false, 3, 3);
91    }
92
93    pub fn for_team(&self, is_team_zero: bool) -> RushTeamStats {
94        if is_team_zero {
95            RushTeamStats {
96                count: self.team_zero_count,
97                two_v_one_count: self.team_zero_two_v_one_count,
98                two_v_two_count: self.team_zero_two_v_two_count,
99                two_v_three_count: self.team_zero_two_v_three_count,
100                three_v_one_count: self.team_zero_three_v_one_count,
101                three_v_two_count: self.team_zero_three_v_two_count,
102                three_v_three_count: self.team_zero_three_v_three_count,
103            }
104        } else {
105            RushTeamStats {
106                count: self.team_one_count,
107                two_v_one_count: self.team_one_two_v_one_count,
108                two_v_two_count: self.team_one_two_v_two_count,
109                two_v_three_count: self.team_one_two_v_three_count,
110                three_v_one_count: self.team_one_three_v_one_count,
111                three_v_two_count: self.team_one_three_v_two_count,
112                three_v_three_count: self.team_one_three_v_three_count,
113            }
114        }
115    }
116}
117
118/// Per-team accumulated rush stats.
119#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, ts_rs::TS)]
120#[ts(export)]
121pub struct RushTeamStats {
122    pub count: u32,
123    pub two_v_one_count: u32,
124    pub two_v_two_count: u32,
125    pub two_v_three_count: u32,
126    pub three_v_one_count: u32,
127    pub three_v_two_count: u32,
128    pub three_v_three_count: u32,
129}
130
131/// Accumulates rush stats over the replay.
132#[derive(Debug, Clone, Default, PartialEq)]
133pub struct RushStatsAccumulator {
134    stats: RushStats,
135}
136
137impl RushStatsAccumulator {
138    pub fn new() -> Self {
139        Self::default()
140    }
141
142    pub fn stats(&self) -> &RushStats {
143        &self.stats
144    }
145
146    pub fn apply_event(&mut self, event: &RushEvent) {
147        self.stats.record(event);
148    }
149}