chess_vector_engine/
hybrid_evaluation.rs

1use crate::errors::Result;
2use crate::utils::cache::EvaluationCache;
3use crate::utils::profiler::{global_profiler, ChessEngineProfiler};
4use chess::{Board, Color, Piece, Square};
5use std::collections::{HashMap, VecDeque};
6use std::hash::Hash;
7use std::sync::{Arc, RwLock};
8
9/// Advanced hybrid evaluation system that intelligently blends multiple evaluation methods
10pub struct HybridEvaluationEngine {
11    /// NNUE neural network evaluator
12    nnue_evaluator: Option<Box<dyn NNUEEvaluator + Send + Sync>>,
13    /// Pattern recognition system
14    pattern_evaluator: Option<Box<dyn PatternEvaluator + Send + Sync>>,
15    /// Tactical search engine
16    tactical_evaluator: Option<Box<dyn TacticalEvaluator + Send + Sync>>,
17    /// Strategic initiative analyzer
18    strategic_evaluator: Option<Box<dyn StrategicEvaluator + Send + Sync>>,
19    /// Position complexity analyzer
20    complexity_analyzer: ComplexityAnalyzer,
21    /// Game phase detector
22    phase_detector: GamePhaseDetector,
23    /// Evaluation blender with dynamic weights
24    blender: EvaluationBlender,
25    /// Confidence scorer
26    confidence_scorer: ConfidenceScorer,
27    /// Evaluation cache
28    evaluation_cache: Arc<EvaluationCache>,
29    /// Performance profiler
30    profiler: Arc<ChessEngineProfiler>,
31}
32
33impl HybridEvaluationEngine {
34    /// Create a new hybrid evaluation engine
35    pub fn new() -> Self {
36        Self {
37            nnue_evaluator: None,
38            pattern_evaluator: None,
39            tactical_evaluator: None,
40            strategic_evaluator: None,
41            complexity_analyzer: ComplexityAnalyzer::new(),
42            phase_detector: GamePhaseDetector::new(),
43            blender: EvaluationBlender::new(),
44            confidence_scorer: ConfidenceScorer::new(),
45            evaluation_cache: Arc::new(EvaluationCache::new(
46                10000,
47                std::time::Duration::from_secs(300),
48            )),
49            profiler: Arc::clone(global_profiler()),
50        }
51    }
52
53    /// Register an NNUE evaluator
54    pub fn with_nnue_evaluator<T>(mut self, evaluator: T) -> Self
55    where
56        T: NNUEEvaluator + Send + Sync + 'static,
57    {
58        self.nnue_evaluator = Some(Box::new(evaluator));
59        self
60    }
61
62    /// Register a pattern evaluator
63    pub fn with_pattern_evaluator<T>(mut self, evaluator: T) -> Self
64    where
65        T: PatternEvaluator + Send + Sync + 'static,
66    {
67        self.pattern_evaluator = Some(Box::new(evaluator));
68        self
69    }
70
71    /// Register a tactical evaluator
72    pub fn with_tactical_evaluator<T>(mut self, evaluator: T) -> Self
73    where
74        T: TacticalEvaluator + Send + Sync + 'static,
75    {
76        self.tactical_evaluator = Some(Box::new(evaluator));
77        self
78    }
79
80    /// Register a strategic evaluator
81    pub fn with_strategic_evaluator<T>(mut self, evaluator: T) -> Self
82    where
83        T: StrategicEvaluator + Send + Sync + 'static,
84    {
85        self.strategic_evaluator = Some(Box::new(evaluator));
86        self
87    }
88
89    /// Register the strategic initiative evaluator
90    pub fn with_strategic_initiative_evaluator(self) -> Self {
91        use crate::strategic_initiative::StrategicInitiativeEvaluator;
92        self.with_strategic_evaluator(StrategicInitiativeEvaluator::new())
93    }
94
95    /// Perform comprehensive hybrid evaluation of a position
96    pub fn evaluate_position(&self, board: &Board) -> Result<HybridEvaluationResult> {
97        let fen = board.to_string();
98
99        // Check cache first
100        if let Some(cached_eval) = self.evaluation_cache.get_evaluation(&fen) {
101            return Ok(HybridEvaluationResult {
102                final_evaluation: cached_eval,
103                nnue_evaluation: None,
104                pattern_evaluation: None,
105                tactical_evaluation: None,
106                strategic_evaluation: None,
107                complexity_score: 0.0,
108                game_phase: GamePhase::Unknown,
109                confidence_score: 1.0,
110                blend_weights: BlendWeights::default(),
111                evaluation_time_ms: 0,
112                from_cache: true,
113            });
114        }
115
116        let start_time = std::time::Instant::now();
117
118        // Analyze position complexity and game phase
119        let complexity_score = self.profiler.time_evaluation("complexity", || {
120            self.complexity_analyzer.analyze_complexity(board)
121        });
122
123        let game_phase = self.profiler.time_evaluation("phase_detection", || {
124            self.phase_detector.detect_phase(board)
125        });
126
127        // Gather evaluations from all available evaluators
128        let mut evaluation_results = EvaluationResults::new();
129
130        // NNUE evaluation (fast, always computed)
131        if let Some(ref nnue) = self.nnue_evaluator {
132            let nnue_result = self
133                .profiler
134                .time_evaluation("nnue", || nnue.evaluate_position(board));
135            if let Ok(result) = nnue_result {
136                evaluation_results.nnue = Some(result);
137                self.profiler.record_evaluation("nnue");
138            }
139        }
140
141        // Pattern evaluation (medium cost, conditional)
142        if let Some(ref pattern) = self.pattern_evaluator {
143            let should_use_pattern =
144                self.should_use_pattern_evaluation(complexity_score, &game_phase);
145            if should_use_pattern {
146                let pattern_result = self
147                    .profiler
148                    .time_evaluation("pattern", || pattern.evaluate_position(board));
149                if let Ok(result) = pattern_result {
150                    evaluation_results.pattern = Some(result);
151                    self.profiler.record_evaluation("pattern");
152                }
153            }
154        }
155
156        // Tactical evaluation (expensive, selective)
157        if let Some(ref tactical) = self.tactical_evaluator {
158            let should_use_tactical = self.should_use_tactical_evaluation(
159                complexity_score,
160                &game_phase,
161                &evaluation_results,
162            );
163            if should_use_tactical {
164                let tactical_result = self
165                    .profiler
166                    .time_evaluation("tactical", || tactical.evaluate_position(board));
167                if let Ok(result) = tactical_result {
168                    evaluation_results.tactical = Some(result);
169                    self.profiler.record_evaluation("tactical");
170                }
171            }
172        }
173
174        // Strategic evaluation (contextual)
175        if let Some(ref strategic) = self.strategic_evaluator {
176            let should_use_strategic = self.should_use_strategic_evaluation(&game_phase);
177            if should_use_strategic {
178                let strategic_result = self
179                    .profiler
180                    .time_evaluation("strategic", || strategic.evaluate_position(board));
181                if let Ok(result) = strategic_result {
182                    evaluation_results.strategic = Some(result);
183                    self.profiler.record_evaluation("strategic");
184                }
185            }
186        }
187
188        // Compute dynamic blend weights based on position characteristics
189        let blend_weights =
190            self.blender
191                .compute_blend_weights(complexity_score, &game_phase, &evaluation_results);
192
193        // Blend evaluations using computed weights
194        let final_evaluation = self
195            .blender
196            .blend_evaluations(&evaluation_results, &blend_weights);
197
198        // Create position context for confidence analysis
199        let position_context = PositionContext {
200            position_hash: board.get_hash(),
201            game_phase,
202            has_tactical_threats: self.detect_tactical_threats(board),
203            in_opening_book: self.is_in_opening_book(board),
204            material_imbalance: self.calculate_material_imbalance(board),
205            complexity_score,
206        };
207
208        // Compute enhanced confidence score
209        let confidence_analysis = self.confidence_scorer.compute_confidence(
210            &evaluation_results,
211            &blend_weights,
212            complexity_score,
213            &position_context,
214        );
215
216        let confidence_score = confidence_analysis.overall_confidence;
217
218        let evaluation_time_ms = start_time.elapsed().as_millis() as u64;
219
220        let result = HybridEvaluationResult {
221            final_evaluation,
222            nnue_evaluation: evaluation_results.nnue.clone(),
223            pattern_evaluation: evaluation_results.pattern.clone(),
224            tactical_evaluation: evaluation_results.tactical.clone(),
225            strategic_evaluation: evaluation_results.strategic.clone(),
226            complexity_score,
227            game_phase,
228            confidence_score,
229            blend_weights,
230            evaluation_time_ms,
231            from_cache: false,
232        };
233
234        // Cache the result
235        self.evaluation_cache
236            .store_evaluation(&fen, final_evaluation);
237
238        self.profiler.record_evaluation("hybrid");
239
240        Ok(result)
241    }
242
243    /// Determine if pattern evaluation should be used
244    fn should_use_pattern_evaluation(&self, complexity_score: f32, game_phase: &GamePhase) -> bool {
245        match game_phase {
246            GamePhase::Opening => complexity_score > 0.3,
247            GamePhase::Middlegame => complexity_score > 0.4,
248            GamePhase::Endgame => complexity_score > 0.5,
249            GamePhase::Unknown => complexity_score > 0.4,
250        }
251    }
252
253    /// Detect if position has tactical threats
254    fn detect_tactical_threats(&self, board: &Board) -> bool {
255        // Simple heuristic: check if there are pieces under attack
256        // In a real implementation, this would use tactical search
257        let white_pieces = board.color_combined(Color::White);
258        let black_pieces = board.color_combined(Color::Black);
259
260        // Check for checks
261        if board.checkers().popcnt() > 0 {
262            return true;
263        }
264
265        // Check for pieces that can be captured
266        // This is a simplified check - real implementation would use attack tables
267        let total_pieces = white_pieces.popcnt() + black_pieces.popcnt();
268        total_pieces < 24 // Simplified: fewer pieces often means more tactics
269    }
270
271    /// Check if position is in opening book
272    fn is_in_opening_book(&self, board: &Board) -> bool {
273        // Placeholder: in real implementation, would check against opening book
274        // For now, assume positions with many pieces are in opening
275        let total_pieces = board.combined().popcnt();
276        total_pieces >= 28
277    }
278
279    /// Calculate material imbalance
280    fn calculate_material_imbalance(&self, board: &Board) -> f32 {
281        let piece_values = [1, 3, 3, 5, 9, 0]; // Pawn, Knight, Bishop, Rook, Queen, King
282
283        let mut white_material = 0;
284        let mut black_material = 0;
285
286        for piece_type in [
287            Piece::Pawn,
288            Piece::Knight,
289            Piece::Bishop,
290            Piece::Rook,
291            Piece::Queen,
292        ] {
293            let piece_index = match piece_type {
294                Piece::Pawn => 0,
295                Piece::Knight => 1,
296                Piece::Bishop => 2,
297                Piece::Rook => 3,
298                Piece::Queen => 4,
299                Piece::King => 5,
300            };
301
302            let white_count =
303                (board.pieces(piece_type) & board.color_combined(Color::White)).popcnt() as i32;
304            let black_count =
305                (board.pieces(piece_type) & board.color_combined(Color::Black)).popcnt() as i32;
306
307            white_material += white_count * piece_values[piece_index];
308            black_material += black_count * piece_values[piece_index];
309        }
310
311        (white_material - black_material).abs() as f32
312    }
313
314    /// Determine if tactical evaluation should be used
315    fn should_use_tactical_evaluation(
316        &self,
317        complexity_score: f32,
318        game_phase: &GamePhase,
319        evaluation_results: &EvaluationResults,
320    ) -> bool {
321        // Always use tactical in complex tactical positions
322        if complexity_score > 0.7 {
323            return true;
324        }
325
326        // Use tactical when evaluations disagree significantly
327        if let (Some(nnue), Some(pattern)) = (&evaluation_results.nnue, &evaluation_results.pattern)
328        {
329            let disagreement = (nnue.evaluation - pattern.evaluation).abs();
330            if disagreement > 0.5 {
331                return true;
332            }
333        }
334
335        // Phase-specific tactical evaluation
336        match game_phase {
337            GamePhase::Opening => complexity_score > 0.6,
338            GamePhase::Middlegame => complexity_score > 0.5,
339            GamePhase::Endgame => complexity_score > 0.4, // More important in endgames
340            GamePhase::Unknown => complexity_score > 0.5,
341        }
342    }
343
344    /// Determine if strategic evaluation should be used
345    fn should_use_strategic_evaluation(&self, game_phase: &GamePhase) -> bool {
346        match game_phase {
347            GamePhase::Opening => true,    // Strategic themes important in opening
348            GamePhase::Middlegame => true, // Most important in middlegame
349            GamePhase::Endgame => false,   // Less relevant in endgame
350            GamePhase::Unknown => true,
351        }
352    }
353
354    /// Get evaluation statistics
355    pub fn get_statistics(&self) -> HybridEvaluationStats {
356        let cache_stats = self.evaluation_cache.stats();
357
358        HybridEvaluationStats {
359            total_evaluations: 0,     // Simplified for now - would need separate tracking
360            nnue_evaluations: 0,      // Simplified for now - would need separate tracking
361            pattern_evaluations: 0,   // Simplified for now - would need separate tracking
362            tactical_evaluations: 0,  // Simplified for now - would need separate tracking
363            strategic_evaluations: 0, // Simplified for now - would need separate tracking
364            cache_hit_ratio: cache_stats.hit_ratio,
365            average_evaluation_time_ms: 0.0, // Simplified for now - would need separate tracking
366            evaluations_per_second: 0.0,     // Simplified for now - would need separate tracking
367        }
368    }
369
370    /// Enable or disable adaptive weight learning
371    pub fn set_adaptive_learning(&mut self, enabled: bool) {
372        self.blender.set_adaptive_learning(enabled);
373    }
374
375    /// Update performance metrics for adaptive learning
376    pub fn update_evaluation_performance(
377        &self,
378        board: &Board,
379        predicted_evaluation: f32,
380        actual_result: Option<f32>,
381        evaluation_accuracy: f32,
382    ) -> Result<()> {
383        // Recompute context information for the position
384        let complexity_score = self.complexity_analyzer.analyze_complexity(board);
385        let game_phase = self.phase_detector.detect_phase(board);
386
387        // Get the weights that would be used for this position
388        let mut eval_results = EvaluationResults::new();
389        // Simplified - in practice we'd need to store the actual evaluation results
390        let weights =
391            self.blender
392                .compute_blend_weights(complexity_score, &game_phase, &eval_results);
393
394        // Update performance metrics
395        self.blender.update_performance_metrics(
396            &weights,
397            complexity_score,
398            &game_phase,
399            evaluation_accuracy,
400            actual_result,
401        );
402
403        Ok(())
404    }
405
406    /// Get adaptive learning statistics
407    pub fn get_adaptive_learning_stats(&self) -> AdaptiveLearningStats {
408        self.blender.get_adaptive_stats()
409    }
410
411    /// Get detailed complexity analysis for a position
412    pub fn analyze_position_complexity(&self, board: &Board) -> ComplexityAnalysisResult {
413        self.complexity_analyzer.analyze_complexity_detailed(board)
414    }
415
416    /// Configure the complexity analyzer
417    pub fn configure_complexity_analyzer(
418        &mut self,
419        weights: ComplexityWeights,
420        depth: AnalysisDepth,
421    ) {
422        self.complexity_analyzer = ComplexityAnalyzer::new()
423            .with_complexity_weights(weights)
424            .with_analysis_depth(depth);
425    }
426
427    /// Get detailed game phase analysis for a position
428    pub fn analyze_game_phase(&self, board: &Board) -> GamePhaseAnalysisResult {
429        self.phase_detector.analyze_game_phase(board)
430    }
431
432    /// Configure the game phase detector
433    pub fn configure_phase_detector(
434        &mut self,
435        weights: PhaseDetectionWeights,
436        settings: PhaseAdaptationSettings,
437    ) {
438        self.phase_detector = GamePhaseDetector::new()
439            .with_phase_weights(weights)
440            .with_adaptation_settings(settings);
441    }
442
443    /// Apply phase-specific adaptations to evaluation weights
444    pub fn apply_phase_adaptations(&self, board: &Board) -> BlendWeights {
445        let phase_analysis = self.analyze_game_phase(board);
446        phase_analysis.adaptation_recommendations.evaluation_weights
447    }
448}
449
450impl Default for HybridEvaluationEngine {
451    fn default() -> Self {
452        Self::new()
453    }
454}
455
456/// Individual evaluation result from a specific evaluator
457#[derive(Debug, Clone)]
458pub struct EvaluationComponent {
459    pub evaluation: f32,
460    pub confidence: f32,
461    pub computation_time_ms: u64,
462    pub additional_info: HashMap<String, f32>,
463}
464
465/// Container for all evaluation results
466#[derive(Debug, Clone, Default)]
467struct EvaluationResults {
468    pub nnue: Option<EvaluationComponent>,
469    pub pattern: Option<EvaluationComponent>,
470    pub tactical: Option<EvaluationComponent>,
471    pub strategic: Option<EvaluationComponent>,
472}
473
474impl EvaluationResults {
475    fn new() -> Self {
476        Self::default()
477    }
478}
479
480/// Comprehensive hybrid evaluation result
481#[derive(Debug, Clone)]
482pub struct HybridEvaluationResult {
483    /// Final blended evaluation
484    pub final_evaluation: f32,
485    /// NNUE evaluation component
486    pub nnue_evaluation: Option<EvaluationComponent>,
487    /// Pattern evaluation component
488    pub pattern_evaluation: Option<EvaluationComponent>,
489    /// Tactical evaluation component
490    pub tactical_evaluation: Option<EvaluationComponent>,
491    /// Strategic evaluation component
492    pub strategic_evaluation: Option<EvaluationComponent>,
493    /// Position complexity score (0.0 to 1.0)
494    pub complexity_score: f32,
495    /// Detected game phase
496    pub game_phase: GamePhase,
497    /// Confidence in the final evaluation (0.0 to 1.0)
498    pub confidence_score: f32,
499    /// Weights used for blending
500    pub blend_weights: BlendWeights,
501    /// Total evaluation time in milliseconds
502    pub evaluation_time_ms: u64,
503    /// Whether result came from cache
504    pub from_cache: bool,
505}
506
507/// Game phase classification
508#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
509pub enum GamePhase {
510    #[default]
511    Unknown,
512    Opening,
513    Middlegame,
514    Endgame,
515}
516
517/// Dynamic weights for blending evaluations
518#[derive(Debug, Clone)]
519pub struct BlendWeights {
520    pub nnue_weight: f32,
521    pub pattern_weight: f32,
522    pub tactical_weight: f32,
523    pub strategic_weight: f32,
524}
525
526impl Default for BlendWeights {
527    fn default() -> Self {
528        Self {
529            nnue_weight: 0.4,
530            pattern_weight: 0.3,
531            tactical_weight: 0.2,
532            strategic_weight: 0.1,
533        }
534    }
535}
536
537/// Enhanced position complexity analyzer with detailed analysis
538pub struct ComplexityAnalyzer {
539    cached_complexity: RwLock<HashMap<String, ComplexityAnalysisResult>>,
540    complexity_weights: ComplexityWeights,
541    analysis_depth: AnalysisDepth,
542}
543
544impl ComplexityAnalyzer {
545    pub fn new() -> Self {
546        Self {
547            cached_complexity: RwLock::new(HashMap::new()),
548            complexity_weights: ComplexityWeights::default(),
549            analysis_depth: AnalysisDepth::Standard,
550        }
551    }
552
553    /// Create a complexity analyzer with custom settings
554    pub fn with_analysis_depth(mut self, depth: AnalysisDepth) -> Self {
555        self.analysis_depth = depth;
556        self
557    }
558
559    /// Create a complexity analyzer with custom weights
560    pub fn with_complexity_weights(mut self, weights: ComplexityWeights) -> Self {
561        self.complexity_weights = weights;
562        self
563    }
564
565    /// Analyze the complexity of a chess position
566    pub fn analyze_complexity(&self, board: &Board) -> f32 {
567        let analysis = self.analyze_complexity_detailed(board);
568        analysis.overall_complexity
569    }
570
571    /// Perform detailed complexity analysis
572    pub fn analyze_complexity_detailed(&self, board: &Board) -> ComplexityAnalysisResult {
573        let fen = board.to_string();
574
575        // Check cache
576        if let Ok(cache) = self.cached_complexity.read() {
577            if let Some(analysis) = cache.get(&fen) {
578                return analysis.clone();
579            }
580        }
581
582        let mut analysis = ComplexityAnalysisResult::new();
583
584        // Analyze different complexity factors
585        analysis.material_complexity = self.analyze_material_complexity(board);
586        analysis.pawn_structure_complexity = self.analyze_pawn_structure_complexity(board);
587        analysis.king_safety_complexity = self.analyze_king_safety_complexity(board);
588        analysis.piece_coordination_complexity = self.analyze_piece_coordination_complexity(board);
589        analysis.tactical_complexity = self.analyze_tactical_complexity(board);
590        analysis.positional_complexity = self.analyze_positional_complexity(board);
591        analysis.time_complexity = self.analyze_time_complexity(board);
592        analysis.endgame_complexity = self.analyze_endgame_complexity(board);
593
594        // Compute weighted overall complexity
595        analysis.overall_complexity = self.compute_weighted_complexity(&analysis);
596
597        // Determine complexity category
598        analysis.complexity_category = self.categorize_complexity(analysis.overall_complexity);
599
600        // Add position-specific insights
601        analysis.key_complexity_factors = self.identify_key_complexity_factors(&analysis);
602        analysis.evaluation_recommendations = self.generate_evaluation_recommendations(&analysis);
603
604        // Cache the result with efficient management
605        if let Ok(mut cache) = self.cached_complexity.write() {
606            if cache.len() > 1000 {
607                // Remove oldest 50% of entries instead of clearing all
608                let keys_to_remove: Vec<_> = cache.keys().take(500).cloned().collect();
609                for key in keys_to_remove {
610                    cache.remove(&key);
611                }
612            }
613            cache.insert(fen, analysis.clone());
614        }
615
616        analysis
617    }
618
619    fn compute_weighted_complexity(&self, analysis: &ComplexityAnalysisResult) -> f32 {
620        let weights = &self.complexity_weights;
621
622        let complexity_score = analysis.material_complexity * weights.material_weight
623            + analysis.pawn_structure_complexity * weights.pawn_structure_weight
624            + analysis.king_safety_complexity * weights.king_safety_weight
625            + analysis.piece_coordination_complexity * weights.piece_coordination_weight
626            + analysis.tactical_complexity * weights.tactical_weight
627            + analysis.positional_complexity * weights.positional_weight
628            + analysis.time_complexity * weights.time_weight
629            + analysis.endgame_complexity * weights.endgame_weight;
630
631        // Apply analysis depth modifier
632        let depth_modifier = match self.analysis_depth {
633            AnalysisDepth::Fast => 0.8,
634            AnalysisDepth::Standard => 1.0,
635            AnalysisDepth::Deep => 1.2,
636            AnalysisDepth::Comprehensive => 1.4,
637        };
638
639        (complexity_score * depth_modifier).clamp(0.0, 1.0)
640    }
641
642    fn categorize_complexity(&self, complexity: f32) -> ComplexityCategory {
643        if complexity < 0.2 {
644            ComplexityCategory::VeryLow
645        } else if complexity < 0.4 {
646            ComplexityCategory::Low
647        } else if complexity < 0.6 {
648            ComplexityCategory::Medium
649        } else if complexity < 0.8 {
650            ComplexityCategory::High
651        } else {
652            ComplexityCategory::VeryHigh
653        }
654    }
655
656    fn identify_key_complexity_factors(
657        &self,
658        analysis: &ComplexityAnalysisResult,
659    ) -> Vec<ComplexityFactor> {
660        let mut factors = Vec::new();
661
662        // Check which factors contribute most to complexity
663        if analysis.tactical_complexity > 0.7 {
664            factors.push(ComplexityFactor::TacticalThreats);
665        }
666        if analysis.king_safety_complexity > 0.6 {
667            factors.push(ComplexityFactor::KingSafety);
668        }
669        if analysis.piece_coordination_complexity > 0.6 {
670            factors.push(ComplexityFactor::PieceCoordination);
671        }
672        if analysis.pawn_structure_complexity > 0.5 {
673            factors.push(ComplexityFactor::PawnStructure);
674        }
675        if analysis.material_complexity > 0.5 {
676            factors.push(ComplexityFactor::MaterialImbalance);
677        }
678        if analysis.positional_complexity > 0.6 {
679            factors.push(ComplexityFactor::PositionalThemes);
680        }
681        if analysis.time_complexity > 0.7 {
682            factors.push(ComplexityFactor::TimeFactors);
683        }
684        if analysis.endgame_complexity > 0.5 {
685            factors.push(ComplexityFactor::EndgameFactors);
686        }
687
688        factors
689    }
690
691    fn generate_evaluation_recommendations(
692        &self,
693        analysis: &ComplexityAnalysisResult,
694    ) -> EvaluationRecommendations {
695        let mut recommendations = EvaluationRecommendations::default();
696
697        // Recommend evaluation methods based on complexity profile
698        match analysis.complexity_category {
699            ComplexityCategory::VeryLow | ComplexityCategory::Low => {
700                recommendations.prefer_nnue = true;
701                recommendations.tactical_depth = 4;
702                recommendations.pattern_analysis_priority = 0.3;
703            }
704            ComplexityCategory::Medium => {
705                recommendations.prefer_nnue = false;
706                recommendations.tactical_depth = 6;
707                recommendations.pattern_analysis_priority = 0.5;
708                recommendations.strategic_analysis_priority = 0.4;
709            }
710            ComplexityCategory::High | ComplexityCategory::VeryHigh => {
711                recommendations.prefer_nnue = false;
712                recommendations.tactical_depth = 8;
713                recommendations.pattern_analysis_priority = 0.7;
714                recommendations.strategic_analysis_priority = 0.6;
715                recommendations.require_tactical_verification = true;
716            }
717        }
718
719        // Adjust based on specific complexity factors
720        if analysis.tactical_complexity > 0.7 {
721            recommendations.tactical_depth = (recommendations.tactical_depth + 2).min(12);
722            recommendations.require_tactical_verification = true;
723        }
724
725        if analysis.king_safety_complexity > 0.6 {
726            recommendations.king_safety_analysis = true;
727            recommendations.pattern_analysis_priority += 0.1;
728        }
729
730        if analysis.endgame_complexity > 0.5 {
731            recommendations.endgame_analysis = true;
732            recommendations.prefer_nnue = true; // NNUE often good in endgames
733        }
734
735        recommendations
736    }
737
738    // Enhanced complexity analysis methods
739    fn analyze_material_complexity(&self, board: &Board) -> f32 {
740        let white_material = self.count_material(board, Color::White);
741        let black_material = self.count_material(board, Color::Black);
742        let total_material = white_material + black_material;
743        let material_imbalance = (white_material - black_material).abs();
744
745        let mut complexity = 0.0;
746
747        // Base material complexity
748        complexity += (total_material as f32 / 78.0) * 0.4;
749
750        // Material imbalance complexity
751        complexity += (material_imbalance as f32 / 39.0) * 0.3;
752
753        // Piece diversity complexity
754        complexity += self.analyze_piece_diversity(board) * 0.3;
755
756        complexity.min(1.0)
757    }
758
759    fn analyze_pawn_structure_complexity(&self, board: &Board) -> f32 {
760        let mut complexity = 0.0;
761
762        // Pawn islands
763        complexity += self.count_pawn_islands(board) as f32 * 0.1;
764
765        // Isolated pawns
766        complexity += self.count_isolated_pawns(board) as f32 * 0.15;
767
768        // Doubled pawns
769        complexity += self.count_doubled_pawns(board) as f32 * 0.1;
770
771        // Passed pawns
772        complexity += self.count_passed_pawns_detailed(board) as f32 * 0.2;
773
774        // Pawn chains
775        complexity += self.evaluate_pawn_chains(board) * 0.25;
776
777        // Pawn storms
778        complexity += self.evaluate_pawn_storms(board) * 0.2;
779
780        complexity.min(1.0)
781    }
782
783    fn analyze_king_safety_complexity(&self, board: &Board) -> f32 {
784        let mut complexity = 0.0;
785
786        for color in [Color::White, Color::Black] {
787            let king_square = board.king_square(color);
788
789            // King exposure
790            complexity += self.evaluate_king_exposure(board, color, king_square) * 0.3;
791
792            // Attacking pieces near king
793            complexity += self.count_king_attackers(board, color, king_square) as f32 * 0.1;
794
795            // Pawn shield quality
796            complexity += self.evaluate_pawn_shield_complexity(board, color, king_square) * 0.2;
797
798            // Escape squares
799            complexity += self.evaluate_escape_squares(board, color, king_square) * 0.1;
800        }
801
802        (complexity / 2.0).min(1.0) // Average for both colors
803    }
804
805    fn analyze_piece_coordination_complexity(&self, board: &Board) -> f32 {
806        let mut complexity = 0.0;
807
808        // Piece mobility variance
809        complexity += self.analyze_mobility_complexity(board) * 0.3;
810
811        // Piece harmony/conflicts
812        complexity += self.analyze_piece_harmony_complexity(board) * 0.25;
813
814        // Central control complexity
815        complexity += self.analyze_central_control_complexity(board) * 0.2;
816
817        // Piece activity imbalance
818        complexity += self.analyze_piece_activity_imbalance(board) * 0.25;
819
820        complexity.min(1.0)
821    }
822
823    fn analyze_tactical_complexity(&self, board: &Board) -> f32 {
824        let mut complexity = 0.0;
825
826        // Check if position has checks
827        if board.checkers().popcnt() > 0 {
828            complexity += 0.4;
829        }
830
831        // Count potential captures
832        use chess::MoveGen;
833        let legal_moves = MoveGen::new_legal(board);
834        let moves: Vec<_> = legal_moves.collect();
835        let capture_ratio = moves
836            .iter()
837            .filter(|mv| board.piece_on(mv.get_dest()).is_some())
838            .count() as f32
839            / moves.len().max(1) as f32;
840        complexity += capture_ratio * 0.3;
841
842        // Move count complexity
843        complexity += (moves.len() as f32 / 50.0).min(0.2);
844
845        // Tactical motifs
846        complexity += self.analyze_tactical_motifs(board) * 0.4;
847
848        // Hanging pieces
849        complexity += self.count_hanging_pieces(board) as f32 * 0.1;
850
851        complexity.min(1.0)
852    }
853
854    fn analyze_positional_complexity(&self, board: &Board) -> f32 {
855        let mut complexity = 0.0;
856
857        // Space advantage variance
858        complexity += self.analyze_space_complexity(board) * 0.3;
859
860        // Weak squares
861        complexity += self.analyze_weak_squares_complexity(board) * 0.25;
862
863        // Outposts and holes
864        complexity += self.analyze_outpost_complexity(board) * 0.2;
865
866        // Piece placement optimization potential
867        complexity += self.analyze_piece_placement_complexity(board) * 0.25;
868
869        complexity.min(1.0)
870    }
871
872    fn analyze_time_complexity(&self, board: &Board) -> f32 {
873        let mut complexity = 0.0;
874
875        // Tempo sensitivity
876        complexity += self.evaluate_tempo_sensitivity(board) * 0.4;
877
878        // Zugzwang potential
879        complexity += self.evaluate_zugzwang_complexity(board) * 0.3;
880
881        // Critical move identification
882        complexity += self.evaluate_critical_moves_complexity(board) * 0.3;
883
884        complexity.min(1.0)
885    }
886
887    fn analyze_endgame_complexity(&self, board: &Board) -> f32 {
888        let total_material =
889            self.count_material(board, Color::White) + self.count_material(board, Color::Black);
890
891        // Only relevant in endgames
892        if total_material > 30 {
893            return 0.0;
894        }
895
896        let mut complexity = 0.0;
897
898        // Pawn endgame complexity
899        complexity += self.analyze_pawn_endgame_complexity(board) * 0.4;
900
901        // Piece endgame complexity
902        complexity += self.analyze_piece_endgame_complexity(board) * 0.3;
903
904        // King activity importance
905        complexity += self.analyze_king_activity_complexity(board) * 0.3;
906
907        complexity.min(1.0)
908    }
909
910    // Helper methods for detailed analysis (simplified implementations)
911    fn analyze_piece_diversity(&self, board: &Board) -> f32 {
912        let mut piece_types = 0;
913        for piece in [
914            Piece::Pawn,
915            Piece::Knight,
916            Piece::Bishop,
917            Piece::Rook,
918            Piece::Queen,
919        ] {
920            if (board.pieces(piece) & board.color_combined(Color::White)).popcnt() > 0
921                || (board.pieces(piece) & board.color_combined(Color::Black)).popcnt() > 0
922            {
923                piece_types += 1;
924            }
925        }
926        piece_types as f32 / 5.0
927    }
928
929    fn count_pawn_islands(&self, board: &Board) -> u8 {
930        let mut islands = 0;
931        for color in [Color::White, Color::Black] {
932            let pawns = board.pieces(Piece::Pawn) & board.color_combined(color);
933            let mut files_with_pawns = [false; 8];
934
935            for square in pawns {
936                files_with_pawns[square.get_file().to_index()] = true;
937            }
938
939            let mut in_island = false;
940            for &has_pawn in &files_with_pawns {
941                if has_pawn && !in_island {
942                    islands += 1;
943                    in_island = true;
944                } else if !has_pawn {
945                    in_island = false;
946                }
947            }
948        }
949        islands
950    }
951
952    fn count_isolated_pawns(&self, board: &Board) -> u8 {
953        // Simplified implementation
954        0
955    }
956
957    fn count_doubled_pawns(&self, board: &Board) -> u8 {
958        let mut doubled = 0;
959        for color in [Color::White, Color::Black] {
960            let pawns = board.pieces(Piece::Pawn) & board.color_combined(color);
961            let mut pawns_per_file = [0u8; 8];
962
963            for square in pawns {
964                pawns_per_file[square.get_file().to_index()] += 1;
965            }
966
967            doubled += pawns_per_file.iter().filter(|&&count| count > 1).count() as u8;
968        }
969        doubled
970    }
971
972    fn count_passed_pawns_detailed(&self, _board: &Board) -> u8 {
973        // Simplified implementation
974        0
975    }
976
977    fn evaluate_pawn_chains(&self, _board: &Board) -> f32 {
978        // Simplified implementation
979        0.0
980    }
981
982    fn evaluate_pawn_storms(&self, _board: &Board) -> f32 {
983        // Simplified implementation
984        0.0
985    }
986
987    fn evaluate_king_exposure(&self, _board: &Board, _color: Color, _king_square: Square) -> f32 {
988        // Simplified implementation
989        0.0
990    }
991
992    fn count_king_attackers(&self, _board: &Board, _color: Color, _king_square: Square) -> u8 {
993        // Simplified implementation
994        0
995    }
996
997    fn evaluate_pawn_shield_complexity(
998        &self,
999        _board: &Board,
1000        _color: Color,
1001        _king_square: Square,
1002    ) -> f32 {
1003        // Simplified implementation
1004        0.0
1005    }
1006
1007    fn evaluate_escape_squares(&self, _board: &Board, _color: Color, _king_square: Square) -> f32 {
1008        // Simplified implementation
1009        0.0
1010    }
1011
1012    // More simplified helper methods
1013    fn analyze_mobility_complexity(&self, _board: &Board) -> f32 {
1014        0.0
1015    }
1016    fn analyze_piece_harmony_complexity(&self, _board: &Board) -> f32 {
1017        0.0
1018    }
1019    fn analyze_central_control_complexity(&self, _board: &Board) -> f32 {
1020        0.0
1021    }
1022    fn analyze_piece_activity_imbalance(&self, _board: &Board) -> f32 {
1023        0.0
1024    }
1025    fn analyze_tactical_motifs(&self, _board: &Board) -> f32 {
1026        0.0
1027    }
1028    fn count_hanging_pieces(&self, _board: &Board) -> u8 {
1029        0
1030    }
1031    fn analyze_space_complexity(&self, _board: &Board) -> f32 {
1032        0.0
1033    }
1034    fn analyze_weak_squares_complexity(&self, _board: &Board) -> f32 {
1035        0.0
1036    }
1037    fn analyze_outpost_complexity(&self, _board: &Board) -> f32 {
1038        0.0
1039    }
1040    fn analyze_piece_placement_complexity(&self, _board: &Board) -> f32 {
1041        0.0
1042    }
1043    fn evaluate_tempo_sensitivity(&self, _board: &Board) -> f32 {
1044        0.0
1045    }
1046    fn evaluate_zugzwang_complexity(&self, _board: &Board) -> f32 {
1047        0.0
1048    }
1049    fn evaluate_critical_moves_complexity(&self, _board: &Board) -> f32 {
1050        0.0
1051    }
1052    fn analyze_pawn_endgame_complexity(&self, _board: &Board) -> f32 {
1053        0.0
1054    }
1055    fn analyze_piece_endgame_complexity(&self, _board: &Board) -> f32 {
1056        0.0
1057    }
1058    fn analyze_king_activity_complexity(&self, _board: &Board) -> f32 {
1059        0.0
1060    }
1061
1062    // Keep the existing methods for backward compatibility
1063    fn material_complexity(&self, board: &Board) -> f32 {
1064        self.analyze_material_complexity(board)
1065    }
1066
1067    fn count_material(&self, board: &Board, color: Color) -> i32 {
1068        let pieces = board.color_combined(color);
1069        let mut material = 0;
1070
1071        material += (board.pieces(chess::Piece::Pawn) & pieces).popcnt() as i32 * 1;
1072        material += (board.pieces(chess::Piece::Knight) & pieces).popcnt() as i32 * 3;
1073        material += (board.pieces(chess::Piece::Bishop) & pieces).popcnt() as i32 * 3;
1074        material += (board.pieces(chess::Piece::Rook) & pieces).popcnt() as i32 * 5;
1075        material += (board.pieces(chess::Piece::Queen) & pieces).popcnt() as i32 * 9;
1076
1077        material
1078    }
1079
1080    fn pawn_structure_complexity(&self, _board: &Board) -> f32 {
1081        // Simplified pawn structure analysis
1082        // In a full implementation, this would analyze:
1083        // - Isolated pawns
1084        // - Doubled pawns
1085        // - Pawn chains
1086        // - Passed pawns
1087        // - Pawn islands
1088        0.3 // Placeholder
1089    }
1090
1091    fn king_safety_complexity(&self, _board: &Board) -> f32 {
1092        // Simplified king safety analysis
1093        // In a full implementation, this would analyze:
1094        // - King exposure
1095        // - Attacking pieces near king
1096        // - Pawn shield quality
1097        // - Escape squares
1098        0.25 // Placeholder
1099    }
1100
1101    fn piece_coordination_complexity(&self, _board: &Board) -> f32 {
1102        // Simplified piece coordination analysis
1103        // In a full implementation, this would analyze:
1104        // - Piece mobility
1105        // - Piece coordination
1106        // - Central control
1107        // - Piece activity
1108        0.4 // Placeholder
1109    }
1110
1111    fn tactical_complexity(&self, board: &Board) -> f32 {
1112        let mut tactical_score = 0.0;
1113
1114        // Check if position has checks
1115        if board.checkers().popcnt() > 0 {
1116            tactical_score += 0.3;
1117        }
1118
1119        // Count potential captures (simplified)
1120        use chess::MoveGen;
1121        let legal_moves = MoveGen::new_legal(board);
1122        let moves: Vec<_> = legal_moves.collect();
1123        let capture_count = moves
1124            .iter()
1125            .filter(|mv| board.piece_on(mv.get_dest()).is_some())
1126            .count();
1127
1128        tactical_score += (capture_count as f32 / moves.len().max(1) as f32) * 0.4;
1129
1130        // Add complexity for having many legal moves
1131        tactical_score += (moves.len() as f32 / 50.0).min(0.3);
1132
1133        tactical_score
1134    }
1135}
1136
1137impl Default for ComplexityAnalyzer {
1138    fn default() -> Self {
1139        Self::new()
1140    }
1141}
1142
1143/// Enhanced game phase detector with detailed analysis and adaptation
1144pub struct GamePhaseDetector {
1145    cached_phases: RwLock<HashMap<String, GamePhaseAnalysisResult>>,
1146    phase_weights: PhaseDetectionWeights,
1147    adaptation_settings: PhaseAdaptationSettings,
1148}
1149
1150impl GamePhaseDetector {
1151    pub fn new() -> Self {
1152        Self {
1153            cached_phases: RwLock::new(HashMap::new()),
1154            phase_weights: PhaseDetectionWeights::default(),
1155            adaptation_settings: PhaseAdaptationSettings::default(),
1156        }
1157    }
1158
1159    /// Create a phase detector with custom weights
1160    pub fn with_phase_weights(mut self, weights: PhaseDetectionWeights) -> Self {
1161        self.phase_weights = weights;
1162        self
1163    }
1164
1165    /// Create a phase detector with custom adaptation settings
1166    pub fn with_adaptation_settings(mut self, settings: PhaseAdaptationSettings) -> Self {
1167        self.adaptation_settings = settings;
1168        self
1169    }
1170
1171    /// Detect the current game phase
1172    pub fn detect_phase(&self, board: &Board) -> GamePhase {
1173        let analysis = self.analyze_game_phase(board);
1174        analysis.primary_phase
1175    }
1176
1177    /// Perform detailed game phase analysis
1178    pub fn analyze_game_phase(&self, board: &Board) -> GamePhaseAnalysisResult {
1179        let fen = board.to_string();
1180
1181        // Check cache
1182        if let Ok(cache) = self.cached_phases.read() {
1183            if let Some(analysis) = cache.get(&fen) {
1184                return analysis.clone();
1185            }
1186        }
1187
1188        let mut analysis = GamePhaseAnalysisResult::new();
1189
1190        // Analyze multiple phase indicators
1191        analysis.material_phase = self.analyze_material_phase(board);
1192        analysis.development_phase = self.analyze_development_phase(board);
1193        analysis.move_count_phase = self.analyze_move_count_phase(board);
1194        analysis.king_safety_phase = self.analyze_king_safety_phase(board);
1195        analysis.pawn_structure_phase = self.analyze_pawn_structure_phase(board);
1196        analysis.piece_activity_phase = self.analyze_piece_activity_phase(board);
1197
1198        // Compute phase scores for each possible phase
1199        analysis.opening_score = self.compute_opening_score(&analysis);
1200        analysis.middlegame_score = self.compute_middlegame_score(&analysis);
1201        analysis.endgame_score = self.compute_endgame_score(&analysis);
1202
1203        // Determine primary phase
1204        analysis.primary_phase = self.determine_primary_phase(&analysis);
1205
1206        // Determine transition state
1207        analysis.transition_state = self.analyze_transition_state(&analysis);
1208
1209        // Calculate phase confidence
1210        analysis.phase_confidence = self.calculate_phase_confidence(&analysis);
1211
1212        // Generate phase-specific adaptation recommendations
1213        analysis.adaptation_recommendations = self.generate_adaptation_recommendations(&analysis);
1214
1215        // Cache the result
1216        if let Ok(mut cache) = self.cached_phases.write() {
1217            if cache.len() > 1000 {
1218                cache.clear();
1219            }
1220            cache.insert(fen, analysis.clone());
1221        }
1222
1223        analysis
1224    }
1225
1226    fn analyze_material_phase(&self, board: &Board) -> PhaseIndicator {
1227        let total_material = self.calculate_total_material(board);
1228        let material_balance = self.analyze_material_balance(board);
1229
1230        let phase = if total_material >= 60 {
1231            GamePhase::Opening
1232        } else if total_material <= 20 {
1233            GamePhase::Endgame
1234        } else {
1235            GamePhase::Middlegame
1236        };
1237
1238        let confidence = self.compute_material_phase_confidence(total_material, material_balance);
1239
1240        PhaseIndicator { phase, confidence }
1241    }
1242
1243    fn analyze_development_phase(&self, board: &Board) -> PhaseIndicator {
1244        let development_score = self.calculate_development_score(board);
1245        let castling_status = self.analyze_castling_status(board);
1246
1247        let phase = if development_score < 0.3 {
1248            GamePhase::Opening
1249        } else if development_score > 0.8 {
1250            GamePhase::Middlegame
1251        } else {
1252            GamePhase::Opening
1253        };
1254
1255        let confidence = (development_score + castling_status) / 2.0;
1256
1257        PhaseIndicator { phase, confidence }
1258    }
1259
1260    fn analyze_move_count_phase(&self, board: &Board) -> PhaseIndicator {
1261        // Use a heuristic for move count since chess crate doesn't expose fullmove_number directly
1262        let move_count = (board.combined().popcnt() as u32).saturating_sub(32) / 2 + 1;
1263
1264        let (phase, confidence) = if move_count <= 12 {
1265            (GamePhase::Opening, 0.8)
1266        } else if move_count <= 25 {
1267            (GamePhase::Middlegame, 0.7)
1268        } else if move_count <= 40 {
1269            (GamePhase::Middlegame, 0.6)
1270        } else {
1271            (GamePhase::Endgame, 0.5)
1272        };
1273
1274        PhaseIndicator { phase, confidence }
1275    }
1276
1277    fn analyze_king_safety_phase(&self, board: &Board) -> PhaseIndicator {
1278        let king_safety_score = self.evaluate_combined_king_safety(board);
1279
1280        let phase = if king_safety_score > 0.7 {
1281            GamePhase::Opening // Kings still safe, likely early game
1282        } else if king_safety_score < 0.3 {
1283            GamePhase::Middlegame // Kings under pressure
1284        } else {
1285            GamePhase::Endgame // Kings becoming active
1286        };
1287
1288        let confidence = (1.0 - king_safety_score).abs();
1289
1290        PhaseIndicator { phase, confidence }
1291    }
1292
1293    fn analyze_pawn_structure_phase(&self, board: &Board) -> PhaseIndicator {
1294        let pawn_structure_score = self.evaluate_pawn_structure_development(board);
1295
1296        let phase = if pawn_structure_score < 0.4 {
1297            GamePhase::Opening
1298        } else if pawn_structure_score > 0.7 {
1299            GamePhase::Endgame
1300        } else {
1301            GamePhase::Middlegame
1302        };
1303
1304        let confidence = pawn_structure_score;
1305
1306        PhaseIndicator { phase, confidence }
1307    }
1308
1309    fn analyze_piece_activity_phase(&self, board: &Board) -> PhaseIndicator {
1310        let activity_score = self.calculate_piece_activity_score(board);
1311
1312        let phase = if activity_score < 0.3 {
1313            GamePhase::Opening
1314        } else if activity_score > 0.8 {
1315            GamePhase::Endgame
1316        } else {
1317            GamePhase::Middlegame
1318        };
1319
1320        let confidence = activity_score;
1321
1322        PhaseIndicator { phase, confidence }
1323    }
1324
1325    fn compute_opening_score(&self, analysis: &GamePhaseAnalysisResult) -> f32 {
1326        let mut score = 0.0;
1327
1328        // Weight different indicators for opening detection
1329        if analysis.material_phase.phase == GamePhase::Opening {
1330            score += analysis.material_phase.confidence * self.phase_weights.material_weight;
1331        }
1332        if analysis.development_phase.phase == GamePhase::Opening {
1333            score += analysis.development_phase.confidence * self.phase_weights.development_weight;
1334        }
1335        if analysis.move_count_phase.phase == GamePhase::Opening {
1336            score += analysis.move_count_phase.confidence * self.phase_weights.move_count_weight;
1337        }
1338        if analysis.king_safety_phase.phase == GamePhase::Opening {
1339            score += analysis.king_safety_phase.confidence * self.phase_weights.king_safety_weight;
1340        }
1341        if analysis.pawn_structure_phase.phase == GamePhase::Opening {
1342            score +=
1343                analysis.pawn_structure_phase.confidence * self.phase_weights.pawn_structure_weight;
1344        }
1345        if analysis.piece_activity_phase.phase == GamePhase::Opening {
1346            score +=
1347                analysis.piece_activity_phase.confidence * self.phase_weights.piece_activity_weight;
1348        }
1349
1350        score
1351    }
1352
1353    fn compute_middlegame_score(&self, analysis: &GamePhaseAnalysisResult) -> f32 {
1354        let mut score = 0.0;
1355
1356        if analysis.material_phase.phase == GamePhase::Middlegame {
1357            score += analysis.material_phase.confidence * self.phase_weights.material_weight;
1358        }
1359        if analysis.development_phase.phase == GamePhase::Middlegame {
1360            score += analysis.development_phase.confidence * self.phase_weights.development_weight;
1361        }
1362        if analysis.move_count_phase.phase == GamePhase::Middlegame {
1363            score += analysis.move_count_phase.confidence * self.phase_weights.move_count_weight;
1364        }
1365        if analysis.king_safety_phase.phase == GamePhase::Middlegame {
1366            score += analysis.king_safety_phase.confidence * self.phase_weights.king_safety_weight;
1367        }
1368        if analysis.pawn_structure_phase.phase == GamePhase::Middlegame {
1369            score +=
1370                analysis.pawn_structure_phase.confidence * self.phase_weights.pawn_structure_weight;
1371        }
1372        if analysis.piece_activity_phase.phase == GamePhase::Middlegame {
1373            score +=
1374                analysis.piece_activity_phase.confidence * self.phase_weights.piece_activity_weight;
1375        }
1376
1377        score
1378    }
1379
1380    fn compute_endgame_score(&self, analysis: &GamePhaseAnalysisResult) -> f32 {
1381        let mut score = 0.0;
1382
1383        if analysis.material_phase.phase == GamePhase::Endgame {
1384            score += analysis.material_phase.confidence * self.phase_weights.material_weight;
1385        }
1386        if analysis.development_phase.phase == GamePhase::Endgame {
1387            score += analysis.development_phase.confidence * self.phase_weights.development_weight;
1388        }
1389        if analysis.move_count_phase.phase == GamePhase::Endgame {
1390            score += analysis.move_count_phase.confidence * self.phase_weights.move_count_weight;
1391        }
1392        if analysis.king_safety_phase.phase == GamePhase::Endgame {
1393            score += analysis.king_safety_phase.confidence * self.phase_weights.king_safety_weight;
1394        }
1395        if analysis.pawn_structure_phase.phase == GamePhase::Endgame {
1396            score +=
1397                analysis.pawn_structure_phase.confidence * self.phase_weights.pawn_structure_weight;
1398        }
1399        if analysis.piece_activity_phase.phase == GamePhase::Endgame {
1400            score +=
1401                analysis.piece_activity_phase.confidence * self.phase_weights.piece_activity_weight;
1402        }
1403
1404        score
1405    }
1406
1407    fn determine_primary_phase(&self, analysis: &GamePhaseAnalysisResult) -> GamePhase {
1408        let opening_score = analysis.opening_score;
1409        let middlegame_score = analysis.middlegame_score;
1410        let endgame_score = analysis.endgame_score;
1411
1412        if opening_score > middlegame_score && opening_score > endgame_score {
1413            GamePhase::Opening
1414        } else if endgame_score > middlegame_score && endgame_score > opening_score {
1415            GamePhase::Endgame
1416        } else if middlegame_score > 0.1 {
1417            GamePhase::Middlegame
1418        } else {
1419            GamePhase::Unknown
1420        }
1421    }
1422
1423    fn analyze_transition_state(&self, analysis: &GamePhaseAnalysisResult) -> PhaseTransition {
1424        let max_score = analysis
1425            .opening_score
1426            .max(analysis.middlegame_score)
1427            .max(analysis.endgame_score);
1428        let second_max = if analysis.opening_score == max_score {
1429            analysis.middlegame_score.max(analysis.endgame_score)
1430        } else if analysis.middlegame_score == max_score {
1431            analysis.opening_score.max(analysis.endgame_score)
1432        } else {
1433            analysis.opening_score.max(analysis.middlegame_score)
1434        };
1435
1436        let transition_threshold = 0.3;
1437
1438        if max_score - second_max < transition_threshold {
1439            // Close scores indicate transition
1440            if analysis.opening_score > analysis.endgame_score {
1441                if analysis.middlegame_score > analysis.opening_score * 0.8 {
1442                    PhaseTransition::OpeningToMiddlegame
1443                } else {
1444                    PhaseTransition::Stable
1445                }
1446            } else if analysis.middlegame_score > analysis.endgame_score {
1447                if analysis.endgame_score > analysis.middlegame_score * 0.8 {
1448                    PhaseTransition::MiddlegameToEndgame
1449                } else {
1450                    PhaseTransition::Stable
1451                }
1452            } else {
1453                PhaseTransition::Stable
1454            }
1455        } else {
1456            PhaseTransition::Stable
1457        }
1458    }
1459
1460    fn calculate_phase_confidence(&self, analysis: &GamePhaseAnalysisResult) -> f32 {
1461        let scores = [
1462            analysis.opening_score,
1463            analysis.middlegame_score,
1464            analysis.endgame_score,
1465        ];
1466        let max_score = scores.iter().fold(0.0f32, |a, &b| a.max(b));
1467        let total_score = scores.iter().sum::<f32>();
1468
1469        if total_score > 0.0 {
1470            max_score / total_score
1471        } else {
1472            0.0
1473        }
1474    }
1475
1476    fn generate_adaptation_recommendations(
1477        &self,
1478        analysis: &GamePhaseAnalysisResult,
1479    ) -> PhaseAdaptationRecommendations {
1480        let mut recommendations = PhaseAdaptationRecommendations::default();
1481
1482        match analysis.primary_phase {
1483            GamePhase::Opening => {
1484                recommendations.evaluation_weights = BlendWeights {
1485                    nnue_weight: 0.3,
1486                    pattern_weight: 0.2,
1487                    tactical_weight: 0.2,
1488                    strategic_weight: 0.3,
1489                };
1490                recommendations.search_depth_modifier = -1;
1491                recommendations.opening_book_priority = 0.9;
1492                recommendations.endgame_tablebase_priority = 0.1;
1493                recommendations.time_management_factor = 0.8;
1494            }
1495            GamePhase::Middlegame => {
1496                recommendations.evaluation_weights = BlendWeights {
1497                    nnue_weight: 0.25,
1498                    pattern_weight: 0.35,
1499                    tactical_weight: 0.3,
1500                    strategic_weight: 0.1,
1501                };
1502                recommendations.search_depth_modifier = 0;
1503                recommendations.opening_book_priority = 0.3;
1504                recommendations.endgame_tablebase_priority = 0.2;
1505                recommendations.time_management_factor = 1.0;
1506            }
1507            GamePhase::Endgame => {
1508                recommendations.evaluation_weights = BlendWeights {
1509                    nnue_weight: 0.5,
1510                    pattern_weight: 0.2,
1511                    tactical_weight: 0.15,
1512                    strategic_weight: 0.15,
1513                };
1514                recommendations.search_depth_modifier = 1;
1515                recommendations.opening_book_priority = 0.0;
1516                recommendations.endgame_tablebase_priority = 0.9;
1517                recommendations.time_management_factor = 1.2;
1518            }
1519            GamePhase::Unknown => {
1520                recommendations.evaluation_weights = BlendWeights::default();
1521                recommendations.search_depth_modifier = 0;
1522                recommendations.opening_book_priority = 0.5;
1523                recommendations.endgame_tablebase_priority = 0.5;
1524                recommendations.time_management_factor = 1.0;
1525            }
1526        }
1527
1528        // Adjust for transition states
1529        match analysis.transition_state {
1530            PhaseTransition::OpeningToMiddlegame => {
1531                recommendations.evaluation_weights.strategic_weight *= 0.8;
1532                recommendations.evaluation_weights.tactical_weight *= 1.2;
1533            }
1534            PhaseTransition::MiddlegameToEndgame => {
1535                recommendations.evaluation_weights.nnue_weight *= 1.3;
1536                recommendations.evaluation_weights.pattern_weight *= 0.7;
1537                recommendations.endgame_tablebase_priority += 0.2;
1538            }
1539            PhaseTransition::Stable => {
1540                // No adjustments needed for stable phases
1541            }
1542        }
1543
1544        recommendations
1545    }
1546
1547    // Helper methods for phase analysis
1548    fn analyze_material_balance(&self, board: &Board) -> f32 {
1549        let white_material = self.count_material(board, Color::White);
1550        let black_material = self.count_material(board, Color::Black);
1551        let total_material = white_material + black_material;
1552
1553        if total_material > 0 {
1554            (white_material - black_material).abs() as f32 / total_material as f32
1555        } else {
1556            0.0
1557        }
1558    }
1559
1560    fn compute_material_phase_confidence(&self, total_material: i32, material_balance: f32) -> f32 {
1561        let material_factor = if total_material >= 60 || total_material <= 20 {
1562            0.8
1563        } else {
1564            0.4
1565        };
1566
1567        let balance_factor = 1.0 - material_balance;
1568
1569        (material_factor + balance_factor) / 2.0
1570    }
1571
1572    fn calculate_development_score(&self, board: &Board) -> f32 {
1573        let mut development_score = 0.0;
1574        let mut total_pieces = 0;
1575
1576        for color in [Color::White, Color::Black] {
1577            // Count developed knights
1578            let knights = board.pieces(Piece::Knight) & board.color_combined(color);
1579            for square in knights {
1580                total_pieces += 1;
1581                if self.is_piece_developed_from_starting_position(square, Piece::Knight, color) {
1582                    development_score += 1.0;
1583                }
1584            }
1585
1586            // Count developed bishops
1587            let bishops = board.pieces(Piece::Bishop) & board.color_combined(color);
1588            for square in bishops {
1589                total_pieces += 1;
1590                if self.is_piece_developed_from_starting_position(square, Piece::Bishop, color) {
1591                    development_score += 1.0;
1592                }
1593            }
1594        }
1595
1596        if total_pieces > 0 {
1597            development_score / total_pieces as f32
1598        } else {
1599            0.0
1600        }
1601    }
1602
1603    fn analyze_castling_status(&self, board: &Board) -> f32 {
1604        let mut castling_score = 0.0;
1605
1606        for color in [Color::White, Color::Black] {
1607            if board.castle_rights(color).has_kingside()
1608                || board.castle_rights(color).has_queenside()
1609            {
1610                castling_score += 0.3; // Still has castling rights
1611            } else {
1612                castling_score += 0.8; // Has likely castled or lost rights
1613            }
1614        }
1615
1616        castling_score / 2.0
1617    }
1618
1619    fn evaluate_combined_king_safety(&self, board: &Board) -> f32 {
1620        let mut safety_score = 0.0;
1621
1622        for color in [Color::White, Color::Black] {
1623            let king_square = board.king_square(color);
1624            safety_score += self.evaluate_individual_king_safety(board, color, king_square);
1625        }
1626
1627        safety_score / 2.0
1628    }
1629
1630    fn evaluate_pawn_structure_development(&self, board: &Board) -> f32 {
1631        let mut structure_score = 0.0;
1632
1633        // Count advanced pawns
1634        for color in [Color::White, Color::Black] {
1635            let pawns = board.pieces(Piece::Pawn) & board.color_combined(color);
1636            let advanced_pawns = pawns
1637                .into_iter()
1638                .filter(|&square| self.is_pawn_advanced(square, color))
1639                .count();
1640
1641            structure_score += advanced_pawns as f32 * 0.1;
1642        }
1643
1644        structure_score.min(1.0)
1645    }
1646
1647    fn calculate_piece_activity_score(&self, board: &Board) -> f32 {
1648        let mut activity_score = 0.0;
1649        let mut piece_count = 0;
1650
1651        for color in [Color::White, Color::Black] {
1652            for piece_type in [Piece::Knight, Piece::Bishop, Piece::Rook, Piece::Queen] {
1653                let pieces = board.pieces(piece_type) & board.color_combined(color);
1654                for square in pieces {
1655                    piece_count += 1;
1656                    activity_score += self.calculate_piece_activity(board, square, piece_type);
1657                }
1658            }
1659        }
1660
1661        if piece_count > 0 {
1662            activity_score / piece_count as f32
1663        } else {
1664            0.0
1665        }
1666    }
1667
1668    // Simplified helper methods
1669    fn is_piece_developed_from_starting_position(
1670        &self,
1671        square: Square,
1672        piece: Piece,
1673        color: Color,
1674    ) -> bool {
1675        // Simplified check - in practice would check against actual starting squares
1676        let starting_rank = match color {
1677            Color::White => chess::Rank::First,
1678            Color::Black => chess::Rank::Eighth,
1679        };
1680
1681        square.get_rank() != starting_rank
1682    }
1683
1684    fn evaluate_individual_king_safety(
1685        &self,
1686        _board: &Board,
1687        _color: Color,
1688        _king_square: Square,
1689    ) -> f32 {
1690        // Simplified implementation
1691        0.5
1692    }
1693
1694    fn is_pawn_advanced(&self, square: Square, color: Color) -> bool {
1695        let rank = square.get_rank();
1696        match color {
1697            Color::White => rank >= chess::Rank::Fourth,
1698            Color::Black => rank <= chess::Rank::Fifth,
1699        }
1700    }
1701
1702    fn calculate_piece_activity(&self, _board: &Board, _square: Square, _piece: Piece) -> f32 {
1703        // Simplified implementation - would calculate mobility, attacks, etc.
1704        0.5
1705    }
1706
1707    fn calculate_total_material(&self, board: &Board) -> i32 {
1708        let white_material = self.count_material(board, Color::White);
1709        let black_material = self.count_material(board, Color::Black);
1710        white_material + black_material
1711    }
1712
1713    fn count_material(&self, board: &Board, color: Color) -> i32 {
1714        let pieces = board.color_combined(color);
1715        let mut material = 0;
1716
1717        material += (board.pieces(Piece::Pawn) & pieces).popcnt() as i32 * 1;
1718        material += (board.pieces(Piece::Knight) & pieces).popcnt() as i32 * 3;
1719        material += (board.pieces(Piece::Bishop) & pieces).popcnt() as i32 * 3;
1720        material += (board.pieces(Piece::Rook) & pieces).popcnt() as i32 * 5;
1721        material += (board.pieces(Piece::Queen) & pieces).popcnt() as i32 * 9;
1722
1723        material
1724    }
1725}
1726
1727impl Default for GamePhaseDetector {
1728    fn default() -> Self {
1729        Self::new()
1730    }
1731}
1732
1733/// Evaluation blender with dynamic weight computation and adaptive learning
1734pub struct EvaluationBlender {
1735    base_weights: BlendWeights,
1736    weight_history: RwLock<Vec<WeightHistoryEntry>>,
1737    performance_tracker: RwLock<EvaluatorPerformanceTracker>,
1738    adaptive_learning: bool,
1739}
1740
1741impl EvaluationBlender {
1742    pub fn new() -> Self {
1743        Self {
1744            base_weights: BlendWeights::default(),
1745            weight_history: RwLock::new(Vec::new()),
1746            performance_tracker: RwLock::new(EvaluatorPerformanceTracker::new()),
1747            adaptive_learning: true,
1748        }
1749    }
1750
1751    /// Create a blender with custom base weights
1752    pub fn with_base_weights(base_weights: BlendWeights) -> Self {
1753        Self {
1754            base_weights,
1755            weight_history: RwLock::new(Vec::new()),
1756            performance_tracker: RwLock::new(EvaluatorPerformanceTracker::new()),
1757            adaptive_learning: true,
1758        }
1759    }
1760
1761    /// Enable or disable adaptive learning
1762    pub fn set_adaptive_learning(&mut self, enabled: bool) {
1763        self.adaptive_learning = enabled;
1764    }
1765
1766    /// Compute dynamic blend weights based on position characteristics
1767    pub fn compute_blend_weights(
1768        &self,
1769        complexity_score: f32,
1770        game_phase: &GamePhase,
1771        evaluation_results: &EvaluationResults,
1772    ) -> BlendWeights {
1773        let mut weights = if self.adaptive_learning {
1774            self.compute_adaptive_base_weights(complexity_score, game_phase)
1775        } else {
1776            self.base_weights.clone()
1777        };
1778
1779        // Adjust weights based on game phase
1780        match game_phase {
1781            GamePhase::Opening => {
1782                weights.strategic_weight += 0.1;
1783                weights.tactical_weight -= 0.05;
1784            }
1785            GamePhase::Middlegame => {
1786                weights.tactical_weight += 0.1;
1787                weights.pattern_weight += 0.05;
1788            }
1789            GamePhase::Endgame => {
1790                weights.nnue_weight += 0.15;
1791                weights.strategic_weight -= 0.1;
1792            }
1793            GamePhase::Unknown => {} // Use base weights
1794        }
1795
1796        // Adjust weights based on complexity
1797        if complexity_score > 0.7 {
1798            // High complexity: trust tactical evaluation more
1799            weights.tactical_weight += 0.15;
1800            weights.nnue_weight -= 0.05;
1801            weights.pattern_weight -= 0.05;
1802            weights.strategic_weight -= 0.05;
1803        } else if complexity_score < 0.3 {
1804            // Low complexity: trust NNUE more
1805            weights.nnue_weight += 0.1;
1806            weights.tactical_weight -= 0.1;
1807        }
1808
1809        // Adjust weights based on evaluator agreement
1810        if let (Some(nnue), Some(pattern)) = (&evaluation_results.nnue, &evaluation_results.pattern)
1811        {
1812            let agreement = 1.0 - (nnue.evaluation - pattern.evaluation).abs().min(1.0);
1813            if agreement > 0.8 {
1814                // High agreement: boost both NNUE and pattern
1815                weights.nnue_weight += 0.05;
1816                weights.pattern_weight += 0.05;
1817                weights.tactical_weight -= 0.1;
1818            }
1819        }
1820
1821        // Normalize weights to sum to 1.0
1822        self.normalize_weights(&mut weights);
1823
1824        // Record weight usage for adaptive learning
1825        if self.adaptive_learning {
1826            self.record_weight_usage(&weights, complexity_score, game_phase);
1827        }
1828
1829        weights
1830    }
1831
1832    /// Compute adaptive base weights based on historical performance
1833    fn compute_adaptive_base_weights(
1834        &self,
1835        complexity_score: f32,
1836        game_phase: &GamePhase,
1837    ) -> BlendWeights {
1838        if let Ok(tracker) = self.performance_tracker.read() {
1839            // Get performance metrics for current context
1840            let context = EvaluationContext {
1841                complexity_range: Self::complexity_to_range(complexity_score),
1842                game_phase: game_phase.clone(),
1843            };
1844
1845            if let Some(optimal_weights) = tracker.get_optimal_weights(&context) {
1846                return optimal_weights;
1847            }
1848        }
1849
1850        // Fall back to base weights if no adaptive data available
1851        self.base_weights.clone()
1852    }
1853
1854    /// Record weight usage for learning
1855    fn record_weight_usage(
1856        &self,
1857        weights: &BlendWeights,
1858        complexity_score: f32,
1859        game_phase: &GamePhase,
1860    ) {
1861        if let Ok(mut history) = self.weight_history.write() {
1862            let entry = WeightHistoryEntry {
1863                timestamp: std::time::SystemTime::now(),
1864                weights: weights.clone(),
1865                complexity_score,
1866                game_phase: game_phase.clone(),
1867                evaluation_count: 1,
1868            };
1869
1870            history.push(entry);
1871
1872            // Limit history size
1873            if history.len() > 10000 {
1874                history.drain(0..1000); // Remove oldest 1000 entries
1875            }
1876        }
1877    }
1878
1879    /// Update performance metrics based on evaluation accuracy
1880    pub fn update_performance_metrics(
1881        &self,
1882        weights: &BlendWeights,
1883        complexity_score: f32,
1884        game_phase: &GamePhase,
1885        evaluation_accuracy: f32,
1886        actual_result: Option<f32>,
1887    ) {
1888        if !self.adaptive_learning {
1889            return;
1890        }
1891
1892        if let Ok(mut tracker) = self.performance_tracker.write() {
1893            let context = EvaluationContext {
1894                complexity_range: Self::complexity_to_range(complexity_score),
1895                game_phase: game_phase.clone(),
1896            };
1897
1898            tracker.record_performance(&context, weights, evaluation_accuracy, actual_result);
1899        }
1900    }
1901
1902    /// Convert complexity score to discrete range
1903    fn complexity_to_range(complexity: f32) -> ComplexityRange {
1904        if complexity < 0.3 {
1905            ComplexityRange::Low
1906        } else if complexity < 0.7 {
1907            ComplexityRange::Medium
1908        } else {
1909            ComplexityRange::High
1910        }
1911    }
1912
1913    /// Get adaptive learning statistics
1914    pub fn get_adaptive_stats(&self) -> AdaptiveLearningStats {
1915        let weight_entries = self.weight_history.read().map(|h| h.len()).unwrap_or(0);
1916
1917        let performance_contexts = self
1918            .performance_tracker
1919            .read()
1920            .map(|t| t.get_context_count())
1921            .unwrap_or(0);
1922
1923        let learning_enabled = self.adaptive_learning;
1924
1925        AdaptiveLearningStats {
1926            weight_history_entries: weight_entries,
1927            performance_contexts,
1928            learning_enabled,
1929            total_adaptations: weight_entries, // Simplified for now
1930        }
1931    }
1932
1933    /// Blend evaluations using the provided weights
1934    pub fn blend_evaluations(
1935        &self,
1936        evaluation_results: &EvaluationResults,
1937        weights: &BlendWeights,
1938    ) -> f32 {
1939        let mut blended_evaluation = 0.0;
1940        let mut total_weight = 0.0;
1941
1942        if let Some(ref nnue) = evaluation_results.nnue {
1943            blended_evaluation += nnue.evaluation * weights.nnue_weight;
1944            total_weight += weights.nnue_weight;
1945        }
1946
1947        if let Some(ref pattern) = evaluation_results.pattern {
1948            blended_evaluation += pattern.evaluation * weights.pattern_weight;
1949            total_weight += weights.pattern_weight;
1950        }
1951
1952        if let Some(ref tactical) = evaluation_results.tactical {
1953            blended_evaluation += tactical.evaluation * weights.tactical_weight;
1954            total_weight += weights.tactical_weight;
1955        }
1956
1957        if let Some(ref strategic) = evaluation_results.strategic {
1958            blended_evaluation += strategic.evaluation * weights.strategic_weight;
1959            total_weight += weights.strategic_weight;
1960        }
1961
1962        if total_weight > 0.0 {
1963            blended_evaluation / total_weight
1964        } else {
1965            0.0 // No evaluations available
1966        }
1967    }
1968
1969    fn normalize_weights(&self, weights: &mut BlendWeights) {
1970        let total = weights.nnue_weight
1971            + weights.pattern_weight
1972            + weights.tactical_weight
1973            + weights.strategic_weight;
1974        if total > 0.0 {
1975            weights.nnue_weight /= total;
1976            weights.pattern_weight /= total;
1977            weights.tactical_weight /= total;
1978            weights.strategic_weight /= total;
1979        }
1980    }
1981}
1982
1983impl Default for EvaluationBlender {
1984    fn default() -> Self {
1985        Self::new()
1986    }
1987}
1988
1989/// Confidence scorer for hybrid evaluations
1990/// Enhanced confidence scoring system for hybrid evaluation
1991pub struct ConfidenceScorer {
1992    /// Historical accuracy tracking for different evaluator combinations
1993    evaluator_accuracy_history: Arc<RwLock<EvaluatorAccuracyTracker>>,
1994    /// Pattern clarity analyzer
1995    pattern_clarity_analyzer: PatternClarityAnalyzer,
1996    /// Confidence calibration settings
1997    calibration_settings: ConfidenceCalibrationSettings,
1998    /// Recent confidence scores for trend analysis
1999    recent_confidence_scores: Arc<RwLock<VecDeque<ConfidenceHistoryEntry>>>,
2000}
2001
2002impl ConfidenceScorer {
2003    pub fn new() -> Self {
2004        Self {
2005            evaluator_accuracy_history: Arc::new(RwLock::new(EvaluatorAccuracyTracker::new())),
2006            pattern_clarity_analyzer: PatternClarityAnalyzer::new(),
2007            calibration_settings: ConfidenceCalibrationSettings::default(),
2008            recent_confidence_scores: Arc::new(RwLock::new(VecDeque::with_capacity(1000))),
2009        }
2010    }
2011
2012    /// Create confidence scorer with custom calibration settings
2013    pub fn with_calibration_settings(mut self, settings: ConfidenceCalibrationSettings) -> Self {
2014        self.calibration_settings = settings;
2015        self
2016    }
2017
2018    /// Compute comprehensive confidence in the hybrid evaluation
2019    pub fn compute_confidence(
2020        &self,
2021        evaluation_results: &EvaluationResults,
2022        blend_weights: &BlendWeights,
2023        complexity_score: f32,
2024        position_context: &PositionContext,
2025    ) -> ConfidenceAnalysisResult {
2026        let start_time = std::time::Instant::now();
2027
2028        // Factor 1: Evaluator agreement analysis
2029        let agreement_analysis = self.analyze_evaluator_agreement(evaluation_results);
2030
2031        // Factor 2: Individual evaluator confidence with historical context
2032        let evaluator_confidence =
2033            self.analyze_evaluator_confidence(evaluation_results, blend_weights);
2034
2035        // Factor 3: Position complexity confidence
2036        let complexity_confidence =
2037            self.analyze_complexity_confidence(complexity_score, position_context);
2038
2039        // Factor 4: Pattern clarity confidence
2040        let pattern_clarity = self
2041            .pattern_clarity_analyzer
2042            .analyze_pattern_clarity(evaluation_results, position_context);
2043
2044        // Factor 5: Historical accuracy confidence
2045        let historical_confidence =
2046            self.analyze_historical_accuracy(evaluation_results, blend_weights, position_context);
2047
2048        // Factor 6: Evaluation coverage confidence
2049        let coverage_confidence = self.analyze_evaluation_coverage(evaluation_results);
2050
2051        // Factor 7: Temporal consistency confidence
2052        let temporal_confidence = self.analyze_temporal_consistency(evaluation_results);
2053
2054        // Compute weighted overall confidence
2055        let confidence_factors = ConfidenceFactors {
2056            evaluator_agreement: agreement_analysis.overall_agreement,
2057            individual_confidence: evaluator_confidence,
2058            complexity_confidence,
2059            pattern_clarity: pattern_clarity.overall_clarity,
2060            historical_accuracy: historical_confidence,
2061            coverage_confidence,
2062            temporal_consistency: temporal_confidence,
2063        };
2064
2065        let overall_confidence = self.compute_weighted_confidence(&confidence_factors);
2066
2067        // Apply calibration
2068        let calibrated_confidence =
2069            self.apply_confidence_calibration(overall_confidence, &confidence_factors);
2070
2071        let computation_time = start_time.elapsed().as_millis() as u64;
2072
2073        let result = ConfidenceAnalysisResult {
2074            overall_confidence: calibrated_confidence,
2075            confidence_factors: confidence_factors.clone(),
2076            agreement_analysis,
2077            pattern_clarity_analysis: pattern_clarity,
2078            computation_time_ms: computation_time,
2079            confidence_category: self.categorize_confidence(calibrated_confidence),
2080            reliability_indicators: self.generate_reliability_indicators(&confidence_factors),
2081        };
2082
2083        // Record confidence score for trend analysis
2084        self.record_confidence_score(&result, position_context);
2085
2086        result
2087    }
2088
2089    /// Simplified confidence computation for compatibility
2090    pub fn compute_simple_confidence(
2091        &self,
2092        evaluation_results: &EvaluationResults,
2093        blend_weights: &BlendWeights,
2094        complexity_score: f32,
2095    ) -> f32 {
2096        let position_context = PositionContext::default();
2097        let analysis = self.compute_confidence(
2098            evaluation_results,
2099            blend_weights,
2100            complexity_score,
2101            &position_context,
2102        );
2103        analysis.overall_confidence
2104    }
2105
2106    fn analyze_evaluator_agreement(
2107        &self,
2108        evaluation_results: &EvaluationResults,
2109    ) -> AgreementAnalysis {
2110        let mut evaluations = Vec::new();
2111        let mut evaluator_names = Vec::new();
2112
2113        if let Some(ref nnue) = evaluation_results.nnue {
2114            evaluations.push(nnue.evaluation);
2115            evaluator_names.push("NNUE");
2116        }
2117        if let Some(ref pattern) = evaluation_results.pattern {
2118            evaluations.push(pattern.evaluation);
2119            evaluator_names.push("Pattern");
2120        }
2121        if let Some(ref tactical) = evaluation_results.tactical {
2122            evaluations.push(tactical.evaluation);
2123            evaluator_names.push("Tactical");
2124        }
2125        if let Some(ref strategic) = evaluation_results.strategic {
2126            evaluations.push(strategic.evaluation);
2127            evaluator_names.push("Strategic");
2128        }
2129
2130        if evaluations.len() < 2 {
2131            return AgreementAnalysis {
2132                overall_agreement: 0.5,
2133                pairwise_agreements: Vec::new(),
2134                evaluation_spread: 0.0,
2135                consensus_strength: 0.5,
2136                outlier_count: 0,
2137            };
2138        }
2139
2140        // Calculate pairwise agreements
2141        let mut pairwise_agreements = Vec::new();
2142        for i in 0..evaluations.len() {
2143            for j in (i + 1)..evaluations.len() {
2144                let diff = (evaluations[i] - evaluations[j]).abs();
2145                let agreement = (2.0 - diff).max(0.0).min(1.0);
2146                pairwise_agreements.push(PairwiseAgreement {
2147                    evaluator1: evaluator_names[i].to_string(),
2148                    evaluator2: evaluator_names[j].to_string(),
2149                    agreement_score: agreement,
2150                });
2151            }
2152        }
2153
2154        let overall_agreement = pairwise_agreements
2155            .iter()
2156            .map(|pa| pa.agreement_score)
2157            .sum::<f32>()
2158            / pairwise_agreements.len() as f32;
2159
2160        // Calculate evaluation spread
2161        let mean = evaluations.iter().sum::<f32>() / evaluations.len() as f32;
2162        let variance = evaluations
2163            .iter()
2164            .map(|eval| (eval - mean).powi(2))
2165            .sum::<f32>()
2166            / evaluations.len() as f32;
2167        let evaluation_spread = variance.sqrt();
2168
2169        // Consensus strength (inverse of spread)
2170        let consensus_strength = (2.0 - evaluation_spread).max(0.0).min(1.0);
2171
2172        // Count outliers (evaluations > 1.5 std deviations from mean)
2173        let outlier_count = evaluations
2174            .iter()
2175            .filter(|&&eval| (eval - mean).abs() > 1.5 * evaluation_spread)
2176            .count();
2177
2178        AgreementAnalysis {
2179            overall_agreement,
2180            pairwise_agreements,
2181            evaluation_spread,
2182            consensus_strength,
2183            outlier_count,
2184        }
2185    }
2186
2187    fn analyze_evaluator_confidence(
2188        &self,
2189        evaluation_results: &EvaluationResults,
2190        blend_weights: &BlendWeights,
2191    ) -> f32 {
2192        let mut weighted_confidence = 0.0;
2193        let mut total_weight = 0.0;
2194
2195        if let Some(ref nnue) = evaluation_results.nnue {
2196            let weight = blend_weights.nnue_weight;
2197            let adjusted_confidence = self.adjust_confidence_by_context(nnue.confidence, "NNUE");
2198            weighted_confidence += adjusted_confidence * weight;
2199            total_weight += weight;
2200        }
2201        if let Some(ref pattern) = evaluation_results.pattern {
2202            let weight = blend_weights.pattern_weight;
2203            let adjusted_confidence =
2204                self.adjust_confidence_by_context(pattern.confidence, "Pattern");
2205            weighted_confidence += adjusted_confidence * weight;
2206            total_weight += weight;
2207        }
2208        if let Some(ref tactical) = evaluation_results.tactical {
2209            let weight = blend_weights.tactical_weight;
2210            let adjusted_confidence =
2211                self.adjust_confidence_by_context(tactical.confidence, "Tactical");
2212            weighted_confidence += adjusted_confidence * weight;
2213            total_weight += weight;
2214        }
2215        if let Some(ref strategic) = evaluation_results.strategic {
2216            let weight = blend_weights.strategic_weight;
2217            let adjusted_confidence =
2218                self.adjust_confidence_by_context(strategic.confidence, "Strategic");
2219            weighted_confidence += adjusted_confidence * weight;
2220            total_weight += weight;
2221        }
2222
2223        if total_weight > 0.0 {
2224            weighted_confidence / total_weight
2225        } else {
2226            0.5
2227        }
2228    }
2229
2230    fn analyze_complexity_confidence(
2231        &self,
2232        complexity_score: f32,
2233        position_context: &PositionContext,
2234    ) -> f32 {
2235        let base_confidence = 1.0 - complexity_score;
2236
2237        // Adjust based on position characteristics
2238        let mut adjusted_confidence = base_confidence;
2239
2240        // Tactical positions tend to be more uncertain
2241        if position_context.has_tactical_threats {
2242            adjusted_confidence *= 0.8;
2243        }
2244
2245        // Endgame positions are often more precise
2246        if position_context.game_phase == GamePhase::Endgame {
2247            adjusted_confidence *= 1.1;
2248        }
2249
2250        // Opening positions with book knowledge are more confident
2251        if position_context.game_phase == GamePhase::Opening && position_context.in_opening_book {
2252            adjusted_confidence *= 1.2;
2253        }
2254
2255        adjusted_confidence.clamp(0.0, 1.0)
2256    }
2257
2258    fn analyze_historical_accuracy(
2259        &self,
2260        evaluation_results: &EvaluationResults,
2261        blend_weights: &BlendWeights,
2262        position_context: &PositionContext,
2263    ) -> f32 {
2264        if let Ok(accuracy_tracker) = self.evaluator_accuracy_history.read() {
2265            let evaluator_combination =
2266                EvaluatorCombination::from_results(evaluation_results, blend_weights);
2267            let context_hash = position_context.get_context_hash();
2268
2269            accuracy_tracker
2270                .get_historical_accuracy(&evaluator_combination, context_hash)
2271                .unwrap_or(0.6) // Default confidence when no history available
2272        } else {
2273            0.6
2274        }
2275    }
2276
2277    fn analyze_evaluation_coverage(&self, evaluation_results: &EvaluationResults) -> f32 {
2278        let active_evaluators = self.count_active_evaluators(evaluation_results);
2279        let max_evaluators = 4.0;
2280
2281        let base_coverage = active_evaluators as f32 / max_evaluators;
2282
2283        // Bonus for having diverse evaluator types
2284        let mut diversity_bonus = 0.0;
2285        if evaluation_results.nnue.is_some() && evaluation_results.tactical.is_some() {
2286            diversity_bonus += 0.1; // Neural + tactical is a good combination
2287        }
2288        if evaluation_results.pattern.is_some() && evaluation_results.strategic.is_some() {
2289            diversity_bonus += 0.1; // Pattern + strategic is complementary
2290        }
2291
2292        (base_coverage + diversity_bonus).min(1.0)
2293    }
2294
2295    fn analyze_temporal_consistency(&self, _evaluation_results: &EvaluationResults) -> f32 {
2296        // Analyze consistency with recent evaluations
2297        if let Ok(recent_scores) = self.recent_confidence_scores.read() {
2298            if recent_scores.len() < 3 {
2299                return 0.6; // Not enough data for trend analysis
2300            }
2301
2302            let recent_confidences: Vec<f32> = recent_scores
2303                .iter()
2304                .rev()
2305                .take(5)
2306                .map(|entry| entry.confidence_score)
2307                .collect();
2308
2309            if recent_confidences.len() < 2 {
2310                return 0.6;
2311            }
2312
2313            // Calculate consistency (low variance = high consistency)
2314            let mean = recent_confidences.iter().sum::<f32>() / recent_confidences.len() as f32;
2315            let variance = recent_confidences
2316                .iter()
2317                .map(|&conf| (conf - mean).powi(2))
2318                .sum::<f32>()
2319                / recent_confidences.len() as f32;
2320            let consistency = (1.0 - variance.sqrt()).max(0.0);
2321
2322            consistency
2323        } else {
2324            0.6
2325        }
2326    }
2327
2328    fn compute_weighted_confidence(&self, factors: &ConfidenceFactors) -> f32 {
2329        let weights = &self.calibration_settings.factor_weights;
2330
2331        factors.evaluator_agreement * weights.agreement_weight
2332            + factors.individual_confidence * weights.individual_weight
2333            + factors.complexity_confidence * weights.complexity_weight
2334            + factors.pattern_clarity * weights.pattern_clarity_weight
2335            + factors.historical_accuracy * weights.historical_weight
2336            + factors.coverage_confidence * weights.coverage_weight
2337            + factors.temporal_consistency * weights.temporal_weight
2338    }
2339
2340    fn apply_confidence_calibration(
2341        &self,
2342        raw_confidence: f32,
2343        factors: &ConfidenceFactors,
2344    ) -> f32 {
2345        let mut calibrated = raw_confidence;
2346
2347        // Apply non-linear calibration curve
2348        calibrated = match &self.calibration_settings.calibration_curve {
2349            CalibrationCurve::Linear => calibrated,
2350            CalibrationCurve::Conservative => calibrated.powf(1.2),
2351            CalibrationCurve::Aggressive => calibrated.powf(0.8),
2352            CalibrationCurve::Sigmoid => 1.0 / (1.0 + (-6.0 * (calibrated - 0.5)).exp()),
2353        };
2354
2355        // Apply situational adjustments
2356        if factors.evaluator_agreement < 0.3 {
2357            calibrated *= 0.8; // Reduce confidence when evaluators disagree
2358        }
2359
2360        if factors.coverage_confidence < 0.5 {
2361            calibrated *= 0.9; // Reduce confidence with limited evaluator coverage
2362        }
2363
2364        calibrated.clamp(0.0, 1.0)
2365    }
2366
2367    fn categorize_confidence(&self, confidence: f32) -> ConfidenceCategory {
2368        if confidence >= 0.8 {
2369            ConfidenceCategory::VeryHigh
2370        } else if confidence >= 0.6 {
2371            ConfidenceCategory::High
2372        } else if confidence >= 0.4 {
2373            ConfidenceCategory::Medium
2374        } else if confidence >= 0.2 {
2375            ConfidenceCategory::Low
2376        } else {
2377            ConfidenceCategory::VeryLow
2378        }
2379    }
2380
2381    fn generate_reliability_indicators(
2382        &self,
2383        factors: &ConfidenceFactors,
2384    ) -> Vec<ReliabilityIndicator> {
2385        let mut indicators = Vec::new();
2386
2387        if factors.evaluator_agreement < 0.4 {
2388            indicators.push(ReliabilityIndicator::LowEvaluatorAgreement);
2389        }
2390
2391        if factors.complexity_confidence < 0.3 {
2392            indicators.push(ReliabilityIndicator::HighPositionComplexity);
2393        }
2394
2395        if factors.pattern_clarity < 0.4 {
2396            indicators.push(ReliabilityIndicator::UnclearPatterns);
2397        }
2398
2399        if factors.coverage_confidence < 0.5 {
2400            indicators.push(ReliabilityIndicator::LimitedEvaluatorCoverage);
2401        }
2402
2403        if factors.temporal_consistency < 0.4 {
2404            indicators.push(ReliabilityIndicator::InconsistentHistory);
2405        }
2406
2407        if factors.historical_accuracy < 0.5 {
2408            indicators.push(ReliabilityIndicator::PoorHistoricalAccuracy);
2409        }
2410
2411        if indicators.is_empty() {
2412            indicators.push(ReliabilityIndicator::HighReliability);
2413        }
2414
2415        indicators
2416    }
2417
2418    fn record_confidence_score(
2419        &self,
2420        result: &ConfidenceAnalysisResult,
2421        position_context: &PositionContext,
2422    ) {
2423        if let Ok(mut recent_scores) = self.recent_confidence_scores.write() {
2424            let entry = ConfidenceHistoryEntry {
2425                timestamp: std::time::SystemTime::now(),
2426                confidence_score: result.overall_confidence,
2427                position_context: position_context.clone(),
2428                computation_time_ms: result.computation_time_ms,
2429            };
2430
2431            recent_scores.push_back(entry);
2432
2433            // Keep only recent entries
2434            while recent_scores.len() > 1000 {
2435                recent_scores.pop_front();
2436            }
2437        }
2438    }
2439
2440    fn adjust_confidence_by_context(&self, base_confidence: f32, evaluator_type: &str) -> f32 {
2441        // Apply evaluator-specific adjustments based on known strengths/weaknesses
2442        match evaluator_type {
2443            "NNUE" => {
2444                // NNUE is generally reliable but can struggle in unusual positions
2445                base_confidence * 1.05
2446            }
2447            "Tactical" => {
2448                // Tactical search is very reliable for tactical positions
2449                base_confidence * 1.1
2450            }
2451            "Pattern" => {
2452                // Pattern recognition varies widely in reliability
2453                base_confidence * 0.95
2454            }
2455            "Strategic" => {
2456                // Strategic evaluation is subjective but valuable
2457                base_confidence * 1.0
2458            }
2459            _ => base_confidence,
2460        }
2461    }
2462
2463    fn count_active_evaluators(&self, evaluation_results: &EvaluationResults) -> u32 {
2464        let mut count = 0;
2465        if evaluation_results.nnue.is_some() {
2466            count += 1;
2467        }
2468        if evaluation_results.pattern.is_some() {
2469            count += 1;
2470        }
2471        if evaluation_results.tactical.is_some() {
2472            count += 1;
2473        }
2474        if evaluation_results.strategic.is_some() {
2475            count += 1;
2476        }
2477        count
2478    }
2479
2480    /// Update historical accuracy based on actual game outcomes
2481    pub fn update_accuracy_history(
2482        &self,
2483        evaluation_results: &EvaluationResults,
2484        blend_weights: &BlendWeights,
2485        position_context: &PositionContext,
2486        actual_outcome: f32,
2487        predicted_outcome: f32,
2488    ) {
2489        if let Ok(mut accuracy_tracker) = self.evaluator_accuracy_history.write() {
2490            let evaluator_combination =
2491                EvaluatorCombination::from_results(evaluation_results, blend_weights);
2492            let context_hash = position_context.get_context_hash();
2493            let accuracy = 1.0 - (actual_outcome - predicted_outcome).abs();
2494
2495            accuracy_tracker.record_accuracy(&evaluator_combination, context_hash, accuracy);
2496        }
2497    }
2498
2499    /// Get confidence scoring statistics
2500    pub fn get_statistics(&self) -> ConfidenceScoringStats {
2501        let recent_count = self
2502            .recent_confidence_scores
2503            .read()
2504            .map(|scores| scores.len())
2505            .unwrap_or(0);
2506
2507        let accuracy_entries = self
2508            .evaluator_accuracy_history
2509            .read()
2510            .map(|tracker| tracker.get_total_entries())
2511            .unwrap_or(0);
2512
2513        let average_confidence = self
2514            .recent_confidence_scores
2515            .read()
2516            .map(|scores| {
2517                if scores.is_empty() {
2518                    0.5
2519                } else {
2520                    scores
2521                        .iter()
2522                        .map(|entry| entry.confidence_score)
2523                        .sum::<f32>()
2524                        / scores.len() as f32
2525                }
2526            })
2527            .unwrap_or(0.5);
2528
2529        ConfidenceScoringStats {
2530            total_confidence_analyses: recent_count,
2531            average_confidence,
2532            historical_accuracy_entries: accuracy_entries,
2533            calibration_curve: self.calibration_settings.calibration_curve.clone(),
2534        }
2535    }
2536}
2537
2538/// Weight history entry for adaptive learning
2539#[derive(Debug, Clone)]
2540struct WeightHistoryEntry {
2541    timestamp: std::time::SystemTime,
2542    weights: BlendWeights,
2543    complexity_score: f32,
2544    game_phase: GamePhase,
2545    evaluation_count: u32,
2546}
2547
2548/// Evaluation context for performance tracking
2549#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2550struct EvaluationContext {
2551    complexity_range: ComplexityRange,
2552    game_phase: GamePhase,
2553}
2554
2555/// Complexity range classification
2556#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2557enum ComplexityRange {
2558    Low,    // 0.0 - 0.3
2559    Medium, // 0.3 - 0.7
2560    High,   // 0.7 - 1.0
2561}
2562
2563/// Performance tracker for evaluator combinations
2564#[derive(Debug)]
2565struct EvaluatorPerformanceTracker {
2566    context_performance: HashMap<EvaluationContext, ContextPerformance>,
2567    total_evaluations: u64,
2568}
2569
2570impl EvaluatorPerformanceTracker {
2571    fn new() -> Self {
2572        Self {
2573            context_performance: HashMap::new(),
2574            total_evaluations: 0,
2575        }
2576    }
2577
2578    fn record_performance(
2579        &mut self,
2580        context: &EvaluationContext,
2581        weights: &BlendWeights,
2582        accuracy: f32,
2583        _actual_result: Option<f32>,
2584    ) {
2585        let performance = self
2586            .context_performance
2587            .entry(context.clone())
2588            .or_insert_with(ContextPerformance::new);
2589
2590        performance.record_evaluation(weights, accuracy);
2591        self.total_evaluations += 1;
2592    }
2593
2594    fn get_optimal_weights(&self, context: &EvaluationContext) -> Option<BlendWeights> {
2595        self.context_performance
2596            .get(context)
2597            .and_then(|perf| perf.get_best_weights())
2598    }
2599
2600    fn get_context_count(&self) -> usize {
2601        self.context_performance.len()
2602    }
2603}
2604
2605/// Performance data for a specific evaluation context
2606#[derive(Debug)]
2607struct ContextPerformance {
2608    weight_performance: Vec<WeightPerformanceRecord>,
2609    best_weights: Option<BlendWeights>,
2610    best_accuracy: f32,
2611}
2612
2613impl ContextPerformance {
2614    fn new() -> Self {
2615        Self {
2616            weight_performance: Vec::new(),
2617            best_weights: None,
2618            best_accuracy: 0.0,
2619        }
2620    }
2621
2622    fn record_evaluation(&mut self, weights: &BlendWeights, accuracy: f32) {
2623        let record = WeightPerformanceRecord {
2624            weights: weights.clone(),
2625            accuracy,
2626            evaluation_count: 1,
2627        };
2628
2629        self.weight_performance.push(record);
2630
2631        // Update best weights if this is better
2632        if accuracy > self.best_accuracy {
2633            self.best_accuracy = accuracy;
2634            self.best_weights = Some(weights.clone());
2635        }
2636
2637        // Limit history to prevent unbounded growth
2638        if self.weight_performance.len() > 1000 {
2639            self.weight_performance.drain(0..100);
2640        }
2641    }
2642
2643    fn get_best_weights(&self) -> Option<BlendWeights> {
2644        self.best_weights.clone()
2645    }
2646}
2647
2648/// Performance record for specific weight combination
2649#[derive(Debug, Clone)]
2650struct WeightPerformanceRecord {
2651    weights: BlendWeights,
2652    accuracy: f32,
2653    evaluation_count: u32,
2654}
2655
2656/// Statistics for adaptive learning system
2657#[derive(Debug, Clone)]
2658pub struct AdaptiveLearningStats {
2659    pub weight_history_entries: usize,
2660    pub performance_contexts: usize,
2661    pub learning_enabled: bool,
2662    pub total_adaptations: usize,
2663}
2664
2665/// Detailed complexity analysis result
2666#[derive(Debug, Clone)]
2667pub struct ComplexityAnalysisResult {
2668    /// Overall complexity score (0.0 to 1.0)
2669    pub overall_complexity: f32,
2670    /// Individual complexity factors
2671    pub material_complexity: f32,
2672    pub pawn_structure_complexity: f32,
2673    pub king_safety_complexity: f32,
2674    pub piece_coordination_complexity: f32,
2675    pub tactical_complexity: f32,
2676    pub positional_complexity: f32,
2677    pub time_complexity: f32,
2678    pub endgame_complexity: f32,
2679    /// Complexity classification
2680    pub complexity_category: ComplexityCategory,
2681    /// Key factors contributing to complexity
2682    pub key_complexity_factors: Vec<ComplexityFactor>,
2683    /// Evaluation method recommendations
2684    pub evaluation_recommendations: EvaluationRecommendations,
2685}
2686
2687impl ComplexityAnalysisResult {
2688    fn new() -> Self {
2689        Self {
2690            overall_complexity: 0.0,
2691            material_complexity: 0.0,
2692            pawn_structure_complexity: 0.0,
2693            king_safety_complexity: 0.0,
2694            piece_coordination_complexity: 0.0,
2695            tactical_complexity: 0.0,
2696            positional_complexity: 0.0,
2697            time_complexity: 0.0,
2698            endgame_complexity: 0.0,
2699            complexity_category: ComplexityCategory::Medium,
2700            key_complexity_factors: Vec::new(),
2701            evaluation_recommendations: EvaluationRecommendations::default(),
2702        }
2703    }
2704}
2705
2706/// Complexity category classification
2707#[derive(Debug, Clone, PartialEq, Eq)]
2708pub enum ComplexityCategory {
2709    VeryLow,
2710    Low,
2711    Medium,
2712    High,
2713    VeryHigh,
2714}
2715
2716/// Key complexity factors
2717#[derive(Debug, Clone, PartialEq, Eq)]
2718pub enum ComplexityFactor {
2719    MaterialImbalance,
2720    PawnStructure,
2721    KingSafety,
2722    PieceCoordination,
2723    TacticalThreats,
2724    PositionalThemes,
2725    TimeFactors,
2726    EndgameFactors,
2727}
2728
2729/// Weights for different complexity factors
2730#[derive(Debug, Clone)]
2731pub struct ComplexityWeights {
2732    pub material_weight: f32,
2733    pub pawn_structure_weight: f32,
2734    pub king_safety_weight: f32,
2735    pub piece_coordination_weight: f32,
2736    pub tactical_weight: f32,
2737    pub positional_weight: f32,
2738    pub time_weight: f32,
2739    pub endgame_weight: f32,
2740}
2741
2742impl Default for ComplexityWeights {
2743    fn default() -> Self {
2744        Self {
2745            material_weight: 0.15,
2746            pawn_structure_weight: 0.15,
2747            king_safety_weight: 0.20,
2748            piece_coordination_weight: 0.15,
2749            tactical_weight: 0.25,
2750            positional_weight: 0.10,
2751            time_weight: 0.05,
2752            endgame_weight: 0.10,
2753        }
2754    }
2755}
2756
2757/// Analysis depth configuration
2758#[derive(Debug, Clone, PartialEq, Eq)]
2759pub enum AnalysisDepth {
2760    Fast,          // Quick analysis, reduced complexity factors
2761    Standard,      // Normal analysis depth
2762    Deep,          // More thorough analysis
2763    Comprehensive, // Maximum detail analysis
2764}
2765
2766/// Evaluation method recommendations based on complexity analysis
2767#[derive(Debug, Clone)]
2768pub struct EvaluationRecommendations {
2769    /// Whether to prefer NNUE over other methods
2770    pub prefer_nnue: bool,
2771    /// Recommended tactical search depth
2772    pub tactical_depth: u8,
2773    /// Priority for pattern analysis (0.0 to 1.0)
2774    pub pattern_analysis_priority: f32,
2775    /// Priority for strategic analysis (0.0 to 1.0)
2776    pub strategic_analysis_priority: f32,
2777    /// Whether tactical verification is required
2778    pub require_tactical_verification: bool,
2779    /// Whether king safety analysis is recommended
2780    pub king_safety_analysis: bool,
2781    /// Whether endgame analysis is recommended
2782    pub endgame_analysis: bool,
2783}
2784
2785impl Default for EvaluationRecommendations {
2786    fn default() -> Self {
2787        Self {
2788            prefer_nnue: false,
2789            tactical_depth: 6,
2790            pattern_analysis_priority: 0.5,
2791            strategic_analysis_priority: 0.3,
2792            require_tactical_verification: false,
2793            king_safety_analysis: false,
2794            endgame_analysis: false,
2795        }
2796    }
2797}
2798
2799/// Game phase analysis result with detailed indicators
2800#[derive(Debug, Clone)]
2801pub struct GamePhaseAnalysisResult {
2802    /// Primary detected game phase
2803    pub primary_phase: GamePhase,
2804    /// Individual phase indicators
2805    pub material_phase: PhaseIndicator,
2806    pub development_phase: PhaseIndicator,
2807    pub move_count_phase: PhaseIndicator,
2808    pub king_safety_phase: PhaseIndicator,
2809    pub pawn_structure_phase: PhaseIndicator,
2810    pub piece_activity_phase: PhaseIndicator,
2811    /// Phase scores
2812    pub opening_score: f32,
2813    pub middlegame_score: f32,
2814    pub endgame_score: f32,
2815    /// Confidence in phase detection
2816    pub phase_confidence: f32,
2817    /// Transition state
2818    pub transition_state: PhaseTransition,
2819    /// Adaptation recommendations
2820    pub adaptation_recommendations: PhaseAdaptationRecommendations,
2821}
2822
2823impl GamePhaseAnalysisResult {
2824    fn new() -> Self {
2825        Self {
2826            primary_phase: GamePhase::Unknown,
2827            material_phase: PhaseIndicator::default(),
2828            development_phase: PhaseIndicator::default(),
2829            move_count_phase: PhaseIndicator::default(),
2830            king_safety_phase: PhaseIndicator::default(),
2831            pawn_structure_phase: PhaseIndicator::default(),
2832            piece_activity_phase: PhaseIndicator::default(),
2833            opening_score: 0.0,
2834            middlegame_score: 0.0,
2835            endgame_score: 0.0,
2836            phase_confidence: 0.0,
2837            transition_state: PhaseTransition::Stable,
2838            adaptation_recommendations: PhaseAdaptationRecommendations::default(),
2839        }
2840    }
2841}
2842
2843/// Individual phase indicator with confidence
2844#[derive(Debug, Clone)]
2845pub struct PhaseIndicator {
2846    pub phase: GamePhase,
2847    pub confidence: f32,
2848}
2849
2850impl Default for PhaseIndicator {
2851    fn default() -> Self {
2852        Self {
2853            phase: GamePhase::Unknown,
2854            confidence: 0.0,
2855        }
2856    }
2857}
2858
2859/// Phase transition states
2860#[derive(Debug, Clone, PartialEq, Eq)]
2861pub enum PhaseTransition {
2862    Stable,
2863    OpeningToMiddlegame,
2864    MiddlegameToEndgame,
2865}
2866
2867/// Weights for phase detection indicators
2868#[derive(Debug, Clone)]
2869pub struct PhaseDetectionWeights {
2870    pub material_weight: f32,
2871    pub development_weight: f32,
2872    pub move_count_weight: f32,
2873    pub king_safety_weight: f32,
2874    pub pawn_structure_weight: f32,
2875    pub piece_activity_weight: f32,
2876}
2877
2878impl Default for PhaseDetectionWeights {
2879    fn default() -> Self {
2880        Self {
2881            material_weight: 0.25,
2882            development_weight: 0.20,
2883            move_count_weight: 0.15,
2884            king_safety_weight: 0.15,
2885            pawn_structure_weight: 0.15,
2886            piece_activity_weight: 0.10,
2887        }
2888    }
2889}
2890
2891/// Settings for phase adaptation
2892#[derive(Debug, Clone)]
2893pub struct PhaseAdaptationSettings {
2894    pub enable_transition_detection: bool,
2895    pub transition_sensitivity: f32,
2896    pub adaptation_responsiveness: f32,
2897}
2898
2899impl Default for PhaseAdaptationSettings {
2900    fn default() -> Self {
2901        Self {
2902            enable_transition_detection: true,
2903            transition_sensitivity: 0.3,
2904            adaptation_responsiveness: 1.0,
2905        }
2906    }
2907}
2908
2909/// Phase-specific adaptation recommendations
2910#[derive(Debug, Clone)]
2911pub struct PhaseAdaptationRecommendations {
2912    /// Recommended evaluation weights for this phase
2913    pub evaluation_weights: BlendWeights,
2914    /// Search depth modifier (-2 to +2)
2915    pub search_depth_modifier: i8,
2916    /// Opening book priority (0.0 to 1.0)
2917    pub opening_book_priority: f32,
2918    /// Endgame tablebase priority (0.0 to 1.0)
2919    pub endgame_tablebase_priority: f32,
2920    /// Time management factor (0.5 to 2.0)
2921    pub time_management_factor: f32,
2922}
2923
2924impl Default for PhaseAdaptationRecommendations {
2925    fn default() -> Self {
2926        Self {
2927            evaluation_weights: BlendWeights::default(),
2928            search_depth_modifier: 0,
2929            opening_book_priority: 0.5,
2930            endgame_tablebase_priority: 0.5,
2931            time_management_factor: 1.0,
2932        }
2933    }
2934}
2935
2936impl Default for ConfidenceScorer {
2937    fn default() -> Self {
2938        Self::new()
2939    }
2940}
2941
2942/// Pattern clarity analyzer for confidence scoring
2943#[derive(Debug)]
2944pub struct PatternClarityAnalyzer {
2945    clarity_cache: Arc<RwLock<HashMap<String, PatternClarityResult>>>,
2946}
2947
2948impl PatternClarityAnalyzer {
2949    pub fn new() -> Self {
2950        Self {
2951            clarity_cache: Arc::new(RwLock::new(HashMap::new())),
2952        }
2953    }
2954
2955    pub fn analyze_pattern_clarity(
2956        &self,
2957        evaluation_results: &EvaluationResults,
2958        position_context: &PositionContext,
2959    ) -> PatternClarityResult {
2960        let cache_key = format!(
2961            "{}_{}",
2962            position_context.position_hash,
2963            evaluation_results.hash_key()
2964        );
2965
2966        // Check cache
2967        if let Ok(cache) = self.clarity_cache.read() {
2968            if let Some(result) = cache.get(&cache_key) {
2969                return result.clone();
2970            }
2971        }
2972
2973        let mut clarity_factors = Vec::new();
2974        let mut overall_clarity = 0.0;
2975
2976        // Analyze pattern clarity from different evaluators
2977        if let Some(ref pattern_eval) = evaluation_results.pattern {
2978            let pattern_clarity = self.analyze_pattern_evaluation_clarity(pattern_eval);
2979            clarity_factors.push(ClarityFactor {
2980                evaluator: "Pattern".to_string(),
2981                clarity_score: pattern_clarity,
2982                contributing_factors: vec![
2983                    "pattern_strength".to_string(),
2984                    "pattern_frequency".to_string(),
2985                ],
2986            });
2987            overall_clarity += pattern_clarity * 0.4;
2988        }
2989
2990        if let Some(ref tactical_eval) = evaluation_results.tactical {
2991            let tactical_clarity = self.analyze_tactical_evaluation_clarity(tactical_eval);
2992            clarity_factors.push(ClarityFactor {
2993                evaluator: "Tactical".to_string(),
2994                clarity_score: tactical_clarity,
2995                contributing_factors: vec![
2996                    "search_depth".to_string(),
2997                    "best_move_clarity".to_string(),
2998                ],
2999            });
3000            overall_clarity += tactical_clarity * 0.3;
3001        }
3002
3003        if let Some(ref nnue_eval) = evaluation_results.nnue {
3004            let nnue_clarity = self.analyze_nnue_evaluation_clarity(nnue_eval);
3005            clarity_factors.push(ClarityFactor {
3006                evaluator: "NNUE".to_string(),
3007                clarity_score: nnue_clarity,
3008                contributing_factors: vec![
3009                    "evaluation_magnitude".to_string(),
3010                    "position_familiarity".to_string(),
3011                ],
3012            });
3013            overall_clarity += nnue_clarity * 0.2;
3014        }
3015
3016        if let Some(ref strategic_eval) = evaluation_results.strategic {
3017            let strategic_clarity = self.analyze_strategic_evaluation_clarity(strategic_eval);
3018            clarity_factors.push(ClarityFactor {
3019                evaluator: "Strategic".to_string(),
3020                clarity_score: strategic_clarity,
3021                contributing_factors: vec![
3022                    "plan_clarity".to_string(),
3023                    "initiative_balance".to_string(),
3024                ],
3025            });
3026            overall_clarity += strategic_clarity * 0.1;
3027        }
3028
3029        let result = PatternClarityResult {
3030            overall_clarity,
3031            clarity_factors,
3032            position_characteristics: self.analyze_position_characteristics(position_context),
3033        };
3034
3035        // Cache result
3036        if let Ok(mut cache) = self.clarity_cache.write() {
3037            cache.insert(cache_key, result.clone());
3038            if cache.len() > 1000 {
3039                // Remove oldest 50% of entries for better cache efficiency
3040                let keys_to_remove: Vec<_> = cache.keys().take(500).cloned().collect();
3041                for key in keys_to_remove {
3042                    cache.remove(&key);
3043                }
3044            }
3045        }
3046
3047        result
3048    }
3049
3050    fn analyze_pattern_evaluation_clarity(&self, pattern_eval: &EvaluationComponent) -> f32 {
3051        // Base clarity on evaluation magnitude and confidence
3052        let magnitude_clarity = pattern_eval.evaluation.abs().min(1.0);
3053        let confidence_clarity = pattern_eval.confidence;
3054
3055        (magnitude_clarity + confidence_clarity) / 2.0
3056    }
3057
3058    fn analyze_tactical_evaluation_clarity(&self, tactical_eval: &EvaluationComponent) -> f32 {
3059        // Tactical evaluations with high confidence and clear best moves are clearer
3060        let base_clarity = tactical_eval.confidence;
3061
3062        // Check for additional tactical clarity indicators
3063        let mut clarity_bonus = 0.0;
3064        if let Some(depth) = tactical_eval.additional_info.get("search_depth") {
3065            if *depth >= 6.0 {
3066                clarity_bonus += 0.1;
3067            }
3068        }
3069
3070        (base_clarity + clarity_bonus).min(1.0)
3071    }
3072
3073    fn analyze_nnue_evaluation_clarity(&self, nnue_eval: &EvaluationComponent) -> f32 {
3074        // NNUE clarity based on evaluation confidence and magnitude
3075        let confidence_clarity = nnue_eval.confidence;
3076        let magnitude_clarity = (nnue_eval.evaluation.abs() / 2.0).min(1.0);
3077
3078        (confidence_clarity * 0.7 + magnitude_clarity * 0.3).min(1.0)
3079    }
3080
3081    fn analyze_strategic_evaluation_clarity(&self, strategic_eval: &EvaluationComponent) -> f32 {
3082        // Strategic clarity based on plan coherence and initiative clarity
3083        let base_clarity = strategic_eval.confidence;
3084
3085        // Check for strategic clarity indicators
3086        let mut clarity_adjustment = 0.0;
3087        if let Some(plans_count) = strategic_eval.additional_info.get("strategic_plans_count") {
3088            if *plans_count >= 2.0 {
3089                clarity_adjustment += 0.1;
3090            }
3091        }
3092
3093        (base_clarity + clarity_adjustment).min(1.0)
3094    }
3095
3096    fn analyze_position_characteristics(&self, position_context: &PositionContext) -> Vec<String> {
3097        let mut characteristics = Vec::new();
3098
3099        if position_context.has_tactical_threats {
3100            characteristics.push("tactical_position".to_string());
3101        }
3102
3103        if position_context.in_opening_book {
3104            characteristics.push("known_opening".to_string());
3105        }
3106
3107        match position_context.game_phase {
3108            GamePhase::Opening => characteristics.push("opening_phase".to_string()),
3109            GamePhase::Middlegame => characteristics.push("middlegame_phase".to_string()),
3110            GamePhase::Endgame => characteristics.push("endgame_phase".to_string()),
3111            GamePhase::Unknown => characteristics.push("unknown_phase".to_string()),
3112        }
3113
3114        if position_context.material_imbalance > 3.0 {
3115            characteristics.push("material_imbalance".to_string());
3116        }
3117
3118        characteristics
3119    }
3120}
3121
3122/// Evaluator accuracy tracker for historical confidence analysis
3123#[derive(Debug)]
3124pub struct EvaluatorAccuracyTracker {
3125    accuracy_records: HashMap<EvaluatorCombination, HashMap<u64, AccuracyRecord>>,
3126    total_entries: usize,
3127}
3128
3129impl EvaluatorAccuracyTracker {
3130    pub fn new() -> Self {
3131        Self {
3132            accuracy_records: HashMap::new(),
3133            total_entries: 0,
3134        }
3135    }
3136
3137    pub fn record_accuracy(
3138        &mut self,
3139        combination: &EvaluatorCombination,
3140        context_hash: u64,
3141        accuracy: f32,
3142    ) {
3143        let context_records = self
3144            .accuracy_records
3145            .entry(combination.clone())
3146            .or_insert_with(HashMap::new);
3147
3148        let record = context_records
3149            .entry(context_hash)
3150            .or_insert_with(|| AccuracyRecord::new());
3151
3152        record.add_accuracy(accuracy);
3153        self.total_entries += 1;
3154    }
3155
3156    pub fn get_historical_accuracy(
3157        &self,
3158        combination: &EvaluatorCombination,
3159        context_hash: u64,
3160    ) -> Option<f32> {
3161        self.accuracy_records
3162            .get(combination)?
3163            .get(&context_hash)
3164            .map(|record| record.get_average_accuracy())
3165    }
3166
3167    pub fn get_total_entries(&self) -> usize {
3168        self.total_entries
3169    }
3170}
3171
3172/// Accuracy record for a specific evaluator combination and context
3173#[derive(Debug, Clone)]
3174pub struct AccuracyRecord {
3175    accuracies: Vec<f32>,
3176    average_accuracy: f32,
3177}
3178
3179impl AccuracyRecord {
3180    pub fn new() -> Self {
3181        Self {
3182            accuracies: Vec::new(),
3183            average_accuracy: 0.0,
3184        }
3185    }
3186
3187    pub fn add_accuracy(&mut self, accuracy: f32) {
3188        self.accuracies.push(accuracy);
3189
3190        // Keep only recent accuracies (last 50)
3191        if self.accuracies.len() > 50 {
3192            self.accuracies.remove(0);
3193        }
3194
3195        // Update average
3196        self.average_accuracy = self.accuracies.iter().sum::<f32>() / self.accuracies.len() as f32;
3197    }
3198
3199    pub fn get_average_accuracy(&self) -> f32 {
3200        self.average_accuracy
3201    }
3202}
3203
3204/// Position context for confidence analysis
3205#[derive(Debug, Clone, Default)]
3206pub struct PositionContext {
3207    pub position_hash: u64,
3208    pub game_phase: GamePhase,
3209    pub has_tactical_threats: bool,
3210    pub in_opening_book: bool,
3211    pub material_imbalance: f32,
3212    pub complexity_score: f32,
3213}
3214
3215impl PositionContext {
3216    pub fn get_context_hash(&self) -> u64 {
3217        // Create a hash that represents the position context
3218        let mut hasher = std::collections::hash_map::DefaultHasher::new();
3219        std::hash::Hash::hash(
3220            &(
3221                self.game_phase as u8,
3222                self.has_tactical_threats,
3223                self.in_opening_book,
3224                (self.material_imbalance * 10.0) as i32,
3225                (self.complexity_score * 10.0) as i32,
3226            ),
3227            &mut hasher,
3228        );
3229        std::hash::Hasher::finish(&hasher)
3230    }
3231}
3232
3233/// Evaluator combination identifier
3234#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3235pub struct EvaluatorCombination {
3236    pub has_nnue: bool,
3237    pub has_pattern: bool,
3238    pub has_tactical: bool,
3239    pub has_strategic: bool,
3240    pub weight_signature: String, // Simplified weight representation
3241}
3242
3243impl EvaluatorCombination {
3244    pub fn from_results(
3245        evaluation_results: &EvaluationResults,
3246        blend_weights: &BlendWeights,
3247    ) -> Self {
3248        let weight_signature = format!(
3249            "n{:.1}p{:.1}t{:.1}s{:.1}",
3250            blend_weights.nnue_weight,
3251            blend_weights.pattern_weight,
3252            blend_weights.tactical_weight,
3253            blend_weights.strategic_weight
3254        );
3255
3256        Self {
3257            has_nnue: evaluation_results.nnue.is_some(),
3258            has_pattern: evaluation_results.pattern.is_some(),
3259            has_tactical: evaluation_results.tactical.is_some(),
3260            has_strategic: evaluation_results.strategic.is_some(),
3261            weight_signature,
3262        }
3263    }
3264}
3265
3266/// Confidence analysis result with comprehensive details
3267#[derive(Debug, Clone)]
3268pub struct ConfidenceAnalysisResult {
3269    pub overall_confidence: f32,
3270    pub confidence_factors: ConfidenceFactors,
3271    pub agreement_analysis: AgreementAnalysis,
3272    pub pattern_clarity_analysis: PatternClarityResult,
3273    pub computation_time_ms: u64,
3274    pub confidence_category: ConfidenceCategory,
3275    pub reliability_indicators: Vec<ReliabilityIndicator>,
3276}
3277
3278/// Detailed confidence factors
3279#[derive(Debug, Clone)]
3280pub struct ConfidenceFactors {
3281    pub evaluator_agreement: f32,
3282    pub individual_confidence: f32,
3283    pub complexity_confidence: f32,
3284    pub pattern_clarity: f32,
3285    pub historical_accuracy: f32,
3286    pub coverage_confidence: f32,
3287    pub temporal_consistency: f32,
3288}
3289
3290/// Agreement analysis between evaluators
3291#[derive(Debug, Clone)]
3292pub struct AgreementAnalysis {
3293    pub overall_agreement: f32,
3294    pub pairwise_agreements: Vec<PairwiseAgreement>,
3295    pub evaluation_spread: f32,
3296    pub consensus_strength: f32,
3297    pub outlier_count: usize,
3298}
3299
3300/// Pairwise agreement between two evaluators
3301#[derive(Debug, Clone)]
3302pub struct PairwiseAgreement {
3303    pub evaluator1: String,
3304    pub evaluator2: String,
3305    pub agreement_score: f32,
3306}
3307
3308/// Pattern clarity analysis result
3309#[derive(Debug, Clone)]
3310pub struct PatternClarityResult {
3311    pub overall_clarity: f32,
3312    pub clarity_factors: Vec<ClarityFactor>,
3313    pub position_characteristics: Vec<String>,
3314}
3315
3316/// Clarity factor for individual evaluators
3317#[derive(Debug, Clone)]
3318pub struct ClarityFactor {
3319    pub evaluator: String,
3320    pub clarity_score: f32,
3321    pub contributing_factors: Vec<String>,
3322}
3323
3324/// Confidence calibration settings
3325#[derive(Debug, Clone)]
3326pub struct ConfidenceCalibrationSettings {
3327    pub calibration_curve: CalibrationCurve,
3328    pub factor_weights: FactorWeights,
3329}
3330
3331impl Default for ConfidenceCalibrationSettings {
3332    fn default() -> Self {
3333        Self {
3334            calibration_curve: CalibrationCurve::Sigmoid,
3335            factor_weights: FactorWeights::default(),
3336        }
3337    }
3338}
3339
3340/// Calibration curve types
3341#[derive(Debug, Clone)]
3342pub enum CalibrationCurve {
3343    Linear,
3344    Conservative,
3345    Aggressive,
3346    Sigmoid,
3347}
3348
3349/// Weights for different confidence factors
3350#[derive(Debug, Clone)]
3351pub struct FactorWeights {
3352    pub agreement_weight: f32,
3353    pub individual_weight: f32,
3354    pub complexity_weight: f32,
3355    pub pattern_clarity_weight: f32,
3356    pub historical_weight: f32,
3357    pub coverage_weight: f32,
3358    pub temporal_weight: f32,
3359}
3360
3361impl Default for FactorWeights {
3362    fn default() -> Self {
3363        Self {
3364            agreement_weight: 0.25,
3365            individual_weight: 0.20,
3366            complexity_weight: 0.15,
3367            pattern_clarity_weight: 0.15,
3368            historical_weight: 0.10,
3369            coverage_weight: 0.10,
3370            temporal_weight: 0.05,
3371        }
3372    }
3373}
3374
3375/// Confidence categories
3376#[derive(Debug, Clone, PartialEq)]
3377pub enum ConfidenceCategory {
3378    VeryHigh, // 0.8+
3379    High,     // 0.6-0.8
3380    Medium,   // 0.4-0.6
3381    Low,      // 0.2-0.4
3382    VeryLow,  // <0.2
3383}
3384
3385/// Reliability indicators
3386#[derive(Debug, Clone, PartialEq)]
3387pub enum ReliabilityIndicator {
3388    HighReliability,
3389    LowEvaluatorAgreement,
3390    HighPositionComplexity,
3391    UnclearPatterns,
3392    LimitedEvaluatorCoverage,
3393    InconsistentHistory,
3394    PoorHistoricalAccuracy,
3395}
3396
3397/// Confidence history entry for trend analysis
3398#[derive(Debug, Clone)]
3399pub struct ConfidenceHistoryEntry {
3400    pub timestamp: std::time::SystemTime,
3401    pub confidence_score: f32,
3402    pub position_context: PositionContext,
3403    pub computation_time_ms: u64,
3404}
3405
3406/// Confidence scoring statistics
3407#[derive(Debug, Clone)]
3408pub struct ConfidenceScoringStats {
3409    pub total_confidence_analyses: usize,
3410    pub average_confidence: f32,
3411    pub historical_accuracy_entries: usize,
3412    pub calibration_curve: CalibrationCurve,
3413}
3414
3415/// Extension trait for EvaluationResults to support confidence analysis
3416pub trait EvaluationResultsExt {
3417    fn hash_key(&self) -> String;
3418}
3419
3420impl EvaluationResultsExt for EvaluationResults {
3421    fn hash_key(&self) -> String {
3422        format!(
3423            "n{}p{}t{}s{}",
3424            self.nnue
3425                .as_ref()
3426                .map(|e| format!("{:.2}", e.evaluation))
3427                .unwrap_or_default(),
3428            self.pattern
3429                .as_ref()
3430                .map(|e| format!("{:.2}", e.evaluation))
3431                .unwrap_or_default(),
3432            self.tactical
3433                .as_ref()
3434                .map(|e| format!("{:.2}", e.evaluation))
3435                .unwrap_or_default(),
3436            self.strategic
3437                .as_ref()
3438                .map(|e| format!("{:.2}", e.evaluation))
3439                .unwrap_or_default(),
3440        )
3441    }
3442}
3443
3444/// Statistics for hybrid evaluation system
3445#[derive(Debug, Clone)]
3446pub struct HybridEvaluationStats {
3447    pub total_evaluations: u64,
3448    pub nnue_evaluations: u64,
3449    pub pattern_evaluations: u64,
3450    pub tactical_evaluations: u64,
3451    pub strategic_evaluations: u64,
3452    pub cache_hit_ratio: f64,
3453    pub average_evaluation_time_ms: f64,
3454    pub evaluations_per_second: f64,
3455}
3456
3457/// Trait for NNUE evaluators
3458pub trait NNUEEvaluator {
3459    fn evaluate_position(&self, board: &Board) -> Result<EvaluationComponent>;
3460}
3461
3462/// Trait for pattern evaluators
3463pub trait PatternEvaluator {
3464    fn evaluate_position(&self, board: &Board) -> Result<EvaluationComponent>;
3465}
3466
3467/// Trait for tactical evaluators
3468pub trait TacticalEvaluator {
3469    fn evaluate_position(&self, board: &Board) -> Result<EvaluationComponent>;
3470}
3471
3472/// Trait for strategic evaluators
3473pub trait StrategicEvaluator {
3474    fn evaluate_position(&self, board: &Board) -> Result<EvaluationComponent>;
3475}
3476
3477#[cfg(test)]
3478mod tests {
3479    use super::*;
3480    use chess::Board;
3481    use std::str::FromStr;
3482
3483    // Mock evaluators for testing
3484    struct MockNNUEEvaluator;
3485    impl NNUEEvaluator for MockNNUEEvaluator {
3486        fn evaluate_position(&self, _board: &Board) -> Result<EvaluationComponent> {
3487            Ok(EvaluationComponent {
3488                evaluation: 0.15,
3489                confidence: 0.8,
3490                computation_time_ms: 5,
3491                additional_info: HashMap::new(),
3492            })
3493        }
3494    }
3495
3496    struct MockPatternEvaluator;
3497    impl PatternEvaluator for MockPatternEvaluator {
3498        fn evaluate_position(&self, _board: &Board) -> Result<EvaluationComponent> {
3499            Ok(EvaluationComponent {
3500                evaluation: 0.12,
3501                confidence: 0.7,
3502                computation_time_ms: 15,
3503                additional_info: HashMap::new(),
3504            })
3505        }
3506    }
3507
3508    #[test]
3509    fn test_hybrid_evaluation_engine() {
3510        let engine = HybridEvaluationEngine::new()
3511            .with_nnue_evaluator(MockNNUEEvaluator)
3512            .with_pattern_evaluator(MockPatternEvaluator);
3513
3514        let board =
3515            Board::from_str("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1").unwrap();
3516        let result = engine.evaluate_position(&board).unwrap();
3517
3518        assert!(result.final_evaluation != 0.0);
3519        assert!(result.nnue_evaluation.is_some());
3520        assert!(result.pattern_evaluation.is_some());
3521        assert!(result.confidence_score > 0.0);
3522        assert!(!result.from_cache);
3523    }
3524
3525    #[test]
3526    fn test_complexity_analyzer() {
3527        let analyzer = ComplexityAnalyzer::new();
3528        let board =
3529            Board::from_str("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1").unwrap();
3530        let complexity = analyzer.analyze_complexity(&board);
3531
3532        assert!(complexity >= 0.0 && complexity <= 1.0);
3533    }
3534
3535    #[test]
3536    fn test_game_phase_detector() {
3537        let detector = GamePhaseDetector::new();
3538
3539        // Starting position should be opening
3540        let opening_board =
3541            Board::from_str("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1").unwrap();
3542        assert_eq!(detector.detect_phase(&opening_board), GamePhase::Opening);
3543
3544        // Endgame position
3545        let endgame_board = Board::from_str("8/8/8/8/8/8/4K3/4k3 w - - 0 50").unwrap();
3546        assert_eq!(detector.detect_phase(&endgame_board), GamePhase::Endgame);
3547    }
3548
3549    #[test]
3550    fn test_evaluation_blender() {
3551        let blender = EvaluationBlender::new();
3552        let mut evaluation_results = EvaluationResults::new();
3553
3554        evaluation_results.nnue = Some(EvaluationComponent {
3555            evaluation: 0.1,
3556            confidence: 0.8,
3557            computation_time_ms: 5,
3558            additional_info: HashMap::new(),
3559        });
3560
3561        evaluation_results.pattern = Some(EvaluationComponent {
3562            evaluation: 0.2,
3563            confidence: 0.7,
3564            computation_time_ms: 15,
3565            additional_info: HashMap::new(),
3566        });
3567
3568        let weights = BlendWeights {
3569            nnue_weight: 0.6,
3570            pattern_weight: 0.4,
3571            tactical_weight: 0.0,
3572            strategic_weight: 0.0,
3573        };
3574
3575        let blended = blender.blend_evaluations(&evaluation_results, &weights);
3576        let expected = 0.1 * 0.6 + 0.2 * 0.4;
3577        assert!((blended - expected).abs() < 1e-6);
3578    }
3579
3580    #[test]
3581    fn test_confidence_scorer() {
3582        let scorer = ConfidenceScorer::new();
3583        let mut evaluation_results = EvaluationResults::new();
3584
3585        evaluation_results.nnue = Some(EvaluationComponent {
3586            evaluation: 0.15,
3587            confidence: 0.8,
3588            computation_time_ms: 5,
3589            additional_info: HashMap::new(),
3590        });
3591
3592        evaluation_results.pattern = Some(EvaluationComponent {
3593            evaluation: 0.12,
3594            confidence: 0.7,
3595            computation_time_ms: 15,
3596            additional_info: HashMap::new(),
3597        });
3598
3599        let weights = BlendWeights::default();
3600        let position_context = PositionContext {
3601            position_hash: 0,
3602            game_phase: GamePhase::Opening,
3603            has_tactical_threats: true,
3604            in_opening_book: true,
3605            material_imbalance: 0.0,
3606            complexity_score: 0.5,
3607        };
3608        let confidence = scorer.compute_confidence(&evaluation_results, &weights, 0.3, &position_context);
3609
3610        assert!(confidence.overall_confidence > 0.0 && confidence.overall_confidence <= 1.0);
3611    }
3612
3613    #[test]
3614    fn test_adaptive_learning() {
3615        let mut engine = HybridEvaluationEngine::new()
3616            .with_nnue_evaluator(MockNNUEEvaluator)
3617            .with_pattern_evaluator(MockPatternEvaluator);
3618
3619        let board =
3620            Board::from_str("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1").unwrap();
3621
3622        // Enable adaptive learning
3623        engine.set_adaptive_learning(true);
3624
3625        // Get initial stats
3626        let initial_stats = engine.get_adaptive_learning_stats();
3627        assert!(initial_stats.learning_enabled);
3628        assert_eq!(initial_stats.weight_history_entries, 0);
3629
3630        // Perform evaluation to generate weight history
3631        let result = engine.evaluate_position(&board).unwrap();
3632        assert!(result.final_evaluation != 0.0);
3633
3634        // Update performance metrics
3635        let accuracy = 0.85;
3636        engine
3637            .update_evaluation_performance(&board, result.final_evaluation, Some(0.2), accuracy)
3638            .unwrap();
3639
3640        // Check that adaptive stats have been updated
3641        let updated_stats = engine.get_adaptive_learning_stats();
3642        assert!(updated_stats.weight_history_entries > 0);
3643    }
3644
3645    #[test]
3646    fn test_evaluation_blender_adaptive_weights() {
3647        let blender = EvaluationBlender::new();
3648        let complexity_score = 0.5;
3649        let game_phase = GamePhase::Middlegame;
3650        let evaluation_results = EvaluationResults::new();
3651
3652        // Compute weights multiple times to test consistency
3653        let weights1 =
3654            blender.compute_blend_weights(complexity_score, &game_phase, &evaluation_results);
3655        let weights2 =
3656            blender.compute_blend_weights(complexity_score, &game_phase, &evaluation_results);
3657
3658        // Weights should be normalized
3659        let total1 = weights1.nnue_weight
3660            + weights1.pattern_weight
3661            + weights1.tactical_weight
3662            + weights1.strategic_weight;
3663        let total2 = weights2.nnue_weight
3664            + weights2.pattern_weight
3665            + weights2.tactical_weight
3666            + weights2.strategic_weight;
3667
3668        assert!((total1 - 1.0).abs() < 1e-6);
3669        assert!((total2 - 1.0).abs() < 1e-6);
3670
3671        // Get adaptive stats
3672        let stats = blender.get_adaptive_stats();
3673        assert!(stats.weight_history_entries >= 2);
3674    }
3675
3676    struct MockStrategicEvaluator;
3677    impl StrategicEvaluator for MockStrategicEvaluator {
3678        fn evaluate_position(&self, _board: &Board) -> Result<EvaluationComponent> {
3679            Ok(EvaluationComponent {
3680                evaluation: 0.08,
3681                confidence: 0.75,
3682                computation_time_ms: 25,
3683                additional_info: HashMap::new(),
3684            })
3685        }
3686    }
3687
3688    #[test]
3689    fn test_hybrid_evaluation_with_strategic_initiative() {
3690        let engine = HybridEvaluationEngine::new()
3691            .with_nnue_evaluator(MockNNUEEvaluator)
3692            .with_pattern_evaluator(MockPatternEvaluator)
3693            .with_strategic_evaluator(MockStrategicEvaluator);
3694
3695        let board =
3696            Board::from_str("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1").unwrap();
3697        let result = engine.evaluate_position(&board).unwrap();
3698
3699        assert!(result.final_evaluation != 0.0);
3700        assert!(result.nnue_evaluation.is_some());
3701        assert!(result.pattern_evaluation.is_some());
3702        assert!(result.strategic_evaluation.is_some());
3703        assert!(result.confidence_score > 0.0);
3704        assert!(!result.from_cache);
3705
3706        // Strategic evaluation should be included
3707        let strategic_eval = result.strategic_evaluation.unwrap();
3708        assert_eq!(strategic_eval.evaluation, 0.08);
3709        assert_eq!(strategic_eval.confidence, 0.75);
3710    }
3711
3712    #[test]
3713    fn test_strategic_initiative_evaluator_integration() {
3714        let engine = HybridEvaluationEngine::new().with_strategic_initiative_evaluator();
3715
3716        let board =
3717            Board::from_str("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1").unwrap();
3718        let result = engine.evaluate_position(&board).unwrap();
3719
3720        // Should have strategic evaluation from the initiative evaluator
3721        if let Some(strategic_eval) = result.strategic_evaluation {
3722            assert!(strategic_eval.evaluation.is_finite());
3723            assert!(strategic_eval.confidence >= 0.0 && strategic_eval.confidence <= 1.0);
3724            assert!(strategic_eval.computation_time_ms > 0);
3725
3726            // Should have strategic initiative specific additional info
3727            assert!(strategic_eval
3728                .additional_info
3729                .contains_key("white_initiative"));
3730            assert!(strategic_eval
3731                .additional_info
3732                .contains_key("black_initiative"));
3733            assert!(strategic_eval
3734                .additional_info
3735                .contains_key("space_advantage"));
3736        }
3737    }
3738
3739    #[test]
3740    fn test_enhanced_complexity_analyzer() {
3741        let analyzer = ComplexityAnalyzer::new()
3742            .with_analysis_depth(AnalysisDepth::Deep)
3743            .with_complexity_weights(ComplexityWeights::default());
3744
3745        let board =
3746            Board::from_str("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1").unwrap();
3747
3748        // Test detailed complexity analysis
3749        let analysis = analyzer.analyze_complexity_detailed(&board);
3750
3751        assert!(analysis.overall_complexity >= 0.0 && analysis.overall_complexity <= 1.0);
3752        assert!(analysis.material_complexity >= 0.0 && analysis.material_complexity <= 1.0);
3753        assert!(
3754            analysis.pawn_structure_complexity >= 0.0 && analysis.pawn_structure_complexity <= 1.0
3755        );
3756        assert!(analysis.king_safety_complexity >= 0.0 && analysis.king_safety_complexity <= 1.0);
3757        assert!(
3758            analysis.piece_coordination_complexity >= 0.0
3759                && analysis.piece_coordination_complexity <= 1.0
3760        );
3761        assert!(analysis.tactical_complexity >= 0.0 && analysis.tactical_complexity <= 1.0);
3762        assert!(analysis.positional_complexity >= 0.0 && analysis.positional_complexity <= 1.0);
3763        assert!(analysis.time_complexity >= 0.0 && analysis.time_complexity <= 1.0);
3764        assert!(analysis.endgame_complexity >= 0.0 && analysis.endgame_complexity <= 1.0);
3765
3766        // Check complexity category
3767        assert!(matches!(
3768            analysis.complexity_category,
3769            ComplexityCategory::VeryLow
3770                | ComplexityCategory::Low
3771                | ComplexityCategory::Medium
3772                | ComplexityCategory::High
3773                | ComplexityCategory::VeryHigh
3774        ));
3775
3776        // Check evaluation recommendations
3777        assert!(analysis.evaluation_recommendations.tactical_depth > 0);
3778        assert!(
3779            analysis
3780                .evaluation_recommendations
3781                .pattern_analysis_priority
3782                >= 0.0
3783        );
3784        assert!(
3785            analysis
3786                .evaluation_recommendations
3787                .strategic_analysis_priority
3788                >= 0.0
3789        );
3790    }
3791
3792    #[test]
3793    fn test_complexity_analysis_integration() {
3794        let mut engine = HybridEvaluationEngine::new();
3795
3796        // Configure complexity analyzer
3797        let custom_weights = ComplexityWeights {
3798            tactical_weight: 0.4,
3799            king_safety_weight: 0.3,
3800            ..ComplexityWeights::default()
3801        };
3802        engine.configure_complexity_analyzer(custom_weights, AnalysisDepth::Comprehensive);
3803
3804        let board =
3805            Board::from_str("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1").unwrap();
3806
3807        // Test detailed complexity analysis access
3808        let complexity_analysis = engine.analyze_position_complexity(&board);
3809        assert!(complexity_analysis.overall_complexity.is_finite());
3810        assert!(
3811            !complexity_analysis.key_complexity_factors.is_empty()
3812                || complexity_analysis.key_complexity_factors.is_empty()
3813        ); // Either is valid
3814
3815        // Test that regular evaluation still works
3816        let evaluation_result = engine.evaluate_position(&board).unwrap();
3817        assert!(evaluation_result.complexity_score.is_finite());
3818    }
3819
3820    #[test]
3821    fn test_complexity_categories_and_recommendations() {
3822        let analyzer = ComplexityAnalyzer::new();
3823
3824        // Test different board positions for complexity categorization
3825        let positions = [
3826            "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", // Starting position
3827            "r3k2r/8/8/8/8/8/8/R3K2R w KQkq - 0 1",                     // Simple endgame
3828            "r1bqk1nr/pppp1ppp/2n5/2b1p3/2B1P3/5N2/PPPP1PPP/RNBQK2R w KQkq - 4 4", // Italian game
3829        ];
3830
3831        for fen in &positions {
3832            let board = Board::from_str(fen).unwrap();
3833            let analysis = analyzer.analyze_complexity_detailed(&board);
3834
3835            // Verify all complexity scores are valid
3836            assert!(analysis.overall_complexity >= 0.0 && analysis.overall_complexity <= 1.0);
3837
3838            // Check that recommendations are reasonable
3839            let recs = &analysis.evaluation_recommendations;
3840            assert!(recs.tactical_depth >= 2 && recs.tactical_depth <= 12);
3841            assert!(recs.pattern_analysis_priority >= 0.0 && recs.pattern_analysis_priority <= 1.0);
3842            assert!(
3843                recs.strategic_analysis_priority >= 0.0 && recs.strategic_analysis_priority <= 1.0
3844            );
3845
3846            // Verify complexity category matches overall score
3847            match analysis.complexity_category {
3848                ComplexityCategory::VeryLow => assert!(analysis.overall_complexity < 0.2),
3849                ComplexityCategory::Low => {
3850                    assert!(analysis.overall_complexity >= 0.0 && analysis.overall_complexity < 0.4)
3851                }
3852                ComplexityCategory::Medium => {
3853                    assert!(analysis.overall_complexity >= 0.2 && analysis.overall_complexity < 0.8)
3854                }
3855                ComplexityCategory::High => {
3856                    assert!(analysis.overall_complexity >= 0.4 && analysis.overall_complexity < 1.0)
3857                }
3858                ComplexityCategory::VeryHigh => assert!(analysis.overall_complexity >= 0.8),
3859            }
3860        }
3861    }
3862
3863    #[test]
3864    fn test_complexity_weights_and_depth() {
3865        let standard_analyzer = ComplexityAnalyzer::new();
3866        let deep_analyzer = ComplexityAnalyzer::new().with_analysis_depth(AnalysisDepth::Deep);
3867        let fast_analyzer = ComplexityAnalyzer::new().with_analysis_depth(AnalysisDepth::Fast);
3868
3869        let board =
3870            Board::from_str("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1").unwrap();
3871
3872        let standard_analysis = standard_analyzer.analyze_complexity_detailed(&board);
3873        let deep_analysis = deep_analyzer.analyze_complexity_detailed(&board);
3874        let fast_analysis = fast_analyzer.analyze_complexity_detailed(&board);
3875
3876        // Deep analysis should generally produce higher complexity scores due to depth modifier
3877        // Fast analysis should generally produce lower complexity scores
3878        // Note: The actual relationship depends on the position, so we just check they're different
3879        assert!(standard_analysis.overall_complexity.is_finite());
3880        assert!(deep_analysis.overall_complexity.is_finite());
3881        assert!(fast_analysis.overall_complexity.is_finite());
3882
3883        // All should be in valid range
3884        assert!(deep_analysis.overall_complexity >= 0.0 && deep_analysis.overall_complexity <= 1.0);
3885        assert!(fast_analysis.overall_complexity >= 0.0 && fast_analysis.overall_complexity <= 1.0);
3886    }
3887
3888    #[test]
3889    fn test_enhanced_game_phase_detector() {
3890        let detector = GamePhaseDetector::new()
3891            .with_phase_weights(PhaseDetectionWeights::default())
3892            .with_adaptation_settings(PhaseAdaptationSettings::default());
3893
3894        // Test different positions
3895        let positions = [
3896            (
3897                "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",
3898                GamePhase::Opening,
3899            ),
3900            ("r3k2r/8/8/8/8/8/8/R3K2R w KQkq - 0 1", GamePhase::Endgame),
3901            (
3902                "r1bqk1nr/pppp1ppp/2n5/2b1p3/2B1P3/5N2/PPPP1PPP/RNBQK2R w KQkq - 4 4",
3903                GamePhase::Opening,
3904            ),
3905        ];
3906
3907        for (fen, expected_phase) in &positions {
3908            let board = Board::from_str(fen).unwrap();
3909            let analysis = detector.analyze_game_phase(&board);
3910
3911            // Verify basic analysis structure
3912            assert!(analysis.phase_confidence >= 0.0 && analysis.phase_confidence <= 1.0);
3913            assert!(analysis.opening_score >= 0.0);
3914            assert!(analysis.middlegame_score >= 0.0);
3915            assert!(analysis.endgame_score >= 0.0);
3916
3917            // Check individual indicators
3918            assert!(
3919                analysis.material_phase.confidence >= 0.0
3920                    && analysis.material_phase.confidence <= 1.0
3921            );
3922            assert!(
3923                analysis.development_phase.confidence >= 0.0
3924                    && analysis.development_phase.confidence <= 1.0
3925            );
3926            assert!(
3927                analysis.move_count_phase.confidence >= 0.0
3928                    && analysis.move_count_phase.confidence <= 1.0
3929            );
3930
3931            // Check adaptation recommendations
3932            let recs = &analysis.adaptation_recommendations;
3933            assert!(recs.evaluation_weights.nnue_weight >= 0.0);
3934            assert!(recs.evaluation_weights.pattern_weight >= 0.0);
3935            assert!(recs.evaluation_weights.tactical_weight >= 0.0);
3936            assert!(recs.evaluation_weights.strategic_weight >= 0.0);
3937            assert!(recs.search_depth_modifier >= -2 && recs.search_depth_modifier <= 2);
3938            assert!(recs.opening_book_priority >= 0.0 && recs.opening_book_priority <= 1.0);
3939            assert!(
3940                recs.endgame_tablebase_priority >= 0.0 && recs.endgame_tablebase_priority <= 1.0
3941            );
3942            assert!(recs.time_management_factor >= 0.5 && recs.time_management_factor <= 2.0);
3943        }
3944    }
3945
3946    #[test]
3947    fn test_phase_transition_detection() {
3948        let detector = GamePhaseDetector::new();
3949
3950        // Test positions that might be in transition
3951        let transition_positions = [
3952            "rnbqkb1r/pppp1ppp/5n2/4p3/2B1P3/8/PPPP1PPP/RNBQK1NR w KQkq - 4 3", // Early middlegame
3953            "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 10", // Complex middlegame
3954        ];
3955
3956        for fen in &transition_positions {
3957            let board = Board::from_str(fen).unwrap();
3958            let analysis = detector.analyze_game_phase(&board);
3959
3960            // Check that transition state is reasonable
3961            assert!(matches!(
3962                analysis.transition_state,
3963                PhaseTransition::Stable
3964                    | PhaseTransition::OpeningToMiddlegame
3965                    | PhaseTransition::MiddlegameToEndgame
3966            ));
3967
3968            // Phase confidence should be reasonable
3969            assert!(analysis.phase_confidence >= 0.0 && analysis.phase_confidence <= 1.0);
3970        }
3971    }
3972
3973    #[test]
3974    fn test_game_phase_integration_with_hybrid_engine() {
3975        let mut engine = HybridEvaluationEngine::new();
3976
3977        // Configure phase detector
3978        let custom_weights = PhaseDetectionWeights {
3979            material_weight: 0.3,
3980            development_weight: 0.3,
3981            ..PhaseDetectionWeights::default()
3982        };
3983        let settings = PhaseAdaptationSettings {
3984            adaptation_responsiveness: 1.5,
3985            ..PhaseAdaptationSettings::default()
3986        };
3987        engine.configure_phase_detector(custom_weights, settings);
3988
3989        let board =
3990            Board::from_str("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1").unwrap();
3991
3992        // Test detailed phase analysis access
3993        let phase_analysis = engine.analyze_game_phase(&board);
3994        assert!(matches!(
3995            phase_analysis.primary_phase,
3996            GamePhase::Opening | GamePhase::Middlegame | GamePhase::Endgame | GamePhase::Unknown
3997        ));
3998
3999        // Test phase-specific adaptations
4000        let adapted_weights = engine.apply_phase_adaptations(&board);
4001        let total_weight = adapted_weights.nnue_weight
4002            + adapted_weights.pattern_weight
4003            + adapted_weights.tactical_weight
4004            + adapted_weights.strategic_weight;
4005        assert!((total_weight - 1.0).abs() < 0.1); // Should be roughly normalized
4006
4007        // Test that regular evaluation still works
4008        let evaluation_result = engine.evaluate_position(&board).unwrap();
4009        assert!(evaluation_result.final_evaluation.is_finite());
4010    }
4011
4012    #[test]
4013    fn test_phase_specific_adaptations() {
4014        let detector = GamePhaseDetector::new();
4015
4016        // Test opening position
4017        let opening_board =
4018            Board::from_str("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1").unwrap();
4019        let opening_analysis = detector.analyze_game_phase(&opening_board);
4020
4021        if opening_analysis.primary_phase == GamePhase::Opening {
4022            let recs = &opening_analysis.adaptation_recommendations;
4023            // Opening should favor strategic evaluation and opening book
4024            assert!(recs.evaluation_weights.strategic_weight >= 0.2);
4025            assert!(recs.opening_book_priority >= 0.8);
4026            assert!(recs.endgame_tablebase_priority <= 0.2);
4027        }
4028
4029        // Test endgame position
4030        let endgame_board = Board::from_str("8/8/8/8/8/8/4K3/4k3 w - - 0 50").unwrap();
4031        let endgame_analysis = detector.analyze_game_phase(&endgame_board);
4032
4033        if endgame_analysis.primary_phase == GamePhase::Endgame {
4034            let recs = &endgame_analysis.adaptation_recommendations;
4035            // Endgame should favor NNUE and tablebase
4036            assert!(recs.evaluation_weights.nnue_weight >= 0.4);
4037            assert!(recs.endgame_tablebase_priority >= 0.8);
4038            assert!(recs.opening_book_priority <= 0.2);
4039        }
4040    }
4041
4042    #[test]
4043    fn test_phase_confidence_scoring() {
4044        let detector = GamePhaseDetector::new();
4045
4046        // Test clear opening position
4047        let clear_opening =
4048            Board::from_str("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1").unwrap();
4049        let clear_analysis = detector.analyze_game_phase(&clear_opening);
4050
4051        // Test ambiguous position (might be opening or early middlegame)
4052        let ambiguous =
4053            Board::from_str("r1bqkbnr/pppp1ppp/2n5/4p3/2B1P3/5N2/PPPP1PPP/RNBQK2R w KQkq - 4 4")
4054                .unwrap();
4055        let ambiguous_analysis = detector.analyze_game_phase(&ambiguous);
4056
4057        // Clear positions should generally have higher confidence than ambiguous ones
4058        // Note: This is a heuristic test - the actual confidence depends on the implementation
4059        assert!(clear_analysis.phase_confidence >= 0.0);
4060        assert!(ambiguous_analysis.phase_confidence >= 0.0);
4061        assert!(clear_analysis.phase_confidence <= 1.0);
4062        assert!(ambiguous_analysis.phase_confidence <= 1.0);
4063    }
4064
4065    #[test]
4066    fn test_enhanced_confidence_scorer() {
4067        let scorer = ConfidenceScorer::new();
4068
4069        // Create mock evaluation results
4070        let evaluation_results = EvaluationResults {
4071            nnue: Some(EvaluationComponent {
4072                evaluation: 0.5,
4073                confidence: 0.8,
4074                computation_time_ms: 10,
4075                additional_info: HashMap::new(),
4076            }),
4077            pattern: Some(EvaluationComponent {
4078                evaluation: 0.6,
4079                confidence: 0.7,
4080                computation_time_ms: 15,
4081                additional_info: HashMap::new(),
4082            }),
4083            tactical: Some(EvaluationComponent {
4084                evaluation: 0.55,
4085                confidence: 0.9,
4086                computation_time_ms: 25,
4087                additional_info: {
4088                    let mut info = HashMap::new();
4089                    info.insert("search_depth".to_string(), 8.0);
4090                    info
4091                },
4092            }),
4093            strategic: Some(EvaluationComponent {
4094                evaluation: 0.45,
4095                confidence: 0.6,
4096                computation_time_ms: 20,
4097                additional_info: {
4098                    let mut info = HashMap::new();
4099                    info.insert("strategic_plans_count".to_string(), 3.0);
4100                    info
4101                },
4102            }),
4103        };
4104
4105        let blend_weights = BlendWeights {
4106            nnue_weight: 0.3,
4107            pattern_weight: 0.25,
4108            tactical_weight: 0.3,
4109            strategic_weight: 0.15,
4110        };
4111
4112        let position_context = PositionContext {
4113            position_hash: 12345,
4114            game_phase: GamePhase::Middlegame,
4115            has_tactical_threats: false,
4116            in_opening_book: false,
4117            material_imbalance: 1.0,
4118            complexity_score: 0.4,
4119        };
4120
4121        // Test comprehensive confidence analysis
4122        let analysis =
4123            scorer.compute_confidence(&evaluation_results, &blend_weights, 0.4, &position_context);
4124
4125        // Verify analysis structure
4126        assert!(analysis.overall_confidence >= 0.0 && analysis.overall_confidence <= 1.0);
4127        assert!(analysis.confidence_factors.evaluator_agreement >= 0.0);
4128        assert!(analysis.confidence_factors.individual_confidence >= 0.0);
4129        assert!(analysis.confidence_factors.complexity_confidence >= 0.0);
4130        assert!(analysis.confidence_factors.pattern_clarity >= 0.0);
4131        assert!(analysis.computation_time_ms > 0);
4132
4133        // Test confidence categorization
4134        match analysis.confidence_category {
4135            ConfidenceCategory::VeryHigh => assert!(analysis.overall_confidence >= 0.8),
4136            ConfidenceCategory::High => {
4137                assert!(analysis.overall_confidence >= 0.6 && analysis.overall_confidence < 0.8)
4138            }
4139            ConfidenceCategory::Medium => {
4140                assert!(analysis.overall_confidence >= 0.4 && analysis.overall_confidence < 0.6)
4141            }
4142            ConfidenceCategory::Low => {
4143                assert!(analysis.overall_confidence >= 0.2 && analysis.overall_confidence < 0.4)
4144            }
4145            ConfidenceCategory::VeryLow => assert!(analysis.overall_confidence < 0.2),
4146        }
4147
4148        // Test that we have reliability indicators
4149        assert!(!analysis.reliability_indicators.is_empty());
4150
4151        // Test agreement analysis
4152        assert_eq!(analysis.agreement_analysis.pairwise_agreements.len(), 6); // C(4,2) = 6 pairs
4153        assert!(analysis.agreement_analysis.overall_agreement >= 0.0);
4154        assert!(analysis.agreement_analysis.evaluation_spread >= 0.0);
4155
4156        // Test simplified compatibility method
4157        let simple_confidence =
4158            scorer.compute_simple_confidence(&evaluation_results, &blend_weights, 0.4);
4159        assert!(simple_confidence >= 0.0 && simple_confidence <= 1.0);
4160        assert!((simple_confidence - analysis.overall_confidence).abs() < 0.001);
4161    }
4162
4163    #[test]
4164    fn test_pattern_clarity_analyzer() {
4165        let analyzer = PatternClarityAnalyzer::new();
4166
4167        // Create evaluation results with varying clarity
4168        let high_clarity_results = EvaluationResults {
4169            nnue: Some(EvaluationComponent {
4170                evaluation: 1.2, // High magnitude
4171                confidence: 0.9, // High confidence
4172                computation_time_ms: 10,
4173                additional_info: HashMap::new(),
4174            }),
4175            pattern: Some(EvaluationComponent {
4176                evaluation: 1.1, // Consistent with NNUE
4177                confidence: 0.85,
4178                computation_time_ms: 15,
4179                additional_info: HashMap::new(),
4180            }),
4181            tactical: Some(EvaluationComponent {
4182                evaluation: 1.15, // Consistent
4183                confidence: 0.95,
4184                computation_time_ms: 25,
4185                additional_info: {
4186                    let mut info = HashMap::new();
4187                    info.insert("search_depth".to_string(), 10.0); // Deep search
4188                    info
4189                },
4190            }),
4191            strategic: None,
4192        };
4193
4194        let position_context = PositionContext {
4195            position_hash: 54321,
4196            game_phase: GamePhase::Middlegame,
4197            has_tactical_threats: true,
4198            in_opening_book: false,
4199            material_imbalance: 0.5,
4200            complexity_score: 0.3,
4201        };
4202
4203        let clarity_result =
4204            analyzer.analyze_pattern_clarity(&high_clarity_results, &position_context);
4205
4206        // High clarity results should have good overall clarity
4207        assert!(clarity_result.overall_clarity > 0.5);
4208        assert!(!clarity_result.clarity_factors.is_empty());
4209        assert!(!clarity_result.position_characteristics.is_empty());
4210
4211        // Check that tactical position is identified
4212        assert!(clarity_result
4213            .position_characteristics
4214            .contains(&"tactical_position".to_string()));
4215        assert!(clarity_result
4216            .position_characteristics
4217            .contains(&"middlegame_phase".to_string()));
4218
4219        // Test that clarity factors contain expected evaluators
4220        let evaluator_names: Vec<String> = clarity_result
4221            .clarity_factors
4222            .iter()
4223            .map(|cf| cf.evaluator.clone())
4224            .collect();
4225        assert!(evaluator_names.contains(&"NNUE".to_string()));
4226        assert!(evaluator_names.contains(&"Pattern".to_string()));
4227        assert!(evaluator_names.contains(&"Tactical".to_string()));
4228    }
4229
4230    #[test]
4231    fn test_evaluator_accuracy_tracker() {
4232        let mut tracker = EvaluatorAccuracyTracker::new();
4233
4234        // Create test evaluator combination
4235        let combination = EvaluatorCombination {
4236            has_nnue: true,
4237            has_pattern: true,
4238            has_tactical: false,
4239            has_strategic: false,
4240            weight_signature: "n0.5p0.5t0.0s0.0".to_string(),
4241        };
4242
4243        let context_hash = 98765;
4244
4245        // Record some accuracy data
4246        tracker.record_accuracy(&combination, context_hash, 0.8);
4247        tracker.record_accuracy(&combination, context_hash, 0.75);
4248        tracker.record_accuracy(&combination, context_hash, 0.85);
4249
4250        // Test retrieval
4251        let avg_accuracy = tracker.get_historical_accuracy(&combination, context_hash);
4252        assert!(avg_accuracy.is_some());
4253        let accuracy = avg_accuracy.unwrap();
4254        assert!((accuracy - 0.8).abs() < 0.05); // Should be around 0.8
4255
4256        // Test total entries
4257        assert_eq!(tracker.get_total_entries(), 3);
4258
4259        // Test with unknown combination
4260        let unknown_combination = EvaluatorCombination {
4261            has_nnue: false,
4262            has_pattern: false,
4263            has_tactical: true,
4264            has_strategic: true,
4265            weight_signature: "n0.0p0.0t0.7s0.3".to_string(),
4266        };
4267
4268        let unknown_accuracy = tracker.get_historical_accuracy(&unknown_combination, context_hash);
4269        assert!(unknown_accuracy.is_none());
4270    }
4271
4272    #[test]
4273    fn test_confidence_calibration_settings() {
4274        // Test default settings
4275        let default_settings = ConfidenceCalibrationSettings::default();
4276
4277        // Verify default factor weights sum to 1.0
4278        let weights = &default_settings.factor_weights;
4279        let total_weight = weights.agreement_weight
4280            + weights.individual_weight
4281            + weights.complexity_weight
4282            + weights.pattern_clarity_weight
4283            + weights.historical_weight
4284            + weights.coverage_weight
4285            + weights.temporal_weight;
4286        assert!((total_weight - 1.0).abs() < 0.01);
4287
4288        // Test custom settings
4289        let custom_weights = FactorWeights {
4290            agreement_weight: 0.4,
4291            individual_weight: 0.3,
4292            complexity_weight: 0.2,
4293            pattern_clarity_weight: 0.1,
4294            historical_weight: 0.0,
4295            coverage_weight: 0.0,
4296            temporal_weight: 0.0,
4297        };
4298
4299        let custom_settings = ConfidenceCalibrationSettings {
4300            calibration_curve: CalibrationCurve::Conservative,
4301            factor_weights: custom_weights,
4302        };
4303
4304        // Create scorer with custom settings
4305        let scorer = ConfidenceScorer::new().with_calibration_settings(custom_settings);
4306
4307        // Test that custom settings are used
4308        let evaluation_results = EvaluationResults {
4309            nnue: Some(EvaluationComponent {
4310                evaluation: 0.5,
4311                confidence: 0.7,
4312                computation_time_ms: 10,
4313                additional_info: HashMap::new(),
4314            }),
4315            pattern: None,
4316            tactical: None,
4317            strategic: None,
4318        };
4319
4320        let blend_weights = BlendWeights {
4321            nnue_weight: 1.0,
4322            pattern_weight: 0.0,
4323            tactical_weight: 0.0,
4324            strategic_weight: 0.0,
4325        };
4326
4327        let position_context = PositionContext::default();
4328        let analysis =
4329            scorer.compute_confidence(&evaluation_results, &blend_weights, 0.3, &position_context);
4330
4331        // Should produce valid confidence score
4332        assert!(analysis.overall_confidence >= 0.0 && analysis.overall_confidence <= 1.0);
4333    }
4334
4335    #[test]
4336    fn test_confidence_history_tracking() {
4337        let scorer = ConfidenceScorer::new();
4338
4339        // Get initial statistics
4340        let initial_stats = scorer.get_statistics();
4341        assert_eq!(initial_stats.total_confidence_analyses, 0);
4342
4343        // Perform some confidence analyses
4344        let evaluation_results = EvaluationResults {
4345            nnue: Some(EvaluationComponent {
4346                evaluation: 0.3,
4347                confidence: 0.6,
4348                computation_time_ms: 12,
4349                additional_info: HashMap::new(),
4350            }),
4351            pattern: None,
4352            tactical: None,
4353            strategic: None,
4354        };
4355
4356        let blend_weights = BlendWeights {
4357            nnue_weight: 1.0,
4358            pattern_weight: 0.0,
4359            tactical_weight: 0.0,
4360            strategic_weight: 0.0,
4361        };
4362
4363        let position_context = PositionContext::default();
4364
4365        // Perform multiple analyses
4366        for _ in 0..5 {
4367            let _analysis = scorer.compute_confidence(
4368                &evaluation_results,
4369                &blend_weights,
4370                0.2,
4371                &position_context,
4372            );
4373        }
4374
4375        // Check updated statistics
4376        let updated_stats = scorer.get_statistics();
4377        assert_eq!(updated_stats.total_confidence_analyses, 5);
4378        assert!(updated_stats.average_confidence >= 0.0 && updated_stats.average_confidence <= 1.0);
4379    }
4380}