subtr_actor/stats/analysis_graph/
mod.rs1#![allow(dead_code)]
2
3use std::collections::HashSet;
4
5use crate::Collector;
6use crate::{SubtrActorError, SubtrActorErrorVariant, SubtrActorResult};
7
8pub mod graph;
9pub use graph::{
10 AnalysisDependency, AnalysisGraph, AnalysisNode, AnalysisNodeDyn, AnalysisStateContext,
11 AnalysisStateRef,
12};
13
14#[macro_use]
15mod node_macros;
16
17mod collector;
18mod nodes;
19
20use crate::stats::calculators::FrameInput;
21
22#[allow(unused_imports)]
23pub use collector::AnalysisNodeCollector;
24#[allow(unused_imports)]
25pub use nodes::*;
26
27pub(crate) fn boxed_analysis_node_by_name(name: &str) -> Option<Box<dyn AnalysisNodeDyn>> {
28 match name {
29 "core" => Some(nodes::match_stats::boxed_default()),
30 "backboard" => Some(nodes::backboard::boxed_default()),
31 "ceiling_shot" => Some(nodes::ceiling_shot::boxed_default()),
32 "center" => Some(nodes::center::boxed_default()),
33 "double_tap" => Some(nodes::double_tap::boxed_default()),
34 "fifty_fifty" => Some(nodes::fifty_fifty::boxed_default()),
35 "possession" => Some(nodes::possession::boxed_default()),
36 "pressure" => Some(nodes::pressure::boxed_default()),
37 "rotation" => Some(nodes::rotation::boxed_default()),
38 "rush" => Some(nodes::rush::boxed_default()),
39 "touch" => Some(nodes::touch::boxed_default()),
40 "wall_aerial" => Some(nodes::wall_aerial::boxed_default()),
41 "wall_aerial_shot" => Some(nodes::wall_aerial_shot::boxed_default()),
42 "whiff" => Some(nodes::whiff::boxed_default()),
43 "wavedash" => Some(nodes::wavedash::boxed_default()),
44 "speed_flip" => Some(nodes::speed_flip::boxed_default()),
45 "half_flip" => Some(nodes::half_flip::boxed_default()),
46 "half_volley" => Some(nodes::half_volley::boxed_default()),
47 "flick" => Some(nodes::flick::boxed_default()),
48 "aerial_goal" => Some(nodes::goal_tags::boxed_aerial_goal()),
49 "high_aerial_goal" => Some(nodes::goal_tags::boxed_high_aerial_goal()),
50 "long_distance_goal" => Some(nodes::goal_tags::boxed_long_distance_goal()),
51 "own_half_goal" => Some(nodes::goal_tags::boxed_own_half_goal()),
52 "empty_net_goal" => Some(nodes::goal_tags::boxed_empty_net_goal()),
53 "counter_attack_goal" => Some(nodes::goal_tags::boxed_counter_attack_goal()),
54 "flick_goal" => Some(nodes::goal_tags::boxed_flick_goal()),
55 "double_tap_goal" => Some(nodes::goal_tags::boxed_double_tap_goal()),
56 "one_timer_goal" => Some(nodes::goal_tags::boxed_one_timer_goal()),
57 "air_dribble_goal" => Some(nodes::goal_tags::boxed_air_dribble_goal()),
58 "flip_reset_goal" => Some(nodes::goal_tags::boxed_flip_reset_goal()),
59 "half_volley_goal" => Some(nodes::goal_tags::boxed_half_volley_goal()),
60 "musty_flick" => Some(nodes::musty_flick::boxed_default()),
61 "one_timer" => Some(nodes::one_timer::boxed_default()),
62 "pass" => Some(nodes::pass::boxed_default()),
63 "dodge_reset" => Some(nodes::dodge_reset::boxed_default()),
64 "ball_carry" => Some(nodes::ball_carry::boxed_default()),
65 "boost" => Some(nodes::boost::boxed_default()),
66 "bump" => Some(nodes::bump::boxed_default()),
67 "movement" => Some(nodes::movement::boxed_default()),
68 "positioning" => Some(nodes::positioning::boxed_default()),
69 "powerslide" => Some(nodes::powerslide::boxed_default()),
70 "demo" => Some(nodes::demo::boxed_default()),
71 _ => None,
72 }
73}
74
75pub fn graph_with_builtin_analysis_nodes<I, S>(names: I) -> SubtrActorResult<AnalysisGraph>
76where
77 I: IntoIterator<Item = S>,
78 S: AsRef<str>,
79{
80 let mut graph = AnalysisGraph::new().with_input_state_type::<FrameInput>();
81 graph.push_boxed_node(nodes::live_play::boxed_default());
82 let mut seen = HashSet::new();
83 for name in names {
84 let name = name.as_ref();
85 if !seen.insert(name.to_owned()) {
86 continue;
87 }
88 graph.push_boxed_node(boxed_analysis_node_by_name(name).ok_or_else(|| {
89 SubtrActorError::new(SubtrActorErrorVariant::UnknownStatsModuleName(
90 name.to_owned(),
91 ))
92 })?);
93 }
94 Ok(graph)
95}
96
97pub fn collect_analysis_graph_for_replay(
98 replay: &boxcars::Replay,
99 graph: AnalysisGraph,
100) -> SubtrActorResult<AnalysisGraph> {
101 let collector = collector::AnalysisNodeCollector::new(graph).process_replay(replay)?;
102 Ok(collector.into_graph())
103}
104
105pub fn collect_builtin_analysis_graph_for_replay<I, S>(
106 replay: &boxcars::Replay,
107 names: I,
108) -> SubtrActorResult<AnalysisGraph>
109where
110 I: IntoIterator<Item = S>,
111 S: AsRef<str>,
112{
113 collect_analysis_graph_for_replay(replay, graph_with_builtin_analysis_nodes(names)?)
114}
115
116pub fn all_analysis_nodes() -> Vec<Box<dyn AnalysisNodeDyn>> {
117 vec![
118 nodes::backboard::boxed_default(),
119 nodes::ball_carry::boxed_default(),
120 nodes::boost::boxed_default(),
121 nodes::bump::boxed_default(),
122 nodes::ceiling_shot::boxed_default(),
123 nodes::center::boxed_default(),
124 nodes::demo::boxed_default(),
125 nodes::dodge_reset::boxed_default(),
126 nodes::double_tap::boxed_default(),
127 nodes::fifty_fifty::boxed_default(),
128 nodes::match_stats::boxed_default(),
129 nodes::movement::boxed_default(),
130 nodes::flick::boxed_default(),
131 nodes::goal_tags::boxed_aerial_goal(),
132 nodes::goal_tags::boxed_high_aerial_goal(),
133 nodes::goal_tags::boxed_long_distance_goal(),
134 nodes::goal_tags::boxed_own_half_goal(),
135 nodes::goal_tags::boxed_empty_net_goal(),
136 nodes::goal_tags::boxed_counter_attack_goal(),
137 nodes::goal_tags::boxed_flick_goal(),
138 nodes::goal_tags::boxed_double_tap_goal(),
139 nodes::goal_tags::boxed_one_timer_goal(),
140 nodes::goal_tags::boxed_air_dribble_goal(),
141 nodes::goal_tags::boxed_flip_reset_goal(),
142 nodes::goal_tags::boxed_half_volley_goal(),
143 nodes::musty_flick::boxed_default(),
144 nodes::one_timer::boxed_default(),
145 nodes::pass::boxed_default(),
146 nodes::positioning::boxed_default(),
147 nodes::possession::boxed_default(),
148 nodes::powerslide::boxed_default(),
149 nodes::pressure::boxed_default(),
150 nodes::rotation::boxed_default(),
151 nodes::rush::boxed_default(),
152 nodes::settings::boxed_default(),
153 nodes::speed_flip::boxed_default(),
154 nodes::half_flip::boxed_default(),
155 nodes::half_volley::boxed_default(),
156 nodes::wavedash::boxed_default(),
157 nodes::touch::boxed_default(),
158 nodes::wall_aerial::boxed_default(),
159 nodes::wall_aerial_shot::boxed_default(),
160 nodes::whiff::boxed_default(),
161 ]
162}
163
164pub fn graph_with_all_analysis_nodes() -> AnalysisGraph {
165 let mut graph = AnalysisGraph::new().with_input_state_type::<FrameInput>();
166 graph.push_boxed_node(nodes::live_play::boxed_default());
167 for node in all_analysis_nodes() {
168 graph.push_boxed_node(node);
169 }
170 graph
171}
172
173#[cfg(test)]
174#[path = "module_tests.rs"]
175mod tests;