rules: use generic bitboard move generation
For bishops, knights, rooks and queens that is.
This commit is contained in:
parent
5efdd5407b
commit
ce62d3ab3a
30
res/scripts/gen_king_rays.py
Executable file
30
res/scripts/gen_king_rays.py
Executable file
|
@ -0,0 +1,30 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Pre-compute king ras bitboards for each square."""
|
||||
|
||||
TEMPLATE = """\
|
||||
/// Pre-computed king rays.
|
||||
const KING_RAYS: [Bitboard; 64] = [
|
||||
{}
|
||||
];
|
||||
"""
|
||||
|
||||
DIRS = [(1, 0), (1, 1), (0, 1), (-1, 1), (-1, 0), (-1, -1), (0, -1), (1, -1)]
|
||||
|
||||
def bit_pos(square):
|
||||
return 1 << square
|
||||
|
||||
def get_rays():
|
||||
rays = []
|
||||
for f in range(8):
|
||||
for r in range(8):
|
||||
bitboard = 0
|
||||
for dir_f, dir_r in DIRS:
|
||||
ray_f = f + dir_f
|
||||
ray_r = r + dir_r
|
||||
if ray_f < 0 or ray_f > 7 or ray_r < 0 or ray_r > 7:
|
||||
continue
|
||||
bitboard |= bit_pos(ray_f * 8 + ray_r)
|
||||
rays.append(" 0b{:064b},".format(bitboard))
|
||||
return rays
|
||||
|
||||
print(TEMPLATE.format("\n".join(get_rays())))
|
|
@ -2,6 +2,7 @@
|
|||
"""Pre-compute knight ray bitboards for each square."""
|
||||
|
||||
TEMPLATE = """\
|
||||
/// Pre-computed knight rays.
|
||||
const KNIGHT_RAYS: [Bitboard; 64] = [
|
||||
{}
|
||||
];
|
||||
|
|
79
src/board.rs
79
src/board.rs
|
@ -99,6 +99,7 @@ pub const H5: Square = 60;
|
|||
pub const H6: Square = 61;
|
||||
pub const H7: Square = 62;
|
||||
pub const H8: Square = 63;
|
||||
pub const NUM_SQUARES: Square = 64;
|
||||
|
||||
/// Get square from file and rank, both starting from 0.
|
||||
#[inline]
|
||||
|
@ -192,9 +193,8 @@ pub const fn after_on_file(file: i8, rank: i8) -> Bitboard {
|
|||
FILES[file as usize] & bits_after(file, rank)
|
||||
}
|
||||
|
||||
/// Debug only: count positive bits of the bitboard.
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn count_bits(bitboard: Bitboard) -> u8 {
|
||||
/// Count positive bits of the bitboard.
|
||||
pub fn count_bits(bitboard: Bitboard) -> u8 {
|
||||
let mut bitboard = bitboard;
|
||||
let mut count = 0;
|
||||
while bitboard > 0 {
|
||||
|
@ -216,6 +216,75 @@ pub(crate) fn draw_bits(bitboard: Bitboard, f: &mut dyn std::io::Write) {
|
|||
}
|
||||
}
|
||||
|
||||
// Generated by gen_king_rays.py.
|
||||
/// Pre-computed king rays.
|
||||
const KING_RAYS: [Bitboard; 64] = [
|
||||
0b00000000_00000000_00000000_00000000_00000000_00000000_00000011_00000010,
|
||||
0b00000000_00000000_00000000_00000000_00000000_00000000_00000111_00000101,
|
||||
0b00000000_00000000_00000000_00000000_00000000_00000000_00001110_00001010,
|
||||
0b00000000_00000000_00000000_00000000_00000000_00000000_00011100_00010100,
|
||||
0b00000000_00000000_00000000_00000000_00000000_00000000_00111000_00101000,
|
||||
0b00000000_00000000_00000000_00000000_00000000_00000000_01110000_01010000,
|
||||
0b00000000_00000000_00000000_00000000_00000000_00000000_11100000_10100000,
|
||||
0b00000000_00000000_00000000_00000000_00000000_00000000_11000000_01000000,
|
||||
0b00000000_00000000_00000000_00000000_00000000_00000011_00000010_00000011,
|
||||
0b00000000_00000000_00000000_00000000_00000000_00000111_00000101_00000111,
|
||||
0b00000000_00000000_00000000_00000000_00000000_00001110_00001010_00001110,
|
||||
0b00000000_00000000_00000000_00000000_00000000_00011100_00010100_00011100,
|
||||
0b00000000_00000000_00000000_00000000_00000000_00111000_00101000_00111000,
|
||||
0b00000000_00000000_00000000_00000000_00000000_01110000_01010000_01110000,
|
||||
0b00000000_00000000_00000000_00000000_00000000_11100000_10100000_11100000,
|
||||
0b00000000_00000000_00000000_00000000_00000000_11000000_01000000_11000000,
|
||||
0b00000000_00000000_00000000_00000000_00000011_00000010_00000011_00000000,
|
||||
0b00000000_00000000_00000000_00000000_00000111_00000101_00000111_00000000,
|
||||
0b00000000_00000000_00000000_00000000_00001110_00001010_00001110_00000000,
|
||||
0b00000000_00000000_00000000_00000000_00011100_00010100_00011100_00000000,
|
||||
0b00000000_00000000_00000000_00000000_00111000_00101000_00111000_00000000,
|
||||
0b00000000_00000000_00000000_00000000_01110000_01010000_01110000_00000000,
|
||||
0b00000000_00000000_00000000_00000000_11100000_10100000_11100000_00000000,
|
||||
0b00000000_00000000_00000000_00000000_11000000_01000000_11000000_00000000,
|
||||
0b00000000_00000000_00000000_00000011_00000010_00000011_00000000_00000000,
|
||||
0b00000000_00000000_00000000_00000111_00000101_00000111_00000000_00000000,
|
||||
0b00000000_00000000_00000000_00001110_00001010_00001110_00000000_00000000,
|
||||
0b00000000_00000000_00000000_00011100_00010100_00011100_00000000_00000000,
|
||||
0b00000000_00000000_00000000_00111000_00101000_00111000_00000000_00000000,
|
||||
0b00000000_00000000_00000000_01110000_01010000_01110000_00000000_00000000,
|
||||
0b00000000_00000000_00000000_11100000_10100000_11100000_00000000_00000000,
|
||||
0b00000000_00000000_00000000_11000000_01000000_11000000_00000000_00000000,
|
||||
0b00000000_00000000_00000011_00000010_00000011_00000000_00000000_00000000,
|
||||
0b00000000_00000000_00000111_00000101_00000111_00000000_00000000_00000000,
|
||||
0b00000000_00000000_00001110_00001010_00001110_00000000_00000000_00000000,
|
||||
0b00000000_00000000_00011100_00010100_00011100_00000000_00000000_00000000,
|
||||
0b00000000_00000000_00111000_00101000_00111000_00000000_00000000_00000000,
|
||||
0b00000000_00000000_01110000_01010000_01110000_00000000_00000000_00000000,
|
||||
0b00000000_00000000_11100000_10100000_11100000_00000000_00000000_00000000,
|
||||
0b00000000_00000000_11000000_01000000_11000000_00000000_00000000_00000000,
|
||||
0b00000000_00000011_00000010_00000011_00000000_00000000_00000000_00000000,
|
||||
0b00000000_00000111_00000101_00000111_00000000_00000000_00000000_00000000,
|
||||
0b00000000_00001110_00001010_00001110_00000000_00000000_00000000_00000000,
|
||||
0b00000000_00011100_00010100_00011100_00000000_00000000_00000000_00000000,
|
||||
0b00000000_00111000_00101000_00111000_00000000_00000000_00000000_00000000,
|
||||
0b00000000_01110000_01010000_01110000_00000000_00000000_00000000_00000000,
|
||||
0b00000000_11100000_10100000_11100000_00000000_00000000_00000000_00000000,
|
||||
0b00000000_11000000_01000000_11000000_00000000_00000000_00000000_00000000,
|
||||
0b00000011_00000010_00000011_00000000_00000000_00000000_00000000_00000000,
|
||||
0b00000111_00000101_00000111_00000000_00000000_00000000_00000000_00000000,
|
||||
0b00001110_00001010_00001110_00000000_00000000_00000000_00000000_00000000,
|
||||
0b00011100_00010100_00011100_00000000_00000000_00000000_00000000_00000000,
|
||||
0b00111000_00101000_00111000_00000000_00000000_00000000_00000000_00000000,
|
||||
0b01110000_01010000_01110000_00000000_00000000_00000000_00000000_00000000,
|
||||
0b11100000_10100000_11100000_00000000_00000000_00000000_00000000_00000000,
|
||||
0b11000000_01000000_11000000_00000000_00000000_00000000_00000000_00000000,
|
||||
0b00000010_00000011_00000000_00000000_00000000_00000000_00000000_00000000,
|
||||
0b00000101_00000111_00000000_00000000_00000000_00000000_00000000_00000000,
|
||||
0b00001010_00001110_00000000_00000000_00000000_00000000_00000000_00000000,
|
||||
0b00010100_00011100_00000000_00000000_00000000_00000000_00000000_00000000,
|
||||
0b00101000_00111000_00000000_00000000_00000000_00000000_00000000_00000000,
|
||||
0b01010000_01110000_00000000_00000000_00000000_00000000_00000000_00000000,
|
||||
0b10100000_11100000_00000000_00000000_00000000_00000000_00000000_00000000,
|
||||
0b01000000_11000000_00000000_00000000_00000000_00000000_00000000_00000000,
|
||||
];
|
||||
|
||||
// Generated by gen_knight_rays.py.
|
||||
/// Pre-computed knight rays.
|
||||
const KNIGHT_RAYS: [Bitboard; 64] = [
|
||||
|
@ -477,6 +546,10 @@ impl Board {
|
|||
KNIGHT_RAYS[square as usize] & !self.by_color(color)
|
||||
}
|
||||
|
||||
pub fn get_king_rays(&self, square: Square, color: Color) -> Bitboard {
|
||||
KING_RAYS[square as usize] & !self.by_color(color)
|
||||
}
|
||||
|
||||
/// Debug only: write a text view of the board.
|
||||
pub(crate) fn draw(&self, f: &mut dyn std::io::Write) {
|
||||
let cbb = self.combined();
|
||||
|
|
233
src/rules.rs
233
src/rules.rs
|
@ -178,37 +178,15 @@ fn get_bishop_moves(
|
|||
color: Color,
|
||||
pseudo_legal: bool,
|
||||
) -> Vec<Move> {
|
||||
let (f, r) = (sq_file(square), sq_rank(square));
|
||||
let mut views = [true; 4]; // Store diagonals where a piece blocks commit.
|
||||
let mut moves = Vec::with_capacity(8);
|
||||
for dist in 1..=7 {
|
||||
for (dir, offset) in [(1, -1), (1, 1), (-1, 1), (-1, -1)].iter().enumerate() {
|
||||
if !views[dir] {
|
||||
continue
|
||||
}
|
||||
|
||||
// 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 {
|
||||
views[dir] = false;
|
||||
continue
|
||||
}
|
||||
let ray_r = r + offset.1 * dist;
|
||||
if ray_r < POS_MIN || ray_r > POS_MAX {
|
||||
views[dir] = false;
|
||||
continue
|
||||
}
|
||||
let ray_square = sq(ray_f, ray_r);
|
||||
|
||||
match get_move_type(board, game_state, square, ray_square, color, pseudo_legal) {
|
||||
MoveType::Simple(m) => { moves.push(m) }
|
||||
MoveType::Capture(m) => { moves.push(m); views[dir] = false; }
|
||||
MoveType::CantTakeFriend => { views[dir] = false; }
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
moves
|
||||
get_moves_from_bb(
|
||||
board,
|
||||
game_state,
|
||||
board.get_bishop_rays(square, color),
|
||||
square,
|
||||
color,
|
||||
BISHOP,
|
||||
pseudo_legal
|
||||
)
|
||||
}
|
||||
|
||||
fn get_knight_moves(
|
||||
|
@ -218,25 +196,15 @@ fn get_knight_moves(
|
|||
color: Color,
|
||||
pseudo_legal: bool,
|
||||
) -> Vec<Move> {
|
||||
let (f, r) = (sq_file(square), sq_rank(square));
|
||||
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 {
|
||||
continue
|
||||
}
|
||||
let ray_r = r + offset.1;
|
||||
if ray_r < POS_MIN || ray_r > POS_MAX {
|
||||
continue
|
||||
}
|
||||
let ray_square = sq(ray_f, ray_r);
|
||||
|
||||
match get_move_type(board, game_state, square, ray_square, color, pseudo_legal) {
|
||||
MoveType::Simple(m) | MoveType::Capture(m) => moves.push(m),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
moves
|
||||
get_moves_from_bb(
|
||||
board,
|
||||
game_state,
|
||||
board.get_knight_rays(square, color),
|
||||
square,
|
||||
color,
|
||||
KNIGHT,
|
||||
pseudo_legal
|
||||
)
|
||||
}
|
||||
|
||||
fn get_rook_moves(
|
||||
|
@ -246,36 +214,15 @@ fn get_rook_moves(
|
|||
color: Color,
|
||||
pseudo_legal: bool,
|
||||
) -> Vec<Move> {
|
||||
let (f, r) = (sq_file(square), sq_rank(square));
|
||||
let mut moves = Vec::with_capacity(8);
|
||||
let mut views = [true; 4]; // Store lines where a piece blocks commit.
|
||||
for dist in 1..=7 {
|
||||
for (dir, offset) in [(0, 1), (1, 0), (0, -1), (-1, 0)].iter().enumerate() {
|
||||
if !views[dir] {
|
||||
continue
|
||||
}
|
||||
|
||||
let ray_f = f + offset.0 * dist;
|
||||
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 {
|
||||
views[dir] = false;
|
||||
continue
|
||||
}
|
||||
let ray_square = sq(ray_f, ray_r);
|
||||
|
||||
match get_move_type(board, game_state, square, ray_square, color, pseudo_legal) {
|
||||
MoveType::Simple(m) => { moves.push(m) }
|
||||
MoveType::Capture(m) => { moves.push(m); views[dir] = false; }
|
||||
MoveType::CantTakeFriend => { views[dir] = false; }
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
moves
|
||||
get_moves_from_bb(
|
||||
board,
|
||||
game_state,
|
||||
board.get_rook_rays(square, color),
|
||||
square,
|
||||
color,
|
||||
ROOK,
|
||||
pseudo_legal
|
||||
)
|
||||
}
|
||||
|
||||
fn get_queen_moves(
|
||||
|
@ -285,11 +232,15 @@ fn get_queen_moves(
|
|||
color: Color,
|
||||
pseudo_legal: bool,
|
||||
) -> Vec<Move> {
|
||||
let mut moves = Vec::with_capacity(16);
|
||||
// Easy way to get queen moves, but may be a bit quicker if everything was rewritten here.
|
||||
moves.append(&mut get_bishop_moves(board, game_state, square, color, pseudo_legal));
|
||||
moves.append(&mut get_rook_moves(board, game_state, square, color, pseudo_legal));
|
||||
moves
|
||||
get_moves_from_bb(
|
||||
board,
|
||||
game_state,
|
||||
board.get_queen_rays(square, color),
|
||||
square,
|
||||
color,
|
||||
QUEEN,
|
||||
pseudo_legal
|
||||
)
|
||||
}
|
||||
|
||||
fn get_king_moves(
|
||||
|
@ -299,24 +250,15 @@ fn get_king_moves(
|
|||
color: Color,
|
||||
pseudo_legal: bool,
|
||||
) -> Vec<Move> {
|
||||
let (f, r) = (sq_file(square), sq_rank(square));
|
||||
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 {
|
||||
continue
|
||||
}
|
||||
let ray_r = r + offset.1;
|
||||
if ray_r < POS_MIN || ray_r > POS_MAX {
|
||||
continue
|
||||
}
|
||||
let ray_square = sq(ray_f, ray_r);
|
||||
|
||||
match get_move_type(board, game_state, square, ray_square, color, pseudo_legal) {
|
||||
MoveType::Simple(m) | MoveType::Capture(m) => moves.push(m),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
let mut moves = get_moves_from_bb(
|
||||
board,
|
||||
game_state,
|
||||
board.get_king_rays(square, color),
|
||||
square,
|
||||
color,
|
||||
KING,
|
||||
pseudo_legal
|
||||
);
|
||||
|
||||
// Stop here for pseudo legal moves as castling is not considered along with them.
|
||||
if pseudo_legal {
|
||||
|
@ -338,6 +280,7 @@ fn get_king_moves(
|
|||
(7, CASTLING_BL_MASK)
|
||||
};
|
||||
|
||||
let r = sq_rank(square);
|
||||
// Check for castling if the king is on its castling rank (R1)
|
||||
// and is not in check (R4).
|
||||
if
|
||||
|
@ -380,56 +323,56 @@ fn get_king_moves(
|
|||
moves
|
||||
}
|
||||
|
||||
/// Get moves from this ray bitboard.
|
||||
fn get_moves_from_bb(
|
||||
board: &Board,
|
||||
game_state: &GameState,
|
||||
bitboard: Bitboard,
|
||||
square: Square,
|
||||
color: Color,
|
||||
piece: Piece,
|
||||
pseudo_legal: bool
|
||||
) -> Vec<Move> {
|
||||
let mut moves = Vec::with_capacity(count_bits(bitboard).into());
|
||||
for ray_square in 0..NUM_SQUARES {
|
||||
if ray_square == square || bitboard & bit_pos(ray_square) == 0 {
|
||||
continue
|
||||
}
|
||||
if let Some(mut m) = inspect_move(board, game_state, square, ray_square, pseudo_legal) {
|
||||
// Automatic queen promotion for pawns moving to the opposite rank.
|
||||
if
|
||||
piece == PAWN
|
||||
&& (color == WHITE && sq_rank(ray_square) == RANK_8)
|
||||
|| (color == BLACK && sq_rank(ray_square) == RANK_1)
|
||||
{
|
||||
m.promotion = Some(QUEEN);
|
||||
}
|
||||
moves.push(m);
|
||||
}
|
||||
}
|
||||
moves
|
||||
}
|
||||
|
||||
/// Accept or ignore a move from `square` to `ray_square`.
|
||||
fn get_move_type(
|
||||
///
|
||||
/// This function checks that the move is legal, unless `pseudo_legal`
|
||||
/// is true. It assumes that `ray_square` is either empty or an enemy
|
||||
/// piece, but not a friend piece: they should have been filtered.
|
||||
///
|
||||
/// This function does not set promotions for pawns reaching last rank.
|
||||
fn inspect_move(
|
||||
board: &Board,
|
||||
game_state: &GameState,
|
||||
square: Square,
|
||||
ray_square: Square,
|
||||
color: Color,
|
||||
commit: bool
|
||||
) -> MoveType {
|
||||
if board.is_empty(ray_square) {
|
||||
pseudo_legal: bool
|
||||
) -> Option<Move> {
|
||||
let m = Move::new(square, ray_square);
|
||||
if is_legal(commit, board, game_state, &m) {
|
||||
MoveType::Simple(m)
|
||||
if pseudo_legal || !is_illegal(board, game_state, &m) {
|
||||
Some(m)
|
||||
} else {
|
||||
MoveType::CantRegister
|
||||
None
|
||||
}
|
||||
} else {
|
||||
let ray_color = board.get_color_on(ray_square);
|
||||
if let Some(m) = get_capture_move(color, square, ray_color, ray_square, false) {
|
||||
if is_legal(commit, board, game_state, &m) {
|
||||
MoveType::Capture(m)
|
||||
} else {
|
||||
MoveType::CantRegister
|
||||
}
|
||||
} else {
|
||||
MoveType::CantTakeFriend
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum MoveType {
|
||||
/// Move to an empty square.
|
||||
Simple(Move),
|
||||
/// Capture an enemy piece.
|
||||
Capture(Move),
|
||||
/// May be illegal, or should not be committed, e.g. to test rays.
|
||||
CantRegister,
|
||||
/// Can't capture a friend piece.
|
||||
CantTakeFriend,
|
||||
}
|
||||
|
||||
/// Return true if `pseudo_legal` is true, or the move is not illegal,
|
||||
///
|
||||
/// Committing a move means that it can be safely played afterwards.
|
||||
/// Sometimes it is not what is needed to accept a move in a collection
|
||||
/// of moves, e.g. when simply checking if some moves would make a
|
||||
/// previous move illegal.
|
||||
#[inline]
|
||||
fn is_legal(pseudo_legal: bool, board: &Board, game_state: &GameState, m: &Move) -> bool {
|
||||
pseudo_legal || !is_illegal(board, game_state, m)
|
||||
}
|
||||
|
||||
/// Return a move from `square1` to `square2` if colors are opposite.
|
||||
|
|
Reference in a new issue