chess_vector_engine/
strategic_motifs.rs

1//! Strategic Motif Recognition System
2//!
3//! This module implements a curated database of strategic chess patterns that provide
4//! master-level positional understanding. Instead of storing millions of positions,
5//! we focus on ~10K strategic motifs that capture the essence of positional play.
6
7use chess::{Board, Piece, Square};
8use serde::{Deserialize, Serialize};
9use std::collections::HashMap;
10
11/// A strategic chess motif representing a meaningful positional pattern
12#[derive(Debug, Clone, Serialize, Deserialize)]
13pub struct StrategicMotif {
14    /// Unique identifier for this motif
15    pub id: u64,
16    /// Pattern hash for fast matching
17    pub pattern_hash: u64,
18    /// Type of strategic pattern
19    pub motif_type: MotifType,
20    /// Strategic evaluation adjustment (-2.0 to +2.0)
21    pub evaluation: f32,
22    /// When this pattern is most relevant
23    pub context: StrategicContext,
24    /// Confidence in this pattern (0.0 to 1.0)
25    pub confidence: f32,
26    /// Source game references where this pattern was successful
27    pub master_games: Vec<GameReference>,
28    /// Human-readable description
29    pub description: String,
30}
31
32/// Types of strategic motifs
33#[derive(Debug, Clone, Serialize, Deserialize)]
34pub enum MotifType {
35    /// Pawn structure patterns
36    PawnStructure(PawnPattern),
37    /// Piece coordination and placement
38    PieceCoordination(CoordinationPattern),
39    /// King safety configurations
40    KingSafety(SafetyPattern),
41    /// Initiative and tempo patterns
42    Initiative(InitiativePattern),
43    /// Endgame-specific patterns
44    Endgame(EndgamePattern),
45    /// Opening-specific strategic ideas
46    Opening(OpeningPattern),
47}
48
49/// Pawn structure patterns that affect strategic evaluation
50#[derive(Debug, Clone, Serialize, Deserialize)]
51pub enum PawnPattern {
52    /// Isolated pawn weaknesses
53    IsolatedPawn {
54        file_index: u8,
55        is_white: bool,
56        weakness_value: f32,
57    },
58    /// Doubled pawn formations
59    DoubledPawns {
60        file_index: u8,
61        is_white: bool,
62        count: u8,
63    },
64    /// Passed pawn advantages
65    PassedPawn {
66        square_index: u8,
67        is_white: bool,
68        advancement: f32,
69    },
70    /// Pawn chains and support structures
71    PawnChain {
72        base_square_index: u8,
73        length: u8,
74        is_white: bool,
75    },
76    /// Hanging pawns (abreast, unsupported)
77    HangingPawns {
78        file1_index: u8,
79        file2_index: u8,
80        is_white: bool,
81    },
82    /// Backward pawn weaknesses
83    BackwardPawn { square_index: u8, is_white: bool },
84    /// Pawn majority patterns
85    PawnMajority {
86        kingside: bool,
87        is_white: bool,
88        advantage: f32,
89    },
90}
91
92/// Piece coordination patterns
93#[derive(Debug, Clone, Serialize, Deserialize)]
94pub enum CoordinationPattern {
95    /// Knight outpost on strong squares
96    KnightOutpost {
97        square_index: u8,
98        is_white: bool,
99        strength: f32,
100    },
101    /// Bishop pair advantages in open positions
102    BishopPair { is_white: bool, open_diagonals: u8 },
103    /// Rook lift patterns for attack
104    RookLift {
105        from_rank_index: u8,
106        to_rank_index: u8,
107        is_white: bool,
108    },
109    /// Queen and knight coordination
110    QueenKnightAttack {
111        target_area: KingArea,
112        is_white: bool,
113    },
114    /// Piece sacrifices for positional advantage
115    PositionalSacrifice { piece_type: u8, compensation: f32 }, // 0=Pawn, 1=Knight, 2=Bishop, 3=Rook, 4=Queen, 5=King
116    /// Piece activity vs opponent passivity
117    ActivityAdvantage {
118        is_white: bool,
119        activity_differential: f32,
120    },
121}
122
123/// King safety patterns
124#[derive(Debug, Clone, Serialize, Deserialize)]
125pub enum SafetyPattern {
126    /// Castling structure integrity
127    CastlingStructure {
128        side: CastlingSide,
129        is_white: bool,
130        safety_value: f32,
131    },
132    /// Pawn shield configurations
133    PawnShield {
134        king_square_index: u8,
135        shield_pattern: u8,
136    },
137    /// King exposure levels
138    KingExposure {
139        square_index: u8,
140        is_white: bool,
141        danger_level: f32,
142    },
143    /// Opposite-side castling attack patterns
144    OppositeCastling {
145        attacker_is_white: bool,
146        attack_potential: f32,
147    },
148}
149
150/// Initiative and tempo patterns
151#[derive(Debug, Clone, Serialize, Deserialize)]
152pub enum InitiativePattern {
153    /// Space advantage in center or wings
154    SpaceAdvantage {
155        area: BoardArea,
156        is_white: bool,
157        space_value: f32,
158    },
159    /// Tempo advantages in development
160    DevelopmentLead { is_white: bool, tempo_count: u8 },
161    /// Pressure point creation
162    PressurePoints {
163        square_indices: Vec<u8>,
164        is_white: bool,
165    },
166    /// Strategic pawn breaks
167    PawnBreak {
168        break_square_index: u8,
169        is_white: bool,
170        timing: f32,
171    },
172}
173
174/// Endgame-specific patterns
175#[derive(Debug, Clone, Serialize, Deserialize)]
176pub enum EndgamePattern {
177    /// Good vs bad bishop evaluations
178    BishopEndgame { is_white: bool, bishop_quality: f32 },
179    /// Rook endgame principles
180    RookEndgame {
181        pattern: RookPattern,
182        advantage: f32,
183    },
184    /// King activity in endgames
185    KingActivity {
186        square_index: u8,
187        is_white: bool,
188        activity: f32,
189    },
190    /// Pawn endgame races
191    PawnRace { is_white: bool, race_advantage: f32 },
192}
193
194/// Opening-specific strategic patterns
195#[derive(Debug, Clone, Serialize, Deserialize)]
196pub enum OpeningPattern {
197    /// Central control strategies
198    CentralControl {
199        square_indices: Vec<u8>,
200        is_white: bool,
201        control_value: f32,
202    },
203    /// Development principles
204    DevelopmentPrinciple {
205        piece_type: u8,
206        target_square_index: u8,
207        value: f32,
208    },
209    /// Opening pawn breaks
210    OpeningBreak {
211        break_move: String,
212        is_white: bool,
213        strategic_value: f32,
214    },
215}
216
217/// Context for when a motif is most relevant
218#[derive(Debug, Clone, Serialize, Deserialize)]
219pub struct StrategicContext {
220    /// Game phase where this motif matters most
221    pub game_phase: GamePhase,
222    /// Material balance context
223    pub material_context: MaterialContext,
224    /// Minimum ply for relevance
225    pub min_ply: u16,
226    /// Maximum ply for relevance (None = always relevant)
227    pub max_ply: Option<u16>,
228}
229
230/// Game phases for contextual relevance
231#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
232pub enum GamePhase {
233    Opening,
234    Middlegame,
235    Endgame,
236    Any,
237}
238
239/// Material context for pattern relevance
240#[derive(Debug, Clone, Serialize, Deserialize)]
241pub enum MaterialContext {
242    /// Equal material
243    Equal,
244    /// Material advantage for white (true) or black (false)
245    Advantage(bool),
246    /// Material disadvantage requires compensation for white (true) or black (false)
247    Compensation(bool),
248    /// Any material situation
249    Any,
250}
251
252/// Supporting types
253#[derive(Debug, Clone, Serialize, Deserialize)]
254pub enum CastlingSide {
255    Kingside,
256    Queenside,
257}
258
259#[derive(Debug, Clone, Serialize, Deserialize)]
260pub enum BoardArea {
261    Center,
262    Kingside,
263    Queenside,
264    Rank(u8), // rank index 0-7
265    File(u8), // file index 0-7
266}
267
268#[derive(Debug, Clone, Serialize, Deserialize)]
269pub enum KingArea {
270    Kingside,
271    Queenside,
272    Center,
273}
274
275#[derive(Debug, Clone, Serialize, Deserialize)]
276pub enum RookPattern {
277    ActiveRook,
278    PassiveRook,
279    RookBehindPasser,
280    SeventhRank,
281}
282
283/// Reference to a master game where this motif appeared
284#[derive(Debug, Clone, Serialize, Deserialize)]
285pub struct GameReference {
286    /// Game identifier (e.g., Lichess game ID)
287    pub game_id: String,
288    /// Player names
289    pub white: String,
290    pub black: String,
291    /// Game result
292    pub result: String,
293    /// Ply where this motif was relevant
294    pub ply: u16,
295    /// Rating of players (for confidence weighting)
296    pub rating: Option<u16>,
297}
298
299/// Match result when finding motifs in a position
300#[derive(Debug, Clone)]
301pub struct MotifMatch {
302    /// The matched motif
303    pub motif: StrategicMotif,
304    /// Relevance score (0.0 to 1.0)
305    pub relevance: f32,
306    /// Specific squares involved in this match
307    pub matching_squares: Vec<Square>,
308}
309
310/// Fast strategic database for real-time pattern matching
311pub struct StrategicDatabase {
312    /// All strategic motifs indexed by pattern hash
313    motifs: HashMap<u64, StrategicMotif>,
314    /// Pattern matchers for different motif types
315    pawn_matcher: PawnPatternMatcher,
316    piece_matcher: PiecePatternMatcher,
317    king_matcher: KingPatternMatcher,
318    /// Cache for recent evaluations
319    evaluation_cache: lru::LruCache<u64, f32>,
320    /// Statistics
321    total_motifs: usize,
322}
323
324impl StrategicDatabase {
325    /// Create new strategic database
326    pub fn new() -> Self {
327        Self {
328            motifs: HashMap::new(),
329            pawn_matcher: PawnPatternMatcher::new(),
330            piece_matcher: PiecePatternMatcher::new(),
331            king_matcher: KingPatternMatcher::new(),
332            evaluation_cache: lru::LruCache::new(std::num::NonZeroUsize::new(10000).unwrap()),
333            total_motifs: 0,
334        }
335    }
336
337    /// Load strategic database from binary file (ultra-fast)
338    pub fn load_from_binary<P: AsRef<std::path::Path>>(
339        path: P,
340    ) -> Result<Self, Box<dyn std::error::Error>> {
341        use std::fs::File;
342        use std::io::BufReader;
343
344        let file = File::open(path)?;
345        let reader = BufReader::new(file);
346        let motifs: Vec<StrategicMotif> = bincode::deserialize_from(reader)?;
347
348        let mut database = Self::new();
349        for motif in motifs {
350            database.add_motif(motif);
351        }
352
353        println!("📚 Loaded {} strategic motifs", database.total_motifs);
354        Ok(database)
355    }
356
357    /// Save strategic database to binary file
358    pub fn save_to_binary<P: AsRef<std::path::Path>>(
359        &self,
360        path: P,
361    ) -> Result<(), Box<dyn std::error::Error>> {
362        use std::fs::File;
363        use std::io::BufWriter;
364
365        let motifs: Vec<&StrategicMotif> = self.motifs.values().collect();
366        let file = File::create(path)?;
367        let writer = BufWriter::new(file);
368        bincode::serialize_into(writer, &motifs)?;
369
370        Ok(())
371    }
372
373    /// Add a motif to the database
374    pub fn add_motif(&mut self, motif: StrategicMotif) {
375        // Index the motif for fast retrieval
376        self.motifs.insert(motif.pattern_hash, motif.clone());
377
378        // Add to specialized pattern matchers
379        match &motif.motif_type {
380            MotifType::PawnStructure(_) => self.pawn_matcher.add_pattern(&motif),
381            MotifType::PieceCoordination(_) => self.piece_matcher.add_pattern(&motif),
382            MotifType::KingSafety(_) => self.king_matcher.add_pattern(&motif),
383            _ => {} // Other types use general pattern matching
384        }
385
386        self.total_motifs += 1;
387    }
388
389    /// Find strategic motifs in a position
390    pub fn find_motifs(&mut self, board: &Board) -> Vec<MotifMatch> {
391        let board_hash = board.get_hash();
392
393        // Check cache first
394        if let Some(&_cached_eval) = self.evaluation_cache.get(&board_hash) {
395            // Return empty matches but signal cached evaluation available
396            return vec![];
397        }
398
399        let mut matches = Vec::new();
400
401        // Find pawn structure motifs
402        matches.extend(self.pawn_matcher.find_matches(board));
403
404        // Find piece coordination motifs
405        matches.extend(self.piece_matcher.find_matches(board));
406
407        // Find king safety motifs
408        matches.extend(self.king_matcher.find_matches(board));
409
410        // Cache the result
411        let total_eval = self.evaluate_motifs(&matches);
412        self.evaluation_cache.put(board_hash, total_eval);
413
414        matches
415    }
416
417    /// Evaluate a collection of motif matches
418    pub fn evaluate_motifs(&self, matches: &[MotifMatch]) -> f32 {
419        if matches.is_empty() {
420            return 0.0;
421        }
422
423        let weighted_sum: f32 = matches
424            .iter()
425            .map(|m| m.motif.evaluation * m.relevance * m.motif.confidence)
426            .sum();
427
428        let total_weight: f32 = matches
429            .iter()
430            .map(|m| m.relevance * m.motif.confidence)
431            .sum();
432
433        if total_weight > 0.0 {
434            weighted_sum / total_weight
435        } else {
436            0.0
437        }
438    }
439
440    /// Get strategic evaluation for a position
441    pub fn get_strategic_evaluation(&mut self, board: &Board) -> f32 {
442        let matches = self.find_motifs(board);
443        self.evaluate_motifs(&matches)
444    }
445
446    /// Get database statistics
447    pub fn stats(&self) -> StrategicDatabaseStats {
448        StrategicDatabaseStats {
449            total_motifs: self.total_motifs,
450            cache_size: self.evaluation_cache.len(),
451            cache_hit_rate: 0.0, // TODO: implement cache hit tracking
452        }
453    }
454}
455
456/// Statistics for the strategic database
457#[derive(Debug)]
458pub struct StrategicDatabaseStats {
459    pub total_motifs: usize,
460    pub cache_size: usize,
461    pub cache_hit_rate: f32,
462}
463
464/// Pattern matcher for pawn structures
465pub struct PawnPatternMatcher {
466    patterns: Vec<StrategicMotif>,
467}
468
469impl PawnPatternMatcher {
470    pub fn new() -> Self {
471        Self {
472            patterns: Vec::new(),
473        }
474    }
475
476    pub fn add_pattern(&mut self, motif: &StrategicMotif) {
477        self.patterns.push(motif.clone());
478    }
479
480    pub fn find_matches(&self, board: &Board) -> Vec<MotifMatch> {
481        let mut matches = Vec::new();
482
483        // Analyze pawn structure for each pattern
484        for pattern in &self.patterns {
485            if let Some(motif_match) = self.match_pawn_pattern(board, pattern) {
486                matches.push(motif_match);
487            }
488        }
489
490        matches
491    }
492
493    fn match_pawn_pattern(&self, _board: &Board, _motif: &StrategicMotif) -> Option<MotifMatch> {
494        // Implementation will analyze specific pawn patterns
495        // For now, return None (to be implemented)
496        None
497    }
498}
499
500/// Pattern matcher for piece coordination
501pub struct PiecePatternMatcher {
502    patterns: Vec<StrategicMotif>,
503}
504
505impl PiecePatternMatcher {
506    pub fn new() -> Self {
507        Self {
508            patterns: Vec::new(),
509        }
510    }
511
512    pub fn add_pattern(&mut self, motif: &StrategicMotif) {
513        self.patterns.push(motif.clone());
514    }
515
516    pub fn find_matches(&self, _board: &Board) -> Vec<MotifMatch> {
517        // Implementation for piece pattern matching
518        Vec::new()
519    }
520}
521
522/// Pattern matcher for king safety
523pub struct KingPatternMatcher {
524    patterns: Vec<StrategicMotif>,
525}
526
527impl KingPatternMatcher {
528    pub fn new() -> Self {
529        Self {
530            patterns: Vec::new(),
531        }
532    }
533
534    pub fn add_pattern(&mut self, motif: &StrategicMotif) {
535        self.patterns.push(motif.clone());
536    }
537
538    pub fn find_matches(&self, _board: &Board) -> Vec<MotifMatch> {
539        // Implementation for king safety pattern matching
540        Vec::new()
541    }
542}
543
544/// Utility functions for pattern analysis
545pub mod pattern_utils {
546    use super::*;
547
548    /// Generate pattern hash for a chess position focusing on strategic elements
549    pub fn generate_pattern_hash(board: &Board) -> u64 {
550        // Use a hash that focuses on positional features rather than exact piece placement
551        // This allows similar positions to match even with minor piece differences
552
553        let mut hash = 0u64;
554
555        // Hash pawn structure (most important for strategic patterns)
556        hash ^= hash_pawn_structure(board);
557
558        // Hash piece placement patterns
559        hash ^= hash_piece_patterns(board);
560
561        // Hash king positions
562        hash ^= hash_king_positions(board);
563
564        hash
565    }
566
567    fn hash_pawn_structure(_board: &Board) -> u64 {
568        // Implementation for hashing pawn structure
569        0
570    }
571
572    fn hash_piece_patterns(_board: &Board) -> u64 {
573        // Implementation for hashing piece patterns
574        0
575    }
576
577    fn hash_king_positions(_board: &Board) -> u64 {
578        // Implementation for hashing king positions
579        0
580    }
581
582    /// Determine game phase based on material
583    pub fn determine_game_phase(board: &Board) -> GamePhase {
584        let material_count = count_material(board);
585
586        if material_count > 60 {
587            GamePhase::Opening
588        } else if material_count > 20 {
589            GamePhase::Middlegame
590        } else {
591            GamePhase::Endgame
592        }
593    }
594
595    fn count_material(board: &Board) -> u32 {
596        // Simple material counting (can be improved)
597        let mut total = 0u32;
598
599        // Count all pieces except kings and pawns for phase determination
600        for square in chess::ALL_SQUARES {
601            if let Some(piece) = board.piece_on(square) {
602                match piece {
603                    Piece::Queen => total += 9,
604                    Piece::Rook => total += 5,
605                    Piece::Bishop => total += 3,
606                    Piece::Knight => total += 3,
607                    _ => {}
608                }
609            }
610        }
611
612        total
613    }
614}