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.
|
||||
|
||||
/// 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();
|
||||
|
|
116
src/stats.rs
116
src/stats.rs
|
@ -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 {
|
||||
|
|
Reference in a new issue