chess_vector_engine/
opening_book.rs

1use chess::{Board, ChessMove};
2use std::collections::HashMap;
3use std::str::FromStr;
4
5/// Opening book entry containing position evaluation and recommended moves
6#[derive(Debug, Clone)]
7pub struct OpeningEntry {
8    pub evaluation: f32,
9    pub best_moves: Vec<(ChessMove, f32)>, // (move, relative_strength)
10    pub name: String,
11    pub eco_code: Option<String>, // ECO (Encyclopedia of Chess Openings) code
12}
13
14/// Opening book for chess positions
15#[derive(Clone)]
16pub struct OpeningBook {
17    /// Map from FEN string to opening entry
18    entries: HashMap<String, OpeningEntry>,
19}
20
21impl Default for OpeningBook {
22    fn default() -> Self {
23        Self::new()
24    }
25}
26
27impl OpeningBook {
28    /// Create a new opening book
29    pub fn new() -> Self {
30        Self {
31            entries: HashMap::new(),
32        }
33    }
34
35    /// Create a basic opening book with common openings
36    pub fn with_standard_openings() -> Self {
37        let mut book = Self::new();
38        book.add_standard_openings();
39        book
40    }
41
42    /// Add an opening entry
43    pub fn add_opening(
44        &mut self,
45        fen: &str,
46        evaluation: f32,
47        best_moves: Vec<(ChessMove, f32)>,
48        name: String,
49        eco_code: Option<String>,
50    ) -> Result<(), String> {
51        // Validate FEN by parsing it
52        Board::from_str(fen).map_err(|_e| "Processing...".to_string())?;
53
54        let entry = OpeningEntry {
55            evaluation,
56            best_moves,
57            name,
58            eco_code,
59        };
60
61        self.entries.insert(fen.to_string(), entry);
62        Ok(())
63    }
64
65    /// Look up position in opening book
66    pub fn lookup(&self, board: &Board) -> Option<&OpeningEntry> {
67        let fen = board.to_string();
68        self.entries.get(&fen)
69    }
70
71    /// Check if position is in opening book
72    pub fn contains(&self, board: &Board) -> bool {
73        let fen = board.to_string();
74        self.entries.contains_key(&fen)
75    }
76
77    /// Get all known openings
78    pub fn get_all_openings(&self) -> &HashMap<String, OpeningEntry> {
79        &self.entries
80    }
81
82    /// Get a random opening move from the starting position
83    pub fn get_random_opening(&self) -> Option<ChessMove> {
84        use rand::seq::SliceRandom;
85        let board = Board::default();
86        if let Some(entry) = self.lookup(&board) {
87            let moves: Vec<ChessMove> = entry.best_moves.iter().map(|(mv, _)| *mv).collect();
88            moves.choose(&mut rand::thread_rng()).copied()
89        } else {
90            None
91        }
92    }
93
94    /// Add standard chess openings
95    fn add_standard_openings(&mut self) {
96        // Starting position
97        if let Ok(board) =
98            Board::from_str("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1")
99        {
100            let moves = vec![
101                (ChessMove::from_str("e2e4").unwrap(), 1.0), // King's Pawn
102                (ChessMove::from_str("d2d4").unwrap(), 0.9), // Queen's Pawn
103                (ChessMove::from_str("g1f3").unwrap(), 0.8), // King's Knight
104                (ChessMove::from_str("c2c4").unwrap(), 0.7), // English Opening
105            ];
106            let _ = self.add_opening(
107                &board.to_string(),
108                0.0,
109                moves,
110                "Starting Position".to_string(),
111                None,
112            );
113        }
114
115        // King's Pawn Game: 1.e4
116        if let Ok(board) =
117            Board::from_str("rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq - 0 1")
118        {
119            let moves = vec![
120                (ChessMove::from_str("e7e5").unwrap(), 1.0), // King's Pawn response
121                (ChessMove::from_str("c7c5").unwrap(), 0.9), // Sicilian Defense
122                (ChessMove::from_str("e7e6").unwrap(), 0.7), // French Defense
123                (ChessMove::from_str("c7c6").unwrap(), 0.6), // Caro-Kann Defense
124            ];
125            let _ = self.add_opening(
126                &board.to_string(),
127                0.25,
128                moves,
129                "King's Pawn Game".to_string(),
130                Some("B00".to_string()),
131            );
132        }
133
134        // King's Pawn Game: 1.e4 e5
135        if let Ok(board) =
136            Board::from_str("rnbqkbnr/pppp1ppp/8/4p3/4P3/8/PPPP1PPP/RNBQKBNR w KQkq - 0 2")
137        {
138            let moves = vec![
139                (ChessMove::from_str("g1f3").unwrap(), 1.0), // King's Knight Attack
140                (ChessMove::from_str("f2f4").unwrap(), 0.6), // King's Gambit
141                (ChessMove::from_str("b1c3").unwrap(), 0.5), // Vienna Game
142            ];
143            let _ = self.add_opening(
144                &board.to_string(),
145                0.15,
146                moves,
147                "Open Game".to_string(),
148                Some("C20".to_string()),
149            );
150        }
151
152        // Sicilian Defense: 1.e4 c5
153        if let Ok(board) =
154            Board::from_str("rnbqkbnr/pp1ppppp/8/2p5/4P3/8/PPPP1PPP/RNBQKBNR w KQkq - 0 2")
155        {
156            let moves = vec![
157                (ChessMove::from_str("g1f3").unwrap(), 1.0), // Open Sicilian
158                (ChessMove::from_str("b1c3").unwrap(), 0.7), // Closed Sicilian
159                (ChessMove::from_str("f2f4").unwrap(), 0.5), // Grand Prix Attack
160            ];
161            let _ = self.add_opening(
162                &board.to_string(),
163                0.3,
164                moves,
165                "Sicilian Defense".to_string(),
166                Some("B20".to_string()),
167            );
168        }
169
170        // Queen's Pawn Game: 1.d4
171        if let Ok(board) =
172            Board::from_str("rnbqkbnr/pppppppp/8/8/3P4/8/PPP1PPPP/RNBQKBNR b KQkq - 0 1")
173        {
174            let moves = vec![
175                (ChessMove::from_str("d7d5").unwrap(), 1.0), // Queen's Gambit
176                (ChessMove::from_str("g8f6").unwrap(), 0.9), // Indian Defenses
177                (ChessMove::from_str("f7f5").unwrap(), 0.4), // Dutch Defense
178            ];
179            let _ = self.add_opening(
180                &board.to_string(),
181                0.2,
182                moves,
183                "Queen's Pawn Game".to_string(),
184                Some("D00".to_string()),
185            );
186        }
187
188        // Queen's Gambit: 1.d4 d5 2.c4
189        if let Ok(board) =
190            Board::from_str("rnbqkbnr/ppp1pppp/8/3p4/2PP4/8/PP2PPPP/RNBQKBNR b KQkq - 0 2")
191        {
192            let moves = vec![
193                (ChessMove::from_str("d5c4").unwrap(), 0.7), // Queen's Gambit Accepted
194                (ChessMove::from_str("e7e6").unwrap(), 1.0), // Queen's Gambit Declined
195                (ChessMove::from_str("c7c6").unwrap(), 0.8), // Slav Defense
196                (ChessMove::from_str("g8f6").unwrap(), 0.8), // Various Defenses
197            ];
198            let _ = self.add_opening(
199                &board.to_string(),
200                0.3,
201                moves,
202                "Queen's Gambit".to_string(),
203                Some("D06".to_string()),
204            );
205        }
206
207        // Italian Game: 1.e4 e5 2.Nf3 Nc6 3.Bc4
208        if let Ok(board) =
209            Board::from_str("r1bqkbnr/pppp1ppp/2n5/4p3/2B1P3/5N2/PPPP1PPP/RNBQK2R b KQkq - 3 3")
210        {
211            let moves = vec![
212                (ChessMove::from_str("g8f6").unwrap(), 1.0), // Italian Game main line
213                (ChessMove::from_str("f7f5").unwrap(), 0.6), // Rousseau Gambit
214                (ChessMove::from_str("f8e7").unwrap(), 0.7), // Hungarian Defense
215            ];
216            let _ = self.add_opening(
217                &board.to_string(),
218                0.4,
219                moves,
220                "Italian Game".to_string(),
221                Some("C50".to_string()),
222            );
223        }
224
225        // Ruy Lopez: 1.e4 e5 2.Nf3 Nc6 3.Bb5
226        if let Ok(board) =
227            Board::from_str("r1bqkbnr/pppp1ppp/2n5/1B2p3/4P3/5N2/PPPP1PPP/RNBQK2R b KQkq - 3 3")
228        {
229            let moves = vec![
230                (ChessMove::from_str("a7a6").unwrap(), 1.0), // Morphy Defense
231                (ChessMove::from_str("g8f6").unwrap(), 0.8), // Berlin Defense
232                (ChessMove::from_str("f7f5").unwrap(), 0.4), // Schliemann Defense
233            ];
234            let _ = self.add_opening(
235                &board.to_string(),
236                0.5,
237                moves,
238                "Ruy Lopez".to_string(),
239                Some("C60".to_string()),
240            );
241        }
242
243        // Add comprehensive opening variations
244        self.add_deeper_openings();
245        self.add_modern_openings();
246        self.add_indian_defenses();
247        self.add_sicilian_variations();
248        self.add_french_caro_kann();
249        self.add_english_reti_systems();
250        self.add_gambit_systems();
251    }
252
253    /// Add deeper opening lines with more positions
254    fn add_deeper_openings(&mut self) {
255        // Italian Game main line: 1.e4 e5 2.Nf3 Nc6 3.Bc4 Nf6
256        if let Ok(board) =
257            Board::from_str("r1bqkb1r/pppp1ppp/2n2n2/4p3/2B1P3/5N2/PPPP1PPP/RNBQK2R w KQkq - 4 4")
258        {
259            let moves = vec![
260                (ChessMove::from_str("d2d3").unwrap(), 0.9), // Italian Game - Classical
261                (ChessMove::from_str("b1c3").unwrap(), 0.8), // Four Knights
262                (ChessMove::from_str("e1g1").unwrap(), 0.7), // Castle early
263            ];
264            let _ = self.add_opening(
265                &board.to_string(),
266                0.3,
267                moves,
268                "Italian Game - Main Line".to_string(),
269                Some("C53".to_string()),
270            );
271        }
272
273        // French Defense: 1.e4 e6 2.d4
274        if let Ok(board) =
275            Board::from_str("rnbqkbnr/pppp1ppp/4p3/8/3PP3/8/PPP2PPP/RNBQKBNR b KQkq - 0 2")
276        {
277            let moves = vec![
278                (ChessMove::from_str("d7d5").unwrap(), 1.0), // French main line
279                (ChessMove::from_str("c7c5").unwrap(), 0.6), // French Sicilian
280            ];
281            let _ = self.add_opening(
282                &board.to_string(),
283                -0.1,
284                moves,
285                "French Defense".to_string(),
286                Some("C00".to_string()),
287            );
288        }
289
290        // Caro-Kann: 1.e4 c6 2.d4
291        if let Ok(board) =
292            Board::from_str("rnbqkbnr/pp1ppppp/2p5/8/3PP3/8/PPP2PPP/RNBQKBNR b KQkq - 0 2")
293        {
294            let moves = vec![
295                (ChessMove::from_str("d7d5").unwrap(), 1.0), // Caro-Kann main line
296            ];
297            let _ = self.add_opening(
298                &board.to_string(),
299                0.0,
300                moves,
301                "Caro-Kann Defense".to_string(),
302                Some("B10".to_string()),
303            );
304        }
305
306        // English Opening: 1.c4 e5
307        if let Ok(board) =
308            Board::from_str("rnbqkbnr/pppp1ppp/8/4p3/2P5/8/PP1PPPPP/RNBQKBNR w KQkq - 0 2")
309        {
310            let moves = vec![
311                (ChessMove::from_str("b1c3").unwrap(), 1.0), // Closed English
312                (ChessMove::from_str("g1f3").unwrap(), 0.8), // King's Knight variation
313                (ChessMove::from_str("g2g3").unwrap(), 0.7), // Fianchetto
314            ];
315            let _ = self.add_opening(
316                &board.to_string(),
317                0.1,
318                moves,
319                "English Opening - King's English".to_string(),
320                Some("A20".to_string()),
321            );
322        }
323
324        // King's Indian Defense: 1.d4 Nf6 2.c4 g6
325        if let Ok(board) =
326            Board::from_str("rnbqkb1r/pppppp1p/5np1/8/2PP4/8/PP2PPPP/RNBQKBNR w KQkq - 0 3")
327        {
328            let moves = vec![
329                (ChessMove::from_str("b1c3").unwrap(), 1.0), // Classical setup
330                (ChessMove::from_str("g1f3").unwrap(), 0.9), // King's Knight
331                (ChessMove::from_str("f2f3").unwrap(), 0.6), // Four Pawns Attack
332            ];
333            let _ = self.add_opening(
334                &board.to_string(),
335                0.2,
336                moves,
337                "King's Indian Defense Setup".to_string(),
338                Some("E60".to_string()),
339            );
340        }
341
342        // Nimzo-Indian: 1.d4 Nf6 2.c4 e6 3.Nc3 Bb4
343        if let Ok(board) =
344            Board::from_str("rnbqk2r/pppp1ppp/4pn2/8/1bPP4/2N5/PP2PPPP/R1BQKBNR w KQkq - 2 4")
345        {
346            let moves = vec![
347                (ChessMove::from_str("d1c2").unwrap(), 0.9), // Classical Nimzo
348                (ChessMove::from_str("e2e3").unwrap(), 1.0), // Rubinstein System
349                (ChessMove::from_str("a2a3").unwrap(), 0.7), // Saemisch Variation
350            ];
351            let _ = self.add_opening(
352                &board.to_string(),
353                0.1,
354                moves,
355                "Nimzo-Indian Defense".to_string(),
356                Some("E20".to_string()),
357            );
358        }
359
360        // Add comprehensive opening database
361        self.add_comprehensive_openings();
362    }
363
364    /// Add modern openings for 2000+ ELO strength
365    fn add_modern_openings(&mut self) {
366        // LONDON SYSTEM
367        if let Ok(board) =
368            Board::from_str("rnbqkb1r/ppp1pppp/5n2/3p4/3P1B2/5N2/PPP1PPPP/RN1QKB1R b KQkq - 3 3")
369        {
370            let moves = vec![
371                (ChessMove::from_str("c7c5").unwrap(), 1.0), // Challenge center
372                (ChessMove::from_str("e7e6").unwrap(), 0.8), // Solid setup
373                (ChessMove::from_str("c8f5").unwrap(), 0.9), // Active bishop
374            ];
375            let _ = self.add_opening(
376                &board.to_string(),
377                0.1,
378                moves,
379                "London System vs ...d5".to_string(),
380                Some("D02".to_string()),
381            );
382        }
383
384        // CATALAN OPENING
385        if let Ok(board) =
386            Board::from_str("rnbqkb1r/pppp1ppp/4pn2/8/2PP4/6P1/PP2PP1P/RNBQKBNR b KQkq - 0 3")
387        {
388            let moves = vec![
389                (ChessMove::from_str("d7d5").unwrap(), 1.0), // Accept challenge
390                (ChessMove::from_str("f8e7").unwrap(), 0.8), // Solid development
391                (ChessMove::from_str("c7c6").unwrap(), 0.7), // Slav-style setup
392            ];
393            let _ = self.add_opening(
394                &board.to_string(),
395                0.2,
396                moves,
397                "Catalan Opening".to_string(),
398                Some("E00".to_string()),
399            );
400        }
401
402        // TROMPOWSKY ATTACK
403        if let Ok(board) =
404            Board::from_str("rnbqkb1r/pppppppp/5n2/8/3P4/8/PPP1PPPP/RNBQKBNR w KQkq - 1 2")
405        {
406            let moves = vec![
407                (ChessMove::from_str("c1g5").unwrap(), 1.0), // Trompowsky
408                (ChessMove::from_str("c2c4").unwrap(), 0.9), // Transpose to systems
409                (ChessMove::from_str("g1f3").unwrap(), 0.8), // Normal development
410            ];
411            let _ = self.add_opening(
412                &board.to_string(),
413                0.2,
414                moves,
415                "Trompowsky Attack".to_string(),
416                Some("A45".to_string()),
417            );
418        }
419
420        // KING'S INDIAN ATTACK
421        if let Ok(board) =
422            Board::from_str("rnbqkb1r/ppp1pppp/5n2/3p4/8/5NP1/PPPPPP1P/RNBQKB1R b KQkq - 0 3")
423        {
424            let moves = vec![
425                (ChessMove::from_str("c8g4").unwrap(), 0.8), // Pin the knight
426                (ChessMove::from_str("e7e6").unwrap(), 1.0), // Solid center
427                (ChessMove::from_str("c7c6").unwrap(), 0.7), // Caro-Kann style
428            ];
429            let _ = self.add_opening(
430                &board.to_string(),
431                0.0,
432                moves,
433                "King's Indian Attack".to_string(),
434                Some("A07".to_string()),
435            );
436        }
437    }
438
439    /// Add Indian Defense systems
440    fn add_indian_defenses(&mut self) {
441        // QUEEN'S INDIAN DEFENSE
442        if let Ok(board) =
443            Board::from_str("rnbqkb1r/p1pppppp/1p3n2/8/2PP4/5N2/PP2PPPP/RNBQKB1R w KQkq - 0 3")
444        {
445            let moves = vec![
446                (ChessMove::from_str("g2g3").unwrap(), 1.0), // Fianchetto setup
447                (ChessMove::from_str("a2a3").unwrap(), 0.8), // Petrosian system
448                (ChessMove::from_str("b1c3").unwrap(), 0.9), // Classical development
449            ];
450            let _ = self.add_opening(
451                &board.to_string(),
452                0.1,
453                moves,
454                "Queen's Indian Defense".to_string(),
455                Some("E12".to_string()),
456            );
457        }
458
459        // BOGO-INDIAN DEFENSE
460        if let Ok(board) =
461            Board::from_str("rnbqk2r/ppppppbp/5n2/8/2PP4/5N2/PP2PPPP/RNBQKB1R w KQkq - 2 4")
462        {
463            let moves = vec![
464                (ChessMove::from_str("c1d2").unwrap(), 1.0), // Exchange bishop
465                (ChessMove::from_str("b1d2").unwrap(), 0.8), // Knight development
466                (ChessMove::from_str("d1c2").unwrap(), 0.7), // Queen development
467            ];
468            let _ = self.add_opening(
469                &board.to_string(),
470                0.0,
471                moves,
472                "Bogo-Indian Defense".to_string(),
473                Some("E11".to_string()),
474            );
475        }
476
477        // GRUNFELD DEFENSE
478        if let Ok(board) =
479            Board::from_str("rnbqkb1r/ppp1pp1p/5np1/3p4/2PP4/2N5/PP2PPPP/R1BQKBNR w KQkq - 0 4")
480        {
481            let moves = vec![
482                (ChessMove::from_str("c4d5").unwrap(), 0.7), // Exchange variation
483                (ChessMove::from_str("g1f3").unwrap(), 1.0), // Fianchetto system
484                (ChessMove::from_str("c1f4").unwrap(), 0.8), // Russian system
485            ];
486            let _ = self.add_opening(
487                &board.to_string(),
488                0.1,
489                moves,
490                "Grunfeld Defense".to_string(),
491                Some("D80".to_string()),
492            );
493        }
494
495        // BENONI DEFENSE
496        if let Ok(board) =
497            Board::from_str("rnbqkb1r/pp2pppp/3p1n2/2pP4/2P5/8/PP2PPPP/RNBQKBNR w KQkq - 0 4")
498        {
499            let moves = vec![
500                (ChessMove::from_str("b1c3").unwrap(), 1.0), // Classical setup
501                (ChessMove::from_str("g1f3").unwrap(), 0.9), // Fianchetto variation
502                (ChessMove::from_str("f2f4").unwrap(), 0.7), // Four Pawns Attack
503            ];
504            let _ = self.add_opening(
505                &board.to_string(),
506                0.3,
507                moves,
508                "Modern Benoni".to_string(),
509                Some("A60".to_string()),
510            );
511        }
512    }
513
514    /// Add comprehensive Sicilian variations
515    fn add_sicilian_variations(&mut self) {
516        // SICILIAN ACCELERATED DRAGON
517        if let Ok(board) =
518            Board::from_str("rnbqkbnr/pp1ppp1p/6p1/2p5/4P3/8/PPPP1PPP/RNBQKBNR w KQkq - 0 3")
519        {
520            let moves = vec![
521                (ChessMove::from_str("g1f3").unwrap(), 1.0), // Standard development
522                (ChessMove::from_str("d2d4").unwrap(), 0.9), // Open Sicilian
523                (ChessMove::from_str("f2f4").unwrap(), 0.6), // Grand Prix
524            ];
525            let _ = self.add_opening(
526                &board.to_string(),
527                0.2,
528                moves,
529                "Sicilian Accelerated Dragon".to_string(),
530                Some("B35".to_string()),
531            );
532        }
533
534        // SICILIAN SVESHNIKOV
535        if let Ok(board) = Board::from_str(
536            "r1bqkb1r/1pp1pppp/p1np1n2/4p3/3P4/2N2N2/PPP1PPPP/R1BQKB1R w KQkq - 0 6",
537        ) {
538            let moves = vec![
539                (ChessMove::from_str("f3d5").unwrap(), 0.8), // Knight sacrifice
540                (ChessMove::from_str("c1g5").unwrap(), 1.0), // Pin the knight
541                (ChessMove::from_str("f1e2").unwrap(), 0.9), // Solid development
542            ];
543            let _ = self.add_opening(
544                &board.to_string(),
545                0.2,
546                moves,
547                "Sicilian Sveshnikov".to_string(),
548                Some("B33".to_string()),
549            );
550        }
551
552        // SICILIAN SCHEVENINGEN
553        if let Ok(board) =
554            Board::from_str("rnbqkb1r/pp3ppp/3ppn2/8/3P4/2N2N2/PPP1PPPP/R1BQKB1R w KQkq - 0 6")
555        {
556            let moves = vec![
557                (ChessMove::from_str("f2f3").unwrap(), 1.0), // English Attack
558                (ChessMove::from_str("f1e2").unwrap(), 0.9), // Classical system
559                (ChessMove::from_str("c1e3").unwrap(), 0.8), // Standard development
560            ];
561            let _ = self.add_opening(
562                &board.to_string(),
563                0.1,
564                moves,
565                "Sicilian Scheveningen".to_string(),
566                Some("B80".to_string()),
567            );
568        }
569
570        // SICILIAN PAULSEN
571        if let Ok(board) =
572            Board::from_str("rnbqkb1r/1pp2ppp/p3pn2/8/3P4/2N2N2/PPP1PPPP/R1BQKB1R w KQkq - 0 6")
573        {
574            let moves = vec![
575                (ChessMove::from_str("f1e2").unwrap(), 1.0), // Classical setup
576                (ChessMove::from_str("g2g3").unwrap(), 0.8), // Fianchetto
577                (ChessMove::from_str("f2f4").unwrap(), 0.7), // Aggressive approach
578            ];
579            let _ = self.add_opening(
580                &board.to_string(),
581                0.1,
582                moves,
583                "Sicilian Paulsen".to_string(),
584                Some("B40".to_string()),
585            );
586        }
587    }
588
589    /// Add French and Caro-Kann systems
590    fn add_french_caro_kann(&mut self) {
591        // FRENCH WINAWER
592        if let Ok(board) =
593            Board::from_str("rnbqk1nr/ppp2ppp/4p3/3p4/1b1PP3/2N5/PPP2PPP/R1BQKBNR w KQkq - 2 4")
594        {
595            let moves = vec![
596                (ChessMove::from_str("e4e5").unwrap(), 1.0), // Advance variation
597                (ChessMove::from_str("f1d3").unwrap(), 0.8), // Positional approach
598                (ChessMove::from_str("a2a3").unwrap(), 0.7), // Force bishop decision
599            ];
600            let _ = self.add_opening(
601                &board.to_string(),
602                0.2,
603                moves,
604                "French Winawer".to_string(),
605                Some("C15".to_string()),
606            );
607        }
608
609        // FRENCH TARRASCH
610        if let Ok(board) =
611            Board::from_str("rnbqkbnr/ppp2ppp/4p3/3p4/3PP3/8/PPPR1PPP/RNBQKBNR b KQkq - 1 3")
612        {
613            let moves = vec![
614                (ChessMove::from_str("c7c5").unwrap(), 1.0), // Challenge center
615                (ChessMove::from_str("g8f6").unwrap(), 0.9), // Develop knight
616                (ChessMove::from_str("b8c6").unwrap(), 0.8), // Classical development
617            ];
618            let _ = self.add_opening(
619                &board.to_string(),
620                -0.1,
621                moves,
622                "French Tarrasch".to_string(),
623                Some("C03".to_string()),
624            );
625        }
626
627        // CARO-KANN ADVANCE
628        if let Ok(board) =
629            Board::from_str("rnbqkbnr/pp2pppp/2p5/3pP3/3P4/8/PPP2PPP/RNBQKBNR b KQkq - 0 3")
630        {
631            let moves = vec![
632                (ChessMove::from_str("c8f5").unwrap(), 1.0), // Active bishop
633                (ChessMove::from_str("e7e6").unwrap(), 0.8), // French-style
634                (ChessMove::from_str("h7h6").unwrap(), 0.6), // Prevent Bg5
635            ];
636            let _ = self.add_opening(
637                &board.to_string(),
638                0.0,
639                moves,
640                "Caro-Kann Advance".to_string(),
641                Some("B12".to_string()),
642            );
643        }
644
645        // CARO-KANN EXCHANGE
646        if let Ok(board) =
647            Board::from_str("rnbqkbnr/pp2pppp/2p5/3P4/8/8/PPP1PPPP/RNBQKBNR b KQkq - 0 3")
648        {
649            let moves = vec![
650                (ChessMove::from_str("d5c4").unwrap(), 1.0), // Recapture
651            ];
652            let _ = self.add_opening(
653                &board.to_string(),
654                0.0,
655                moves,
656                "Caro-Kann Exchange".to_string(),
657                Some("B13".to_string()),
658            );
659        }
660    }
661
662    /// Add English and Reti systems
663    fn add_english_reti_systems(&mut self) {
664        // ENGLISH SYMMETRICAL
665        if let Ok(board) =
666            Board::from_str("rnbqkbnr/pp1ppppp/8/2p5/2P5/8/PP1PPPPP/RNBQKBNR w KQkq - 0 2")
667        {
668            let moves = vec![
669                (ChessMove::from_str("b1c3").unwrap(), 1.0), // Symmetrical variation
670                (ChessMove::from_str("g1f3").unwrap(), 0.9), // King's Knight
671                (ChessMove::from_str("g2g3").unwrap(), 0.8), // Fianchetto
672            ];
673            let _ = self.add_opening(
674                &board.to_string(),
675                0.0,
676                moves,
677                "English Symmetrical".to_string(),
678                Some("A30".to_string()),
679            );
680        }
681
682        // RETI OPENING
683        if let Ok(board) =
684            Board::from_str("rnbqkb1r/pppppppp/5n2/8/8/5N2/PPPPPPPP/RNBQKB1R w KQkq - 1 2")
685        {
686            let moves = vec![
687                (ChessMove::from_str("c2c4").unwrap(), 1.0), // English setup
688                (ChessMove::from_str("g2g3").unwrap(), 0.9), // Fianchetto
689                (ChessMove::from_str("d2d4").unwrap(), 0.8), // Transpose to d4
690            ];
691            let _ = self.add_opening(
692                &board.to_string(),
693                0.1,
694                moves,
695                "Reti Opening".to_string(),
696                Some("A04".to_string()),
697            );
698        }
699
700        // BIRD'S OPENING
701        if let Ok(board) =
702            Board::from_str("rnbqkbnr/pppppppp/8/8/5P2/8/PPPPP1PP/RNBQKBNR b KQkq - 0 1")
703        {
704            let moves = vec![
705                (ChessMove::from_str("d7d5").unwrap(), 1.0), // Challenge center
706                (ChessMove::from_str("g8f6").unwrap(), 0.8), // Develop knight
707                (ChessMove::from_str("e7e5").unwrap(), 0.9), // Counter-attack
708            ];
709            let _ = self.add_opening(
710                &board.to_string(),
711                -0.1,
712                moves,
713                "Bird's Opening".to_string(),
714                Some("A02".to_string()),
715            );
716        }
717    }
718
719    /// Add gambit systems
720    fn add_gambit_systems(&mut self) {
721        // KING'S GAMBIT ACCEPTED
722        if let Ok(board) =
723            Board::from_str("rnbqkbnr/pppp1ppp/8/4p3/4PP2/8/PPPP2PP/RNBQKBNR b KQkq - 0 2")
724        {
725            let moves = vec![
726                (ChessMove::from_str("e5f4").unwrap(), 1.0), // Accept gambit
727                (ChessMove::from_str("d7d6").unwrap(), 0.7), // Decline solidly
728                (ChessMove::from_str("f8c5").unwrap(), 0.8), // Classical defense
729            ];
730            let _ = self.add_opening(
731                &board.to_string(),
732                -0.2,
733                moves,
734                "King's Gambit".to_string(),
735                Some("C30".to_string()),
736            );
737        }
738
739        // QUEEN'S GAMBIT ACCEPTED
740        if let Ok(board) =
741            Board::from_str("rnbqkbnr/ppp1pppp/8/8/2pP4/8/PP2PPPP/RNBQKBNR w KQkq - 0 3")
742        {
743            let moves = vec![
744                (ChessMove::from_str("g1f3").unwrap(), 1.0), // Regain pawn
745                (ChessMove::from_str("e2e3").unwrap(), 0.9), // Support center
746                (ChessMove::from_str("a2a4").unwrap(), 0.7), // Prevent b5
747            ];
748            let _ = self.add_opening(
749                &board.to_string(),
750                0.3,
751                moves,
752                "Queen's Gambit Accepted".to_string(),
753                Some("D20".to_string()),
754            );
755        }
756
757        // EVANS GAMBIT
758        if let Ok(board) =
759            Board::from_str("r1bqk1nr/pppp1ppp/2n5/2b1p3/1PB1P3/5N2/P1PP1PPP/RNBQK2R b KQkq - 2 4")
760        {
761            let moves = vec![
762                (ChessMove::from_str("c5b4").unwrap(), 1.0), // Accept gambit
763                (ChessMove::from_str("c5a5").unwrap(), 0.8), // Retreat bishop
764                (ChessMove::from_str("c5e7").unwrap(), 0.7), // Safe retreat
765            ];
766            let _ = self.add_opening(
767                &board.to_string(),
768                0.4,
769                moves,
770                "Evans Gambit".to_string(),
771                Some("C51".to_string()),
772            );
773        }
774    }
775
776    /// Add comprehensive opening database with hundreds of positions
777    fn add_comprehensive_openings(&mut self) {
778        // SCANDINAVIAN DEFENSE
779        if let Ok(board) =
780            Board::from_str("rnbqkbnr/ppp1pppp/8/3p4/4P3/8/PPPP1PPP/RNBQKBNR w KQkq - 0 2")
781        {
782            let moves = vec![
783                (ChessMove::from_str("e4d5").unwrap(), 1.0), // Main line
784            ];
785            let _ = self.add_opening(
786                &board.to_string(),
787                0.3,
788                moves,
789                "Scandinavian Defense".to_string(),
790                Some("B01".to_string()),
791            );
792        }
793
794        // ALEKHINE'S DEFENSE
795        if let Ok(board) =
796            Board::from_str("rnbqkb1r/pppppppp/5n2/8/4P3/8/PPPP1PPP/RNBQKBNR w KQkq - 1 2")
797        {
798            let moves = vec![
799                (ChessMove::from_str("e4e5").unwrap(), 1.0), // Chase the knight
800                (ChessMove::from_str("b1c3").unwrap(), 0.8), // Four Knights variation
801            ];
802            let _ = self.add_opening(
803                &board.to_string(),
804                0.2,
805                moves,
806                "Alekhine's Defense".to_string(),
807                Some("B02".to_string()),
808            );
809        }
810
811        // PIRC DEFENSE
812        if let Ok(board) =
813            Board::from_str("rnbqkb1r/ppp1pppp/3p1n2/8/3PP3/8/PPP2PPP/RNBQKBNR w KQkq - 0 3")
814        {
815            let moves = vec![
816                (ChessMove::from_str("b1c3").unwrap(), 1.0), // Classical setup
817                (ChessMove::from_str("f2f4").unwrap(), 0.8), // Austrian Attack
818                (ChessMove::from_str("g1f3").unwrap(), 0.9), // Quiet development
819            ];
820            let _ = self.add_opening(
821                &board.to_string(),
822                0.1,
823                moves,
824                "Pirc Defense".to_string(),
825                Some("B07".to_string()),
826            );
827        }
828
829        // SICILIAN NAJDORF
830        if let Ok(board) =
831            Board::from_str("rnbqkb1r/1pp1pppp/p4n2/3p4/3P4/5N2/PPP1PPPP/RNBQKB1R w KQkq - 0 4")
832        {
833            let moves = vec![
834                (ChessMove::from_str("f1e2").unwrap(), 0.9), // Be2 system
835                (ChessMove::from_str("f2f3").unwrap(), 0.8), // English Attack setup
836                (ChessMove::from_str("b1c3").unwrap(), 1.0), // Classical development
837            ];
838            let _ = self.add_opening(
839                &board.to_string(),
840                0.2,
841                moves,
842                "Sicilian Najdorf".to_string(),
843                Some("B90".to_string()),
844            );
845        }
846
847        // SICILIAN DRAGON
848        if let Ok(board) =
849            Board::from_str("rnbqk2r/pp2ppbp/3p1np1/8/3P4/2N2N2/PPP1PPPP/R1BQKB1R w KQkq - 0 6")
850        {
851            let moves = vec![
852                (ChessMove::from_str("f2f3").unwrap(), 1.0), // Yugoslav Attack
853                (ChessMove::from_str("f1e2").unwrap(), 0.8), // Positional system
854                (ChessMove::from_str("c1e3").unwrap(), 0.9), // Standard development
855            ];
856            let _ = self.add_opening(
857                &board.to_string(),
858                0.2,
859                moves,
860                "Sicilian Dragon".to_string(),
861                Some("B70".to_string()),
862            );
863        }
864
865        // RUY LOPEZ - MORPHY DEFENSE
866        if let Ok(board) =
867            Board::from_str("r1bqkbnr/1ppp1ppp/p1n5/1B2p3/4P3/5N2/PPPP1PPP/RNBQK2R w KQkq - 0 4")
868        {
869            let moves = vec![
870                (ChessMove::from_str("b5a4").unwrap(), 1.0), // Spanish main line
871                (ChessMove::from_str("b5c6").unwrap(), 0.6), // Exchange variation
872            ];
873            let _ = self.add_opening(
874                &board.to_string(),
875                0.4,
876                moves,
877                "Ruy Lopez - Morphy Defense".to_string(),
878                Some("C78".to_string()),
879            );
880        }
881
882        // RUY LOPEZ CLOSED
883        if let Ok(board) =
884            Board::from_str("r1bqk1nr/pppp1ppp/2n5/1Bb1p3/4P3/5N2/PPPP1PPP/RNBQK2R w KQkq - 4 4")
885        {
886            let moves = vec![
887                (ChessMove::from_str("e1g1").unwrap(), 1.0), // Castle kingside
888                (ChessMove::from_str("d2d3").unwrap(), 0.9), // Support the center
889                (ChessMove::from_str("c2c3").unwrap(), 0.8), // Prepare d4
890            ];
891            let _ = self.add_opening(
892                &board.to_string(),
893                0.3,
894                moves,
895                "Ruy Lopez - Closed System".to_string(),
896                Some("C84".to_string()),
897            );
898        }
899
900        // TWO KNIGHTS DEFENSE
901        if let Ok(board) =
902            Board::from_str("r1bqkb1r/pppp1ppp/2n2n2/4p3/2B1P3/5N2/PPPP1PPP/RNBQK2R w KQkq - 4 4")
903        {
904            let moves = vec![
905                (ChessMove::from_str("f3g5").unwrap(), 0.8), // Aggressive Knight Attack
906                (ChessMove::from_str("d2d3").unwrap(), 1.0), // Italian Game continuation
907                (ChessMove::from_str("b1c3").unwrap(), 0.9), // Four Knights
908            ];
909            let _ = self.add_opening(
910                &board.to_string(),
911                0.2,
912                moves,
913                "Two Knights Defense".to_string(),
914                Some("C55".to_string()),
915            );
916        }
917
918        // DUTCH DEFENSE
919        if let Ok(board) =
920            Board::from_str("rnbqkbnr/ppppp1pp/8/5p2/3P4/8/PPP1PPPP/RNBQKBNR w KQkq - 0 2")
921        {
922            let moves = vec![
923                (ChessMove::from_str("g1f3").unwrap(), 1.0), // Standard development
924                (ChessMove::from_str("c2c4").unwrap(), 0.9), // English setup
925                (ChessMove::from_str("g2g3").unwrap(), 0.8), // Fianchetto setup
926            ];
927            let _ = self.add_opening(
928                &board.to_string(),
929                0.3,
930                moves,
931                "Dutch Defense".to_string(),
932                Some("A80".to_string()),
933            );
934        }
935
936        // ITALIAN GAME - FRIED LIVER SETUP
937        if let Ok(board) =
938            Board::from_str("r1bqkb1r/pppp1ppp/2n2n2/4p3/2B1P3/5N2/PPPP1PPP/RNBQK2R w KQkq - 4 4")
939        {
940            let moves = vec![
941                (ChessMove::from_str("f3g5").unwrap(), 0.8), // Aggressive fried liver attack
942                (ChessMove::from_str("d2d3").unwrap(), 1.0), // Solid Italian
943                (ChessMove::from_str("b1c3").unwrap(), 0.9), // Four Knights game
944            ];
945            let _ = self.add_opening(
946                &board.to_string(),
947                0.3,
948                moves,
949                "Italian Game - Two Knights".to_string(),
950                Some("C55".to_string()),
951            );
952        }
953
954        // KING'S GAMBIT
955        if let Ok(board) =
956            Board::from_str("rnbqkbnr/pppp1ppp/8/4p3/4PP2/8/PPPP2PP/RNBQKBNR b KQkq - 0 2")
957        {
958            let moves = vec![
959                (ChessMove::from_str("e5f4").unwrap(), 1.0), // Accept the gambit
960                (ChessMove::from_str("f8c5").unwrap(), 0.7), // Decline with Bc5
961            ];
962            let _ = self.add_opening(
963                &board.to_string(),
964                0.0,
965                moves,
966                "King's Gambit".to_string(),
967                Some("C30".to_string()),
968            );
969        }
970
971        // QUEEN'S GAMBIT ACCEPTED
972        if let Ok(board) =
973            Board::from_str("rnbqkbnr/ppp1pppp/8/8/2pP4/8/PP2PPPP/RNBQKBNR w KQkq - 0 3")
974        {
975            let moves = vec![
976                (ChessMove::from_str("g1f3").unwrap(), 1.0), // Develop knight first
977                (ChessMove::from_str("e2e3").unwrap(), 0.9), // Support center
978                (ChessMove::from_str("e2e4").unwrap(), 0.6), // Central gambit
979            ];
980            let _ = self.add_opening(
981                &board.to_string(),
982                0.2,
983                moves,
984                "Queen's Gambit Accepted".to_string(),
985                Some("D20".to_string()),
986            );
987        }
988
989        // QUEEN'S GAMBIT DECLINED
990        if let Ok(board) =
991            Board::from_str("rnbqkbnr/ppp2ppp/4p3/3p4/2PP4/8/PP2PPPP/RNBQKBNR w KQkq - 0 3")
992        {
993            let moves = vec![
994                (ChessMove::from_str("b1c3").unwrap(), 1.0), // Classical QGD
995                (ChessMove::from_str("g1f3").unwrap(), 0.9), // Quiet development
996                (ChessMove::from_str("c4d5").unwrap(), 0.6), // Exchange variation
997            ];
998            let _ = self.add_opening(
999                &board.to_string(),
1000                0.2,
1001                moves,
1002                "Queen's Gambit Declined".to_string(),
1003                Some("D30".to_string()),
1004            );
1005        }
1006
1007        // LONDON SYSTEM
1008        if let Ok(board) =
1009            Board::from_str("rnbqkbnr/ppp1pppp/8/3p4/3P4/5N2/PPP1PPPP/RNBQKB1R w KQkq - 0 2")
1010        {
1011            let moves = vec![
1012                (ChessMove::from_str("c1f4").unwrap(), 1.0), // London system bishop
1013                (ChessMove::from_str("c2c4").unwrap(), 0.8), // Transpose to QG
1014            ];
1015            let _ = self.add_opening(
1016                &board.to_string(),
1017                0.2,
1018                moves,
1019                "London System".to_string(),
1020                Some("D02".to_string()),
1021            );
1022        }
1023
1024        // CATALAN OPENING
1025        if let Ok(board) =
1026            Board::from_str("rnbqkbnr/ppp2ppp/4p3/3p4/2PP4/6P1/PP2PP1P/RNBQKBNR b KQkq - 0 3")
1027        {
1028            let moves = vec![
1029                (ChessMove::from_str("d5c4").unwrap(), 0.8), // Catalan accepted
1030                (ChessMove::from_str("g8f6").unwrap(), 1.0), // Catalan declined
1031                (ChessMove::from_str("f8e7").unwrap(), 0.7), // Quiet development
1032            ];
1033            let _ = self.add_opening(
1034                &board.to_string(),
1035                0.3,
1036                moves,
1037                "Catalan Opening".to_string(),
1038                Some("E00".to_string()),
1039            );
1040        }
1041
1042        // GRUNFELD DEFENSE
1043        if let Ok(board) =
1044            Board::from_str("rnbqkb1r/ppp1pp1p/5np1/3p4/2PP4/2N5/PP2PPPP/R1BQKBNR w KQkq - 0 4")
1045        {
1046            let moves = vec![
1047                (ChessMove::from_str("c4d5").unwrap(), 0.8), // Exchange Grunfeld
1048                (ChessMove::from_str("g1f3").unwrap(), 1.0), // Quiet system
1049                (ChessMove::from_str("f2f3").unwrap(), 0.7), // Russian system
1050            ];
1051            let _ = self.add_opening(
1052                &board.to_string(),
1053                0.2,
1054                moves,
1055                "Grunfeld Defense".to_string(),
1056                Some("D80".to_string()),
1057            );
1058        }
1059
1060        // BENONI DEFENSE
1061        if let Ok(board) =
1062            Board::from_str("rnbqkbnr/pp2pppp/8/2pp4/3P4/8/PPP1PPPP/RNBQKBNR w KQkq - 0 3")
1063        {
1064            let moves = vec![
1065                (ChessMove::from_str("d4d5").unwrap(), 1.0), // Modern Benoni
1066                (ChessMove::from_str("g1f3").unwrap(), 0.8), // Quiet approach
1067            ];
1068            let _ = self.add_opening(
1069                &board.to_string(),
1070                0.3,
1071                moves,
1072                "Benoni Defense".to_string(),
1073                Some("A60".to_string()),
1074            );
1075        }
1076
1077        // DUTCH DEFENSE
1078        if let Ok(board) =
1079            Board::from_str("rnbqkbnr/ppppp1pp/8/5p2/3P4/8/PPP1PPPP/RNBQKBNR w KQkq - 0 2")
1080        {
1081            let moves = vec![
1082                (ChessMove::from_str("g1f3").unwrap(), 1.0), // Solid development
1083                (ChessMove::from_str("g2g3").unwrap(), 0.8), // Fianchetto setup
1084                (ChessMove::from_str("b1c3").unwrap(), 0.7), // Classical development
1085            ];
1086            let _ = self.add_opening(
1087                &board.to_string(),
1088                0.1,
1089                moves,
1090                "Dutch Defense".to_string(),
1091                Some("A80".to_string()),
1092            );
1093        }
1094
1095        // VIENNA GAME
1096        if let Ok(board) =
1097            Board::from_str("rnbqkbnr/pppp1ppp/8/4p3/4P3/2N5/PPPP1PPP/R1BQKBNR b KQkq - 1 2")
1098        {
1099            let moves = vec![
1100                (ChessMove::from_str("g8f6").unwrap(), 1.0), // Vienna game main line
1101                (ChessMove::from_str("b8c6").unwrap(), 0.8), // Classical response
1102                (ChessMove::from_str("f7f5").unwrap(), 0.6), // Aggressive counter
1103            ];
1104            let _ = self.add_opening(
1105                &board.to_string(),
1106                0.2,
1107                moves,
1108                "Vienna Game".to_string(),
1109                Some("C25".to_string()),
1110            );
1111        }
1112
1113        // SCOTCH GAME
1114        if let Ok(board) =
1115            Board::from_str("rnbqkbnr/pppp1ppp/8/4p3/3PP3/8/PPP2PPP/RNBQKBNR b KQkq - 0 2")
1116        {
1117            let moves = vec![
1118                (ChessMove::from_str("e5d4").unwrap(), 1.0), // Main line Scotch
1119                (ChessMove::from_str("g8f6").unwrap(), 0.8), // Scotch Game declined
1120            ];
1121            let _ = self.add_opening(
1122                &board.to_string(),
1123                0.3,
1124                moves,
1125                "Scotch Game".to_string(),
1126                Some("C45".to_string()),
1127            );
1128        }
1129
1130        // PETROFF DEFENSE
1131        if let Ok(board) =
1132            Board::from_str("rnbqkb1r/pppp1ppp/5n2/4p3/4P3/5N2/PPPP1PPP/RNBQKB1R w KQkq - 2 3")
1133        {
1134            let moves = vec![
1135                (ChessMove::from_str("f3e5").unwrap(), 1.0), // Main line Petroff
1136                (ChessMove::from_str("d2d4").unwrap(), 0.8), // Scotch Four Knights
1137            ];
1138            let _ = self.add_opening(
1139                &board.to_string(),
1140                0.1,
1141                moves,
1142                "Petroff Defense".to_string(),
1143                Some("C42".to_string()),
1144            );
1145        }
1146
1147        // BIRD'S OPENING
1148        if let Ok(board) =
1149            Board::from_str("rnbqkbnr/pppppppp/8/8/5P2/8/PPPPP1PP/RNBQKBNR b KQkq - 0 1")
1150        {
1151            let moves = vec![
1152                (ChessMove::from_str("d7d5").unwrap(), 1.0), // Classical response
1153                (ChessMove::from_str("g8f6").unwrap(), 0.8), // Hypermodern approach
1154                (ChessMove::from_str("e7e5").unwrap(), 0.7), // Aggressive counter
1155            ];
1156            let _ = self.add_opening(
1157                &board.to_string(),
1158                0.0,
1159                moves,
1160                "Bird's Opening".to_string(),
1161                Some("A02".to_string()),
1162            );
1163        }
1164
1165        // Add extra modern variations
1166        self.add_extra_modern_openings();
1167    }
1168
1169    /// Add modern and aggressive opening variations (part of comprehensive)
1170    fn add_extra_modern_openings(&mut self) {
1171        // ACCELERATED DRAGON
1172        if let Ok(board) =
1173            Board::from_str("rnbqkb1r/pp1ppp1p/5np1/2p5/3P4/5N2/PPP1PPPP/RNBQKB1R w KQkq - 0 4")
1174        {
1175            let moves = vec![
1176                (ChessMove::from_str("b1c3").unwrap(), 1.0), // Main line
1177                (ChessMove::from_str("c2c4").unwrap(), 0.8), // Maroczy Bind
1178                (ChessMove::from_str("f1e2").unwrap(), 0.7), // Quiet system
1179            ];
1180            let _ = self.add_opening(
1181                &board.to_string(),
1182                0.2,
1183                moves,
1184                "Sicilian - Accelerated Dragon".to_string(),
1185                Some("B35".to_string()),
1186            );
1187        }
1188
1189        // SICILIAN SVESHNIKOV
1190        if let Ok(board) =
1191            Board::from_str("r1bqkb1r/1p2pppp/p1np1n2/4p3/3PP3/2N2N2/PPP2PPP/R1BQKB1R w KQkq - 0 6")
1192        {
1193            let moves = vec![
1194                (ChessMove::from_str("f1e2").unwrap(), 1.0), // Be2 system
1195                (ChessMove::from_str("f1c4").unwrap(), 0.8), // Aggressive Bc4
1196                (ChessMove::from_str("a2a4").unwrap(), 0.7), // Positional approach
1197            ];
1198            let _ = self.add_opening(
1199                &board.to_string(),
1200                0.1,
1201                moves,
1202                "Sicilian - Sveshnikov Variation".to_string(),
1203                Some("B33".to_string()),
1204            );
1205        }
1206
1207        // TROMPOWSKY ATTACK
1208        if let Ok(board) =
1209            Board::from_str("rnbqkb1r/pppppppp/5n2/8/3P4/8/PPP1PPPP/RNBQKBNR w KQkq - 1 2")
1210        {
1211            let moves = vec![
1212                (ChessMove::from_str("c1g5").unwrap(), 1.0), // Trompowsky Bishop
1213                (ChessMove::from_str("g1f3").unwrap(), 0.8), // Transpose to normal lines
1214                (ChessMove::from_str("b1c3").unwrap(), 0.7), // Classical development
1215            ];
1216            let _ = self.add_opening(
1217                &board.to_string(),
1218                0.2,
1219                moves,
1220                "Trompowsky Attack".to_string(),
1221                Some("A45".to_string()),
1222            );
1223        }
1224
1225        // TORRE ATTACK
1226        if let Ok(board) =
1227            Board::from_str("rnbqkb1r/ppp1pppp/5n2/3p4/3P4/5N2/PPP1PPPP/RNBQKB1R w KQkq - 0 3")
1228        {
1229            let moves = vec![
1230                (ChessMove::from_str("c1g5").unwrap(), 1.0), // Torre Bishop
1231                (ChessMove::from_str("c2c4").unwrap(), 0.8), // Transpose to QG
1232                (ChessMove::from_str("e2e3").unwrap(), 0.7), // Colle system
1233            ];
1234            let _ = self.add_opening(
1235                &board.to_string(),
1236                0.2,
1237                moves,
1238                "Torre Attack".to_string(),
1239                Some("D03".to_string()),
1240            );
1241        }
1242
1243        // BLACKMAR-DIEMER GAMBIT
1244        if let Ok(board) =
1245            Board::from_str("rnbqkbnr/ppp1pppp/8/3p4/3PP3/8/PPP2PPP/RNBQKBNR b KQkq - 0 2")
1246        {
1247            let moves = vec![
1248                (ChessMove::from_str("d5e4").unwrap(), 0.8), // Accept the gambit
1249                (ChessMove::from_str("g8f6").unwrap(), 1.0), // Decline with Nf6
1250                (ChessMove::from_str("c7c6").unwrap(), 0.7), // Caro-Kann setup
1251            ];
1252            let _ = self.add_opening(
1253                &board.to_string(),
1254                0.0,
1255                moves,
1256                "Blackmar-Diemer Gambit".to_string(),
1257                Some("D00".to_string()),
1258            );
1259        }
1260
1261        // SCANDINAVIAN MAIN LINE
1262        if let Ok(board) =
1263            Board::from_str("rnbqkbnr/ppp1pppp/8/8/3p4/8/PPP1PPPP/RNBQKBNR w KQkq - 0 2")
1264        {
1265            let moves = vec![
1266                (ChessMove::from_str("d1d4").unwrap(), 1.0), // Recapture with queen
1267                (ChessMove::from_str("g1f3").unwrap(), 0.8), // Develop knight first
1268            ];
1269            let _ = self.add_opening(
1270                &board.to_string(),
1271                0.3,
1272                moves,
1273                "Scandinavian - Main Line".to_string(),
1274                Some("B01".to_string()),
1275            );
1276        }
1277
1278        // POLISH OPENING (SOKOLSKY)
1279        if let Ok(board) =
1280            Board::from_str("rnbqkbnr/pppppppp/8/8/1P6/8/P1PPPPPP/RNBQKBNR b KQkq - 0 1")
1281        {
1282            let moves = vec![
1283                (ChessMove::from_str("d7d5").unwrap(), 1.0), // Central response
1284                (ChessMove::from_str("g8f6").unwrap(), 0.8), // Hypermodern approach
1285                (ChessMove::from_str("e7e5").unwrap(), 0.7), // Aggressive counter
1286            ];
1287            let _ = self.add_opening(
1288                &board.to_string(),
1289                0.0,
1290                moves,
1291                "Polish Opening".to_string(),
1292                Some("A00".to_string()),
1293            );
1294        }
1295
1296        // NIMZOWITSCH-LARSEN ATTACK
1297        if let Ok(board) =
1298            Board::from_str("rnbqkbnr/pppppppp/8/8/8/1P6/P1PPPPPP/RNBQKBNR b KQkq - 0 1")
1299        {
1300            let moves = vec![
1301                (ChessMove::from_str("d7d5").unwrap(), 1.0), // Central control
1302                (ChessMove::from_str("g8f6").unwrap(), 0.8), // Nimzo-Indian style
1303                (ChessMove::from_str("e7e5").unwrap(), 0.7), // Aggressive center
1304            ];
1305            let _ = self.add_opening(
1306                &board.to_string(),
1307                0.1,
1308                moves,
1309                "Nimzowitsch-Larsen Attack".to_string(),
1310                Some("A01".to_string()),
1311            );
1312        }
1313
1314        // RETI OPENING
1315        if let Ok(board) =
1316            Board::from_str("rnbqkbnr/pppppppp/8/8/8/5N2/PPPPPPPP/RNBQKB1R b KQkq - 1 1")
1317        {
1318            let moves = vec![
1319                (ChessMove::from_str("d7d5").unwrap(), 1.0), // Classical response
1320                (ChessMove::from_str("g8f6").unwrap(), 0.9), // Hypermodern mirror
1321                (ChessMove::from_str("c7c5").unwrap(), 0.6), // English Defense
1322            ];
1323            let _ = self.add_opening(
1324                &board.to_string(),
1325                0.1,
1326                moves,
1327                "Reti Opening".to_string(),
1328                Some("A04".to_string()),
1329            );
1330        }
1331
1332        // KING'S INDIAN ATTACK (vs French setup)
1333        if let Ok(board) =
1334            Board::from_str("rnbqkbnr/pppppppp/8/8/8/5NP1/PPPPPP1P/RNBQKB1R b KQkq - 0 2")
1335        {
1336            let moves = vec![
1337                (ChessMove::from_str("d7d5").unwrap(), 1.0), // Classical center
1338                (ChessMove::from_str("g8f6").unwrap(), 0.8), // Hypermodern response
1339                (ChessMove::from_str("e7e6").unwrap(), 0.7), // French setup
1340            ];
1341            let _ = self.add_opening(
1342                &board.to_string(),
1343                0.1,
1344                moves,
1345                "King's Indian Attack".to_string(),
1346                Some("A07".to_string()),
1347            );
1348        }
1349
1350        // BUDAPEST GAMBIT
1351        if let Ok(board) =
1352            Board::from_str("rnbqkb1r/pppppppp/5n2/4p3/3P4/8/PPP1PPPP/RNBQKBNR w KQkq - 1 2")
1353        {
1354            let moves = vec![
1355                (ChessMove::from_str("d4e5").unwrap(), 1.0), // Accept the gambit
1356                (ChessMove::from_str("g1f3").unwrap(), 0.7), // Decline and develop
1357            ];
1358            let _ = self.add_opening(
1359                &board.to_string(),
1360                0.2,
1361                moves,
1362                "Budapest Gambit".to_string(),
1363                Some("A51".to_string()),
1364            );
1365        }
1366
1367        // BENKO GAMBIT
1368        if let Ok(board) =
1369            Board::from_str("rnbqkb1r/p1pppppp/5n2/1p6/2PP4/8/PP2PPPP/RNBQKBNR w KQkq - 0 3")
1370        {
1371            let moves = vec![
1372                (ChessMove::from_str("c4b5").unwrap(), 1.0), // Accept the gambit
1373                (ChessMove::from_str("g1f3").unwrap(), 0.8), // Decline and develop
1374                (ChessMove::from_str("a2a4").unwrap(), 0.6), // Counter-gambit
1375            ];
1376            let _ = self.add_opening(
1377                &board.to_string(),
1378                0.3,
1379                moves,
1380                "Benko Gambit".to_string(),
1381                Some("A57".to_string()),
1382            );
1383        }
1384
1385        // ENGLISH - SYMMETRICAL
1386        if let Ok(board) =
1387            Board::from_str("rnbqkbnr/pp1ppppp/8/2p5/2P5/8/PP1PPPPP/RNBQKBNR w KQkq - 0 2")
1388        {
1389            let moves = vec![
1390                (ChessMove::from_str("b1c3").unwrap(), 1.0), // Symmetrical English
1391                (ChessMove::from_str("g1f3").unwrap(), 0.8), // Reti-style
1392                (ChessMove::from_str("g2g3").unwrap(), 0.7), // Fianchetto
1393            ];
1394            let _ = self.add_opening(
1395                &board.to_string(),
1396                0.1,
1397                moves,
1398                "English Opening - Symmetrical".to_string(),
1399                Some("A30".to_string()),
1400            );
1401        }
1402
1403        // MODERN DEFENSE
1404        if let Ok(board) =
1405            Board::from_str("rnbqkbnr/pppppp1p/6p1/8/4P3/8/PPPP1PPP/RNBQKBNR w KQkq - 0 2")
1406        {
1407            let moves = vec![
1408                (ChessMove::from_str("d2d4").unwrap(), 1.0), // Classical center
1409                (ChessMove::from_str("b1c3").unwrap(), 0.8), // Develop pieces
1410                (ChessMove::from_str("f2f4").unwrap(), 0.7), // Austrian attack
1411            ];
1412            let _ = self.add_opening(
1413                &board.to_string(),
1414                0.2,
1415                moves,
1416                "Modern Defense".to_string(),
1417                Some("B06".to_string()),
1418            );
1419        }
1420
1421        // HIPPOPOTAMUS DEFENSE REMOVED - contained objectively bad moves like Nh6
1422        // The real Hippopotamus Defense doesn't start with such terrible moves
1423    }
1424
1425    /// Get statistics about the opening book
1426    pub fn stats(&self) -> OpeningBookStats {
1427        let total_positions = self.entries.len();
1428        let eco_codes: std::collections::HashSet<_> = self
1429            .entries
1430            .values()
1431            .filter_map(|entry| entry.eco_code.as_ref())
1432            .collect();
1433        let eco_classifications = eco_codes.len();
1434
1435        let avg_moves_per_position = if total_positions > 0 {
1436            self.entries
1437                .values()
1438                .map(|entry| entry.best_moves.len())
1439                .sum::<usize>() as f32
1440                / total_positions as f32
1441        } else {
1442            0.0
1443        };
1444
1445        OpeningBookStats {
1446            total_positions,
1447            eco_classifications,
1448            avg_moves_per_position,
1449        }
1450    }
1451}
1452
1453/// Opening book statistics
1454#[derive(Debug)]
1455pub struct OpeningBookStats {
1456    pub total_positions: usize,
1457    pub eco_classifications: usize,
1458    pub avg_moves_per_position: f32,
1459}
1460
1461#[cfg(test)]
1462mod tests {
1463    use super::*;
1464
1465    #[test]
1466    fn test_opening_book_creation() {
1467        let book = OpeningBook::new();
1468        assert_eq!(book.entries.len(), 0);
1469    }
1470
1471    #[test]
1472    fn test_standard_openings() {
1473        let book = OpeningBook::with_standard_openings();
1474        assert!(!book.entries.is_empty());
1475
1476        // Test starting position lookup
1477        let board = Board::default();
1478        let entry = book.lookup(&board);
1479        assert!(entry.is_some());
1480        assert_eq!(entry.unwrap().name, "Starting Position");
1481    }
1482
1483    #[test]
1484    fn test_opening_lookup() {
1485        let mut book = OpeningBook::new();
1486        let board = Board::default();
1487
1488        // Should not be found initially
1489        assert!(!book.contains(&board));
1490
1491        // Add entry
1492        let moves = vec![(ChessMove::from_str("e2e4").unwrap(), 1.0)];
1493        book.add_opening(
1494            &board.to_string(),
1495            0.0,
1496            moves,
1497            "Test Opening".to_string(),
1498            None,
1499        )
1500        .unwrap();
1501
1502        // Should be found now
1503        assert!(book.contains(&board));
1504        let entry = book.lookup(&board).unwrap();
1505        assert_eq!(entry.name, "Test Opening");
1506        assert_eq!(entry.best_moves.len(), 1);
1507    }
1508
1509    #[test]
1510    fn test_stats() {
1511        let book = OpeningBook::with_standard_openings();
1512        let stats = book.stats();
1513
1514        assert!(stats.total_positions > 0);
1515        assert!(stats.avg_moves_per_position > 0.0);
1516    }
1517
1518    #[test]
1519    fn test_invalid_fen_handling() {
1520        let mut book = OpeningBook::new();
1521
1522        // Test adding entry with invalid FEN
1523        let moves = vec![(ChessMove::from_str("e2e4").unwrap(), 1.0)];
1524        let result = book.add_opening(
1525            "invalid_fen_string",
1526            0.0,
1527            moves,
1528            "Invalid Opening".to_string(),
1529            None,
1530        );
1531
1532        assert!(result.is_err());
1533        assert_eq!(book.entries.len(), 0);
1534    }
1535
1536    #[test]
1537    fn test_eco_code_filtering() {
1538        let book = OpeningBook::with_standard_openings();
1539        let stats = book.stats();
1540
1541        // Should have multiple ECO classifications
1542        assert!(stats.eco_classifications > 0);
1543
1544        // Count entries with ECO codes
1545        let eco_entries: Vec<_> = book
1546            .entries
1547            .values()
1548            .filter(|entry| entry.eco_code.is_some())
1549            .collect();
1550
1551        assert!(!eco_entries.is_empty());
1552
1553        // Check for some specific ECO codes
1554        let has_e4_openings = book.entries.values().any(|entry| {
1555            entry.eco_code.as_deref() == Some("C20") || entry.eco_code.as_deref() == Some("C44")
1556        });
1557        assert!(has_e4_openings);
1558    }
1559
1560    #[test]
1561    fn test_opening_move_strengths() {
1562        let book = OpeningBook::with_standard_openings();
1563        let board = Board::default();
1564
1565        if let Some(entry) = book.lookup(&board) {
1566            // All move strengths should be between 0.0 and 1.0
1567            for (_, strength) in &entry.best_moves {
1568                assert!(*strength >= 0.0 && *strength <= 1.0);
1569            }
1570
1571            // Should have at least one strong move
1572            let max_strength = entry
1573                .best_moves
1574                .iter()
1575                .map(|(_, strength)| *strength)
1576                .fold(0.0, f32::max);
1577            assert!(max_strength > 0.5);
1578        }
1579    }
1580
1581    #[test]
1582    fn test_non_starting_position_openings() {
1583        let book = OpeningBook::with_standard_openings();
1584
1585        // Test a well-known opening position (Italian Game)
1586        if let Ok(board) =
1587            Board::from_str("r1bqkbnr/pppp1ppp/2n5/4p3/2B1P3/5N2/PPPP1PPP/RNBQK2R b KQkq - 3 3")
1588        {
1589            let entry = book.lookup(&board);
1590            if entry.is_some() {
1591                let opening = entry.unwrap();
1592                assert!(!opening.best_moves.is_empty());
1593                assert!(
1594                    opening.name.contains("Italian")
1595                        || opening.name.contains("Game")
1596                        || opening.evaluation.abs() <= 1.0
1597                );
1598            }
1599        }
1600    }
1601
1602    #[test]
1603    fn test_opening_book_consistency() {
1604        let book = OpeningBook::with_standard_openings();
1605
1606        // Test that all entries have valid board positions
1607        for (fen, entry) in &book.entries {
1608            assert!(
1609                Board::from_str(fen).is_ok(),
1610                "Invalid FEN in opening book: {fen}",
1611            );
1612            assert!(!entry.name.is_empty(), "Empty opening name");
1613            assert!(
1614                !entry.best_moves.is_empty(),
1615                "No moves for opening: {}",
1616                entry.name
1617            );
1618
1619            // Evaluation should be reasonable
1620            assert!(
1621                entry.evaluation >= -10.0 && entry.evaluation <= 10.0,
1622                "Unreasonable evaluation: {}",
1623                entry.evaluation
1624            );
1625        }
1626    }
1627
1628    #[test]
1629    fn test_duplicate_position_handling() {
1630        let mut book = OpeningBook::new();
1631        let board = Board::default();
1632        let fen = board.to_string();
1633
1634        let moves1 = vec![(ChessMove::from_str("e2e4").unwrap(), 1.0)];
1635        let moves2 = vec![(ChessMove::from_str("d2d4").unwrap(), 0.9)];
1636
1637        // Add first entry
1638        let result1 = book.add_opening(
1639            &fen,
1640            0.0,
1641            moves1,
1642            "First Entry".to_string(),
1643            Some("E00".to_string()),
1644        );
1645        assert!(result1.is_ok());
1646        assert_eq!(book.entries.len(), 1);
1647
1648        // Add second entry for same position (should replace)
1649        let result2 = book.add_opening(
1650            &fen,
1651            0.1,
1652            moves2,
1653            "Second Entry".to_string(),
1654            Some("D00".to_string()),
1655        );
1656        assert!(result2.is_ok());
1657        assert_eq!(book.entries.len(), 1);
1658
1659        // Should have the second entry
1660        let entry = book.lookup(&board).unwrap();
1661        assert_eq!(entry.name, "Second Entry");
1662        assert_eq!(entry.evaluation, 0.1);
1663        assert_eq!(entry.eco_code, Some("D00".to_string()));
1664    }
1665
1666    #[test]
1667    fn test_advanced_opening_coverage() {
1668        let book = OpeningBook::with_standard_openings();
1669        let stats = book.stats();
1670
1671        // Should have substantial opening coverage (at least 40 positions)
1672        assert!(
1673            stats.total_positions >= 40,
1674            "Opening book should have substantial coverage, got {}",
1675            stats.total_positions
1676        );
1677
1678        // Should cover major opening families
1679        let opening_names: Vec<_> = book.entries.values().map(|entry| &entry.name).collect();
1680
1681        let has_sicilian = opening_names.iter().any(|name| name.contains("Sicilian"));
1682        let has_french = opening_names.iter().any(|name| name.contains("French"));
1683        let has_caro_kann = opening_names.iter().any(|name| name.contains("Caro"));
1684        let has_queens_gambit = opening_names.iter().any(|name| name.contains("Queen"));
1685
1686        assert!(
1687            has_sicilian || has_french || has_caro_kann || has_queens_gambit,
1688            "Opening book should cover major opening families"
1689        );
1690    }
1691}