stats: work with bitboards
This commit is contained in:
parent
fce38693bf
commit
d958e617d0
95
src/board.rs
95
src/board.rs
|
@ -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();
|
||||||
|
|
116
src/stats.rs
116
src/stats.rs
|
@ -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 {
|
||||||
|
|
Reference in a new issue