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.
/// Bitboard for color or piece bits.
pub type Bitboard = u64;
/// Color type, used to index `Board.color`.
pub type Color = usize;
@ -137,6 +134,56 @@ pub fn sq_to_string(square: Square) -> 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.
#[derive(Clone, PartialEq)]
pub struct Board {
@ -221,7 +268,7 @@ impl Board {
/// Get the bitboard of a piece type for this color.
#[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)
}
@ -333,12 +380,16 @@ impl Board {
mod tests {
use super::*;
// Color
#[test]
fn test_opposite() {
assert_eq!(opposite(WHITE), BLACK);
assert_eq!(opposite(BLACK), WHITE);
}
// Square
#[test]
fn test_sq_from_string() {
assert_eq!(sq_from_string("a1"), A1);
@ -356,6 +407,42 @@ mod tests {
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]
fn test_new_from_fen() {
let b1 = Board::new();

View file

@ -1,7 +1,7 @@
//! Board statistics used for heuristics.
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.
#[derive(Debug, Clone, PartialEq)]
@ -78,75 +78,63 @@ impl BoardStats {
KING => self.num_kings += 1,
PAWN => {
self.num_pawns += 1;
// FIXME redo pawn stats properly
// let mut doubled = false;
// let mut isolated = true;
// let mut backward = true;
// for r in 0..8 {
// // Check for doubled pawns.
// 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
// doubled = true;
// }
// // Check for isolated pawns.
// if
// isolated &&
// (
// // Check on the left file if not on a-file...
// (
// pos_f > POS_MIN &&
// is_piece(get_square(board, &(pos_f - 1, r)), color|SQ_P)
// ) ||
// // Check on the right file if not on h-file...
// (
// pos_f < POS_MAX &&
// is_piece(get_square(board, &(pos_f + 1, r)), color|SQ_P)
// )
// )
// {
// 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;
// }
let pawn_bb = board.by_color_and_piece(color, PAWN);
// Check for doubled pawns.
let file_bb = FILES[file as usize];
if (pawn_bb ^ bit_pos(square)) & file_bb != 0 {
self.num_doubled_pawns += 1;
}
// Check for isolated and backward pawns.
let (iso_on_prev_file, bw_on_prev_file) = if file > POS_MIN {
self.find_isolated_and_backward(pawn_bb, square, color, file - 1)
} else {
(true, true)
};
let (iso_on_next_file, bw_on_next_file) = if file < POS_MAX {
self.find_isolated_and_backward(pawn_bb, square, color, file + 1)
} else {
(true, true)
};
if iso_on_prev_file && iso_on_next_file {
self.num_isolated_pawns += 1;
}
if bw_on_prev_file && bw_on_next_file {
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 {