bitboard: various changes and test fixes

This commit is contained in:
dece 2020-06-20 03:42:20 +02:00
parent b4dd16d87d
commit fce38693bf
4 changed files with 108 additions and 89 deletions

View file

@ -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<Square> {
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]

View file

@ -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.

View file

@ -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<Move> {
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);

View file

@ -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,