use crate::strategic_evaluator::{StrategicEvaluator, StrategicConfig, StrategicEvaluation};
use crate::utils::lazy_motifs::{LazyStrategicDatabase, LazyLoadConfig, LazyLoadStats};
use crate::strategic_motifs::MotifMatch;
use crate::strategic_motifs::{GamePhase, MotifType};
use chess::{Board, ChessMove};
use std::path::Path;
use std::sync::Arc;
pub struct LazyStrategicEvaluator {
core_evaluator: StrategicEvaluator,
motif_database: Arc<LazyStrategicDatabase>,
lazy_config: LazyLoadConfig,
use_motifs: bool,
}
#[derive(Debug, Clone)]
pub struct EnhancedStrategicEvaluation {
pub core_evaluation: StrategicEvaluation,
pub motif_matches: Vec<MotifMatch>,
pub motif_adjustment: f32,
pub motif_confidence: f32,
pub detected_phase: GamePhase,
}
impl LazyStrategicEvaluator {
pub fn new<P: AsRef<Path>>(
config: StrategicConfig,
motif_database_path: P,
lazy_config: LazyLoadConfig,
) -> Result<Self, Box<dyn std::error::Error>> {
let core_evaluator = StrategicEvaluator::new(config);
let motif_database = match LazyStrategicDatabase::new(motif_database_path, lazy_config.clone()) {
Ok(db) => Arc::new(db),
Err(e) => {
eprintln!("Warning: Failed to load motif database: {}", e);
return Ok(Self {
core_evaluator,
motif_database: Arc::new(Self::create_empty_database()?),
lazy_config,
use_motifs: false,
});
}
};
Ok(Self {
core_evaluator,
motif_database,
lazy_config,
use_motifs: true,
})
}
fn create_empty_database() -> Result<LazyStrategicDatabase, Box<dyn std::error::Error>> {
use std::env;
use crate::utils::lazy_motifs::MotifSegmentBuilder;
let temp_dir_path = env::temp_dir().join("chess_empty_motifs");
std::fs::create_dir_all(&temp_dir_path)?;
let config = LazyLoadConfig::default();
let builder = MotifSegmentBuilder::new(&temp_dir_path, config.clone());
let _index = builder.create_segments(Vec::new(), 100)?;
LazyStrategicDatabase::new(&temp_dir_path, config)
}
pub fn evaluate_position(&self, board: &Board) -> Result<EnhancedStrategicEvaluation, Box<dyn std::error::Error>> {
let core_evaluation = self.core_evaluator.evaluate_strategic(board);
if !self.use_motifs {
return Ok(EnhancedStrategicEvaluation {
core_evaluation,
motif_matches: Vec::new(),
motif_adjustment: 0.0,
motif_confidence: 0.0,
detected_phase: GamePhase::Any,
});
}
let detected_phase = self.detect_game_phase(board);
let motif_matches = self.motif_database.evaluate_position(board)?;
let (motif_adjustment, motif_confidence) = self.calculate_motif_adjustments(&motif_matches);
Ok(EnhancedStrategicEvaluation {
core_evaluation,
motif_matches,
motif_adjustment,
motif_confidence,
detected_phase,
})
}
pub fn evaluate_move(&self, board: &Board, chess_move: &ChessMove) -> Result<f32, Box<dyn std::error::Error>> {
let mut new_board = *board;
new_board = new_board.make_move_new(*chess_move);
let evaluation = self.evaluate_position(&new_board)?;
let total_adjustment = evaluation.core_evaluation.total_evaluation + evaluation.motif_adjustment;
Ok(total_adjustment)
}
pub fn preload_phase_motifs(&self, phase: GamePhase) -> Result<usize, Box<dyn std::error::Error>> {
if !self.use_motifs {
return Ok(0);
}
self.motif_database.preload_phase(phase)
}
pub fn find_motifs_by_type(&self, _motif_type: &MotifType) -> Result<Vec<MotifMatch>, Box<dyn std::error::Error>> {
if !self.use_motifs {
return Ok(Vec::new());
}
Ok(Vec::new())
}
pub fn analyze_strategic_themes(&self, board: &Board) -> Result<StrategicThemeAnalysis, Box<dyn std::error::Error>> {
let evaluation = self.evaluate_position(board)?;
let mut themes = StrategicThemeAnalysis::new();
for motif_match in &evaluation.motif_matches {
match &motif_match.motif.motif_type {
MotifType::PawnStructure(_) => {
themes.pawn_themes.push(motif_match.clone());
}
MotifType::PieceCoordination(_) => {
themes.piece_coordination_themes.push(motif_match.clone());
}
MotifType::KingSafety(_) => {
themes.king_safety_themes.push(motif_match.clone());
}
MotifType::Initiative(_) => {
themes.initiative_themes.push(motif_match.clone());
}
MotifType::Endgame(_) => {
themes.endgame_themes.push(motif_match.clone());
}
MotifType::Opening(_) => {
themes.opening_themes.push(motif_match.clone());
}
}
}
Ok(themes)
}
pub fn get_lazy_load_stats(&self) -> LazyLoadStats {
self.motif_database.get_stats()
}
pub fn clear_motif_caches(&self) {
self.motif_database.clear_caches();
}
pub fn total_available_motifs(&self) -> usize {
self.motif_database.total_motifs()
}
pub fn cached_motifs_count(&self) -> usize {
self.motif_database.cached_motifs()
}
fn detect_game_phase(&self, board: &Board) -> GamePhase {
let piece_count = board.combined().popcnt();
if piece_count > 28 {
GamePhase::Opening
} else if piece_count > 12 {
GamePhase::Middlegame
} else {
GamePhase::Endgame
}
}
fn calculate_motif_adjustments(&self, motif_matches: &[MotifMatch]) -> (f32, f32) {
if motif_matches.is_empty() {
return (0.0, 0.0);
}
let mut total_adjustment = 0.0;
let mut total_confidence = 0.0;
let mut weight_sum = 0.0;
for motif_match in motif_matches {
let weight = motif_match.relevance * motif_match.motif.confidence;
total_adjustment += motif_match.motif.evaluation * weight;
total_confidence += motif_match.motif.confidence * weight;
weight_sum += weight;
}
if weight_sum > 0.0 {
total_adjustment /= weight_sum;
total_confidence /= weight_sum;
}
total_adjustment = total_adjustment.clamp(-1.0, 1.0);
total_confidence = total_confidence.clamp(0.0, 1.0);
(total_adjustment, total_confidence)
}
}
#[derive(Debug, Clone, Default)]
pub struct StrategicThemeAnalysis {
pub pawn_themes: Vec<MotifMatch>,
pub piece_coordination_themes: Vec<MotifMatch>,
pub king_safety_themes: Vec<MotifMatch>,
pub initiative_themes: Vec<MotifMatch>,
pub endgame_themes: Vec<MotifMatch>,
pub opening_themes: Vec<MotifMatch>,
}
impl StrategicThemeAnalysis {
pub fn new() -> Self {
Self::default()
}
pub fn primary_theme(&self) -> Option<&MotifMatch> {
let all_themes: Vec<&MotifMatch> = [
&self.pawn_themes,
&self.piece_coordination_themes,
&self.king_safety_themes,
&self.initiative_themes,
&self.endgame_themes,
&self.opening_themes,
]
.iter()
.flat_map(|themes| themes.iter())
.collect();
all_themes.into_iter().max_by(|a, b| {
a.relevance.partial_cmp(&b.relevance).unwrap_or(std::cmp::Ordering::Equal)
})
}
pub fn theme_count(&self) -> usize {
self.pawn_themes.len()
+ self.piece_coordination_themes.len()
+ self.king_safety_themes.len()
+ self.initiative_themes.len()
+ self.endgame_themes.len()
+ self.opening_themes.len()
}
pub fn get_recommendations(&self) -> Vec<String> {
let mut recommendations = Vec::new();
if let Some(primary) = self.primary_theme() {
recommendations.push(primary.motif.description.clone());
}
if !self.pawn_themes.is_empty() {
recommendations.push("Consider pawn structure improvements".to_string());
}
if !self.king_safety_themes.is_empty() {
recommendations.push("Focus on king safety measures".to_string());
}
if !self.initiative_themes.is_empty() {
recommendations.push("Maintain initiative and create pressure".to_string());
}
recommendations
}
}
#[cfg(test)]
mod tests {
use super::*;
use chess::Board;
use std::str::FromStr;
use std::env;
#[test]
fn test_lazy_strategic_evaluator_creation() {
let temp_dir_path = env::temp_dir().join("chess_test_lazy");
std::fs::create_dir_all(&temp_dir_path).unwrap();
let config = StrategicConfig::default();
let lazy_config = LazyLoadConfig::default();
let evaluator = LazyStrategicEvaluator::new(config, &temp_dir_path, lazy_config);
assert!(evaluator.is_ok());
}
#[test]
fn test_game_phase_detection() {
let temp_dir_path = env::temp_dir().join("chess_test_phase");
std::fs::create_dir_all(&temp_dir_path).unwrap();
let config = StrategicConfig::default();
let lazy_config = LazyLoadConfig::default();
let evaluator = LazyStrategicEvaluator::new(config, &temp_dir_path, lazy_config).unwrap();
let board = Board::default();
let phase = evaluator.detect_game_phase(&board);
matches!(phase, GamePhase::Opening);
let endgame_fen = "8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - - 0 1";
let endgame_board = Board::from_str(endgame_fen).unwrap();
let endgame_phase = evaluator.detect_game_phase(&endgame_board);
matches!(endgame_phase, GamePhase::Endgame);
}
#[test]
fn test_strategic_theme_analysis() {
let analysis = StrategicThemeAnalysis::new();
assert_eq!(analysis.theme_count(), 0);
let recommendations = analysis.get_recommendations();
assert!(recommendations.is_empty());
}
}