aboutsummaryrefslogtreecommitdiff
path: root/src/board/engine.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/board/engine.rs')
-rw-r--r--src/board/engine.rs152
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);
+ }
+}