aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/board/engine.rs152
-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);
- }
}