chess_vector_engine/
strategic_initiative.rs

1use crate::errors::Result;
2use crate::hybrid_evaluation::{EvaluationComponent, StrategicEvaluator};
3use crate::utils::cache::EvaluationCache;
4use chess::{Board, ChessMove, Color, Piece, Rank, Square};
5use std::collections::HashMap;
6use std::sync::{Arc, RwLock};
7
8/// Strategic initiative evaluation system that analyzes long-term positional factors
9pub struct StrategicInitiativeEvaluator {
10    /// Initiative analyzer
11    initiative_analyzer: InitiativeAnalyzer,
12    /// Positional pressure evaluator
13    pressure_evaluator: PositionalPressureEvaluator,
14    /// Strategic plan generator
15    plan_generator: StrategicPlanGenerator,
16    /// Time pressure analyzer
17    time_analyzer: TimePressureAnalyzer,
18    /// Initiative cache
19    initiative_cache: Arc<EvaluationCache>,
20}
21
22impl StrategicInitiativeEvaluator {
23    /// Create a new strategic initiative evaluator
24    pub fn new() -> Self {
25        Self {
26            initiative_analyzer: InitiativeAnalyzer::new(),
27            pressure_evaluator: PositionalPressureEvaluator::new(),
28            plan_generator: StrategicPlanGenerator::new(),
29            time_analyzer: TimePressureAnalyzer::new(),
30            initiative_cache: Arc::new(EvaluationCache::new(
31                5000,
32                std::time::Duration::from_secs(600),
33            )),
34        }
35    }
36
37    /// Evaluate strategic initiative for a position
38    pub fn evaluate_strategic_initiative(
39        &self,
40        board: &Board,
41    ) -> Result<StrategicInitiativeResult> {
42        let fen = board.to_string();
43
44        // Check cache first
45        if let Some(cached_eval) = self.initiative_cache.get_evaluation(&fen) {
46            return Ok(StrategicInitiativeResult {
47                initiative_score: cached_eval,
48                white_initiative: 0.0,
49                black_initiative: 0.0,
50                positional_pressure: PositionalPressure::default(),
51                strategic_plans: Vec::new(),
52                time_pressure: TimePressure::default(),
53                initiative_factors: InitiativeFactors::default(),
54                from_cache: true,
55            });
56        }
57
58        let start_time = std::time::Instant::now();
59
60        // Analyze initiative for both sides
61        let white_initiative = self
62            .initiative_analyzer
63            .analyze_initiative(board, Color::White)?;
64        let black_initiative = self
65            .initiative_analyzer
66            .analyze_initiative(board, Color::Black)?;
67
68        // Evaluate positional pressure
69        let positional_pressure = self.pressure_evaluator.evaluate_pressure(board)?;
70
71        // Generate strategic plans
72        let strategic_plans = self.plan_generator.generate_plans(board, 3)?;
73
74        // Analyze time pressure factors
75        let time_pressure = self.time_analyzer.analyze_time_factors(board)?;
76
77        // Compute overall initiative score (positive = white advantage)
78        let initiative_score = white_initiative.total_score - black_initiative.total_score;
79
80        let result = StrategicInitiativeResult {
81            initiative_score,
82            white_initiative: white_initiative.total_score,
83            black_initiative: black_initiative.total_score,
84            positional_pressure,
85            strategic_plans,
86            time_pressure,
87            initiative_factors: InitiativeFactors {
88                white: white_initiative,
89                black: black_initiative,
90            },
91            from_cache: false,
92        };
93
94        // Cache the result
95        self.initiative_cache
96            .store_evaluation(&fen, initiative_score);
97
98        Ok(result)
99    }
100
101    /// Get strategic initiative statistics
102    pub fn get_statistics(&self) -> StrategicInitiativeStats {
103        let cache_stats = self.initiative_cache.stats();
104
105        StrategicInitiativeStats {
106            evaluations_performed: 0, // Would need to track this
107            cache_hit_ratio: cache_stats.hit_ratio,
108            average_evaluation_time_ms: 0.0, // Would need to compute this
109            plans_generated: 0,              // Would need to track this
110        }
111    }
112}
113
114impl Default for StrategicInitiativeEvaluator {
115    fn default() -> Self {
116        Self::new()
117    }
118}
119
120impl StrategicEvaluator for StrategicInitiativeEvaluator {
121    fn evaluate_position(&self, board: &Board) -> Result<EvaluationComponent> {
122        let start_time = std::time::Instant::now();
123        let initiative_result = self.evaluate_strategic_initiative(board)?;
124        let computation_time = start_time.elapsed().as_millis() as u64;
125
126        let mut additional_info = HashMap::new();
127        additional_info.insert(
128            "white_initiative".to_string(),
129            initiative_result.white_initiative,
130        );
131        additional_info.insert(
132            "black_initiative".to_string(),
133            initiative_result.black_initiative,
134        );
135        additional_info.insert(
136            "space_advantage".to_string(),
137            initiative_result.positional_pressure.space_advantage,
138        );
139        additional_info.insert(
140            "development_advantage".to_string(),
141            initiative_result.positional_pressure.development_advantage,
142        );
143        additional_info.insert(
144            "attacking_potential".to_string(),
145            initiative_result.positional_pressure.attacking_potential,
146        );
147        additional_info.insert(
148            "strategic_plans_count".to_string(),
149            initiative_result.strategic_plans.len() as f32,
150        );
151
152        // Compute confidence based on initiative clarity
153        let confidence = self.compute_initiative_confidence(&initiative_result);
154
155        Ok(EvaluationComponent {
156            evaluation: initiative_result.initiative_score,
157            confidence,
158            computation_time_ms: computation_time,
159            additional_info,
160        })
161    }
162}
163
164impl StrategicInitiativeEvaluator {
165    fn compute_initiative_confidence(&self, result: &StrategicInitiativeResult) -> f32 {
166        let mut confidence_factors = Vec::new();
167
168        // Factor 1: Initiative clarity (larger difference = higher confidence)
169        let initiative_clarity = result.initiative_score.abs().min(1.0);
170        confidence_factors.push(initiative_clarity);
171
172        // Factor 2: Number of strategic plans found
173        let plan_confidence = (result.strategic_plans.len() as f32 / 5.0).min(1.0);
174        confidence_factors.push(plan_confidence);
175
176        // Factor 3: Positional pressure consistency
177        let pressure_consistency = (result.positional_pressure.space_advantage.abs()
178            + result.positional_pressure.development_advantage.abs()
179            + result.positional_pressure.attacking_potential.abs())
180            / 3.0;
181        confidence_factors.push(pressure_consistency.min(1.0));
182
183        // Factor 4: Time pressure clarity
184        let time_clarity = if result.time_pressure.critical_moves.is_empty() {
185            0.7
186        } else {
187            0.9
188        };
189        confidence_factors.push(time_clarity);
190
191        // Average confidence
192        if confidence_factors.is_empty() {
193            0.5
194        } else {
195            confidence_factors.iter().sum::<f32>() / confidence_factors.len() as f32
196        }
197    }
198}
199
200/// Initiative analyzer for individual colors
201pub struct InitiativeAnalyzer {
202    cached_analyses: RwLock<HashMap<String, ColorInitiativeAnalysis>>,
203}
204
205impl InitiativeAnalyzer {
206    pub fn new() -> Self {
207        Self {
208            cached_analyses: RwLock::new(HashMap::new()),
209        }
210    }
211
212    /// Analyze initiative for a specific color
213    pub fn analyze_initiative(
214        &self,
215        board: &Board,
216        color: Color,
217    ) -> Result<ColorInitiativeAnalysis> {
218        let cache_key = format!("{}_{:?}", board.to_string(), color);
219
220        // Check cache
221        if let Ok(cache) = self.cached_analyses.read() {
222            if let Some(analysis) = cache.get(&cache_key) {
223                return Ok(analysis.clone());
224            }
225        }
226
227        let mut analysis = ColorInitiativeAnalysis::default();
228
229        // Analyze development initiative
230        analysis.development_initiative = self.analyze_development_initiative(board, color);
231
232        // Analyze attacking initiative
233        analysis.attacking_initiative = self.analyze_attacking_initiative(board, color);
234
235        // Analyze space initiative
236        analysis.space_initiative = self.analyze_space_initiative(board, color);
237
238        // Analyze tempo initiative
239        analysis.tempo_initiative = self.analyze_tempo_initiative(board, color);
240
241        // Analyze pawn initiative
242        analysis.pawn_initiative = self.analyze_pawn_initiative(board, color);
243
244        // Analyze coordination initiative
245        analysis.coordination_initiative = self.analyze_coordination_initiative(board, color);
246
247        // Compute total initiative score
248        analysis.total_score = analysis.development_initiative * 0.2
249            + analysis.attacking_initiative * 0.25
250            + analysis.space_initiative * 0.15
251            + analysis.tempo_initiative * 0.1
252            + analysis.pawn_initiative * 0.15
253            + analysis.coordination_initiative * 0.15;
254
255        // Cache the result with LRU-style management
256        if let Ok(mut cache) = self.cached_analyses.write() {
257            if cache.len() > 1000 {
258                // Remove oldest 50% of entries to maintain performance
259                let keys_to_remove: Vec<_> = cache.keys().take(500).cloned().collect();
260                for key in keys_to_remove {
261                    cache.remove(&key);
262                }
263            }
264            cache.insert(cache_key, analysis.clone());
265        }
266
267        Ok(analysis)
268    }
269
270    fn analyze_development_initiative(&self, board: &Board, color: Color) -> f32 {
271        let pieces = board.color_combined(color);
272        let mut development_score = 0.0;
273
274        // Count developed pieces
275        let developed_knights = (board.pieces(Piece::Knight) & pieces)
276            .into_iter()
277            .filter(|&square| self.is_piece_developed(square, Piece::Knight, color))
278            .count();
279
280        let developed_bishops = (board.pieces(Piece::Bishop) & pieces)
281            .into_iter()
282            .filter(|&square| self.is_piece_developed(square, Piece::Bishop, color))
283            .count();
284
285        development_score += developed_knights as f32 * 0.3;
286        development_score += developed_bishops as f32 * 0.3;
287
288        // Castling bonus
289        if board.castle_rights(color).has_kingside() || board.castle_rights(color).has_queenside() {
290            development_score += 0.2;
291        }
292
293        // Central control bonus
294        let central_squares = [Square::E4, Square::E5, Square::D4, Square::D5];
295        let central_control = central_squares
296            .iter()
297            .filter(|&&square| self.attacks_square(board, color, square))
298            .count();
299
300        development_score += central_control as f32 * 0.1;
301
302        development_score.min(1.0)
303    }
304
305    fn analyze_attacking_initiative(&self, board: &Board, color: Color) -> f32 {
306        let opponent_color = !color;
307        let opponent_king_square = board.king_square(opponent_color);
308        let mut attacking_score = 0.0;
309
310        // King safety pressure
311        let king_attackers = self.count_attackers_near_king(board, color, opponent_king_square);
312        attacking_score += king_attackers as f32 * 0.2;
313
314        // Piece activity in opponent territory
315        let active_pieces = self.count_active_pieces_in_enemy_territory(board, color);
316        attacking_score += active_pieces as f32 * 0.15;
317
318        // Tactical threats
319        let threats = self.count_tactical_threats(board, color);
320        attacking_score += threats as f32 * 0.25;
321
322        // Weak square control
323        let weak_squares_controlled = self.count_weak_squares_controlled(board, color);
324        attacking_score += weak_squares_controlled as f32 * 0.1;
325
326        attacking_score.min(1.0)
327    }
328
329    fn analyze_space_initiative(&self, board: &Board, color: Color) -> f32 {
330        let mut space_score = 0.0;
331
332        // Count squares controlled
333        let squares_controlled = self.count_controlled_squares(board, color);
334        space_score += (squares_controlled as f32 / 64.0) * 0.5;
335
336        // Advanced pawn structure
337        let advanced_pawns = self.count_advanced_pawns(board, color);
338        space_score += advanced_pawns as f32 * 0.1;
339
340        // Outposts
341        let outposts = self.count_outposts(board, color);
342        space_score += outposts as f32 * 0.2;
343
344        space_score.min(1.0)
345    }
346
347    fn analyze_tempo_initiative(&self, board: &Board, color: Color) -> f32 {
348        let mut tempo_score = 0.0;
349
350        // Check if it's this color's turn
351        if board.side_to_move() == color {
352            tempo_score += 0.1;
353        }
354
355        // Count forcing moves available
356        let forcing_moves = self.count_forcing_moves(board, color);
357        tempo_score += forcing_moves as f32 * 0.05;
358
359        // Check for opponent pieces under attack
360        let pieces_under_attack = self.count_opponent_pieces_under_attack(board, color);
361        tempo_score += pieces_under_attack as f32 * 0.1;
362
363        tempo_score.min(1.0)
364    }
365
366    fn analyze_pawn_initiative(&self, board: &Board, color: Color) -> f32 {
367        let mut pawn_score = 0.0;
368
369        // Passed pawns
370        let passed_pawns = self.count_passed_pawns(board, color);
371        pawn_score += passed_pawns as f32 * 0.2;
372
373        // Pawn chains
374        let pawn_chains = self.count_pawn_chains(board, color);
375        pawn_score += pawn_chains as f32 * 0.1;
376
377        // Pawn storms
378        let pawn_storms = self.evaluate_pawn_storms(board, color);
379        pawn_score += pawn_storms * 0.3;
380
381        pawn_score.min(1.0)
382    }
383
384    fn analyze_coordination_initiative(&self, board: &Board, color: Color) -> f32 {
385        let mut coordination_score = 0.0;
386
387        // Piece coordination
388        let coordinated_pieces = self.count_coordinated_pieces(board, color);
389        coordination_score += coordinated_pieces as f32 * 0.1;
390
391        // Battery formations (rook+queen, bishop+queen)
392        let batteries = self.count_batteries(board, color);
393        coordination_score += batteries as f32 * 0.2;
394
395        // Piece harmony (pieces working together)
396        let harmony_score = self.evaluate_piece_harmony(board, color);
397        coordination_score += harmony_score * 0.3;
398
399        coordination_score.min(1.0)
400    }
401
402    // Helper methods (simplified implementations for now)
403    fn is_piece_developed(&self, _square: Square, _piece: Piece, _color: Color) -> bool {
404        // Simplified - in practice would check if piece is on starting square
405        true
406    }
407
408    fn attacks_square(&self, _board: &Board, _color: Color, _square: Square) -> bool {
409        // Simplified implementation
410        false
411    }
412
413    fn count_attackers_near_king(&self, _board: &Board, _color: Color, _king_square: Square) -> u8 {
414        // Simplified implementation
415        0
416    }
417
418    fn count_active_pieces_in_enemy_territory(&self, _board: &Board, _color: Color) -> u8 {
419        // Simplified implementation
420        0
421    }
422
423    fn count_tactical_threats(&self, _board: &Board, _color: Color) -> u8 {
424        // Simplified implementation
425        0
426    }
427
428    fn count_weak_squares_controlled(&self, _board: &Board, _color: Color) -> u8 {
429        // Simplified implementation
430        0
431    }
432
433    fn count_controlled_squares(&self, _board: &Board, _color: Color) -> u8 {
434        // Simplified implementation
435        20
436    }
437
438    fn count_advanced_pawns(&self, board: &Board, color: Color) -> u8 {
439        let pawns = board.pieces(Piece::Pawn) & board.color_combined(color);
440        let mut count = 0;
441
442        for square in pawns {
443            let rank = square.get_rank();
444            let advanced = match color {
445                Color::White => rank >= Rank::Fifth,
446                Color::Black => rank <= Rank::Fourth,
447            };
448
449            if advanced {
450                count += 1;
451            }
452        }
453
454        count
455    }
456
457    fn count_outposts(&self, _board: &Board, _color: Color) -> u8 {
458        // Simplified implementation
459        0
460    }
461
462    fn count_forcing_moves(&self, _board: &Board, _color: Color) -> u8 {
463        // Simplified implementation
464        0
465    }
466
467    fn count_opponent_pieces_under_attack(&self, _board: &Board, _color: Color) -> u8 {
468        // Simplified implementation
469        0
470    }
471
472    fn count_passed_pawns(&self, _board: &Board, _color: Color) -> u8 {
473        // Simplified implementation
474        0
475    }
476
477    fn count_pawn_chains(&self, _board: &Board, _color: Color) -> u8 {
478        // Simplified implementation
479        0
480    }
481
482    fn evaluate_pawn_storms(&self, _board: &Board, _color: Color) -> f32 {
483        // Simplified implementation
484        0.0
485    }
486
487    fn count_coordinated_pieces(&self, _board: &Board, _color: Color) -> u8 {
488        // Simplified implementation
489        0
490    }
491
492    fn count_batteries(&self, _board: &Board, _color: Color) -> u8 {
493        // Simplified implementation
494        0
495    }
496
497    fn evaluate_piece_harmony(&self, _board: &Board, _color: Color) -> f32 {
498        // Simplified implementation
499        0.0
500    }
501}
502
503/// Positional pressure evaluator
504pub struct PositionalPressureEvaluator;
505
506impl PositionalPressureEvaluator {
507    pub fn new() -> Self {
508        Self
509    }
510
511    pub fn evaluate_pressure(&self, board: &Board) -> Result<PositionalPressure> {
512        let mut pressure = PositionalPressure::default();
513
514        // Evaluate space advantage
515        pressure.space_advantage = self.evaluate_space_advantage(board);
516
517        // Evaluate development advantage
518        pressure.development_advantage = self.evaluate_development_advantage(board);
519
520        // Evaluate attacking potential
521        pressure.attacking_potential = self.evaluate_attacking_potential(board);
522
523        // Evaluate positional weaknesses
524        pressure.positional_weaknesses = self.evaluate_positional_weaknesses(board);
525
526        Ok(pressure)
527    }
528
529    fn evaluate_space_advantage(&self, _board: &Board) -> f32 {
530        // Simplified implementation
531        0.0
532    }
533
534    fn evaluate_development_advantage(&self, _board: &Board) -> f32 {
535        // Simplified implementation
536        0.0
537    }
538
539    fn evaluate_attacking_potential(&self, _board: &Board) -> f32 {
540        // Simplified implementation
541        0.0
542    }
543
544    fn evaluate_positional_weaknesses(&self, _board: &Board) -> f32 {
545        // Simplified implementation
546        0.0
547    }
548}
549
550/// Strategic plan generator
551pub struct StrategicPlanGenerator;
552
553impl StrategicPlanGenerator {
554    pub fn new() -> Self {
555        Self
556    }
557
558    pub fn generate_plans(&self, board: &Board, max_plans: usize) -> Result<Vec<StrategicPlan>> {
559        let mut plans = Vec::new();
560
561        // Generate different types of strategic plans
562        plans.extend(self.generate_attacking_plans(board)?);
563        plans.extend(self.generate_positional_plans(board)?);
564        plans.extend(self.generate_endgame_plans(board)?);
565
566        // Sort by priority and take top plans
567        plans.sort_by(|a, b| {
568            b.priority
569                .partial_cmp(&a.priority)
570                .unwrap_or(std::cmp::Ordering::Equal)
571        });
572        plans.truncate(max_plans);
573
574        Ok(plans)
575    }
576
577    fn generate_attacking_plans(&self, _board: &Board) -> Result<Vec<StrategicPlan>> {
578        // Simplified implementation
579        Ok(vec![StrategicPlan {
580            plan_type: StrategicPlanType::KingsideAttack,
581            priority: 0.8,
582            key_moves: Vec::new(),
583            target_squares: Vec::new(),
584            expected_outcome: PlanOutcome::MaterialGain,
585        }])
586    }
587
588    fn generate_positional_plans(&self, _board: &Board) -> Result<Vec<StrategicPlan>> {
589        // Simplified implementation
590        Ok(vec![StrategicPlan {
591            plan_type: StrategicPlanType::CentralControl,
592            priority: 0.6,
593            key_moves: Vec::new(),
594            target_squares: Vec::new(),
595            expected_outcome: PlanOutcome::PositionalAdvantage,
596        }])
597    }
598
599    fn generate_endgame_plans(&self, _board: &Board) -> Result<Vec<StrategicPlan>> {
600        // Simplified implementation
601        Ok(Vec::new())
602    }
603}
604
605/// Time pressure analyzer
606pub struct TimePressureAnalyzer;
607
608impl TimePressureAnalyzer {
609    pub fn new() -> Self {
610        Self
611    }
612
613    pub fn analyze_time_factors(&self, board: &Board) -> Result<TimePressure> {
614        let mut time_pressure = TimePressure::default();
615
616        // Identify critical moves that must be played soon
617        time_pressure.critical_moves = self.identify_critical_moves(board)?;
618
619        // Evaluate zugzwang potential
620        time_pressure.zugzwang_potential = self.evaluate_zugzwang_potential(board);
621
622        // Evaluate tempo importance
623        time_pressure.tempo_importance = self.evaluate_tempo_importance(board);
624
625        Ok(time_pressure)
626    }
627
628    fn identify_critical_moves(&self, _board: &Board) -> Result<Vec<ChessMove>> {
629        // Simplified implementation
630        Ok(Vec::new())
631    }
632
633    fn evaluate_zugzwang_potential(&self, _board: &Board) -> f32 {
634        // Simplified implementation
635        0.0
636    }
637
638    fn evaluate_tempo_importance(&self, _board: &Board) -> f32 {
639        // Simplified implementation
640        0.5
641    }
642}
643
644/// Strategic initiative evaluation result
645#[derive(Debug, Clone)]
646pub struct StrategicInitiativeResult {
647    /// Overall initiative score (positive = white advantage)
648    pub initiative_score: f32,
649    /// White's initiative score
650    pub white_initiative: f32,
651    /// Black's initiative score
652    pub black_initiative: f32,
653    /// Positional pressure analysis
654    pub positional_pressure: PositionalPressure,
655    /// Generated strategic plans
656    pub strategic_plans: Vec<StrategicPlan>,
657    /// Time pressure factors
658    pub time_pressure: TimePressure,
659    /// Detailed initiative factors
660    pub initiative_factors: InitiativeFactors,
661    /// Whether result came from cache
662    pub from_cache: bool,
663}
664
665/// Initiative analysis for a single color
666#[derive(Debug, Clone, Default)]
667pub struct ColorInitiativeAnalysis {
668    pub development_initiative: f32,
669    pub attacking_initiative: f32,
670    pub space_initiative: f32,
671    pub tempo_initiative: f32,
672    pub pawn_initiative: f32,
673    pub coordination_initiative: f32,
674    pub total_score: f32,
675}
676
677/// Initiative factors for both colors
678#[derive(Debug, Clone, Default)]
679pub struct InitiativeFactors {
680    pub white: ColorInitiativeAnalysis,
681    pub black: ColorInitiativeAnalysis,
682}
683
684/// Positional pressure analysis
685#[derive(Debug, Clone, Default)]
686pub struct PositionalPressure {
687    pub space_advantage: f32,
688    pub development_advantage: f32,
689    pub attacking_potential: f32,
690    pub positional_weaknesses: f32,
691}
692
693/// Strategic plan
694#[derive(Debug, Clone)]
695pub struct StrategicPlan {
696    pub plan_type: StrategicPlanType,
697    pub priority: f32,
698    pub key_moves: Vec<ChessMove>,
699    pub target_squares: Vec<Square>,
700    pub expected_outcome: PlanOutcome,
701}
702
703/// Types of strategic plans
704#[derive(Debug, Clone)]
705pub enum StrategicPlanType {
706    KingsideAttack,
707    QueensideAttack,
708    CentralControl,
709    PawnStorm,
710    PieceManeuver,
711    EndgameTransition,
712}
713
714/// Expected outcome of a strategic plan
715#[derive(Debug, Clone)]
716pub enum PlanOutcome {
717    MaterialGain,
718    PositionalAdvantage,
719    KingAttack,
720    Promotion,
721    Draw,
722}
723
724/// Time pressure factors
725#[derive(Debug, Clone, Default)]
726pub struct TimePressure {
727    pub critical_moves: Vec<ChessMove>,
728    pub zugzwang_potential: f32,
729    pub tempo_importance: f32,
730}
731
732/// Strategic initiative statistics
733#[derive(Debug, Clone)]
734pub struct StrategicInitiativeStats {
735    pub evaluations_performed: u64,
736    pub cache_hit_ratio: f64,
737    pub average_evaluation_time_ms: f64,
738    pub plans_generated: u64,
739}
740
741#[cfg(test)]
742mod tests {
743    use super::*;
744    use chess::Board;
745    use std::str::FromStr;
746
747    #[test]
748    fn test_strategic_initiative_evaluator() {
749        let evaluator = StrategicInitiativeEvaluator::new();
750        let board =
751            Board::from_str("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1").unwrap();
752
753        let result = evaluator.evaluate_strategic_initiative(&board).unwrap();
754        assert!(result.initiative_score.is_finite());
755        assert!(result.white_initiative >= 0.0);
756        assert!(result.black_initiative >= 0.0);
757        assert!(!result.from_cache);
758    }
759
760    #[test]
761    fn test_initiative_analyzer() {
762        let analyzer = InitiativeAnalyzer::new();
763        let board =
764            Board::from_str("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1").unwrap();
765
766        let white_analysis = analyzer.analyze_initiative(&board, Color::White).unwrap();
767        let black_analysis = analyzer.analyze_initiative(&board, Color::Black).unwrap();
768
769        assert!(white_analysis.total_score.is_finite());
770        assert!(black_analysis.total_score.is_finite());
771        assert!(white_analysis.development_initiative >= 0.0);
772        assert!(black_analysis.development_initiative >= 0.0);
773    }
774
775    #[test]
776    fn test_strategic_evaluator_trait() {
777        let evaluator = StrategicInitiativeEvaluator::new();
778        let board =
779            Board::from_str("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1").unwrap();
780
781        let evaluation = evaluator.evaluate_position(&board).unwrap();
782        assert!(evaluation.evaluation.is_finite());
783        assert!(evaluation.confidence >= 0.0 && evaluation.confidence <= 1.0);
784        assert!(evaluation.computation_time_ms > 0);
785        assert!(!evaluation.additional_info.is_empty());
786    }
787
788    #[test]
789    fn test_strategic_plan_generator() {
790        let generator = StrategicPlanGenerator::new();
791        let board =
792            Board::from_str("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1").unwrap();
793
794        let plans = generator.generate_plans(&board, 3).unwrap();
795        assert!(plans.len() <= 3);
796
797        for plan in &plans {
798            assert!(plan.priority >= 0.0 && plan.priority <= 1.0);
799        }
800    }
801
802    #[test]
803    fn test_positional_pressure_evaluator() {
804        let evaluator = PositionalPressureEvaluator::new();
805        let board =
806            Board::from_str("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1").unwrap();
807
808        let pressure = evaluator.evaluate_pressure(&board).unwrap();
809        assert!(pressure.space_advantage.is_finite());
810        assert!(pressure.development_advantage.is_finite());
811        assert!(pressure.attacking_potential.is_finite());
812        assert!(pressure.positional_weaknesses.is_finite());
813    }
814
815    #[test]
816    fn test_time_pressure_analyzer() {
817        let analyzer = TimePressureAnalyzer::new();
818        let board =
819            Board::from_str("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1").unwrap();
820
821        let time_pressure = analyzer.analyze_time_factors(&board).unwrap();
822        assert!(time_pressure.zugzwang_potential >= 0.0);
823        assert!(time_pressure.tempo_importance >= 0.0);
824    }
825}