diff options
author | eug-vs <eugene@eug-vs.xyz> | 2023-01-24 21:40:06 +0300 |
---|---|---|
committer | eug-vs <eugene@eug-vs.xyz> | 2023-01-24 21:40:06 +0300 |
commit | e27b950db851c91231abf3f3a3afebae18af47af (patch) | |
tree | 74bffdf56b2a64163204374f5309ff36384d7549 /src/board/engine.rs | |
parent | 4a62a723f052ed0506cf9342c009cc315a8379a3 (diff) | |
download | chessnost-e27b950db851c91231abf3f3a3afebae18af47af.tar.gz |
refactor: separate engine into submodule
Diffstat (limited to 'src/board/engine.rs')
-rw-r--r-- | src/board/engine.rs | 152 |
1 files changed, 152 insertions, 0 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); + } +} |