chess_vector_engine/
motif_extractor.rs

1//! Strategic Motif Extraction System
2//!
3//! This module extracts strategic motifs from the existing position database,
4//! focusing on patterns that provide master-level positional understanding.
5
6// Simplified motif extraction - complex pattern analyzer removed during cleanup
7// use crate::pattern_recognition::{PatternAnalysisResult, AdvancedPatternRecognizer};
8use crate::strategic_motifs::*;
9use crate::ChessVectorEngine;
10use chess::{BitBoard, Board, Color, File, Piece, Square, ALL_FILES};
11use std::collections::HashMap;
12
13/// Simplified strategic pattern for cleanup phase
14#[derive(Debug, Clone)]
15pub struct StrategicPattern {
16    pub pattern_type: SimplePatternType,
17    pub strength: f32,
18    pub reliability: f32,
19}
20
21/// Simplified pattern types  
22#[derive(Debug, Clone)]
23pub enum SimplePatternType {
24    MaterialImbalance,
25    KingSafety,
26    CenterControl,
27    PieceActivity,
28}
29
30/// Extracts strategic motifs from a chess engine's position database
31pub struct MotifExtractor {
32    /// Minimum evaluation difference to consider a pattern strategic
33    min_eval_significance: f32,
34    /// Minimum number of occurrences for a pattern to be considered valid
35    min_pattern_frequency: usize,
36    /// Confidence threshold for including patterns
37    confidence_threshold: f32,
38    /// Extracted motifs
39    extracted_motifs: Vec<StrategicMotif>,
40    /// Pattern occurrence tracking
41    pattern_counts: HashMap<u64, PatternOccurrence>,
42    // Simplified pattern analyzer - replaced during cleanup
43    // pattern_analyzer: AdvancedPatternAnalyzer,
44}
45
46/// Tracks occurrences of a specific pattern
47#[derive(Debug, Clone)]
48struct PatternOccurrence {
49    count: usize,
50    evaluations: Vec<f32>,
51    positions: Vec<Board>,
52    game_phases: Vec<GamePhase>,
53}
54
55impl MotifExtractor {
56    /// Create new motif extractor with default parameters
57    pub fn new() -> Self {
58        Self {
59            min_eval_significance: 0.1, // 10 centipawn minimum significance (more sensitive)
60            min_pattern_frequency: 20, // Pattern must appear at least 20 times (more statistical power with 998K positions)
61            confidence_threshold: 0.4, // 40% confidence minimum (more lenient)
62            extracted_motifs: Vec::new(),
63            pattern_counts: HashMap::new(),
64            // pattern_analyzer: AdvancedPatternAnalyzer::new(),
65        }
66    }
67
68    /// Extract strategic motifs from a chess engine's database
69    pub fn extract_from_engine(
70        &mut self,
71        engine: &ChessVectorEngine,
72    ) -> Result<Vec<StrategicMotif>, Box<dyn std::error::Error>> {
73        println!("๐Ÿ” Starting strategic motif extraction...");
74
75        let total_positions = engine.knowledge_base_size();
76        println!(
77            "๐Ÿ“Š Analyzing {} positions for strategic patterns",
78            total_positions
79        );
80
81        if total_positions == 0 {
82            return Err("No positions in engine database to extract from".into());
83        }
84
85        // Phase 1: Analyze all positions for patterns
86        self.analyze_positions(engine)?;
87
88        // Phase 2: Extract significant patterns
89        self.extract_significant_patterns()?;
90
91        // Phase 3: Validate and refine patterns
92        self.validate_patterns()?;
93
94        println!(
95            "โœ… Extracted {} strategic motifs",
96            self.extracted_motifs.len()
97        );
98        Ok(self.extracted_motifs.clone())
99    }
100
101    /// Analyze all positions in the engine database
102    fn analyze_positions(
103        &mut self,
104        engine: &ChessVectorEngine,
105    ) -> Result<(), Box<dyn std::error::Error>> {
106        use indicatif::{ProgressBar, ProgressStyle};
107
108        let total_positions = engine.knowledge_base_size();
109        let pb = ProgressBar::new(total_positions as u64);
110        pb.set_style(
111            ProgressStyle::default_bar()
112                .template("๐Ÿ” Analyzing patterns [{elapsed_precise}] [{bar:40.cyan/blue}] {pos}/{len} ({per_sec}) {msg}")?
113                .progress_chars("โ–ˆโ–ˆโ–‘")
114        );
115
116        // Analyze each position for strategic patterns
117        for i in 0..total_positions {
118            if let (Some(board), Some(evaluation)) = (
119                engine.get_board_by_index(i),
120                engine.get_evaluation_by_index(i),
121            ) {
122                self.analyze_single_position(board, evaluation);
123            }
124
125            if i % 1000 == 0 {
126                pb.set_position(i as u64);
127                pb.set_message(format!("Found {} patterns", self.pattern_counts.len()));
128            }
129        }
130
131        pb.finish_with_message(format!(
132            "โœ… Analysis complete: {} unique patterns found",
133            self.pattern_counts.len()
134        ));
135        Ok(())
136    }
137
138    /// Analyze a single position for strategic patterns using advanced pattern recognition
139    fn analyze_single_position(&mut self, board: &Board, evaluation: f32) {
140        let game_phase = pattern_utils::determine_game_phase(board);
141
142        // Generate simplified patterns based on basic position features
143        let strategic_patterns = self.generate_simple_patterns(board, evaluation);
144
145        // Convert simple patterns to trackable motifs
146        for pattern in strategic_patterns {
147            // Only track patterns with significant strength
148            if pattern.strength.abs() > 0.1 && pattern.reliability > 0.6 {
149                let pattern_hash = self.generate_pattern_hash(&pattern);
150                self.record_pattern_occurrence(
151                    pattern_hash,
152                    board,
153                    evaluation,
154                    &game_phase,
155                );
156            }
157        }
158    }
159
160    /// Generate simplified strategic patterns from basic position features
161    fn generate_simple_patterns(&self, board: &Board, evaluation: f32) -> Vec<StrategicPattern> {
162        let mut patterns = Vec::new();
163
164        // Material imbalance pattern
165        let material_balance = self.calculate_material_balance(board);
166        if material_balance.abs() > 1 {
167            patterns.push(StrategicPattern {
168                pattern_type: SimplePatternType::MaterialImbalance,
169                strength: material_balance as f32 * 0.1,
170                reliability: 0.8,
171            });
172        }
173
174        // King safety pattern
175        if self.is_king_exposed(board, chess::Color::White) || self.is_king_exposed(board, chess::Color::Black) {
176            patterns.push(StrategicPattern {
177                pattern_type: SimplePatternType::KingSafety,
178                strength: evaluation.signum() * 0.3,
179                reliability: 0.7,
180            });
181        }
182
183        patterns
184    }
185
186    /// Check if king is exposed
187    fn is_king_exposed(&self, board: &Board, color: chess::Color) -> bool {
188        let king_square = board.king_square(color);
189        // Simple check: king on edge ranks (1st or 8th) is potentially exposed
190        let rank = king_square.get_rank().to_index();
191        rank == 0 || rank == 7
192    }
193
194    /// Calculate basic material balance
195    fn calculate_material_balance(&self, board: &Board) -> i32 {
196        let mut balance = 0;
197        let piece_values = [1, 3, 3, 5, 9, 0]; // Pawn, Knight, Bishop, Rook, Queen, King
198
199        for square in chess::ALL_SQUARES {
200            if let Some(piece) = board.piece_on(square) {
201                let value = piece_values[piece as usize];
202                match board.color_on(square) {
203                    Some(Color::White) => balance += value,
204                    Some(Color::Black) => balance -= value,
205                    None => {}
206                }
207            }
208        }
209        balance
210    }
211
212    /// Extract pawn structure patterns
213    fn extract_pawn_patterns(&mut self, board: &Board, evaluation: f32, game_phase: &GamePhase) {
214        // Analyze for isolated pawns
215        for color in [Color::White, Color::Black] {
216            let isolated_pawns = self.find_isolated_pawns(board, color);
217            for (file, _square) in isolated_pawns {
218                let pattern_hash = self.hash_isolated_pawn_pattern(file, color);
219                self.record_pattern_occurrence(pattern_hash, board, evaluation, game_phase);
220            }
221        }
222
223        // Analyze for passed pawns
224        for color in [Color::White, Color::Black] {
225            let passed_pawns = self.find_passed_pawns(board, color);
226            for (square, advancement) in passed_pawns {
227                let pattern_hash = self.hash_passed_pawn_pattern(square, color, advancement);
228                self.record_pattern_occurrence(pattern_hash, board, evaluation, game_phase);
229            }
230        }
231
232        // Analyze for doubled pawns
233        for color in [Color::White, Color::Black] {
234            let doubled_pawns = self.find_doubled_pawns(board, color);
235            for (file, count) in doubled_pawns {
236                let pattern_hash = self.hash_doubled_pawn_pattern(file, color, count);
237                self.record_pattern_occurrence(pattern_hash, board, evaluation, game_phase);
238            }
239        }
240    }
241
242    /// Extract piece coordination patterns
243    fn extract_piece_patterns(&mut self, board: &Board, evaluation: f32, game_phase: &GamePhase) {
244        // Analyze for knight outposts
245        for color in [Color::White, Color::Black] {
246            let outposts = self.find_knight_outposts(board, color);
247            for (square, strength) in outposts {
248                let pattern_hash = self.hash_knight_outpost_pattern(square, color, strength);
249                self.record_pattern_occurrence(pattern_hash, board, evaluation, game_phase);
250            }
251        }
252
253        // Analyze for bishop pairs
254        for color in [Color::White, Color::Black] {
255            if self.has_bishop_pair(board, color) {
256                let open_diagonals = self.count_open_diagonals(board, color);
257                let pattern_hash = self.hash_bishop_pair_pattern(color, open_diagonals);
258                self.record_pattern_occurrence(pattern_hash, board, evaluation, game_phase);
259            }
260        }
261
262        // Analyze for rook activity
263        for color in [Color::White, Color::Black] {
264            let rook_patterns = self.analyze_rook_activity(board, color);
265            for (pattern_type, square) in rook_patterns {
266                let pattern_hash = self.hash_rook_pattern(pattern_type, square, color);
267                self.record_pattern_occurrence(pattern_hash, board, evaluation, game_phase);
268            }
269        }
270    }
271
272    /// Extract king safety patterns
273    fn extract_king_safety_patterns(
274        &mut self,
275        board: &Board,
276        evaluation: f32,
277        game_phase: &GamePhase,
278    ) {
279        for color in [Color::White, Color::Black] {
280            let king_square = board.king_square(color);
281
282            // Analyze castling structure
283            let castling_safety = self.evaluate_castling_safety(board, color, king_square);
284            if castling_safety.is_some() {
285                let pattern_hash =
286                    self.hash_king_safety_pattern(king_square, color, castling_safety.unwrap());
287                self.record_pattern_occurrence(pattern_hash, board, evaluation, game_phase);
288            }
289
290            // Analyze pawn shield
291            let shield_pattern = self.analyze_pawn_shield(board, color, king_square);
292            let pattern_hash = self.hash_pawn_shield_pattern(king_square, color, shield_pattern);
293            self.record_pattern_occurrence(pattern_hash, board, evaluation, game_phase);
294        }
295    }
296
297    /// Extract initiative patterns
298    fn extract_initiative_patterns(
299        &mut self,
300        board: &Board,
301        evaluation: f32,
302        game_phase: &GamePhase,
303    ) {
304        // Analyze space advantage
305        for color in [Color::White, Color::Black] {
306            let space_value = self.calculate_space_advantage(board, color);
307            if space_value.abs() > 0.2 {
308                // Significant space advantage
309                let pattern_hash = self.hash_space_pattern(color, space_value);
310                self.record_pattern_occurrence(pattern_hash, board, evaluation, game_phase);
311            }
312        }
313
314        // Analyze development patterns (for opening/early middlegame)
315        if matches!(game_phase, GamePhase::Opening) {
316            for color in [Color::White, Color::Black] {
317                let development_score = self.calculate_development_score(board, color);
318                let pattern_hash = self.hash_development_pattern(color, development_score);
319                self.record_pattern_occurrence(pattern_hash, board, evaluation, game_phase);
320            }
321        }
322    }
323
324    /// Record a pattern occurrence
325    fn record_pattern_occurrence(
326        &mut self,
327        pattern_hash: u64,
328        board: &Board,
329        evaluation: f32,
330        game_phase: &GamePhase,
331    ) {
332        let occurrence =
333            self.pattern_counts
334                .entry(pattern_hash)
335                .or_insert_with(|| PatternOccurrence {
336                    count: 0,
337                    evaluations: Vec::new(),
338                    positions: Vec::new(),
339                    game_phases: Vec::new(),
340                });
341
342        occurrence.count += 1;
343        occurrence.evaluations.push(evaluation);
344        occurrence.positions.push(*board);
345        occurrence.game_phases.push(game_phase.clone());
346    }
347
348
349    /// Generate simplified hash for basic patterns
350    fn generate_pattern_hash(&self, pattern: &StrategicPattern) -> u64 {
351        use std::collections::hash_map::DefaultHasher;
352        use std::hash::{Hash, Hasher};
353
354        let mut hasher = DefaultHasher::new();
355
356        // Hash based on simplified pattern type
357        match &pattern.pattern_type {
358            SimplePatternType::MaterialImbalance => {
359                "material_imbalance".hash(&mut hasher);
360            }
361            SimplePatternType::KingSafety => {
362                "king_safety".hash(&mut hasher);
363            }
364            SimplePatternType::CenterControl => {
365                "center_control".hash(&mut hasher);
366            }
367            SimplePatternType::PieceActivity => {
368                "piece_activity".hash(&mut hasher);
369            }
370        }
371
372        // Include pattern strength range for differentiation
373        let strength_bucket = ((pattern.strength + 2.0) * 10.0) as i32; // -2.0 to +2.0 -> 0 to 40
374        strength_bucket.hash(&mut hasher);
375
376        hasher.finish()
377    }
378
379    /// Extract significant patterns from occurrence data
380    fn extract_significant_patterns(&mut self) -> Result<(), Box<dyn std::error::Error>> {
381        println!("๐ŸŽฏ Extracting significant strategic patterns...");
382        println!(
383            "   ๐Ÿ“Š Found {} unique patterns to analyze",
384            self.pattern_counts.len()
385        );
386
387        let mut motif_id = 1u64;
388        let mut frequency_filtered = 0;
389        let mut significance_filtered = 0;
390        let mut confidence_filtered = 0;
391
392        for (pattern_hash, occurrence) in &self.pattern_counts {
393            if occurrence.count < self.min_pattern_frequency {
394                frequency_filtered += 1;
395                continue;
396            }
397
398            // Calculate statistical significance
399            let avg_evaluation =
400                occurrence.evaluations.iter().sum::<f32>() / occurrence.evaluations.len() as f32;
401            let _eval_std_dev = self.calculate_std_dev(&occurrence.evaluations, avg_evaluation);
402
403            // Only include patterns with significant evaluation impact
404            if avg_evaluation.abs() < self.min_eval_significance {
405                significance_filtered += 1;
406                continue;
407            }
408
409            // Calculate confidence based on consistency
410            let confidence = self.calculate_pattern_confidence(occurrence);
411            if confidence < self.confidence_threshold {
412                confidence_filtered += 1;
413                continue;
414            }
415
416            // Create strategic motif (simplified for now - will enhance pattern recognition)
417            let motif = StrategicMotif {
418                id: motif_id,
419                pattern_hash: *pattern_hash,
420                motif_type: self.infer_motif_type(&occurrence.positions[0], *pattern_hash),
421                evaluation: avg_evaluation,
422                context: self.determine_context(&occurrence.game_phases, &occurrence.evaluations),
423                confidence,
424                master_games: self.create_game_references(&occurrence.positions),
425                description: format!(
426                    "Strategic pattern {} (avg eval: {:.2})",
427                    motif_id, avg_evaluation
428                ),
429            };
430
431            self.extracted_motifs.push(motif);
432            motif_id += 1;
433        }
434
435        println!(
436            "๐Ÿ“ˆ Extracted {} statistically significant patterns",
437            self.extracted_motifs.len()
438        );
439        println!("   ๐Ÿšซ Filtered out:");
440        println!(
441            "      - {} patterns (frequency < {})",
442            frequency_filtered, self.min_pattern_frequency
443        );
444        println!(
445            "      - {} patterns (significance < {:.1})",
446            significance_filtered, self.min_eval_significance
447        );
448        println!(
449            "      - {} patterns (confidence < {:.1})",
450            confidence_filtered, self.confidence_threshold
451        );
452        Ok(())
453    }
454
455    /// Validate extracted patterns
456    fn validate_patterns(&mut self) -> Result<(), Box<dyn std::error::Error>> {
457        println!("โœ… Validating extracted patterns...");
458
459        // Remove patterns that might be too position-specific
460        let original_count = self.extracted_motifs.len();
461
462        self.extracted_motifs.retain(|motif| {
463            // Keep patterns with high confidence and reasonable frequency
464            motif.confidence >= self.confidence_threshold
465                && self
466                    .pattern_counts
467                    .get(&motif.pattern_hash)
468                    .map(|occ| occ.count >= self.min_pattern_frequency)
469                    .unwrap_or(false)
470        });
471
472        let removed_count = original_count - self.extracted_motifs.len();
473        if removed_count > 0 {
474            println!("๐Ÿงน Removed {} low-quality patterns", removed_count);
475        }
476
477        println!(
478            "โœจ Validation complete: {} high-quality strategic motifs ready",
479            self.extracted_motifs.len()
480        );
481        Ok(())
482    }
483
484    // Helper methods for pattern analysis (simplified implementations)
485
486    fn find_isolated_pawns(&self, board: &Board, color: Color) -> Vec<(File, Square)> {
487        let mut isolated = Vec::new();
488        let pawns = board.pieces(Piece::Pawn) & board.color_combined(color);
489
490        for square in pawns {
491            let file = square.get_file();
492            let adjacent_files = [
493                if file != File::A {
494                    Some(File::from_index(file.to_index() - 1))
495                } else {
496                    None
497                },
498                if file != File::H {
499                    Some(File::from_index(file.to_index() + 1))
500                } else {
501                    None
502                },
503            ];
504
505            let is_isolated = adjacent_files.iter().filter_map(|&f| f).all(|adj_file| {
506                // Check if adjacent files have no pawns
507                let mut has_adjacent_pawn = false;
508                for rank in chess::ALL_RANKS {
509                    let square = Square::make_square(rank, adj_file);
510                    if (pawns & BitBoard::from_square(square)) != BitBoard(0) {
511                        has_adjacent_pawn = true;
512                        break;
513                    }
514                }
515                !has_adjacent_pawn
516            });
517
518            if is_isolated {
519                isolated.push((file, square));
520            }
521        }
522
523        isolated
524    }
525
526    fn find_passed_pawns(&self, _board: &Board, _color: Color) -> Vec<(Square, f32)> {
527        // Simplified implementation - needs full passed pawn detection logic
528        Vec::new()
529    }
530
531    fn find_doubled_pawns(&self, board: &Board, color: Color) -> Vec<(File, u8)> {
532        let mut doubled = Vec::new();
533        let pawns = board.pieces(Piece::Pawn) & board.color_combined(color);
534
535        for file in ALL_FILES {
536            let mut count = 0u8;
537            for rank in chess::ALL_RANKS {
538                let square = Square::make_square(rank, file);
539                if (pawns & BitBoard::from_square(square)) != BitBoard(0) {
540                    count += 1;
541                }
542            }
543            if count > 1 {
544                doubled.push((file, count));
545            }
546        }
547
548        doubled
549    }
550
551    fn find_knight_outposts(&self, _board: &Board, _color: Color) -> Vec<(Square, f32)> {
552        // Simplified implementation
553        Vec::new()
554    }
555
556    fn has_bishop_pair(&self, board: &Board, color: Color) -> bool {
557        let bishops = board.pieces(Piece::Bishop) & board.color_combined(color);
558        bishops.count() >= 2
559    }
560
561    fn count_open_diagonals(&self, _board: &Board, _color: Color) -> u8 {
562        // Simplified implementation
563        0
564    }
565
566    fn analyze_rook_activity(&self, _board: &Board, _color: Color) -> Vec<(String, Square)> {
567        // Simplified implementation
568        Vec::new()
569    }
570
571    fn evaluate_castling_safety(
572        &self,
573        _board: &Board,
574        _color: Color,
575        _king_square: Square,
576    ) -> Option<f32> {
577        // Simplified implementation
578        Some(0.0)
579    }
580
581    fn analyze_pawn_shield(&self, _board: &Board, _color: Color, _king_square: Square) -> u8 {
582        // Simplified implementation
583        0
584    }
585
586    fn calculate_space_advantage(&self, _board: &Board, _color: Color) -> f32 {
587        // Simplified implementation
588        0.0
589    }
590
591    fn calculate_development_score(&self, _board: &Board, _color: Color) -> f32 {
592        // Simplified implementation
593        0.0
594    }
595
596    fn calculate_std_dev(&self, values: &[f32], mean: f32) -> f32 {
597        if values.len() <= 1 {
598            return 0.0;
599        }
600
601        let variance =
602            values.iter().map(|v| (v - mean).powi(2)).sum::<f32>() / (values.len() - 1) as f32;
603
604        variance.sqrt()
605    }
606
607    fn calculate_pattern_confidence(&self, occurrence: &PatternOccurrence) -> f32 {
608        // Calculate confidence based on consistency and frequency
609        let avg_eval =
610            occurrence.evaluations.iter().sum::<f32>() / occurrence.evaluations.len() as f32;
611        let std_dev = self.calculate_std_dev(&occurrence.evaluations, avg_eval);
612
613        // Higher frequency and lower deviation = higher confidence
614        let frequency_factor = (occurrence.count as f32 / 100.0).min(1.0); // Cap at 100 occurrences
615        let consistency_factor = (1.0 - (std_dev / 2.0).min(1.0)).max(0.0);
616
617        (frequency_factor + consistency_factor) / 2.0
618    }
619
620    fn infer_motif_type(&self, _board: &Board, _pattern_hash: u64) -> MotifType {
621        // Simplified type inference - in practice, this would be more sophisticated
622        MotifType::PawnStructure(PawnPattern::IsolatedPawn {
623            file_index: 3, // D file
624            is_white: true,
625            weakness_value: 0.3,
626        })
627    }
628
629    fn determine_context(
630        &self,
631        game_phases: &[GamePhase],
632        _evaluations: &[f32],
633    ) -> StrategicContext {
634        // Determine most common game phase
635        let mut phase_counts = HashMap::new();
636        for phase in game_phases {
637            *phase_counts.entry(format!("{:?}", phase)).or_insert(0) += 1;
638        }
639
640        let most_common_phase = phase_counts
641            .into_iter()
642            .max_by_key(|(_, count)| *count)
643            .map(|(phase, _)| phase)
644            .unwrap_or_else(|| "Any".to_string());
645
646        let game_phase = match most_common_phase.as_str() {
647            "Opening" => GamePhase::Opening,
648            "Middlegame" => GamePhase::Middlegame,
649            "Endgame" => GamePhase::Endgame,
650            _ => GamePhase::Any,
651        };
652
653        StrategicContext {
654            game_phase,
655            material_context: MaterialContext::Any,
656            min_ply: 1,
657            max_ply: None,
658        }
659    }
660
661    fn create_game_references(&self, positions: &[Board]) -> Vec<GameReference> {
662        // Create simplified game references
663        positions
664            .iter()
665            .take(3)
666            .enumerate()
667            .map(|(i, _)| GameReference {
668                game_id: format!("extracted_{}", i),
669                white: "Master".to_string(),
670                black: "Player".to_string(),
671                result: "1-0".to_string(),
672                ply: 20,
673                rating: Some(2500),
674            })
675            .collect()
676    }
677
678    // Hash generation methods (simplified)
679    fn hash_isolated_pawn_pattern(&self, file: File, color: Color) -> u64 {
680        let mut hash = 0x1234567890abcdefu64;
681        hash ^= file.to_index() as u64;
682        hash ^= if color == Color::White { 1 } else { 0 };
683        hash
684    }
685
686    fn hash_passed_pawn_pattern(&self, square: Square, color: Color, advancement: f32) -> u64 {
687        let mut hash = 0x2345678901bcdef0u64;
688        hash ^= square.to_index() as u64;
689        hash ^= if color == Color::White { 1 } else { 0 };
690        hash ^= (advancement * 1000.0) as u64;
691        hash
692    }
693
694    fn hash_doubled_pawn_pattern(&self, file: File, color: Color, count: u8) -> u64 {
695        let mut hash = 0x3456789012cdef01u64;
696        hash ^= file.to_index() as u64;
697        hash ^= if color == Color::White { 1 } else { 0 };
698        hash ^= count as u64;
699        hash
700    }
701
702    fn hash_knight_outpost_pattern(&self, square: Square, color: Color, strength: f32) -> u64 {
703        let mut hash = 0x456789013def012u64;
704        hash ^= square.to_index() as u64;
705        hash ^= if color == Color::White { 1 } else { 0 };
706        hash ^= (strength * 1000.0) as u64;
707        hash
708    }
709
710    fn hash_bishop_pair_pattern(&self, color: Color, open_diagonals: u8) -> u64 {
711        let mut hash = 0x56789014ef0123u64;
712        hash ^= if color == Color::White { 1 } else { 0 };
713        hash ^= open_diagonals as u64;
714        hash
715    }
716
717    fn hash_rook_pattern(&self, pattern_type: String, square: Square, color: Color) -> u64 {
718        let mut hash = 0x6789015f01234u64;
719        hash ^= square.to_index() as u64;
720        hash ^= if color == Color::White { 1 } else { 0 };
721        hash ^= pattern_type.len() as u64;
722        hash
723    }
724
725    fn hash_king_safety_pattern(
726        &self,
727        king_square: Square,
728        color: Color,
729        safety_value: f32,
730    ) -> u64 {
731        let mut hash = 0x789016012345u64;
732        hash ^= king_square.to_index() as u64;
733        hash ^= if color == Color::White { 1 } else { 0 };
734        hash ^= (safety_value * 1000.0) as u64;
735        hash
736    }
737
738    fn hash_pawn_shield_pattern(
739        &self,
740        king_square: Square,
741        color: Color,
742        shield_pattern: u8,
743    ) -> u64 {
744        let mut hash = 0x89017123456u64;
745        hash ^= king_square.to_index() as u64;
746        hash ^= if color == Color::White { 1 } else { 0 };
747        hash ^= shield_pattern as u64;
748        hash
749    }
750
751    fn hash_space_pattern(&self, color: Color, space_value: f32) -> u64 {
752        let mut hash = 0x9018234567u64;
753        hash ^= if color == Color::White { 1 } else { 0 };
754        hash ^= (space_value * 1000.0) as u64;
755        hash
756    }
757
758    fn hash_development_pattern(&self, color: Color, development_score: f32) -> u64 {
759        let mut hash = 0xa019345678u64;
760        hash ^= if color == Color::White { 1 } else { 0 };
761        hash ^= (development_score * 1000.0) as u64;
762        hash
763    }
764}