1use crate::errors::Result;
2use crate::hybrid_evaluation::{EvaluationComponent, PatternEvaluator};
3use crate::utils::cache::PatternCache;
4use chess::{Board, Color, Piece};
5use ndarray::{s, Array1};
6use std::collections::HashMap;
7use std::sync::{Arc, RwLock};
8
9pub struct AdvancedPatternRecognizer {
11 pawn_structure_recognizer: PawnStructureRecognizer,
13 piece_coordination_recognizer: PieceCoordinationRecognizer,
14 king_safety_recognizer: KingSafetyRecognizer,
15 tactical_pattern_recognizer: TacticalPatternRecognizer,
16 endgame_pattern_recognizer: EndgamePatternRecognizer,
17
18 pattern_weights: PatternWeights,
20
21 pattern_cache: Arc<PatternCache<String, PatternAnalysisResult>>,
23
24 learned_patterns: Arc<RwLock<LearnedPatternDatabase>>,
26}
27
28impl AdvancedPatternRecognizer {
29 pub fn new() -> Self {
31 Self {
32 pawn_structure_recognizer: PawnStructureRecognizer::new(),
33 piece_coordination_recognizer: PieceCoordinationRecognizer::new(),
34 king_safety_recognizer: KingSafetyRecognizer::new(),
35 tactical_pattern_recognizer: TacticalPatternRecognizer::new(),
36 endgame_pattern_recognizer: EndgamePatternRecognizer::new(),
37 pattern_weights: PatternWeights::default(),
38 pattern_cache: Arc::new(PatternCache::new(10000)),
39 learned_patterns: Arc::new(RwLock::new(LearnedPatternDatabase::new())),
40 }
41 }
42
43 pub fn analyze_patterns(&self, board: &Board) -> Result<PatternAnalysisResult> {
45 let fen = board.to_string();
46
47 if let Some(cached_result) = self.pattern_cache.get(&fen) {
49 return Ok(cached_result);
50 }
51
52 let mut result = PatternAnalysisResult::new();
53
54 result.pawn_structure = self.pawn_structure_recognizer.analyze(board)?;
56
57 result.piece_coordination = self.piece_coordination_recognizer.analyze(board)?;
59
60 result.king_safety = self.king_safety_recognizer.analyze(board)?;
62
63 result.tactical_patterns = self.tactical_pattern_recognizer.analyze(board)?;
65
66 result.endgame_patterns = self.endgame_pattern_recognizer.analyze(board)?;
68
69 result.overall_score = self.compute_overall_score(&result);
71
72 result.learned_patterns = self.check_learned_patterns(board)?;
74
75 self.pattern_cache.insert(fen, result.clone());
77
78 Ok(result)
79 }
80
81 pub fn encode_patterns(&self, board: &Board) -> Result<Array1<f32>> {
83 let analysis = self.analyze_patterns(board)?;
84
85 let mut pattern_vector = Array1::zeros(256);
87
88 self.encode_pawn_structure(
90 &analysis.pawn_structure,
91 pattern_vector.slice_mut(s![0..64]),
92 );
93
94 self.encode_piece_coordination(
96 &analysis.piece_coordination,
97 pattern_vector.slice_mut(s![64..128]),
98 );
99
100 self.encode_king_safety(
102 &analysis.king_safety,
103 pattern_vector.slice_mut(s![128..160]),
104 );
105
106 self.encode_tactical_patterns(
108 &analysis.tactical_patterns,
109 pattern_vector.slice_mut(s![160..192]),
110 );
111
112 self.encode_endgame_patterns(
114 &analysis.endgame_patterns,
115 pattern_vector.slice_mut(s![192..224]),
116 );
117
118 self.encode_learned_patterns(
120 &analysis.learned_patterns,
121 pattern_vector.slice_mut(s![224..256]),
122 );
123
124 Ok(pattern_vector)
125 }
126
127 fn compute_overall_score(&self, analysis: &PatternAnalysisResult) -> f32 {
128 let mut score = 0.0;
129
130 score += analysis.pawn_structure.score * self.pattern_weights.pawn_structure;
131 score += analysis.piece_coordination.score * self.pattern_weights.piece_coordination;
132 score += analysis.king_safety.score * self.pattern_weights.king_safety;
133 score += analysis.tactical_patterns.score * self.pattern_weights.tactical_patterns;
134 score += analysis.endgame_patterns.score * self.pattern_weights.endgame_patterns;
135
136 for pattern_match in &analysis.learned_patterns {
138 score += pattern_match.strength * pattern_match.confidence * 0.1;
139 }
140
141 score
142 }
143
144 fn check_learned_patterns(&self, board: &Board) -> Result<Vec<LearnedPatternMatch>> {
145 if let Ok(database) = self.learned_patterns.read() {
146 database.find_matching_patterns(board)
147 } else {
148 Ok(Vec::new())
149 }
150 }
151
152 fn encode_pawn_structure(
153 &self,
154 pawn_analysis: &PawnStructureAnalysis,
155 mut slice: ndarray::ArrayViewMut1<f32>,
156 ) {
157 slice[0] = pawn_analysis.isolated_pawns as f32 / 8.0;
158 slice[1] = pawn_analysis.doubled_pawns as f32 / 8.0;
159 slice[2] = pawn_analysis.passed_pawns as f32 / 8.0;
160 slice[3] = pawn_analysis.backward_pawns as f32 / 8.0;
161 slice[4] = pawn_analysis.pawn_islands as f32 / 8.0;
162 slice[5] = pawn_analysis.pawn_chains as f32 / 8.0;
163 slice[6] = pawn_analysis.pawn_storm_potential;
164 slice[7] = pawn_analysis.pawn_shield_quality;
165
166 for i in 8..64 {
168 slice[i] = 0.0; }
170 }
171
172 fn encode_piece_coordination(
173 &self,
174 coord_analysis: &PieceCoordinationAnalysis,
175 mut slice: ndarray::ArrayViewMut1<f32>,
176 ) {
177 slice[0] = coord_analysis.piece_activity;
178 slice[1] = coord_analysis.piece_harmony;
179 slice[2] = coord_analysis.central_control;
180 slice[3] = coord_analysis.outpost_strength;
181 slice[4] = coord_analysis.piece_mobility;
182 slice[5] = coord_analysis.piece_safety;
183
184 for i in 6..64 {
186 slice[i] = 0.0; }
188 }
189
190 fn encode_king_safety(
191 &self,
192 safety_analysis: &KingSafetyAnalysis,
193 mut slice: ndarray::ArrayViewMut1<f32>,
194 ) {
195 slice[0] = safety_analysis.king_exposure;
196 slice[1] = safety_analysis.attacking_pieces;
197 slice[2] = safety_analysis.escape_squares;
198 slice[3] = safety_analysis.pawn_shield;
199 slice[4] = safety_analysis.king_tropism;
200
201 for i in 5..32 {
203 slice[i] = 0.0; }
205 }
206
207 fn encode_tactical_patterns(
208 &self,
209 tactical_analysis: &TacticalPatternAnalysis,
210 mut slice: ndarray::ArrayViewMut1<f32>,
211 ) {
212 slice[0] = if tactical_analysis.pins > 0 { 1.0 } else { 0.0 };
213 slice[1] = if tactical_analysis.forks > 0 {
214 1.0
215 } else {
216 0.0
217 };
218 slice[2] = if tactical_analysis.skewers > 0 {
219 1.0
220 } else {
221 0.0
222 };
223 slice[3] = if tactical_analysis.discovered_attacks > 0 {
224 1.0
225 } else {
226 0.0
227 };
228 slice[4] = tactical_analysis.hanging_pieces as f32 / 16.0;
229 slice[5] = tactical_analysis.undefended_pieces as f32 / 16.0;
230
231 for i in 6..32 {
233 slice[i] = 0.0; }
235 }
236
237 fn encode_endgame_patterns(
238 &self,
239 endgame_analysis: &EndgamePatternAnalysis,
240 mut slice: ndarray::ArrayViewMut1<f32>,
241 ) {
242 slice[0] = if endgame_analysis.opposition {
243 1.0
244 } else {
245 0.0
246 };
247 slice[1] = if endgame_analysis.zugzwang_potential {
248 1.0
249 } else {
250 0.0
251 };
252 slice[2] = endgame_analysis.king_activity;
253 slice[3] = endgame_analysis.pawn_majority_value;
254 slice[4] = endgame_analysis.piece_vs_pawns_evaluation;
255
256 for i in 5..32 {
258 slice[i] = 0.0; }
260 }
261
262 fn encode_learned_patterns(
263 &self,
264 learned_matches: &[LearnedPatternMatch],
265 mut slice: ndarray::ArrayViewMut1<f32>,
266 ) {
267 for (i, pattern_match) in learned_matches.iter().take(16).enumerate() {
269 slice[i * 2] = pattern_match.strength;
270 slice[i * 2 + 1] = pattern_match.confidence;
271 }
272
273 for i in (learned_matches.len().min(16) * 2)..32 {
275 slice[i] = 0.0;
276 }
277 }
278
279 pub fn train_patterns(&self, training_data: &[(Board, f32)]) -> Result<()> {
281 if let Ok(mut database) = self.learned_patterns.write() {
282 database.learn_from_data(training_data)?;
283 }
284 Ok(())
285 }
286
287 pub fn get_statistics(&self) -> PatternRecognitionStats {
289 let cache_stats = self.pattern_cache.stats();
290 let learned_count = self
291 .learned_patterns
292 .read()
293 .map(|db| db.pattern_count())
294 .unwrap_or(0);
295
296 PatternRecognitionStats {
297 cached_analyses: cache_stats.cache_size,
298 learned_patterns: learned_count,
299 cache_hit_ratio: cache_stats.cache_hit_ratio,
300 }
301 }
302}
303
304impl Default for AdvancedPatternRecognizer {
305 fn default() -> Self {
306 Self::new()
307 }
308}
309
310impl PatternEvaluator for AdvancedPatternRecognizer {
311 fn evaluate_position(&self, board: &Board) -> Result<EvaluationComponent> {
312 let start_time = std::time::Instant::now();
313 let analysis = self.analyze_patterns(board)?;
314 let computation_time = start_time.elapsed().as_millis() as u64;
315
316 let mut additional_info = HashMap::new();
317 additional_info.insert(
318 "pawn_structure_score".to_string(),
319 analysis.pawn_structure.score,
320 );
321 additional_info.insert(
322 "piece_coordination_score".to_string(),
323 analysis.piece_coordination.score,
324 );
325 additional_info.insert("king_safety_score".to_string(), analysis.king_safety.score);
326 additional_info.insert(
327 "tactical_score".to_string(),
328 analysis.tactical_patterns.score,
329 );
330 additional_info.insert("endgame_score".to_string(), analysis.endgame_patterns.score);
331 additional_info.insert(
332 "learned_patterns_count".to_string(),
333 analysis.learned_patterns.len() as f32,
334 );
335
336 let confidence = self.compute_pattern_confidence(&analysis);
338
339 Ok(EvaluationComponent {
340 evaluation: analysis.overall_score,
341 confidence,
342 computation_time_ms: computation_time,
343 additional_info,
344 })
345 }
346}
347
348impl AdvancedPatternRecognizer {
349 fn compute_pattern_confidence(&self, analysis: &PatternAnalysisResult) -> f32 {
350 let mut confidence_factors = Vec::new();
351
352 confidence_factors.push(analysis.pawn_structure.score.abs().min(1.0));
354 confidence_factors.push(analysis.piece_coordination.score.abs().min(1.0));
355 confidence_factors.push(analysis.king_safety.score.abs().min(1.0));
356
357 if analysis.tactical_patterns.pins > 0 || analysis.tactical_patterns.forks > 0 {
359 confidence_factors.push(0.8);
360 }
361
362 let learned_confidence = analysis
364 .learned_patterns
365 .iter()
366 .map(|p| p.confidence)
367 .fold(0.0, f32::max);
368 confidence_factors.push(learned_confidence);
369
370 if confidence_factors.is_empty() {
372 0.5
373 } else {
374 confidence_factors.iter().sum::<f32>() / confidence_factors.len() as f32
375 }
376 }
377}
378
379#[derive(Debug, Clone)]
381pub struct PatternAnalysisResult {
382 pub pawn_structure: PawnStructureAnalysis,
383 pub piece_coordination: PieceCoordinationAnalysis,
384 pub king_safety: KingSafetyAnalysis,
385 pub tactical_patterns: TacticalPatternAnalysis,
386 pub endgame_patterns: EndgamePatternAnalysis,
387 pub learned_patterns: Vec<LearnedPatternMatch>,
388 pub overall_score: f32,
389}
390
391impl PatternAnalysisResult {
392 fn new() -> Self {
393 Self {
394 pawn_structure: PawnStructureAnalysis::default(),
395 piece_coordination: PieceCoordinationAnalysis::default(),
396 king_safety: KingSafetyAnalysis::default(),
397 tactical_patterns: TacticalPatternAnalysis::default(),
398 endgame_patterns: EndgamePatternAnalysis::default(),
399 learned_patterns: Vec::new(),
400 overall_score: 0.0,
401 }
402 }
403}
404
405pub struct PawnStructureRecognizer;
407
408impl PawnStructureRecognizer {
409 pub fn new() -> Self {
410 Self
411 }
412
413 pub fn analyze(&self, board: &Board) -> Result<PawnStructureAnalysis> {
414 let mut analysis = PawnStructureAnalysis::default();
415
416 let white_analysis = self.analyze_color(board, Color::White);
418 let black_analysis = self.analyze_color(board, Color::Black);
419
420 analysis.isolated_pawns =
422 (black_analysis.isolated_pawns - white_analysis.isolated_pawns) as i8;
423 analysis.doubled_pawns =
424 (black_analysis.doubled_pawns - white_analysis.doubled_pawns) as i8;
425 analysis.passed_pawns = (white_analysis.passed_pawns - black_analysis.passed_pawns) as i8;
426 analysis.backward_pawns =
427 (black_analysis.backward_pawns - white_analysis.backward_pawns) as i8;
428 analysis.pawn_islands = (black_analysis.pawn_islands - white_analysis.pawn_islands) as i8;
429 analysis.pawn_chains = (white_analysis.pawn_chains - black_analysis.pawn_chains) as i8;
430
431 analysis.score = self.compute_pawn_score(&analysis);
433
434 analysis.pawn_storm_potential = self.evaluate_pawn_storm(board);
436 analysis.pawn_shield_quality = self.evaluate_pawn_shield(board);
437
438 Ok(analysis)
439 }
440
441 fn analyze_color(&self, board: &Board, color: Color) -> PawnColorAnalysis {
442 let pawns = board.pieces(Piece::Pawn) & board.color_combined(color);
443 let mut analysis = PawnColorAnalysis::default();
444
445 let mut pawns_per_file = [0u8; 8];
447 for square in pawns {
448 pawns_per_file[square.get_file().to_index()] += 1;
449 }
450
451 analysis.doubled_pawns = pawns_per_file.iter().filter(|&&count| count > 1).count();
453
454 for (file_idx, &pawn_count) in pawns_per_file.iter().enumerate() {
456 if pawn_count > 0 {
457 let has_neighbor = (file_idx > 0 && pawns_per_file[file_idx - 1] > 0)
458 || (file_idx < 7 && pawns_per_file[file_idx + 1] > 0);
459 if !has_neighbor {
460 analysis.isolated_pawns += pawn_count as usize;
461 }
462 }
463 }
464
465 let mut in_island = false;
467 for &pawn_count in &pawns_per_file {
468 if pawn_count > 0 {
469 if !in_island {
470 analysis.pawn_islands += 1;
471 in_island = true;
472 }
473 } else {
474 in_island = false;
475 }
476 }
477
478 analysis.passed_pawns = self.count_passed_pawns(board, color);
480
481 analysis.backward_pawns = self.count_backward_pawns(board, color);
483
484 analysis.pawn_chains = self.count_pawn_chains(board, color);
486
487 analysis
488 }
489
490 fn count_passed_pawns(&self, _board: &Board, _color: Color) -> usize {
491 0
493 }
494
495 fn count_backward_pawns(&self, _board: &Board, _color: Color) -> usize {
496 0
498 }
499
500 fn count_pawn_chains(&self, _board: &Board, _color: Color) -> usize {
501 0
503 }
504
505 fn compute_pawn_score(&self, analysis: &PawnStructureAnalysis) -> f32 {
506 let mut score = 0.0;
507
508 score -= analysis.isolated_pawns as f32 * 0.2;
510
511 score -= analysis.doubled_pawns as f32 * 0.15;
513
514 score += analysis.passed_pawns as f32 * 0.5;
516
517 score -= analysis.backward_pawns as f32 * 0.1;
519
520 score -= analysis.pawn_islands as f32 * 0.1;
522
523 score += analysis.pawn_chains as f32 * 0.15;
525
526 score
527 }
528
529 fn evaluate_pawn_storm(&self, _board: &Board) -> f32 {
530 0.0
532 }
533
534 fn evaluate_pawn_shield(&self, _board: &Board) -> f32 {
535 0.0
537 }
538}
539
540impl Default for PawnStructureRecognizer {
541 fn default() -> Self {
542 Self::new()
543 }
544}
545
546pub struct PieceCoordinationRecognizer;
548pub struct KingSafetyRecognizer;
549pub struct TacticalPatternRecognizer;
550pub struct EndgamePatternRecognizer;
551
552impl PieceCoordinationRecognizer {
553 pub fn new() -> Self {
554 Self
555 }
556 pub fn analyze(&self, _board: &Board) -> Result<PieceCoordinationAnalysis> {
557 Ok(PieceCoordinationAnalysis::default())
558 }
559}
560
561impl KingSafetyRecognizer {
562 pub fn new() -> Self {
563 Self
564 }
565 pub fn analyze(&self, _board: &Board) -> Result<KingSafetyAnalysis> {
566 Ok(KingSafetyAnalysis::default())
567 }
568}
569
570impl TacticalPatternRecognizer {
571 pub fn new() -> Self {
572 Self
573 }
574 pub fn analyze(&self, _board: &Board) -> Result<TacticalPatternAnalysis> {
575 Ok(TacticalPatternAnalysis::default())
576 }
577}
578
579impl EndgamePatternRecognizer {
580 pub fn new() -> Self {
581 Self
582 }
583 pub fn analyze(&self, _board: &Board) -> Result<EndgamePatternAnalysis> {
584 Ok(EndgamePatternAnalysis::default())
585 }
586}
587
588#[derive(Debug, Clone, Default)]
590pub struct PawnStructureAnalysis {
591 pub isolated_pawns: i8,
592 pub doubled_pawns: i8,
593 pub passed_pawns: i8,
594 pub backward_pawns: i8,
595 pub pawn_islands: i8,
596 pub pawn_chains: i8,
597 pub pawn_storm_potential: f32,
598 pub pawn_shield_quality: f32,
599 pub score: f32,
600}
601
602#[derive(Debug, Clone, Default)]
603struct PawnColorAnalysis {
604 isolated_pawns: usize,
605 doubled_pawns: usize,
606 passed_pawns: usize,
607 backward_pawns: usize,
608 pawn_islands: usize,
609 pawn_chains: usize,
610}
611
612#[derive(Debug, Clone, Default)]
613pub struct PieceCoordinationAnalysis {
614 pub piece_activity: f32,
615 pub piece_harmony: f32,
616 pub central_control: f32,
617 pub outpost_strength: f32,
618 pub piece_mobility: f32,
619 pub piece_safety: f32,
620 pub score: f32,
621}
622
623#[derive(Debug, Clone, Default)]
624pub struct KingSafetyAnalysis {
625 pub king_exposure: f32,
626 pub attacking_pieces: f32,
627 pub escape_squares: f32,
628 pub pawn_shield: f32,
629 pub king_tropism: f32,
630 pub score: f32,
631}
632
633#[derive(Debug, Clone, Default)]
634pub struct TacticalPatternAnalysis {
635 pub pins: u8,
636 pub forks: u8,
637 pub skewers: u8,
638 pub discovered_attacks: u8,
639 pub hanging_pieces: u8,
640 pub undefended_pieces: u8,
641 pub score: f32,
642}
643
644#[derive(Debug, Clone, Default)]
645pub struct EndgamePatternAnalysis {
646 pub opposition: bool,
647 pub zugzwang_potential: bool,
648 pub king_activity: f32,
649 pub pawn_majority_value: f32,
650 pub piece_vs_pawns_evaluation: f32,
651 pub score: f32,
652}
653
654pub struct LearnedPatternDatabase {
656 patterns: Vec<LearnedPattern>,
657 pattern_cache: HashMap<u64, Vec<usize>>, }
659
660impl LearnedPatternDatabase {
661 pub fn new() -> Self {
662 Self {
663 patterns: Vec::new(),
664 pattern_cache: HashMap::new(),
665 }
666 }
667
668 pub fn learn_from_data(&mut self, training_data: &[(Board, f32)]) -> Result<()> {
669 for (board, evaluation) in training_data {
671 if evaluation.abs() > 0.5 {
672 let pattern = LearnedPattern {
674 position_hash: board.get_hash(),
675 evaluation: *evaluation,
676 frequency: 1,
677 strength: evaluation.abs(),
678 };
679 self.patterns.push(pattern);
680 }
681 }
682 Ok(())
683 }
684
685 pub fn find_matching_patterns(&self, board: &Board) -> Result<Vec<LearnedPatternMatch>> {
686 let position_hash = board.get_hash();
687 let mut matches = Vec::new();
688
689 for (pattern_id, pattern) in self.patterns.iter().enumerate() {
691 let mut match_confidence = 0.0;
692
693 if pattern.position_hash == position_hash {
695 match_confidence = 1.0;
696 } else {
697 let hash_diff = (pattern.position_hash ^ position_hash).count_ones();
699 if hash_diff <= 3 { match_confidence = 1.0 - (hash_diff as f32 / 64.0);
701 }
702 }
703
704 if match_confidence > 0.1 { matches.push(LearnedPatternMatch {
706 pattern_id: pattern_id as u32,
707 strength: pattern.strength * match_confidence, confidence: (pattern.frequency as f32 / 100.0).min(1.0) * match_confidence,
709 });
710 }
711 }
712
713 Ok(matches)
714 }
715
716 pub fn pattern_count(&self) -> usize {
717 self.patterns.len()
718 }
719}
720
721#[derive(Debug, Clone)]
722pub struct LearnedPattern {
723 pub position_hash: u64,
724 pub evaluation: f32,
725 pub frequency: u32,
726 pub strength: f32,
727}
728
729#[derive(Debug, Clone)]
730pub struct LearnedPatternMatch {
731 pub pattern_id: u32,
732 pub strength: f32,
733 pub confidence: f32,
734}
735
736#[derive(Debug, Clone)]
738pub struct PatternWeights {
739 pub pawn_structure: f32,
740 pub piece_coordination: f32,
741 pub king_safety: f32,
742 pub tactical_patterns: f32,
743 pub endgame_patterns: f32,
744}
745
746impl Default for PatternWeights {
747 fn default() -> Self {
748 Self {
749 pawn_structure: 0.25,
750 piece_coordination: 0.25,
751 king_safety: 0.20,
752 tactical_patterns: 0.20,
753 endgame_patterns: 0.10,
754 }
755 }
756}
757
758#[derive(Debug, Clone)]
760pub struct PatternRecognitionStats {
761 pub cached_analyses: usize,
762 pub learned_patterns: usize,
763 pub cache_hit_ratio: f64,
764}
765
766#[cfg(test)]
767mod tests {
768 use super::*;
769 use chess::Board;
770 use std::str::FromStr;
771
772 #[test]
773 fn test_pattern_recognizer() {
774 let recognizer = AdvancedPatternRecognizer::new();
775 let board =
776 Board::from_str("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1").unwrap();
777
778 let analysis = recognizer.analyze_patterns(&board).unwrap();
779 assert!(analysis.overall_score.is_finite());
780 }
781
782 #[test]
783 fn test_pattern_encoding() {
784 let recognizer = AdvancedPatternRecognizer::new();
785 let board =
786 Board::from_str("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1").unwrap();
787
788 let pattern_vector = recognizer.encode_patterns(&board).unwrap();
789 assert_eq!(pattern_vector.len(), 256);
790 assert!(pattern_vector.iter().all(|&x| x.is_finite()));
791 }
792
793 #[test]
794 fn test_pawn_structure_recognizer() {
795 let recognizer = PawnStructureRecognizer::new();
796 let board =
797 Board::from_str("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1").unwrap();
798
799 let analysis = recognizer.analyze(&board).unwrap();
800 assert!(analysis.score.is_finite());
801 }
802
803 #[test]
804 fn test_pattern_evaluator_trait() {
805 let recognizer = AdvancedPatternRecognizer::new();
806 let board =
807 Board::from_str("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1").unwrap();
808
809 let evaluation = recognizer.evaluate_position(&board).unwrap();
810 assert!(evaluation.evaluation.is_finite());
811 assert!(evaluation.confidence >= 0.0 && evaluation.confidence <= 1.0);
812 assert!(evaluation.computation_time_ms > 0);
813 assert!(!evaluation.additional_info.is_empty());
814 }
815}