1use chess::{Board, Piece, Square};
8use serde::{Deserialize, Serialize};
9use std::collections::HashMap;
10
11#[derive(Debug, Clone, Serialize, Deserialize)]
13pub struct StrategicMotif {
14 pub id: u64,
16 pub pattern_hash: u64,
18 pub motif_type: MotifType,
20 pub evaluation: f32,
22 pub context: StrategicContext,
24 pub confidence: f32,
26 pub master_games: Vec<GameReference>,
28 pub description: String,
30}
31
32#[derive(Debug, Clone, Serialize, Deserialize)]
34pub enum MotifType {
35 PawnStructure(PawnPattern),
37 PieceCoordination(CoordinationPattern),
39 KingSafety(SafetyPattern),
41 Initiative(InitiativePattern),
43 Endgame(EndgamePattern),
45 Opening(OpeningPattern),
47}
48
49#[derive(Debug, Clone, Serialize, Deserialize)]
51pub enum PawnPattern {
52 IsolatedPawn {
54 file_index: u8,
55 is_white: bool,
56 weakness_value: f32,
57 },
58 DoubledPawns {
60 file_index: u8,
61 is_white: bool,
62 count: u8,
63 },
64 PassedPawn {
66 square_index: u8,
67 is_white: bool,
68 advancement: f32,
69 },
70 PawnChain {
72 base_square_index: u8,
73 length: u8,
74 is_white: bool,
75 },
76 HangingPawns {
78 file1_index: u8,
79 file2_index: u8,
80 is_white: bool,
81 },
82 BackwardPawn { square_index: u8, is_white: bool },
84 PawnMajority {
86 kingside: bool,
87 is_white: bool,
88 advantage: f32,
89 },
90}
91
92#[derive(Debug, Clone, Serialize, Deserialize)]
94pub enum CoordinationPattern {
95 KnightOutpost {
97 square_index: u8,
98 is_white: bool,
99 strength: f32,
100 },
101 BishopPair { is_white: bool, open_diagonals: u8 },
103 RookLift {
105 from_rank_index: u8,
106 to_rank_index: u8,
107 is_white: bool,
108 },
109 QueenKnightAttack {
111 target_area: KingArea,
112 is_white: bool,
113 },
114 PositionalSacrifice { piece_type: u8, compensation: f32 }, ActivityAdvantage {
118 is_white: bool,
119 activity_differential: f32,
120 },
121}
122
123#[derive(Debug, Clone, Serialize, Deserialize)]
125pub enum SafetyPattern {
126 CastlingStructure {
128 side: CastlingSide,
129 is_white: bool,
130 safety_value: f32,
131 },
132 PawnShield {
134 king_square_index: u8,
135 shield_pattern: u8,
136 },
137 KingExposure {
139 square_index: u8,
140 is_white: bool,
141 danger_level: f32,
142 },
143 OppositeCastling {
145 attacker_is_white: bool,
146 attack_potential: f32,
147 },
148}
149
150#[derive(Debug, Clone, Serialize, Deserialize)]
152pub enum InitiativePattern {
153 SpaceAdvantage {
155 area: BoardArea,
156 is_white: bool,
157 space_value: f32,
158 },
159 DevelopmentLead { is_white: bool, tempo_count: u8 },
161 PressurePoints {
163 square_indices: Vec<u8>,
164 is_white: bool,
165 },
166 PawnBreak {
168 break_square_index: u8,
169 is_white: bool,
170 timing: f32,
171 },
172}
173
174#[derive(Debug, Clone, Serialize, Deserialize)]
176pub enum EndgamePattern {
177 BishopEndgame { is_white: bool, bishop_quality: f32 },
179 RookEndgame {
181 pattern: RookPattern,
182 advantage: f32,
183 },
184 KingActivity {
186 square_index: u8,
187 is_white: bool,
188 activity: f32,
189 },
190 PawnRace { is_white: bool, race_advantage: f32 },
192}
193
194#[derive(Debug, Clone, Serialize, Deserialize)]
196pub enum OpeningPattern {
197 CentralControl {
199 square_indices: Vec<u8>,
200 is_white: bool,
201 control_value: f32,
202 },
203 DevelopmentPrinciple {
205 piece_type: u8,
206 target_square_index: u8,
207 value: f32,
208 },
209 OpeningBreak {
211 break_move: String,
212 is_white: bool,
213 strategic_value: f32,
214 },
215}
216
217#[derive(Debug, Clone, Serialize, Deserialize)]
219pub struct StrategicContext {
220 pub game_phase: GamePhase,
222 pub material_context: MaterialContext,
224 pub min_ply: u16,
226 pub max_ply: Option<u16>,
228}
229
230#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
232pub enum GamePhase {
233 Opening,
234 Middlegame,
235 Endgame,
236 Any,
237}
238
239#[derive(Debug, Clone, Serialize, Deserialize)]
241pub enum MaterialContext {
242 Equal,
244 Advantage(bool),
246 Compensation(bool),
248 Any,
250}
251
252#[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), File(u8), }
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#[derive(Debug, Clone, Serialize, Deserialize)]
285pub struct GameReference {
286 pub game_id: String,
288 pub white: String,
290 pub black: String,
291 pub result: String,
293 pub ply: u16,
295 pub rating: Option<u16>,
297}
298
299#[derive(Debug, Clone)]
301pub struct MotifMatch {
302 pub motif: StrategicMotif,
304 pub relevance: f32,
306 pub matching_squares: Vec<Square>,
308}
309
310pub struct StrategicDatabase {
312 motifs: HashMap<u64, StrategicMotif>,
314 pawn_matcher: PawnPatternMatcher,
316 piece_matcher: PiecePatternMatcher,
317 king_matcher: KingPatternMatcher,
318 evaluation_cache: lru::LruCache<u64, f32>,
320 total_motifs: usize,
322}
323
324impl StrategicDatabase {
325 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 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 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 pub fn add_motif(&mut self, motif: StrategicMotif) {
375 self.motifs.insert(motif.pattern_hash, motif.clone());
377
378 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 _ => {} }
385
386 self.total_motifs += 1;
387 }
388
389 pub fn find_motifs(&mut self, board: &Board) -> Vec<MotifMatch> {
391 let board_hash = board.get_hash();
392
393 if let Some(&_cached_eval) = self.evaluation_cache.get(&board_hash) {
395 return vec![];
397 }
398
399 let mut matches = Vec::new();
400
401 matches.extend(self.pawn_matcher.find_matches(board));
403
404 matches.extend(self.piece_matcher.find_matches(board));
406
407 matches.extend(self.king_matcher.find_matches(board));
409
410 let total_eval = self.evaluate_motifs(&matches);
412 self.evaluation_cache.put(board_hash, total_eval);
413
414 matches
415 }
416
417 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 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 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, }
453 }
454}
455
456#[derive(Debug)]
458pub struct StrategicDatabaseStats {
459 pub total_motifs: usize,
460 pub cache_size: usize,
461 pub cache_hit_rate: f32,
462}
463
464pub 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 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 None
497 }
498}
499
500pub 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 Vec::new()
519 }
520}
521
522pub 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 Vec::new()
541 }
542}
543
544pub mod pattern_utils {
546 use super::*;
547
548 pub fn generate_pattern_hash(board: &Board) -> u64 {
550 let mut hash = 0u64;
554
555 hash ^= hash_pawn_structure(board);
557
558 hash ^= hash_piece_patterns(board);
560
561 hash ^= hash_king_positions(board);
563
564 hash
565 }
566
567 fn hash_pawn_structure(_board: &Board) -> u64 {
568 0
570 }
571
572 fn hash_piece_patterns(_board: &Board) -> u64 {
573 0
575 }
576
577 fn hash_king_positions(_board: &Board) -> u64 {
578 0
580 }
581
582 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 let mut total = 0u32;
598
599 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}