From fce38693bf8cdb2045aafe59dcf16d8c6c99fc3c Mon Sep 17 00:00:00 2001 From: dece Date: Sat, 20 Jun 2020 03:42:20 +0200 Subject: [PATCH] bitboard: various changes and test fixes --- src/board.rs | 121 ++++++++++++++++++++++++++++-------------------- src/movement.rs | 36 +++++++------- src/rules.rs | 36 +++++++------- src/stats.rs | 4 +- 4 files changed, 108 insertions(+), 89 deletions(-) diff --git a/src/board.rs b/src/board.rs index 0a9896a..6484ab7 100644 --- a/src/board.rs +++ b/src/board.rs @@ -150,16 +150,16 @@ impl Board { pub const fn new() -> Board { Board { colors: [ - 0b11000000_11000000_11000000_11000000_11000000_11000000_11000000_11000000, - 0b00000011_00000011_00000011_00000011_00000011_00000011_00000011_00000011, + 0b00000011_00000011_00000011_00000011_00000011_00000011_00000011_00000011, // W + 0b11000000_11000000_11000000_11000000_11000000_11000000_11000000_11000000, // B ], pieces: [ - 0b01000010_01000010_01000010_01000010_01000010_01000010_01000010_01000010, - 0b00000000_00000000_10000001_00000000_00000000_10000001_00000000_00000000, - 0b00000000_10000001_00000000_00000000_00000000_00000000_10000001_00000000, - 0b10000001_00000000_00000000_00000000_00000000_00000000_00000000_10000001, - 0b00000000_00000000_00000000_10000001_00000000_00000000_00000000_00000000, - 0b00000000_00000000_00000000_00000000_10000001_00000000_00000000_00000000, + 0b01000010_01000010_01000010_01000010_01000010_01000010_01000010_01000010, // P + 0b00000000_00000000_10000001_00000000_00000000_10000001_00000000_00000000, // B + 0b00000000_10000001_00000000_00000000_00000000_00000000_10000001_00000000, // N + 0b10000001_00000000_00000000_00000000_00000000_00000000_00000000_10000001, // R + 0b00000000_00000000_00000000_00000000_10000001_00000000_00000000_00000000, // Q + 0b00000000_00000000_00000000_10000001_00000000_00000000_00000000_00000000, // K ] } } @@ -179,18 +179,18 @@ impl Board { let mut r = 7; for c in fen.chars() { match c { - 'r' => { board.set_square(f * 8 + r, BLACK, ROOK); f += 1 } - 'n' => { board.set_square(f * 8 + r, BLACK, KNIGHT); f += 1 } - 'b' => { board.set_square(f * 8 + r, BLACK, BISHOP); f += 1 } - 'q' => { board.set_square(f * 8 + r, BLACK, QUEEN); f += 1 } - 'k' => { board.set_square(f * 8 + r, BLACK, KING); f += 1 } - 'p' => { board.set_square(f * 8 + r, BLACK, PAWN); f += 1 } - 'R' => { board.set_square(f * 8 + r, WHITE, ROOK); f += 1 } - 'N' => { board.set_square(f * 8 + r, WHITE, KNIGHT); f += 1 } - 'B' => { board.set_square(f * 8 + r, WHITE, BISHOP); f += 1 } - 'Q' => { board.set_square(f * 8 + r, WHITE, QUEEN); f += 1 } - 'K' => { board.set_square(f * 8 + r, WHITE, KING); f += 1 } - 'P' => { board.set_square(f * 8 + r, WHITE, PAWN); f += 1 } + 'r' => { board.set_square(sq(f, r), BLACK, ROOK); f += 1 } + 'n' => { board.set_square(sq(f, r), BLACK, KNIGHT); f += 1 } + 'b' => { board.set_square(sq(f, r), BLACK, BISHOP); f += 1 } + 'q' => { board.set_square(sq(f, r), BLACK, QUEEN); f += 1 } + 'k' => { board.set_square(sq(f, r), BLACK, KING); f += 1 } + 'p' => { board.set_square(sq(f, r), BLACK, PAWN); f += 1 } + 'R' => { board.set_square(sq(f, r), WHITE, ROOK); f += 1 } + 'N' => { board.set_square(sq(f, r), WHITE, KNIGHT); f += 1 } + 'B' => { board.set_square(sq(f, r), WHITE, BISHOP); f += 1 } + 'Q' => { board.set_square(sq(f, r), WHITE, QUEEN); f += 1 } + 'K' => { board.set_square(sq(f, r), WHITE, KING); f += 1 } + 'P' => { board.set_square(sq(f, r), WHITE, PAWN); f += 1 } '/' => { f = 0; r -= 1; } d if d.is_digit(10) => { f += d.to_digit(10).unwrap() as i8 } _ => break, @@ -207,28 +207,46 @@ impl Board { self.colors[WHITE] | self.colors[BLACK] } + /// Get the bitboard of a color. + #[inline] + pub fn by_color(&self, color: Color) -> Bitboard { + self.colors[color] + } + + /// Get the bitboard of a piece type. + #[inline] + pub fn by_piece(&self, piece: Piece) -> Bitboard { + self.pieces[piece] + } + + /// Get the bitboard of a piece type for this color. + #[inline] + pub fn by_color_piece(&self, color: Color, piece: Piece) -> Bitboard { + self.by_color(color) & self.by_piece(piece) + } + /// True if this square is empty. pub fn is_empty(&self, square: Square) -> bool { self.combined() & bit_pos(square) == 0 } /// Get color type at position. It must hold a piece! - pub fn get_color(&self, square: Square) -> Color { + pub fn get_color_on(&self, square: Square) -> Color { let bp = bit_pos(square); - if (self.colors[WHITE] & bp) == 1 { WHITE } - else if (self.pieces[BLACK] & bp) == 1 { BLACK } + if (self.colors[WHITE] & bp) != 0 { WHITE } + else if (self.colors[BLACK] & bp) != 0 { BLACK } else { panic!("Empty square.") } } /// Get piece type at position. It must hold a piece! - pub fn get_piece(&self, square: Square) -> Piece { + pub fn get_piece_on(&self, square: Square) -> Piece { let bp = bit_pos(square); - if (self.pieces[PAWN] & bp) == 1 { PAWN } - else if (self.pieces[BISHOP] & bp) == 1 { BISHOP } - else if (self.pieces[KNIGHT] & bp) == 1 { KNIGHT } - else if (self.pieces[ROOK] & bp) == 1 { ROOK } - else if (self.pieces[QUEEN] & bp) == 1 { QUEEN } - else if (self.pieces[KING] & bp) == 1 { KING } + if (self.pieces[PAWN] & bp) != 0 { PAWN } + else if (self.pieces[BISHOP] & bp) != 0 { BISHOP } + else if (self.pieces[KNIGHT] & bp) != 0 { KNIGHT } + else if (self.pieces[ROOK] & bp) != 0 { ROOK } + else if (self.pieces[QUEEN] & bp) != 0 { QUEEN } + else if (self.pieces[KING] & bp) != 0 { KING } else { panic!("Empty square.") } } @@ -236,6 +254,7 @@ impl Board { #[inline] pub fn set_square(&mut self, square: Square, color: Color, piece: Piece) { self.colors[color] |= bit_pos(square); + self.colors[opposite(color)] &= !bit_pos(square); self.pieces[piece] |= bit_pos(square); } @@ -249,7 +268,7 @@ impl Board { /// Move a piece from a position to another, clearing initial position. #[inline] pub fn move_square(&mut self, source: Square, dest: Square) { - self.set_square(dest, self.get_color(source), self.get_piece(source)); + self.set_square(dest, self.get_color_on(source), self.get_piece_on(source)); self.clear_square(source); } @@ -257,7 +276,7 @@ impl Board { pub fn find_king(&self, color: Color) -> Option { let king_bb = self.colors[color] & self.pieces[KING]; for square in 0..64 { - if king_bb & bit_pos(square) == 1 { + if king_bb & bit_pos(square) != 0 { return Some(square) } } @@ -278,16 +297,16 @@ impl Board { /// Debug only: write a text view of the board. pub(crate) fn draw(&self, f: &mut dyn std::io::Write) { - let cbb = self.colors[WHITE] | self.colors[BLACK]; + let cbb = self.combined(); for rank in (0..8).rev() { let mut rank_str = String::with_capacity(8); for file in 0..8 { - let square = file * 8 + rank; + let square = sq(file, rank); let bp = bit_pos(square); let piece_char = if cbb & bp == 0 { '.' } else { - let (color, piece) = (self.get_color(square), self.get_piece(square)); + let (color, piece) = (self.get_color_on(square), self.get_piece_on(square)); let mut piece_char = match piece { PAWN => 'p', BISHOP => 'b', @@ -347,27 +366,27 @@ mod tests { #[test] fn test_get_color() { let b = Board::new(); - assert_eq!(b.get_color(A1), WHITE); - assert_eq!(b.get_color(A2), WHITE); - assert_eq!(b.get_color(A7), BLACK); - assert_eq!(b.get_color(A8), BLACK); - assert_eq!(b.get_color(D1), WHITE); - assert_eq!(b.get_color(D8), BLACK); - assert_eq!(b.get_color(E1), WHITE); - assert_eq!(b.get_color(E8), BLACK); + assert_eq!(b.get_color_on(A1), WHITE); + assert_eq!(b.get_color_on(A2), WHITE); + assert_eq!(b.get_color_on(A7), BLACK); + assert_eq!(b.get_color_on(A8), BLACK); + assert_eq!(b.get_color_on(D1), WHITE); + assert_eq!(b.get_color_on(D8), BLACK); + assert_eq!(b.get_color_on(E1), WHITE); + assert_eq!(b.get_color_on(E8), BLACK); } #[test] fn test_get_piece() { let b = Board::new(); - assert_eq!(b.get_piece(A1), ROOK); - assert_eq!(b.get_piece(A2), PAWN); - assert_eq!(b.get_piece(A7), PAWN); - assert_eq!(b.get_piece(A8), ROOK); - assert_eq!(b.get_piece(D1), QUEEN); - assert_eq!(b.get_piece(D8), QUEEN); - assert_eq!(b.get_piece(E1), KING); - assert_eq!(b.get_piece(E8), KING); + assert_eq!(b.get_piece_on(A1), ROOK); + assert_eq!(b.get_piece_on(A2), PAWN); + assert_eq!(b.get_piece_on(A7), PAWN); + assert_eq!(b.get_piece_on(A8), ROOK); + assert_eq!(b.get_piece_on(D1), QUEEN); + assert_eq!(b.get_piece_on(D8), QUEEN); + assert_eq!(b.get_piece_on(E1), KING); + assert_eq!(b.get_piece_on(E8), KING); } #[test] diff --git a/src/movement.rs b/src/movement.rs index 17c1bb0..2367529 100644 --- a/src/movement.rs +++ b/src/movement.rs @@ -64,9 +64,9 @@ impl Move { } // Else, check if the king or a rook moved to update castling options. else { - let color = board.get_color(self.dest); + let color = board.get_color_on(self.dest); if color == WHITE && game_state.castling & CASTLING_WH_MASK != 0 { - match board.get_piece(self.dest) { + match board.get_piece_on(self.dest) { KING => { if self.source == E1 { game_state.castling &= !CASTLING_WH_MASK; @@ -82,7 +82,7 @@ impl Move { _ => {} } } else if color == BLACK && game_state.castling & CASTLING_BL_MASK != 0 { - match board.get_piece(self.dest) { + match board.get_piece_on(self.dest) { KING => { if self.source == E8 { game_state.castling &= !CASTLING_BL_MASK; @@ -126,7 +126,7 @@ impl Move { } else { board.move_square(self.source, self.dest); if let Some(piece) = self.promotion { - let color = board.get_color(self.dest); + let color = board.get_color_on(self.dest); board.set_square(self.dest, color, piece); } } @@ -213,7 +213,7 @@ mod tests { use super::*; #[test] - fn test_apply_move_to_board() { + fn test_apply_to_board() { let mut b = Board::new_empty(); // Put 2 enemy knights on board. @@ -222,18 +222,18 @@ mod tests { // Move white knight in a position attacked by black knight. Move::new(D4, E6).apply_to_board(&mut b); assert!(b.is_empty(D4)); - assert_eq!(b.get_color(E6), WHITE); - assert_eq!(b.get_piece(E6), KNIGHT); + assert_eq!(b.get_color_on(E6), WHITE); + assert_eq!(b.get_piece_on(E6), KNIGHT); assert_eq!(b.num_pieces(), 2); // Sack it with black knight Move::new(F4, E6).apply_to_board(&mut b); - assert_eq!(b.get_color(E6), BLACK); - assert_eq!(b.get_piece(E6), KNIGHT); + assert_eq!(b.get_color_on(E6), BLACK); + assert_eq!(b.get_piece_on(E6), KNIGHT); assert_eq!(b.num_pieces(), 1); } #[test] - fn test_apply_move_to_castling() { + fn test_apply_to_castling() { let mut b = Board::new(); let mut gs = GameState::new(); assert_eq!(gs.castling, CASTLING_MASK); @@ -251,19 +251,19 @@ mod tests { b.clear_square(G8); // White queen-side castling. Move::new(E1, C1).apply_to(&mut b, &mut gs); - assert_eq!(b.get_color(C1), WHITE); - assert_eq!(b.get_piece(C1), KING); - assert_eq!(b.get_color(D1), WHITE); - assert_eq!(b.get_piece(D1), ROOK); + assert_eq!(b.get_color_on(C1), WHITE); + assert_eq!(b.get_piece_on(C1), KING); + assert_eq!(b.get_color_on(D1), WHITE); + assert_eq!(b.get_piece_on(D1), ROOK); assert!(b.is_empty(A1)); assert!(b.is_empty(E1)); assert_eq!(gs.castling, CASTLING_BL_MASK); // Black king-side castling. Move::new(E8, G8).apply_to(&mut b, &mut gs); - assert_eq!(b.get_color(G1), BLACK); - assert_eq!(b.get_piece(G1), KING); - assert_eq!(b.get_color(F1), BLACK); - assert_eq!(b.get_piece(F1), ROOK); + assert_eq!(b.get_color_on(G8), BLACK); + assert_eq!(b.get_piece_on(G8), KING); + assert_eq!(b.get_color_on(F8), BLACK); + assert_eq!(b.get_piece_on(F8), ROOK); assert!(b.is_empty(H8)); assert!(b.is_empty(E8)); // At the end, no more castling options for both sides. diff --git a/src/rules.rs b/src/rules.rs index a979a14..6701553 100644 --- a/src/rules.rs +++ b/src/rules.rs @@ -5,8 +5,8 @@ use crate::castling::*; use crate::fen; use crate::movement::Move; -const POS_MIN: i8 = 0; -const POS_MAX: i8 = 7; +pub const POS_MIN: i8 = 0; +pub const POS_MAX: i8 = 7; /// Characteristics of the state of a game. /// @@ -74,7 +74,7 @@ pub fn get_player_moves( if board.is_empty(square) { continue } - if board.get_color(square) == game_state.color { + if board.get_color_on(square) == game_state.color { moves.append( &mut get_piece_moves(board, game_state, square, game_state.color, commit) ); @@ -97,7 +97,7 @@ pub fn get_piece_moves( color: Color, commit: bool, ) -> Vec { - match board.get_piece(square) { + match board.get_piece_on(square) { PAWN => get_pawn_moves(board, game_state, square, color, commit), BISHOP => get_bishop_moves(board, game_state, square, color, commit), KNIGHT => get_knight_moves(board, game_state, square, color, commit), @@ -147,7 +147,7 @@ fn get_pawn_moves( if f - 1 >= POS_MIN { let diag = sq(f - 1, forward_r); if !board.is_empty(diag) { - let diag_color = board.get_color(diag); + let diag_color = board.get_color_on(diag); if let Some(m) = move_if_enemy(color, square, diag_color, diag, true) { if can_register(commit, board, game_state, &m) { moves.push(m); @@ -160,7 +160,7 @@ fn get_pawn_moves( if f + 1 <= POS_MAX { let diag = sq(f + 1, forward_r); if !board.is_empty(diag) { - let diag_color = board.get_color(diag); + let diag_color = board.get_color_on(diag); if let Some(m) = move_if_enemy(color, square, diag_color, diag, true) { if can_register(commit, board, game_state, &m) { moves.push(m); @@ -192,12 +192,12 @@ fn get_bishop_moves( // If this position is out of the board, stop looking in that direction. let ray_f = f + offset.0 * dist; - if ray_f <= POS_MIN || ray_f >= POS_MAX { + if ray_f < POS_MIN || ray_f > POS_MAX { views[dir] = false; continue } let ray_r = r + offset.1 * dist; - if ray_r <= POS_MIN || ray_r >= POS_MAX { + if ray_r < POS_MIN || ray_r > POS_MAX { views[dir] = false; continue } @@ -209,7 +209,7 @@ fn get_bishop_moves( moves.push(m); } } else { - let ray_color = board.get_color(ray_square); + let ray_color = board.get_color_on(ray_square); if let Some(m) = move_if_enemy(color, square, ray_color, ray_square, false) { if can_register(commit, board, game_state, &m) { moves.push(m); @@ -233,11 +233,11 @@ fn get_knight_moves( let mut moves = Vec::with_capacity(8); for offset in [(1, 2), (2, 1), (2, -1), (1, -2), (-1, -2), (-2, -1), (-2, 1), (-1, 2)].iter() { let ray_f = f + offset.0; - if ray_f <= POS_MIN || ray_f >= POS_MAX { + if ray_f < POS_MIN || ray_f > POS_MAX { continue } let ray_r = r + offset.1; - if ray_r <= POS_MIN || ray_r >= POS_MAX { + if ray_r < POS_MIN || ray_r > POS_MAX { continue } let ray_square = sq(ray_f, ray_r); @@ -248,7 +248,7 @@ fn get_knight_moves( moves.push(m); } } else { - let ray_color = board.get_color(ray_square); + let ray_color = board.get_color_on(ray_square); if let Some(m) = move_if_enemy(color, square, ray_color, ray_square, false) { if can_register(commit, board, game_state, &m) { moves.push(m); @@ -276,12 +276,12 @@ fn get_rook_moves( } let ray_f = f + offset.0 * dist; - if ray_f <= POS_MIN || ray_f >= POS_MAX { + if ray_f < POS_MIN || ray_f > POS_MAX { views[dir] = false; continue } let ray_r = r + offset.1 * dist; - if ray_r <= POS_MIN || ray_r >= POS_MAX { + if ray_r < POS_MIN || ray_r > POS_MAX { views[dir] = false; continue } @@ -293,7 +293,7 @@ fn get_rook_moves( moves.push(m); } } else { - let ray_color = board.get_color(ray_square); + let ray_color = board.get_color_on(ray_square); if let Some(m) = move_if_enemy(color, square, ray_color, ray_square, false) { if can_register(commit, board, game_state, &m) { moves.push(m); @@ -331,11 +331,11 @@ fn get_king_moves( let mut moves = Vec::with_capacity(8); for offset in [(-1, 1), (0, 1), (1, 1), (-1, 0), (1, 0), (-1, -1), (0, -1), (1, -1)].iter() { let ray_f = f + offset.0; - if ray_f <= POS_MIN || ray_f >= POS_MAX { + if ray_f < POS_MIN || ray_f > POS_MAX { continue } let ray_r = r + offset.1; - if ray_r <= POS_MIN || ray_r >= POS_MAX { + if ray_r < POS_MIN || ray_r > POS_MAX { continue } let ray_square = sq(ray_f, ray_r); @@ -346,7 +346,7 @@ fn get_king_moves( moves.push(m); } } else { - let ray_color = board.get_color(ray_square); + let ray_color = board.get_color_on(ray_square); if let Some(m) = move_if_enemy(color, square, ray_color, ray_square, false) { if can_register(commit, board, game_state, &m) { moves.push(m); diff --git a/src/stats.rs b/src/stats.rs index 6db8c5a..04dd0a2 100644 --- a/src/stats.rs +++ b/src/stats.rs @@ -67,10 +67,10 @@ impl BoardStats { for file in 0..8 { for rank in 0..8 { let square = sq(file, rank); - if board.is_empty(square) || board.get_color(square) != color { + if board.is_empty(square) || board.get_color_on(square) != color { continue } - match board.get_piece(square) { + match board.get_piece_on(square) { ROOK => self.num_rooks += 1, KNIGHT => self.num_knights += 1, BISHOP => self.num_bishops += 1,