diff options
-rw-r--r-- | src/board/engine.rs | 152 | ||||
-rw-r--r-- | src/board/mod.rs (renamed from src/board.rs) | 141 |
2 files changed, 153 insertions, 140 deletions
diff --git a/src/board/engine.rs b/src/board/engine.rs new file mode 100644 index 0000000..c8b174f --- /dev/null +++ b/src/board/engine.rs @@ -0,0 +1,152 @@ +use crate::{bitboard::pop_count, board::*}; + +impl Board { + pub fn perft(&mut self, depth: u8, print: bool) -> (u64, u64, u64, u64, u64) { + if depth == 0 { + return (1, 0, 0, 0, 0) // This a leaf, exactly one node + } + let color = self.color_to_move(); + + let moves = self.generate_pseudolegal_moves(color); + + if print { + println!("Running perft for depth {}. Color to move is {:?}\n{} moves available", depth, color, moves.len()); + println!("{} moves available", moves.len()); + } + + let mut total = 0; + let mut captures = 0; + let mut checks = 0; + let mut castles = 0; + let mut en_passants = 0; + + for mov in moves { + let ep_target_before = self.ep_target.clone(); + let castling_rights_before = self.castling_rights.clone(); + let captured_piece = self.make_move(mov); + // King can not be in check after our own move + if !self.is_king_in_check(color) { + if depth == 1 { + match mov.kind { + MoveKind::Capture => { + captures += 1; + } + MoveKind::EnPassant => { + en_passants += 1; + captures += 1; + } + MoveKind::Castle => { + castles += 1; + } + _ => {} + } + if self.is_king_in_check(color.flip()) { + checks += 1; + } + } + + if print { + println!("{:?}", mov); + self.print(); + } + let (children_total, children_tactical, children_checks, children_castles, children_ep) = self.perft(depth - 1, print); + total += children_total; + captures += children_tactical; + checks += children_checks; + castles += children_castles; + en_passants += children_ep; + + } + self.unmake_move(mov, captured_piece, ep_target_before, castling_rights_before); + } + + if print { + println!("Found {} nodes in this subtree (depth {})", total, depth); + } + + (total, captures, checks, castles, en_passants) + } + pub fn evaluate(&self) -> f32 { + let mut eval = 0f32; + let pieces = self.pieces_by_color(self.color()); + eval += pop_count(pieces[PieceType::Pawn as usize]) as f32; + eval += pop_count(pieces[PieceType::Bishop as usize]) as f32 * 3.; + eval += pop_count(pieces[PieceType::Knight as usize]) as f32 * 3.; + eval += pop_count(pieces[PieceType::Rook as usize]) as f32 * 4.5; + eval += pop_count(pieces[PieceType::Queen as usize]) as f32 * 9.; + eval + } + + pub fn negamax_search(&mut self, mut alpha: f32, beta: f32, depth_left: u8) -> f32 { + let color = Color::from(self.ply as u8 % 2); + + self.print(); + + if depth_left == 0 { + return self.evaluate(); + } + let moves = self.generate_pseudolegal_moves(color); + + for mov in moves { + let ep_target_before = self.ep_target.clone(); + let castling_rights_before = self.castling_rights.clone(); + let captured_piece = self.make_move(mov); + + if !self.is_king_in_check(color) { + let evaluation = -self.negamax_search(-beta, -alpha, depth_left - 1); + self.unmake_move(mov, captured_piece, ep_target_before, castling_rights_before); + + if evaluation >= beta { + return beta; // Fail-hard beta-cutoff + } + if evaluation > alpha { + alpha = evaluation + } + } else { + self.unmake_move(mov, captured_piece, ep_target_before, castling_rights_before); + } + } + alpha + } + +} + + +#[cfg(test)] +mod tests { + use std::f32::INFINITY; + + use crate::board::Board; + + #[test] + fn perft() { + let mut board = Board::new(); + + assert_eq!(board.perft(0, false), (1, 0, 0, 0, 0)); + assert_eq!(board.perft(1, false), (20, 0, 0, 0, 0)); + assert_eq!(board.perft(2, false), (400, 0, 0, 0, 0)); + assert_eq!(board.perft(3, false), (8902, 34, 12, 0, 0)); + assert_eq!(board.perft(4, false), (197281, 1576, 469, 0, 0)); + // assert_eq!(board.perft(5, false), (4865609, 82719, 27351, 0, 258)); + // assert_eq!(board.perft(6, false), (119060324, 2812008, 809099, 0, 5248)); + } + + #[test] + fn position_perft() { + let fen = String::from("r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - "); + let mut board = Board::from_FEN(fen); + assert_eq!(board.perft(0, false), (1, 0, 0, 0, 0)); + assert_eq!(board.perft(1, false), (48, 8, 0, 2, 0)); + assert_eq!(board.perft(2, false), (2039, 351, 3, 91, 1)); + assert_eq!(board.perft(3, false), (97862, 17102, 993, 3162, 45)); + } + + #[test] + fn negamax_search() { + let fen = String::from("r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - "); + let mut board = Board::from_FEN(fen); + + let eval = board.negamax_search(-INFINITY, INFINITY, 4); + println!("{}", eval); + } +} diff --git a/src/board.rs b/src/board/mod.rs index 9ff81b1..10d0a73 100644 --- a/src/board.rs +++ b/src/board/mod.rs @@ -1,4 +1,5 @@ use crate::{bitboard::{Bitboard, serialize_bitboard, bitscan, pop_count}, moves::{Move, MoveKind}, attacks::Attacks, square::Square}; +mod engine; pub enum CastlingSide { King, @@ -501,72 +502,6 @@ impl Board { self.ply -= 1; } - pub fn perft(&mut self, depth: u8, print: bool) -> (u64, u64, u64, u64, u64) { - if depth == 0 { - return (1, 0, 0, 0, 0) // This a leaf, exactly one node - } - let color = self.color_to_move(); - - let moves = self.generate_pseudolegal_moves(color); - - if print { - println!("Running perft for depth {}. Color to move is {:?}\n{} moves available", depth, color, moves.len()); - println!("{} moves available", moves.len()); - } - - let mut total = 0; - let mut captures = 0; - let mut checks = 0; - let mut castles = 0; - let mut en_passants = 0; - - for mov in moves { - let ep_target_before = self.ep_target.clone(); - let castling_rights_before = self.castling_rights.clone(); - let captured_piece = self.make_move(mov); - // King can not be in check after our own move - if !self.is_king_in_check(color) { - if depth == 1 { - match mov.kind { - MoveKind::Capture => { - captures += 1; - } - MoveKind::EnPassant => { - en_passants += 1; - captures += 1; - } - MoveKind::Castle => { - castles += 1; - } - _ => {} - } - if self.is_king_in_check(color.flip()) { - checks += 1; - } - } - - if print { - println!("{:?}", mov); - self.print(); - } - let (children_total, children_tactical, children_checks, children_castles, children_ep) = self.perft(depth - 1, print); - total += children_total; - captures += children_tactical; - checks += children_checks; - castles += children_castles; - en_passants += children_ep; - - } - self.unmake_move(mov, captured_piece, ep_target_before, castling_rights_before); - } - - if print { - println!("Found {} nodes in this subtree (depth {})", total, depth); - } - - (total, captures, checks, castles, en_passants) - } - fn is_square_attacked(&self, square: Square, attacker_color: Color) -> bool { let square_bb = square.to_bitboard(); for (piece_type, piece) in self.pieces_by_color(attacker_color).iter().enumerate() { @@ -611,49 +546,6 @@ impl Board { let square = bitscan(king_bb); self.is_square_attacked(square, color.flip()) } - - fn evaluate(&self) -> f32 { - let mut eval = 0f32; - let pieces = self.pieces_by_color(self.color()); - eval += pop_count(pieces[PieceType::Pawn as usize]) as f32; - eval += pop_count(pieces[PieceType::Bishop as usize]) as f32 * 3.; - eval += pop_count(pieces[PieceType::Knight as usize]) as f32 * 3.; - eval += pop_count(pieces[PieceType::Rook as usize]) as f32 * 4.5; - eval += pop_count(pieces[PieceType::Queen as usize]) as f32 * 9.; - eval - } - - fn negamax_search(&mut self, mut alpha: f32, beta: f32, depth_left: u8) -> f32 { - let color = Color::from(self.ply as u8 % 2); - - self.print(); - - if depth_left == 0 { - return self.evaluate(); - } - let moves = self.generate_pseudolegal_moves(color); - - for mov in moves { - let ep_target_before = self.ep_target.clone(); - let castling_rights_before = self.castling_rights.clone(); - let captured_piece = self.make_move(mov); - - if !self.is_king_in_check(color) { - let evaluation = -self.negamax_search(-beta, -alpha, depth_left - 1); - self.unmake_move(mov, captured_piece, ep_target_before, castling_rights_before); - - if (evaluation >= beta) { - return beta; // Fail-hard beta-cutoff - } - if (evaluation > alpha) { - alpha = evaluation - } - } else { - self.unmake_move(mov, captured_piece, ep_target_before, castling_rights_before); - } - } - alpha - } } @@ -796,29 +688,6 @@ mod tests { } #[test] - fn perft() { - let mut board = Board::new(); - - assert_eq!(board.perft(0, false), (1, 0, 0, 0, 0)); - assert_eq!(board.perft(1, false), (20, 0, 0, 0, 0)); - assert_eq!(board.perft(2, false), (400, 0, 0, 0, 0)); - assert_eq!(board.perft(3, false), (8902, 34, 12, 0, 0)); - assert_eq!(board.perft(4, false), (197281, 1576, 469, 0, 0)); - // assert_eq!(board.perft(5, false), (4865609, 82719, 27351, 0, 258)); - // assert_eq!(board.perft(6, false), (119060324, 2812008, 809099, 0, 5248)); - } - - #[test] - fn position_perft() { - let fen = String::from("r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - "); - let mut board = Board::from_FEN(fen); - assert_eq!(board.perft(0, false), (1, 0, 0, 0, 0)); - assert_eq!(board.perft(1, false), (48, 8, 0, 2, 0)); - assert_eq!(board.perft(2, false), (2039, 351, 3, 91, 1)); - assert_eq!(board.perft(3, false), (97862, 17102, 993, 3162, 45)); - } - - #[test] fn is_square_attacked() { let board = Board::new(); @@ -828,12 +697,4 @@ mod tests { assert_eq!(board.is_square_attacked(Square::B6, Color::Black), true); } - #[test] - fn negamax_search() { - let fen = String::from("r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - "); - let mut board = Board::from_FEN(fen); - - let eval = board.negamax_search(-INFINITY, INFINITY, 4); - println!("{}", eval); - } } |