stats: work with bitboards

This commit is contained in:
dece 2020-06-20 18:46:37 +02:00
parent fce38693bf
commit d958e617d0
2 changed files with 143 additions and 68 deletions

View file

@ -1,8 +1,5 @@
//! Basic type definitions and functions. //! Basic type definitions and functions.
/// Bitboard for color or piece bits.
pub type Bitboard = u64;
/// Color type, used to index `Board.color`. /// Color type, used to index `Board.color`.
pub type Color = usize; pub type Color = usize;
@ -137,6 +134,56 @@ pub fn sq_to_string(square: Square) -> String {
String::from_utf8_lossy(&bytes).to_string() String::from_utf8_lossy(&bytes).to_string()
} }
/// Bitboard for color or piece bits.
pub type Bitboard = u64;
pub const FILES: [Bitboard; 8] = [
0b00000000_00000000_00000000_00000000_00000000_00000000_00000000_11111111,
0b00000000_00000000_00000000_00000000_00000000_00000000_11111111_00000000,
0b00000000_00000000_00000000_00000000_00000000_11111111_00000000_00000000,
0b00000000_00000000_00000000_00000000_11111111_00000000_00000000_00000000,
0b00000000_00000000_00000000_11111111_00000000_00000000_00000000_00000000,
0b00000000_00000000_11111111_00000000_00000000_00000000_00000000_00000000,
0b00000000_11111111_00000000_00000000_00000000_00000000_00000000_00000000,
0b11111111_00000000_00000000_00000000_00000000_00000000_00000000_00000000,
];
/// Get the bitboard of bits before the square ("left-most" bits).
#[inline]
const fn bits_before(file: i8, rank: i8) -> Bitboard {
(1 << sq(file, rank)) - 1
}
/// Get the bitboard of bits after the square ("right-most" bits).
#[inline]
const fn bits_after(file: i8, rank: i8) -> Bitboard {
!bits_before(file, rank) << 1
}
/// Get the bitboard of squares on lower ranks of the file.
#[inline]
pub const fn before_on_file(file: i8, rank: i8) -> Bitboard {
FILES[file as usize] & bits_before(file, rank)
}
/// Get the bitboard of squares on upper ranks of the file.
#[inline]
pub const fn after_on_file(file: i8, rank: i8) -> Bitboard {
FILES[file as usize] & bits_after(file, rank)
}
/// Get the bitboard of squares on lower ranks of the `square` file.
#[inline]
pub const fn before_on_square_file(square: Square) -> Bitboard {
before_on_file(sq_file(square), sq_rank(square))
}
/// Get the bitboard of squares on upper ranks of the `square` file.
#[inline]
pub const fn after_on_square_file(square: Square) -> Bitboard {
after_on_file(sq_file(square), sq_rank(square))
}
/// Board representation with color/piece bitboards. /// Board representation with color/piece bitboards.
#[derive(Clone, PartialEq)] #[derive(Clone, PartialEq)]
pub struct Board { pub struct Board {
@ -221,7 +268,7 @@ impl Board {
/// Get the bitboard of a piece type for this color. /// Get the bitboard of a piece type for this color.
#[inline] #[inline]
pub fn by_color_piece(&self, color: Color, piece: Piece) -> Bitboard { pub fn by_color_and_piece(&self, color: Color, piece: Piece) -> Bitboard {
self.by_color(color) & self.by_piece(piece) self.by_color(color) & self.by_piece(piece)
} }
@ -333,12 +380,16 @@ impl Board {
mod tests { mod tests {
use super::*; use super::*;
// Color
#[test] #[test]
fn test_opposite() { fn test_opposite() {
assert_eq!(opposite(WHITE), BLACK); assert_eq!(opposite(WHITE), BLACK);
assert_eq!(opposite(BLACK), WHITE); assert_eq!(opposite(BLACK), WHITE);
} }
// Square
#[test] #[test]
fn test_sq_from_string() { fn test_sq_from_string() {
assert_eq!(sq_from_string("a1"), A1); assert_eq!(sq_from_string("a1"), A1);
@ -356,6 +407,42 @@ mod tests {
assert_eq!(sq_to_string(H8), "h8"); assert_eq!(sq_to_string(H8), "h8");
} }
// Bitboard
#[test]
fn test_before_on_square_file() {
// Only should the 4 lowest files for readability.
assert_eq!(before_on_square_file(A1), 0b00000000_00000000_00000000_00000000);
assert_eq!(before_on_square_file(A2), 0b00000000_00000000_00000000_00000001);
assert_eq!(before_on_square_file(A4), 0b00000000_00000000_00000000_00000111);
assert_eq!(before_on_square_file(A8), 0b00000000_00000000_00000000_01111111);
assert_eq!(before_on_square_file(B1), 0b00000000_00000000_00000000_00000000);
assert_eq!(before_on_square_file(C1), 0b00000000_00000000_00000000_00000000);
assert_eq!(before_on_square_file(C4), 0b00000000_00000111_00000000_00000000);
// 4 highest files.
assert_eq!(before_on_square_file(H4), 0b00000111_00000000_00000000_00000000 << 32);
assert_eq!(before_on_square_file(H7), 0b00111111_00000000_00000000_00000000 << 32);
assert_eq!(before_on_square_file(H8), 0b01111111_00000000_00000000_00000000 << 32);
}
#[test]
fn test_after_on_file() {
assert_eq!(after_on_square_file(A1), 0b00000000_00000000_00000000_11111110);
assert_eq!(after_on_square_file(A2), 0b00000000_00000000_00000000_11111100);
assert_eq!(after_on_square_file(A4), 0b00000000_00000000_00000000_11110000);
assert_eq!(after_on_square_file(A8), 0b00000000_00000000_00000000_00000000);
assert_eq!(after_on_square_file(B1), 0b00000000_00000000_11111110_00000000);
assert_eq!(after_on_square_file(C1), 0b00000000_11111110_00000000_00000000);
assert_eq!(after_on_square_file(C4), 0b00000000_11110000_00000000_00000000);
assert_eq!(after_on_square_file(C8), 0b00000000_00000000_00000000_00000000);
// 4 highest files.
assert_eq!(after_on_square_file(H4), 0b11110000_00000000_00000000_00000000 << 32);
assert_eq!(after_on_square_file(H7), 0b10000000_00000000_00000000_00000000 << 32);
assert_eq!(after_on_square_file(H8), 0b00000000_00000000_00000000_00000000 << 32);
}
// Board
#[test] #[test]
fn test_new_from_fen() { fn test_new_from_fen() {
let b1 = Board::new(); let b1 = Board::new();

View file

@ -1,7 +1,7 @@
//! Board statistics used for heuristics. //! Board statistics used for heuristics.
use crate::board::*; use crate::board::*;
use crate::rules::{GameState, get_player_moves}; use crate::rules::{GameState, get_player_moves, POS_MIN, POS_MAX};
/// Storage for board pieces stats. /// Storage for board pieces stats.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
@ -78,75 +78,63 @@ impl BoardStats {
KING => self.num_kings += 1, KING => self.num_kings += 1,
PAWN => { PAWN => {
self.num_pawns += 1; self.num_pawns += 1;
// FIXME redo pawn stats properly let pawn_bb = board.by_color_and_piece(color, PAWN);
// let mut doubled = false;
// let mut isolated = true; // Check for doubled pawns.
// let mut backward = true; let file_bb = FILES[file as usize];
// for r in 0..8 { if (pawn_bb ^ bit_pos(square)) & file_bb != 0 {
// // Check for doubled pawns. self.num_doubled_pawns += 1;
// if !doubled { }
// let other_color = board.get_color(sq(file, r));
// is_piece(get_square(board, &(pos_f, r)), color|SQ_P) && r != pos_r // Check for isolated and backward pawns.
// doubled = true; let (iso_on_prev_file, bw_on_prev_file) = if file > POS_MIN {
// } self.find_isolated_and_backward(pawn_bb, square, color, file - 1)
// // Check for isolated pawns. } else {
// if (true, true)
// isolated && };
// ( let (iso_on_next_file, bw_on_next_file) = if file < POS_MAX {
// // Check on the left file if not on a-file... self.find_isolated_and_backward(pawn_bb, square, color, file + 1)
// ( } else {
// pos_f > POS_MIN && (true, true)
// is_piece(get_square(board, &(pos_f - 1, r)), color|SQ_P) };
// ) || if iso_on_prev_file && iso_on_next_file {
// // Check on the right file if not on h-file... self.num_isolated_pawns += 1;
// ( }
// pos_f < POS_MAX && if bw_on_prev_file && bw_on_next_file {
// is_piece(get_square(board, &(pos_f + 1, r)), color|SQ_P) self.num_backward_pawns += 1;
// ) }
// )
// {
// isolated = false;
// }
// // Check for backward pawns.
// if backward {
// if color == SQ_WH && r <= pos_r {
// if (
// pos_f > POS_MIN &&
// is_type(get_square(board, &(pos_f - 1, r)), SQ_P)
// ) || (
// pos_f < POS_MAX &&
// is_type(get_square(board, &(pos_f + 1, r)), SQ_P)
// ) {
// backward = false;
// }
// } else if color == SQ_BL && r >= pos_r {
// if (
// pos_f > POS_MIN &&
// is_type(get_square(board, &(pos_f - 1, r)), SQ_P)
// ) || (
// pos_f < POS_MAX &&
// is_type(get_square(board, &(pos_f + 1, r)), SQ_P)
// ) {
// backward = false;
// }
// }
// }
// }
// if doubled {
// self.num_doubled_pawns += 1;
// }
// if isolated {
// self.num_isolated_pawns += 1;
// }
// if backward {
// self.num_backward_pawns += 1;
// }
}, },
_ => {} _ => {}
} }
} }
} }
} }
/// Find isolated and backward pawns from `square` perspective.
///
/// `bb` is the bitboard of `color`. `square` is only used to have
/// the reference rank. `file` is the file to inspect. To detect
/// isolated and backward pawns, `bb` should be the bitboard of
/// pawns of `color`.
fn find_isolated_and_backward(
&mut self,
bb: Bitboard,
square: Square,
color: Color,
file: i8
) -> (bool, bool) {
if bb & FILES[file as usize] == 0 {
// If the piece is isolated for this file, it's backward as well.
(true, true)
} else {
let backward_file_bb = if color == WHITE {
before_on_file(file, sq_rank(square)) | bit_pos(sq(file, sq_rank(square)))
} else {
after_on_file(file, sq_rank(square)) | bit_pos(sq(file, sq_rank(square)))
};
(false, bb & backward_file_bb == 0)
}
}
} }
impl std::fmt::Display for BoardStats { impl std::fmt::Display for BoardStats {