Merge branch 'checking-pieces-struct'
This commit is contained in:
commit
efcc65d8d6
14 changed files with 407 additions and 129 deletions
39
Cargo.lock
generated
39
Cargo.lock
generated
|
@ -56,16 +56,40 @@ version = "2.4.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf"
|
checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "board"
|
|
||||||
version = "0.1.0"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "chessfriend_bitboard"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"chessfriend_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "chessfriend_core"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "chessfriend_move_generator"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"chessfriend_bitboard",
|
||||||
|
"chessfriend_core",
|
||||||
|
"chessfriend_position",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "chessfriend_position"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"chessfriend_bitboard",
|
||||||
|
"chessfriend_core",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "4.4.18"
|
version = "4.4.18"
|
||||||
|
@ -121,10 +145,6 @@ version = "1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
|
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "core"
|
|
||||||
version = "0.1.0"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "endian-type"
|
name = "endian-type"
|
||||||
version = "0.1.2"
|
version = "0.1.2"
|
||||||
|
@ -151,7 +171,8 @@ checksum = "281e452d3bad4005426416cdba5ccfd4f5c1280e10099e21db27f7c1c28347fc"
|
||||||
name = "explorer"
|
name = "explorer"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"board",
|
"chessfriend_core",
|
||||||
|
"chessfriend_position",
|
||||||
"clap",
|
"clap",
|
||||||
"rustyline",
|
"rustyline",
|
||||||
"shlex",
|
"shlex",
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// Eryn Wells <eryn@erynwells.me>
|
// Eryn Wells <eryn@erynwells.me>
|
||||||
|
|
||||||
use crate::library;
|
use crate::library;
|
||||||
use crate::LeadingBitScanner;
|
use crate::{LeadingBitScanner, TrailingBitScanner};
|
||||||
use chessfriend_core::{Color, Direction, Square};
|
use chessfriend_core::{Color, Direction, Square};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::ops::Not;
|
use std::ops::Not;
|
||||||
|
@ -39,7 +39,7 @@ impl BitBoard {
|
||||||
library::FILES[*file as usize]
|
library::FILES[*file as usize]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ray(sq: Square, dir: Direction) -> BitBoard {
|
pub fn ray(sq: Square, dir: Direction) -> &'static BitBoard {
|
||||||
library::library().ray(sq, dir)
|
library::library().ray(sq, dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,6 +80,20 @@ impl BitBoard {
|
||||||
!(self & square_bitboard).is_empty()
|
!(self & square_bitboard).is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The number of 1 bits in the BitBoard.
|
||||||
|
///
|
||||||
|
/// ## Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use chessfriend_bitboard::BitBoard;
|
||||||
|
/// assert_eq!(BitBoard::EMPTY.population_count(), 0);
|
||||||
|
/// assert_eq!(BitBoard::new(0b01011110010).population_count(), 6);
|
||||||
|
/// assert_eq!(BitBoard::FULL.population_count(), 64);
|
||||||
|
/// ```
|
||||||
|
pub fn population_count(&self) -> u32 {
|
||||||
|
self.0.count_ones()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_square(&mut self, sq: Square) {
|
pub fn set_square(&mut self, sq: Square) {
|
||||||
let sq_bb: BitBoard = sq.into();
|
let sq_bb: BitBoard = sq.into();
|
||||||
*self |= sq_bb
|
*self |= sq_bb
|
||||||
|
@ -105,13 +119,31 @@ impl BitBoard {
|
||||||
/// Return an Iterator over the occupied squares, starting from the trailing
|
/// Return an Iterator over the occupied squares, starting from the trailing
|
||||||
/// (least-significant bit) end of the field.
|
/// (least-significant bit) end of the field.
|
||||||
pub fn occupied_squares_trailing(&self) -> impl Iterator<Item = Square> {
|
pub fn occupied_squares_trailing(&self) -> impl Iterator<Item = Square> {
|
||||||
LeadingBitScanner::new(self.0).map(|idx| unsafe { Square::from_index(idx as u8) })
|
TrailingBitScanner::new(self.0).map(|idx| unsafe { Square::from_index(idx as u8) })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn first_occupied_square(&self) -> Option<Square> {
|
||||||
|
let leading_zeros = self.0.leading_zeros() as u8;
|
||||||
|
if leading_zeros < Square::NUM as u8 {
|
||||||
|
unsafe { Some(Square::from_index(Square::NUM as u8 - leading_zeros - 1)) }
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn first_occupied_square_trailing(&self) -> Option<Square> {
|
||||||
|
let trailing_zeros = self.0.trailing_zeros() as u8;
|
||||||
|
if trailing_zeros < Square::NUM as u8 {
|
||||||
|
unsafe { Some(Square::from_index(trailing_zeros)) }
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for BitBoard {
|
impl Default for BitBoard {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
BitBoard::empty()
|
BitBoard::EMPTY
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -377,4 +409,14 @@ mod tests {
|
||||||
assert_eq!(BitBoard::from(Square::A1), BitBoard(0b1));
|
assert_eq!(BitBoard::from(Square::A1), BitBoard(0b1));
|
||||||
assert_eq!(BitBoard::from(Square::H8), BitBoard(1 << 63));
|
assert_eq!(BitBoard::from(Square::H8), BitBoard(1 << 63));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn first_occupied_squares() {
|
||||||
|
let bb = bitboard![A8, E1];
|
||||||
|
assert_eq!(bb.first_occupied_square(), Some(Square::A8));
|
||||||
|
assert_eq!(bb.first_occupied_square_trailing(), Some(Square::E1));
|
||||||
|
|
||||||
|
let bb = bitboard![D6, E7, F8];
|
||||||
|
assert_eq!(bb.first_occupied_square_trailing(), Some(Square::D6));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -220,8 +220,8 @@ impl MoveLibrary {
|
||||||
ray
|
ray
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn ray(&self, sq: Square, dir: Direction) -> BitBoard {
|
pub(super) fn ray(&self, sq: Square, dir: Direction) -> &BitBoard {
|
||||||
self.rays[sq as usize][dir as usize]
|
&self.rays[sq as usize][dir as usize]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn pawn_pushes(&self, sq: Square, color: Color) -> BitBoard {
|
pub(super) fn pawn_pushes(&self, sq: Square, color: Color) -> BitBoard {
|
||||||
|
|
97
position/src/check.rs
Normal file
97
position/src/check.rs
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
// Eryn Wells <eryn@erynwells.me>
|
||||||
|
|
||||||
|
use chessfriend_bitboard::BitBoard;
|
||||||
|
use chessfriend_core::{Color, Direction, Shape, Square};
|
||||||
|
|
||||||
|
use crate::sight::SliderRayToSquareExt;
|
||||||
|
|
||||||
|
pub struct CheckingPieces {
|
||||||
|
bitboards: [BitBoard; 5],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CheckingPieces {
|
||||||
|
pub(crate) fn new(
|
||||||
|
pawn: BitBoard,
|
||||||
|
knight: BitBoard,
|
||||||
|
bishop: BitBoard,
|
||||||
|
rook: BitBoard,
|
||||||
|
queen: BitBoard,
|
||||||
|
) -> CheckingPieces {
|
||||||
|
CheckingPieces {
|
||||||
|
bitboards: [pawn, knight, bishop, rook, queen],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn count(&self) -> u32 {
|
||||||
|
self.bitboards.iter().map(|b| b.population_count()).sum()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A BitBoard representing the set of pieces that must be captured to
|
||||||
|
/// resolve check.
|
||||||
|
pub fn capture_mask(&self) -> BitBoard {
|
||||||
|
if self.count() == 0 {
|
||||||
|
BitBoard::FULL
|
||||||
|
} else {
|
||||||
|
self.bitboards
|
||||||
|
.iter()
|
||||||
|
.fold(BitBoard::EMPTY, std::ops::BitOr::bitor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A BitBoard representing the set of squares to which a player can move a
|
||||||
|
/// piece to block a checking piece.
|
||||||
|
pub fn push_mask(&self, king: &BitBoard) -> BitBoard {
|
||||||
|
let target = king.first_occupied_square().unwrap();
|
||||||
|
|
||||||
|
macro_rules! push_mask_for_shape {
|
||||||
|
($push_mask:expr, $shape:ident, $king:expr) => {{
|
||||||
|
let checking_pieces = self.bitboard_for_shape(Shape::$shape);
|
||||||
|
if !checking_pieces.is_empty() {
|
||||||
|
if let Some(checking_ray) = checking_pieces
|
||||||
|
.occupied_squares()
|
||||||
|
.flat_map(|sq| Shape::$shape.ray_to_square(sq, target).into_iter())
|
||||||
|
.find(|bb| !(bb & $king).is_empty())
|
||||||
|
{
|
||||||
|
$push_mask |= checking_ray & !$king
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut push_mask = BitBoard::EMPTY;
|
||||||
|
|
||||||
|
push_mask_for_shape!(push_mask, Bishop, king);
|
||||||
|
push_mask_for_shape!(push_mask, Rook, king);
|
||||||
|
push_mask_for_shape!(push_mask, Queen, king);
|
||||||
|
|
||||||
|
push_mask
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bitboard_for_shape(&self, shape: Shape) -> &BitBoard {
|
||||||
|
&self.bitboards[shape as usize]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use chessfriend_bitboard::{bitboard, BitBoard};
|
||||||
|
|
||||||
|
/// This is a test position from [this execellent blog post][1] about how to
|
||||||
|
/// efficiently generate legal chess moves.
|
||||||
|
///
|
||||||
|
/// [1]: https://peterellisjones.com/posts/generating-legal-chess-moves-efficiently/
|
||||||
|
#[test]
|
||||||
|
fn rook_push_mask() {
|
||||||
|
let checks = CheckingPieces::new(
|
||||||
|
BitBoard::EMPTY,
|
||||||
|
BitBoard::EMPTY,
|
||||||
|
BitBoard::EMPTY,
|
||||||
|
bitboard![E5],
|
||||||
|
BitBoard::EMPTY,
|
||||||
|
);
|
||||||
|
|
||||||
|
let push_mask = checks.push_mask(&bitboard![E8]);
|
||||||
|
assert_eq!(push_mask, bitboard![E6, E7]);
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
pub mod fen;
|
pub mod fen;
|
||||||
|
|
||||||
|
mod check;
|
||||||
mod display;
|
mod display;
|
||||||
mod r#move;
|
mod r#move;
|
||||||
mod move_generator;
|
mod move_generator;
|
||||||
|
|
|
@ -51,10 +51,15 @@ macro_rules! move_generator_declaration {
|
||||||
capture_mask: chessfriend_bitboard::BitBoard,
|
capture_mask: chessfriend_bitboard::BitBoard,
|
||||||
push_mask: chessfriend_bitboard::BitBoard,
|
push_mask: chessfriend_bitboard::BitBoard,
|
||||||
) -> $name {
|
) -> $name {
|
||||||
$name {
|
let move_sets = if Self::shape() == chessfriend_core::Shape::King
|
||||||
color,
|
|| (!capture_mask.is_empty() && !push_mask.is_empty())
|
||||||
move_sets: Self::move_sets(position, color, capture_mask, push_mask),
|
{
|
||||||
}
|
Self::move_sets(position, color, capture_mask, push_mask)
|
||||||
|
} else {
|
||||||
|
std::collections::BTreeMap::new()
|
||||||
|
};
|
||||||
|
|
||||||
|
$name { color, move_sets }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -84,7 +89,11 @@ macro_rules! move_generator_declaration {
|
||||||
pub(self) use move_generator_declaration;
|
pub(self) use move_generator_declaration;
|
||||||
|
|
||||||
trait MoveGeneratorInternal {
|
trait MoveGeneratorInternal {
|
||||||
fn piece(color: Color) -> Piece;
|
fn shape() -> Shape;
|
||||||
|
|
||||||
|
fn piece(color: Color) -> Piece {
|
||||||
|
Piece::new(color, Self::shape())
|
||||||
|
}
|
||||||
|
|
||||||
fn move_sets(
|
fn move_sets(
|
||||||
position: &Position,
|
position: &Position,
|
||||||
|
|
|
@ -3,13 +3,13 @@
|
||||||
use super::{move_generator_declaration, MoveGeneratorInternal, MoveSet};
|
use super::{move_generator_declaration, MoveGeneratorInternal, MoveSet};
|
||||||
use crate::Position;
|
use crate::Position;
|
||||||
use chessfriend_bitboard::BitBoard;
|
use chessfriend_bitboard::BitBoard;
|
||||||
use chessfriend_core::{Color, Direction, Piece, PlacedPiece};
|
use chessfriend_core::{Direction, PlacedPiece, Shape};
|
||||||
|
|
||||||
move_generator_declaration!(ClassicalMoveGenerator);
|
move_generator_declaration!(ClassicalMoveGenerator);
|
||||||
|
|
||||||
impl MoveGeneratorInternal for ClassicalMoveGenerator {
|
impl MoveGeneratorInternal for ClassicalMoveGenerator {
|
||||||
fn piece(color: Color) -> Piece {
|
fn shape() -> Shape {
|
||||||
Piece::bishop(color)
|
Shape::Bishop
|
||||||
}
|
}
|
||||||
|
|
||||||
fn move_set_for_piece(
|
fn move_set_for_piece(
|
||||||
|
|
|
@ -6,22 +6,22 @@
|
||||||
use super::{move_generator_declaration, MoveGeneratorInternal, MoveSet};
|
use super::{move_generator_declaration, MoveGeneratorInternal, MoveSet};
|
||||||
use crate::{r#move::Castle, Position};
|
use crate::{r#move::Castle, Position};
|
||||||
use chessfriend_bitboard::BitBoard;
|
use chessfriend_bitboard::BitBoard;
|
||||||
use chessfriend_core::{Color, Piece, PlacedPiece};
|
use chessfriend_core::{PlacedPiece, Shape};
|
||||||
|
|
||||||
move_generator_declaration!(KingMoveGenerator, struct);
|
move_generator_declaration!(KingMoveGenerator, struct);
|
||||||
move_generator_declaration!(KingMoveGenerator, new);
|
move_generator_declaration!(KingMoveGenerator, new);
|
||||||
move_generator_declaration!(KingMoveGenerator, getters);
|
move_generator_declaration!(KingMoveGenerator, getters);
|
||||||
|
|
||||||
impl MoveGeneratorInternal for KingMoveGenerator {
|
impl MoveGeneratorInternal for KingMoveGenerator {
|
||||||
fn piece(color: Color) -> Piece {
|
fn shape() -> Shape {
|
||||||
Piece::king(color)
|
Shape::King
|
||||||
}
|
}
|
||||||
|
|
||||||
fn move_set_for_piece(
|
fn move_set_for_piece(
|
||||||
position: &Position,
|
position: &Position,
|
||||||
placed_piece: PlacedPiece,
|
placed_piece: PlacedPiece,
|
||||||
capture_mask: BitBoard,
|
_capture_mask: BitBoard,
|
||||||
push_mask: BitBoard,
|
_push_mask: BitBoard,
|
||||||
) -> MoveSet {
|
) -> MoveSet {
|
||||||
let piece = placed_piece.piece();
|
let piece = placed_piece.piece();
|
||||||
let color = piece.color();
|
let color = piece.color();
|
||||||
|
@ -59,7 +59,7 @@ mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{assert_move_list, position, test_position, Move, MoveBuilder, PositionBuilder};
|
use crate::{assert_move_list, position, test_position, Move, MoveBuilder, PositionBuilder};
|
||||||
use chessfriend_bitboard::bitboard;
|
use chessfriend_bitboard::bitboard;
|
||||||
use chessfriend_core::{piece, Square};
|
use chessfriend_core::{piece, Color, Square};
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -3,13 +3,13 @@
|
||||||
use super::{move_generator_declaration, MoveGeneratorInternal, MoveSet};
|
use super::{move_generator_declaration, MoveGeneratorInternal, MoveSet};
|
||||||
use crate::Position;
|
use crate::Position;
|
||||||
use chessfriend_bitboard::BitBoard;
|
use chessfriend_bitboard::BitBoard;
|
||||||
use chessfriend_core::{Color, Piece, PlacedPiece};
|
use chessfriend_core::{PlacedPiece, Shape};
|
||||||
|
|
||||||
move_generator_declaration!(KnightMoveGenerator);
|
move_generator_declaration!(KnightMoveGenerator);
|
||||||
|
|
||||||
impl MoveGeneratorInternal for KnightMoveGenerator {
|
impl MoveGeneratorInternal for KnightMoveGenerator {
|
||||||
fn piece(color: Color) -> Piece {
|
fn shape() -> Shape {
|
||||||
Piece::knight(color)
|
Shape::Knight
|
||||||
}
|
}
|
||||||
|
|
||||||
fn move_set_for_piece(
|
fn move_set_for_piece(
|
||||||
|
@ -35,7 +35,7 @@ impl MoveGeneratorInternal for KnightMoveGenerator {
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{position, Move, MoveBuilder};
|
use crate::{position, Move, MoveBuilder};
|
||||||
use chessfriend_core::{piece, Square};
|
use chessfriend_core::{piece, Color, Square};
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
use super::{move_generator_declaration, MoveGeneratorInternal, MoveSet};
|
use super::{move_generator_declaration, MoveGeneratorInternal, MoveSet};
|
||||||
use crate::Position;
|
use crate::Position;
|
||||||
use chessfriend_bitboard::BitBoard;
|
use chessfriend_bitboard::BitBoard;
|
||||||
use chessfriend_core::{Color, Piece, PlacedPiece, Rank, Square};
|
use chessfriend_core::{Piece, PlacedPiece, Rank, Shape, Square};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct MoveIterator(usize, usize);
|
struct MoveIterator(usize, usize);
|
||||||
|
@ -11,8 +11,8 @@ struct MoveIterator(usize, usize);
|
||||||
move_generator_declaration!(PawnMoveGenerator);
|
move_generator_declaration!(PawnMoveGenerator);
|
||||||
|
|
||||||
impl MoveGeneratorInternal for PawnMoveGenerator {
|
impl MoveGeneratorInternal for PawnMoveGenerator {
|
||||||
fn piece(color: Color) -> Piece {
|
fn shape() -> Shape {
|
||||||
Piece::pawn(color)
|
Shape::Pawn
|
||||||
}
|
}
|
||||||
|
|
||||||
fn move_set_for_piece(
|
fn move_set_for_piece(
|
||||||
|
@ -63,7 +63,7 @@ impl PawnMoveGenerator {
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{assert_move_list, position::DiagramFormatter, test_position, Move, MoveBuilder};
|
use crate::{assert_move_list, position::DiagramFormatter, test_position, Move, MoveBuilder};
|
||||||
use chessfriend_core::{piece, Square};
|
use chessfriend_core::{piece, Color, Square};
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -3,13 +3,13 @@
|
||||||
use super::{move_generator_declaration, MoveGeneratorInternal, MoveSet};
|
use super::{move_generator_declaration, MoveGeneratorInternal, MoveSet};
|
||||||
use crate::Position;
|
use crate::Position;
|
||||||
use chessfriend_bitboard::BitBoard;
|
use chessfriend_bitboard::BitBoard;
|
||||||
use chessfriend_core::{Color, Direction, Piece, PlacedPiece};
|
use chessfriend_core::{Direction, PlacedPiece, Shape};
|
||||||
|
|
||||||
move_generator_declaration!(ClassicalMoveGenerator);
|
move_generator_declaration!(ClassicalMoveGenerator);
|
||||||
|
|
||||||
impl MoveGeneratorInternal for ClassicalMoveGenerator {
|
impl MoveGeneratorInternal for ClassicalMoveGenerator {
|
||||||
fn piece(color: Color) -> Piece {
|
fn shape() -> Shape {
|
||||||
Piece::queen(color)
|
Shape::Queen
|
||||||
}
|
}
|
||||||
|
|
||||||
fn move_set_for_piece(
|
fn move_set_for_piece(
|
||||||
|
|
|
@ -3,13 +3,13 @@
|
||||||
use super::{move_generator_declaration, MoveGeneratorInternal, MoveSet};
|
use super::{move_generator_declaration, MoveGeneratorInternal, MoveSet};
|
||||||
use crate::Position;
|
use crate::Position;
|
||||||
use chessfriend_bitboard::BitBoard;
|
use chessfriend_bitboard::BitBoard;
|
||||||
use chessfriend_core::{Color, Direction, Piece, PlacedPiece};
|
use chessfriend_core::{Direction, PlacedPiece, Shape};
|
||||||
|
|
||||||
move_generator_declaration!(ClassicalMoveGenerator);
|
move_generator_declaration!(ClassicalMoveGenerator);
|
||||||
|
|
||||||
impl MoveGeneratorInternal for ClassicalMoveGenerator {
|
impl MoveGeneratorInternal for ClassicalMoveGenerator {
|
||||||
fn piece(color: Color) -> Piece {
|
fn shape() -> Shape {
|
||||||
Piece::rook(color)
|
Shape::Rook
|
||||||
}
|
}
|
||||||
|
|
||||||
fn move_set_for_piece(
|
fn move_set_for_piece(
|
||||||
|
|
|
@ -2,11 +2,11 @@
|
||||||
|
|
||||||
use super::{flags::Flags, piece_sets::PieceBitBoards, Pieces};
|
use super::{flags::Flags, piece_sets::PieceBitBoards, Pieces};
|
||||||
use crate::{
|
use crate::{
|
||||||
move_generator::{MoveSet, Moves},
|
check::{self, CheckingPieces},
|
||||||
|
move_generator::Moves,
|
||||||
position::DiagramFormatter,
|
position::DiagramFormatter,
|
||||||
r#move::Castle,
|
r#move::Castle,
|
||||||
sight::SightExt,
|
sight::SightExt,
|
||||||
Move,
|
|
||||||
};
|
};
|
||||||
use chessfriend_bitboard::BitBoard;
|
use chessfriend_bitboard::BitBoard;
|
||||||
use chessfriend_core::{Color, Piece, PlacedPiece, Shape, Square};
|
use chessfriend_core::{Color, Piece, PlacedPiece, Shape, Square};
|
||||||
|
@ -26,16 +26,7 @@ pub struct Position {
|
||||||
|
|
||||||
impl Position {
|
impl Position {
|
||||||
pub fn empty() -> Position {
|
pub fn empty() -> Position {
|
||||||
Position {
|
Default::default()
|
||||||
color_to_move: Color::White,
|
|
||||||
flags: Default::default(),
|
|
||||||
pieces: PieceBitBoards::default(),
|
|
||||||
en_passant_square: None,
|
|
||||||
sight: [OnceCell::new(), OnceCell::new()],
|
|
||||||
moves: OnceCell::new(),
|
|
||||||
half_move_counter: 0,
|
|
||||||
full_move_number: 1,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return a starting position.
|
/// Return a starting position.
|
||||||
|
@ -60,13 +51,8 @@ impl Position {
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
color_to_move: Color::White,
|
color_to_move: Color::White,
|
||||||
flags: Flags::default(),
|
|
||||||
pieces: PieceBitBoards::new([white_pieces, black_pieces]),
|
pieces: PieceBitBoards::new([white_pieces, black_pieces]),
|
||||||
en_passant_square: None,
|
..Default::default()
|
||||||
sight: [OnceCell::new(), OnceCell::new()],
|
|
||||||
moves: OnceCell::new(),
|
|
||||||
half_move_counter: 0,
|
|
||||||
full_move_number: 1,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,8 +121,22 @@ impl Position {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn moves(&self) -> &Moves {
|
pub fn moves(&self) -> &Moves {
|
||||||
self.moves
|
self.moves.get_or_init(|| {
|
||||||
.get_or_init(|| Moves::new(self, self.color_to_move, BitBoard::FULL, BitBoard::FULL))
|
let checking_pieces = self.checking_pieces();
|
||||||
|
match checking_pieces.count() {
|
||||||
|
// Normal, unrestricted move generation
|
||||||
|
0 => Moves::new(self, self.color_to_move, BitBoard::FULL, BitBoard::FULL),
|
||||||
|
1 => {
|
||||||
|
// Calculate push and capture masks for checking piece. Moves are restricted to those that intersect those masks.
|
||||||
|
let capture_mask = checking_pieces.capture_mask();
|
||||||
|
let push_mask =
|
||||||
|
checking_pieces.push_mask(self.king_bitboard(self.color_to_move));
|
||||||
|
Moves::new(self, self.color_to_move, capture_mask, push_mask)
|
||||||
|
}
|
||||||
|
// With more than one checking piece, the only legal moves are king moves.
|
||||||
|
_ => Moves::new(self, self.color_to_move, BitBoard::EMPTY, BitBoard::EMPTY),
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return a BitBoard representing the set of squares containing a piece.
|
/// Return a BitBoard representing the set of squares containing a piece.
|
||||||
|
@ -249,6 +249,44 @@ impl Position {
|
||||||
.next()
|
.next()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn checking_pieces(&self) -> CheckingPieces {
|
||||||
|
let opponent = self.color_to_move.other();
|
||||||
|
let king_square = self.king_square(self.color_to_move);
|
||||||
|
|
||||||
|
let checking_pawns = {
|
||||||
|
// The current player's pawn attack moves *from* this square are the
|
||||||
|
// same as the pawn moves for the opposing player attacking this square.
|
||||||
|
let pawn_moves_to_king_square = BitBoard::pawn_attacks(king_square, self.color_to_move);
|
||||||
|
let opposing_pawn = Piece::pawn(opponent);
|
||||||
|
let opposing_pawns = self.pieces.bitboard_for_piece(&opposing_pawn);
|
||||||
|
|
||||||
|
pawn_moves_to_king_square & opposing_pawns
|
||||||
|
};
|
||||||
|
|
||||||
|
macro_rules! checking_piece {
|
||||||
|
($moves_bb_fn:path, $piece_fn:ident) => {{
|
||||||
|
let moves_from_opposing_square = $moves_bb_fn(king_square);
|
||||||
|
let piece = Piece::$piece_fn(opponent);
|
||||||
|
let opposing_pieces = self.pieces.bitboard_for_piece(&piece);
|
||||||
|
|
||||||
|
moves_from_opposing_square & opposing_pieces
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
let checking_knights = checking_piece!(BitBoard::knight_moves, knight);
|
||||||
|
let checking_bishops = checking_piece!(BitBoard::bishop_moves, bishop);
|
||||||
|
let checking_rooks = checking_piece!(BitBoard::rook_moves, rook);
|
||||||
|
let checking_queens = checking_piece!(BitBoard::queen_moves, queen);
|
||||||
|
|
||||||
|
CheckingPieces::new(
|
||||||
|
checking_pawns,
|
||||||
|
checking_knights,
|
||||||
|
checking_bishops,
|
||||||
|
checking_rooks,
|
||||||
|
checking_queens,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// crate::position methods
|
// crate::position methods
|
||||||
|
@ -302,7 +340,16 @@ impl Position {
|
||||||
|
|
||||||
impl Default for Position {
|
impl Default for Position {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::empty()
|
Self {
|
||||||
|
color_to_move: Color::White,
|
||||||
|
flags: Flags::default(),
|
||||||
|
pieces: PieceBitBoards::default(),
|
||||||
|
en_passant_square: None,
|
||||||
|
sight: [OnceCell::new(), OnceCell::new()],
|
||||||
|
moves: OnceCell::new(),
|
||||||
|
half_move_counter: 0,
|
||||||
|
full_move_number: 1,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,20 @@ use crate::position::piece_sets::PieceBitBoards;
|
||||||
use chessfriend_bitboard::BitBoard;
|
use chessfriend_bitboard::BitBoard;
|
||||||
use chessfriend_core::{Color, Direction, PlacedPiece, Shape, Square};
|
use chessfriend_core::{Color, Direction, PlacedPiece, Shape, Square};
|
||||||
|
|
||||||
|
macro_rules! ray_in_direction {
|
||||||
|
($square:expr, $blockers:expr, $direction:ident, $occupied_squares:tt) => {{
|
||||||
|
let ray = BitBoard::ray($square, Direction::$direction);
|
||||||
|
if let Some(first_occupied_square) = BitBoard::$occupied_squares(&(ray & $blockers)).next()
|
||||||
|
{
|
||||||
|
let remainder = BitBoard::ray(first_occupied_square, Direction::$direction);
|
||||||
|
let attack_ray = ray & !remainder;
|
||||||
|
attack_ray
|
||||||
|
} else {
|
||||||
|
*ray
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) trait SightExt {
|
pub(crate) trait SightExt {
|
||||||
fn sight(&self, pieces: &PieceBitBoards, en_passant_square: Option<Square>) -> BitBoard;
|
fn sight(&self, pieces: &PieceBitBoards, en_passant_square: Option<Square>) -> BitBoard;
|
||||||
|
|
||||||
|
@ -26,6 +40,10 @@ pub(crate) trait SightExt {
|
||||||
fn king_sight(&self, pieces: &PieceBitBoards) -> BitBoard;
|
fn king_sight(&self, pieces: &PieceBitBoards) -> BitBoard;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) trait SliderRayToSquareExt {
|
||||||
|
fn ray_to_square(&self, origin: Square, target: Square) -> Option<BitBoard>;
|
||||||
|
}
|
||||||
|
|
||||||
impl SightExt for PlacedPiece {
|
impl SightExt for PlacedPiece {
|
||||||
fn sight(&self, pieces: &PieceBitBoards, en_passant_square: Option<Square>) -> BitBoard {
|
fn sight(&self, pieces: &PieceBitBoards, en_passant_square: Option<Square>) -> BitBoard {
|
||||||
match self.shape() {
|
match self.shape() {
|
||||||
|
@ -88,25 +106,10 @@ impl SightExt for PlacedPiece {
|
||||||
|
|
||||||
let blockers = pieces.all_pieces();
|
let blockers = pieces.all_pieces();
|
||||||
|
|
||||||
macro_rules! update_moves_with_ray {
|
sight |= ray_in_direction!(square, blockers, NorthEast, occupied_squares_trailing);
|
||||||
($direction:ident, $occupied_squares:tt) => {
|
sight |= ray_in_direction!(square, blockers, NorthWest, occupied_squares_trailing);
|
||||||
let ray = BitBoard::ray(square, Direction::$direction);
|
sight |= ray_in_direction!(square, blockers, SouthEast, occupied_squares);
|
||||||
if let Some(first_occupied_square) =
|
sight |= ray_in_direction!(square, blockers, SouthWest, occupied_squares);
|
||||||
BitBoard::$occupied_squares(&(ray & blockers)).next()
|
|
||||||
{
|
|
||||||
let remainder = BitBoard::ray(first_occupied_square, Direction::$direction);
|
|
||||||
let attack_ray = ray & !remainder;
|
|
||||||
sight |= attack_ray;
|
|
||||||
} else {
|
|
||||||
sight |= ray;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
update_moves_with_ray!(NorthEast, occupied_squares_trailing);
|
|
||||||
update_moves_with_ray!(NorthWest, occupied_squares_trailing);
|
|
||||||
update_moves_with_ray!(SouthEast, occupied_squares);
|
|
||||||
update_moves_with_ray!(SouthWest, occupied_squares);
|
|
||||||
|
|
||||||
let friendly_pieces = pieces.all_pieces_of_color(self.color());
|
let friendly_pieces = pieces.all_pieces_of_color(self.color());
|
||||||
sight & !friendly_pieces
|
sight & !friendly_pieces
|
||||||
|
@ -119,25 +122,10 @@ impl SightExt for PlacedPiece {
|
||||||
|
|
||||||
let blockers = pieces.all_pieces();
|
let blockers = pieces.all_pieces();
|
||||||
|
|
||||||
macro_rules! update_moves_with_ray {
|
sight |= ray_in_direction!(square, blockers, North, occupied_squares_trailing);
|
||||||
($direction:ident, $occupied_squares:tt) => {
|
sight |= ray_in_direction!(square, blockers, East, occupied_squares_trailing);
|
||||||
let ray = BitBoard::ray(square, Direction::$direction);
|
sight |= ray_in_direction!(square, blockers, South, occupied_squares);
|
||||||
if let Some(first_occupied_square) =
|
sight |= ray_in_direction!(square, blockers, West, occupied_squares);
|
||||||
BitBoard::$occupied_squares(&(ray & blockers)).next()
|
|
||||||
{
|
|
||||||
let remainder = BitBoard::ray(first_occupied_square, Direction::$direction);
|
|
||||||
let attack_ray = ray & !remainder;
|
|
||||||
sight |= attack_ray;
|
|
||||||
} else {
|
|
||||||
sight |= ray;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
update_moves_with_ray!(North, occupied_squares_trailing);
|
|
||||||
update_moves_with_ray!(East, occupied_squares_trailing);
|
|
||||||
update_moves_with_ray!(South, occupied_squares);
|
|
||||||
update_moves_with_ray!(West, occupied_squares);
|
|
||||||
|
|
||||||
let friendly_pieces = pieces.all_pieces_of_color(self.color());
|
let friendly_pieces = pieces.all_pieces_of_color(self.color());
|
||||||
sight & !friendly_pieces
|
sight & !friendly_pieces
|
||||||
|
@ -150,29 +138,14 @@ impl SightExt for PlacedPiece {
|
||||||
|
|
||||||
let blockers = pieces.all_pieces();
|
let blockers = pieces.all_pieces();
|
||||||
|
|
||||||
macro_rules! update_moves_with_ray {
|
sight |= ray_in_direction!(square, blockers, NorthWest, occupied_squares_trailing);
|
||||||
($direction:ident, $occupied_squares:tt) => {
|
sight |= ray_in_direction!(square, blockers, North, occupied_squares_trailing);
|
||||||
let ray = BitBoard::ray(square, Direction::$direction);
|
sight |= ray_in_direction!(square, blockers, NorthEast, occupied_squares_trailing);
|
||||||
if let Some(first_occupied_square) =
|
sight |= ray_in_direction!(square, blockers, East, occupied_squares_trailing);
|
||||||
BitBoard::$occupied_squares(&(ray & blockers)).next()
|
sight |= ray_in_direction!(square, blockers, SouthEast, occupied_squares);
|
||||||
{
|
sight |= ray_in_direction!(square, blockers, South, occupied_squares);
|
||||||
let remainder = BitBoard::ray(first_occupied_square, Direction::$direction);
|
sight |= ray_in_direction!(square, blockers, SouthWest, occupied_squares);
|
||||||
let attack_ray = ray & !remainder;
|
sight |= ray_in_direction!(square, blockers, West, occupied_squares);
|
||||||
sight |= attack_ray;
|
|
||||||
} else {
|
|
||||||
sight |= ray;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
update_moves_with_ray!(NorthWest, occupied_squares_trailing);
|
|
||||||
update_moves_with_ray!(North, occupied_squares_trailing);
|
|
||||||
update_moves_with_ray!(NorthEast, occupied_squares_trailing);
|
|
||||||
update_moves_with_ray!(East, occupied_squares_trailing);
|
|
||||||
update_moves_with_ray!(SouthEast, occupied_squares);
|
|
||||||
update_moves_with_ray!(South, occupied_squares);
|
|
||||||
update_moves_with_ray!(SouthWest, occupied_squares);
|
|
||||||
update_moves_with_ray!(West, occupied_squares);
|
|
||||||
|
|
||||||
let friendly_pieces = pieces.all_pieces_of_color(self.color());
|
let friendly_pieces = pieces.all_pieces_of_color(self.color());
|
||||||
sight & !friendly_pieces
|
sight & !friendly_pieces
|
||||||
|
@ -183,8 +156,66 @@ impl SightExt for PlacedPiece {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl SliderRayToSquareExt for Shape {
|
||||||
|
fn ray_to_square(&self, origin: Square, target: Square) -> Option<BitBoard> {
|
||||||
|
macro_rules! ray {
|
||||||
|
($square:expr, $direction:ident) => {
|
||||||
|
(
|
||||||
|
BitBoard::ray($square, Direction::$direction),
|
||||||
|
Direction::$direction,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let target_bitboard: BitBoard = target.into();
|
||||||
|
|
||||||
|
let ray_and_direction = match self {
|
||||||
|
Shape::Bishop => [
|
||||||
|
ray!(origin, NorthEast),
|
||||||
|
ray!(origin, SouthEast),
|
||||||
|
ray!(origin, SouthWest),
|
||||||
|
ray!(origin, NorthWest),
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.find(|(&ray, _)| !(target_bitboard & ray).is_empty()),
|
||||||
|
Shape::Rook => [
|
||||||
|
ray!(origin, North),
|
||||||
|
ray!(origin, East),
|
||||||
|
ray!(origin, South),
|
||||||
|
ray!(origin, West),
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.find(|(&ray, _)| !(target_bitboard & ray).is_empty()),
|
||||||
|
Shape::Queen => [
|
||||||
|
ray!(origin, North),
|
||||||
|
ray!(origin, NorthEast),
|
||||||
|
ray!(origin, East),
|
||||||
|
ray!(origin, SouthEast),
|
||||||
|
ray!(origin, South),
|
||||||
|
ray!(origin, SouthWest),
|
||||||
|
ray!(origin, West),
|
||||||
|
ray!(origin, NorthWest),
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.find(|(&ray, _)| !(target_bitboard & ray).is_empty()),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some((ray, direction)) = ray_and_direction {
|
||||||
|
let remainder = BitBoard::ray(target, direction);
|
||||||
|
return Some(ray & !remainder);
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use chessfriend_bitboard::bitboard;
|
||||||
|
use chessfriend_core::{piece, Square};
|
||||||
|
|
||||||
macro_rules! sight_test {
|
macro_rules! sight_test {
|
||||||
($test_name:ident, $position:expr, $piece:expr, $bitboard:expr) => {
|
($test_name:ident, $position:expr, $piece:expr, $bitboard:expr) => {
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -201,6 +232,12 @@ mod tests {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn pawns_and_knights_cannot_make_rays() {
|
||||||
|
assert_eq!(Shape::Pawn.ray_to_square(Square::F7, Square::E8), None);
|
||||||
|
assert_eq!(Shape::Knight.ray_to_square(Square::F6, Square::E8), None);
|
||||||
|
}
|
||||||
|
|
||||||
mod pawn {
|
mod pawn {
|
||||||
use crate::test_position;
|
use crate::test_position;
|
||||||
use chessfriend_bitboard::{bitboard, BitBoard};
|
use chessfriend_bitboard::{bitboard, BitBoard};
|
||||||
|
@ -273,20 +310,25 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
mod bishop {
|
mod bishop {
|
||||||
use chessfriend_bitboard::bitboard;
|
use super::*;
|
||||||
use chessfriend_core::piece;
|
|
||||||
|
|
||||||
sight_test!(
|
sight_test!(
|
||||||
c2_bishop,
|
c2_bishop,
|
||||||
piece!(Black Bishop on C2),
|
piece!(Black Bishop on C2),
|
||||||
bitboard!(D1, B3, A4, B1, D3, E4, F5, G6, H7)
|
bitboard!(D1, B3, A4, B1, D3, E4, F5, G6, H7)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ray_to_square() {
|
||||||
|
let generated_ray = Shape::Bishop.ray_to_square(Square::C5, Square::E7);
|
||||||
|
let expected_ray = bitboard![D6, E7];
|
||||||
|
assert_eq!(generated_ray, Some(expected_ray));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod rook {
|
mod rook {
|
||||||
|
use super::*;
|
||||||
use crate::test_position;
|
use crate::test_position;
|
||||||
use chessfriend_bitboard::bitboard;
|
|
||||||
use chessfriend_core::piece;
|
|
||||||
|
|
||||||
sight_test!(
|
sight_test!(
|
||||||
g3_rook,
|
g3_rook,
|
||||||
|
@ -304,6 +346,25 @@ mod tests {
|
||||||
piece!(White Rook on E4),
|
piece!(White Rook on E4),
|
||||||
bitboard!(A4, B4, C4, D4, F4, G4, H4, E2, E3, E5, E6, E7)
|
bitboard!(A4, B4, C4, D4, F4, G4, H4, E2, E3, E5, E6, E7)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ray_to_square() {
|
||||||
|
let generated_ray = Shape::Rook.ray_to_square(Square::C2, Square::C6);
|
||||||
|
let expected_ray = bitboard![C3, C4, C5, C6];
|
||||||
|
assert_eq!(generated_ray, Some(expected_ray));
|
||||||
|
|
||||||
|
let generated_ray = Shape::Rook.ray_to_square(Square::D2, Square::H2);
|
||||||
|
let expected_ray = bitboard![E2, F2, G2, H2];
|
||||||
|
assert_eq!(generated_ray, Some(expected_ray));
|
||||||
|
|
||||||
|
let generated_ray = Shape::Rook.ray_to_square(Square::G6, Square::B6);
|
||||||
|
let expected_ray = bitboard![B6, C6, D6, E6, F6];
|
||||||
|
assert_eq!(generated_ray, Some(expected_ray));
|
||||||
|
|
||||||
|
let generated_ray = Shape::Rook.ray_to_square(Square::A6, Square::A3);
|
||||||
|
let expected_ray = bitboard![A5, A4, A3];
|
||||||
|
assert_eq!(generated_ray, Some(expected_ray));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod king {
|
mod king {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue