1use std::time::Duration;
7
8#[derive(Debug, Clone)]
10pub struct SearchStatistics {
11 pub iterations: usize,
13
14 pub total_time: Duration,
16
17 pub tree_size: usize,
19
20 pub max_depth: usize,
22
23 pub stopped_early: bool,
25
26 pub node_pool_stats: Option<NodePoolStats>,
28}
29
30#[derive(Debug, Clone)]
32pub struct NodePoolStats {
33 pub capacity: usize,
35
36 pub available: usize,
38
39 pub total_allocated: usize,
41
42 pub total_returned: usize,
44}
45
46impl SearchStatistics {
47 pub fn new() -> Self {
49 SearchStatistics {
50 iterations: 0,
51 total_time: Duration::from_secs(0),
52 tree_size: 1, max_depth: 0,
54 stopped_early: false,
55 node_pool_stats: None,
56 }
57 }
58
59 pub fn update_node_pool_stats(
61 &mut self,
62 capacity: usize,
63 available: usize,
64 allocated: usize,
65 returned: usize,
66 ) {
67 self.node_pool_stats = Some(NodePoolStats {
68 capacity,
69 available,
70 total_allocated: allocated,
71 total_returned: returned,
72 });
73 }
74
75 pub fn avg_time_per_iteration_us(&self) -> f64 {
77 if self.iterations == 0 {
78 return 0.0;
79 }
80 self.total_time.as_micros() as f64 / self.iterations as f64
81 }
82
83 pub fn iterations_per_second(&self) -> f64 {
85 if self.total_time.as_secs_f64() <= 0.0 {
86 return 0.0;
87 }
88 self.iterations as f64 / self.total_time.as_secs_f64()
89 }
90
91 pub fn summary(&self) -> String {
93 let mut summary = format!(
94 "MCTS Search Statistics:\n\
95 - Iterations: {}\n\
96 - Total time: {:.3} seconds\n\
97 - Tree size: {} nodes\n\
98 - Max depth: {}\n\
99 - Avg time per iteration: {:.3} µs\n\
100 - Iterations per second: {:.1}\n\
101 - Stopped early: {}",
102 self.iterations,
103 self.total_time.as_secs_f64(),
104 self.tree_size,
105 self.max_depth,
106 self.avg_time_per_iteration_us(),
107 self.iterations_per_second(),
108 self.stopped_early
109 );
110
111 if let Some(pool_stats) = &self.node_pool_stats {
113 summary.push_str(&format!(
114 "\n\nNode Pool Statistics:\n\
115 - Capacity: {}\n\
116 - Available nodes: {}\n\
117 - Total allocated: {}\n\
118 - Total returned: {}\n\
119 - Reuse ratio: {:.2}%",
120 pool_stats.capacity,
121 pool_stats.available,
122 pool_stats.total_allocated,
123 pool_stats.total_returned,
124 if pool_stats.total_allocated > 0 {
125 (pool_stats.total_returned as f64 / pool_stats.total_allocated as f64) * 100.0
126 } else {
127 0.0
128 }
129 ));
130 }
131
132 summary
133 }
134}
135
136impl Default for SearchStatistics {
137 fn default() -> Self {
138 Self::new()
139 }
140}