const lookup table
This commit is contained in:
parent
752150107e
commit
0d22c59cc3
8 changed files with 573 additions and 542 deletions
169
src/position.rs
169
src/position.rs
|
|
@ -3,7 +3,7 @@
|
|||
use crate::array_vec::*;
|
||||
use crate::bitboard::*;
|
||||
use crate::board::*;
|
||||
use crate::lookup::*;
|
||||
use crate::lookup;
|
||||
use crate::san::*;
|
||||
use crate::setup::*;
|
||||
use crate::uci::*;
|
||||
|
|
@ -55,10 +55,7 @@ use std::iter::FusedIterator;
|
|||
/// requirement is to be efficient while respecting the [`Ord`] trait protocol. It should not be
|
||||
/// considered stable.
|
||||
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct Position {
|
||||
setup: Setup,
|
||||
lookup: InitialisedLookup,
|
||||
}
|
||||
pub struct Position(Setup);
|
||||
|
||||
const MAX_LEGAL_MOVES: usize = 218;
|
||||
|
||||
|
|
@ -68,18 +65,15 @@ impl Position {
|
|||
/// i.e. `rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq -`
|
||||
#[inline]
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
setup: Setup {
|
||||
w: Bitboard(0x000000000000FFFF),
|
||||
p_b_q: Bitboard(0x2CFF00000000FF2C),
|
||||
n_b_k: Bitboard(0x7600000000000076),
|
||||
r_q_k: Bitboard(0x9900000000000099),
|
||||
turn: Color::White,
|
||||
castling_rights: CastlingRights::full(),
|
||||
en_passant: OptionSquare::None,
|
||||
},
|
||||
lookup: InitialisedLookup::init(),
|
||||
}
|
||||
Self(Setup {
|
||||
w: Bitboard(0x000000000000FFFF),
|
||||
p_b_q: Bitboard(0x2CFF00000000FF2C),
|
||||
n_b_k: Bitboard(0x7600000000000076),
|
||||
r_q_k: Bitboard(0x9900000000000099),
|
||||
turn: Color::White,
|
||||
castling_rights: CastlingRights::full(),
|
||||
en_passant: OptionSquare::None,
|
||||
})
|
||||
}
|
||||
|
||||
/// Tries to read a valid position from a text record.
|
||||
|
|
@ -160,25 +154,25 @@ impl Position {
|
|||
/// responsibility to rule out the legality of en passant before calling this function.
|
||||
#[inline]
|
||||
pub fn remove_en_passant_target_square(&mut self) {
|
||||
self.setup.en_passant = OptionSquare::None;
|
||||
self.0.en_passant = OptionSquare::None;
|
||||
}
|
||||
|
||||
/// Returns the occupancy of a square.
|
||||
#[inline]
|
||||
pub fn get(&self, square: Square) -> Option<Piece> {
|
||||
self.setup.get(square)
|
||||
self.0.get(square)
|
||||
}
|
||||
|
||||
/// Returns the color whose turn it is to play.
|
||||
#[inline]
|
||||
pub fn turn(&self) -> Color {
|
||||
self.setup.turn()
|
||||
self.0.turn()
|
||||
}
|
||||
|
||||
/// Returns `true` if castling is available for the given color and side.
|
||||
#[inline]
|
||||
pub fn castling_rights(&self, color: Color, side: CastlingSide) -> bool {
|
||||
self.setup.castling_rights(color, side)
|
||||
self.0.castling_rights(color, side)
|
||||
}
|
||||
|
||||
/// Returns the en passant target square if it exists.
|
||||
|
|
@ -187,26 +181,26 @@ impl Position {
|
|||
/// legal or even pseudo-legal.
|
||||
#[inline]
|
||||
pub fn en_passant_target_square(&self) -> Option<Square> {
|
||||
self.setup.en_passant_target_square()
|
||||
self.0.en_passant_target_square()
|
||||
}
|
||||
|
||||
/// Discards the castling rights for the given color and side.
|
||||
#[inline]
|
||||
pub fn remove_castling_rights(&mut self, color: Color, side: CastlingSide) {
|
||||
self.setup.set_castling_rights(color, side, false);
|
||||
self.0.set_castling_rights(color, side, false);
|
||||
}
|
||||
|
||||
/// Borrows the position as a [`Setup`].
|
||||
#[inline]
|
||||
pub fn as_setup(&self) -> &Setup {
|
||||
&self.setup
|
||||
&self.0
|
||||
}
|
||||
|
||||
/// Converts a position to a [`Setup`], allowing to edit the position without enforcing its
|
||||
/// legality.
|
||||
#[inline]
|
||||
pub fn into_setup(self) -> Setup {
|
||||
self.setup
|
||||
self.0
|
||||
}
|
||||
|
||||
/// Tries to pass the turn to the other color, failing if it would leave the king in check.
|
||||
|
|
@ -214,8 +208,7 @@ impl Position {
|
|||
/// When possible, this inverts the color to play and removes the en passant square if it
|
||||
/// exists.
|
||||
pub fn pass(&self) -> Option<Self> {
|
||||
let d = self.lookup;
|
||||
let setup = &self.setup;
|
||||
let setup = &self.0;
|
||||
let blockers = setup.p_b_q | setup.n_b_k | setup.r_q_k;
|
||||
let k = setup.n_b_k & setup.r_q_k;
|
||||
let q = setup.p_b_q & setup.r_q_k;
|
||||
|
|
@ -229,27 +222,23 @@ impl Position {
|
|||
};
|
||||
let king_square = (us & k).next().unwrap();
|
||||
let checkers = them
|
||||
& (d.pawn_attack(setup.turn, king_square) & p
|
||||
| d.knight(king_square) & n
|
||||
| d.bishop(king_square, blockers) & (q | b)
|
||||
| d.rook(king_square, blockers) & (q | r));
|
||||
checkers.is_empty().then(|| Self {
|
||||
setup: Setup {
|
||||
& (lookup::pawn_attack(setup.turn, king_square) & p
|
||||
| lookup::knight(king_square) & n
|
||||
| lookup::bishop(king_square, blockers) & (q | b)
|
||||
| lookup::rook(king_square, blockers) & (q | r));
|
||||
checkers.is_empty().then(|| {
|
||||
Self(Setup {
|
||||
turn: !setup.turn,
|
||||
en_passant: OptionSquare::None,
|
||||
..setup.clone()
|
||||
},
|
||||
lookup: d,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the mirror image of the position (see [`Setup::mirror`]).
|
||||
#[inline]
|
||||
pub fn mirror(&self) -> Self {
|
||||
Self {
|
||||
setup: self.setup.mirror(),
|
||||
lookup: self.lookup,
|
||||
}
|
||||
Self(self.0.mirror())
|
||||
}
|
||||
|
||||
/// Returns the number of possible chess games for a given number of moves.
|
||||
|
|
@ -329,7 +318,7 @@ impl Position {
|
|||
to,
|
||||
promotion,
|
||||
} = uci;
|
||||
let role = self.setup.get_role(from).ok_or(InvalidUciMove::Illegal)?;
|
||||
let role = self.0.get_role(from).ok_or(InvalidUciMove::Illegal)?;
|
||||
#[inline]
|
||||
fn aux<'l, const ROLE: u8>(
|
||||
position: &'l Position,
|
||||
|
|
@ -536,9 +525,7 @@ impl<'l> Move<'l> {
|
|||
#[inline]
|
||||
pub fn is_capture(self) -> bool {
|
||||
self.raw.kind == MoveType::EnPassant
|
||||
|| !((self.position.setup.p_b_q
|
||||
| self.position.setup.n_b_k
|
||||
| self.position.setup.r_q_k)
|
||||
|| !((self.position.0.p_b_q | self.position.0.n_b_k | self.position.0.r_q_k)
|
||||
& self.to().bitboard())
|
||||
.is_empty()
|
||||
}
|
||||
|
|
@ -548,7 +535,7 @@ impl<'l> Move<'l> {
|
|||
pub fn captured(self) -> Option<Role> {
|
||||
match self.raw.kind {
|
||||
MoveType::EnPassant => Some(Role::Pawn),
|
||||
_ => self.position.setup.get_role(self.raw.to),
|
||||
_ => self.position.0.get_role(self.raw.to),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -918,10 +905,7 @@ impl<'l> Visitor for Moves<'l> {
|
|||
impl Position {
|
||||
/// SAFETY: The position must be valid.
|
||||
pub(crate) unsafe fn from_setup(setup: Setup) -> Self {
|
||||
Self {
|
||||
setup,
|
||||
lookup: InitialisedLookup::init(),
|
||||
}
|
||||
Self(setup)
|
||||
}
|
||||
|
||||
fn generate_moves<T>(&self, visitor: &mut T)
|
||||
|
|
@ -939,8 +923,7 @@ impl Position {
|
|||
turn,
|
||||
en_passant,
|
||||
castling_rights,
|
||||
} = self.setup;
|
||||
let d = self.lookup;
|
||||
} = self.0;
|
||||
|
||||
let blockers = p_b_q | n_b_k | r_q_k;
|
||||
let (us, them) = match turn {
|
||||
|
|
@ -970,10 +953,10 @@ impl Position {
|
|||
|
||||
let forward = turn.forward();
|
||||
|
||||
let x = d.bishop(king_square, blockers);
|
||||
let y = d.rook(king_square, blockers);
|
||||
let checkers = d.pawn_attack(turn, king_square) & theirs.pawn()
|
||||
| d.knight(king_square) & theirs.knight()
|
||||
let x = lookup::bishop(king_square, blockers);
|
||||
let y = lookup::rook(king_square, blockers);
|
||||
let checkers = lookup::pawn_attack(turn, king_square) & theirs.pawn()
|
||||
| lookup::knight(king_square) & theirs.knight()
|
||||
| x & theirs.bishop()
|
||||
| y & theirs.rook();
|
||||
|
||||
|
|
@ -982,10 +965,10 @@ impl Position {
|
|||
let blockers = blockers ^ ours.king();
|
||||
theirs
|
||||
.king()
|
||||
.map(|sq| d.king(sq))
|
||||
.chain(theirs.bishop().map(|sq| d.bishop(sq, blockers)))
|
||||
.chain(theirs.rook().map(|sq| d.rook(sq, blockers)))
|
||||
.chain(theirs.knight().map(|sq| d.knight(sq)))
|
||||
.map(|sq| lookup::king(sq))
|
||||
.chain(theirs.bishop().map(|sq| lookup::bishop(sq, blockers)))
|
||||
.chain(theirs.rook().map(|sq| lookup::rook(sq, blockers)))
|
||||
.chain(theirs.knight().map(|sq| lookup::knight(sq)))
|
||||
.chain(std::iter::once(
|
||||
theirs.pawn().trans(!forward).trans(Direction::East),
|
||||
))
|
||||
|
|
@ -996,7 +979,7 @@ impl Position {
|
|||
};
|
||||
// king moves
|
||||
visitor.moves(
|
||||
(global_mask_to & d.king(king_square) & !us & !attacked).map(|to| RawMove {
|
||||
(global_mask_to & lookup::king(king_square) & !us & !attacked).map(|to| RawMove {
|
||||
kind: MoveType::KingMove,
|
||||
from: king_square,
|
||||
to,
|
||||
|
|
@ -1054,17 +1037,17 @@ impl Position {
|
|||
}
|
||||
|
||||
let blockers_x_ray = blockers & !(x | y);
|
||||
let pinned_diagonally = (d.bishop(king_square, blockers_x_ray) & theirs.bishop())
|
||||
.map(|sq| d.segment(king_square, sq))
|
||||
let pinned_diagonally = (lookup::bishop(king_square, blockers_x_ray) & theirs.bishop())
|
||||
.map(|sq| lookup::segment(king_square, sq))
|
||||
.reduce_or();
|
||||
let pinned_horizontally = (d.rook(king_square, blockers_x_ray) & theirs.rook())
|
||||
.map(|sq| d.segment(king_square, sq))
|
||||
let pinned_horizontally = (lookup::rook(king_square, blockers_x_ray) & theirs.rook())
|
||||
.map(|sq| lookup::segment(king_square, sq))
|
||||
.reduce_or();
|
||||
let pinned = pinned_diagonally | pinned_horizontally;
|
||||
|
||||
let checker = checkers.first();
|
||||
let block_check = checker
|
||||
.map(|checker| d.segment(king_square, checker))
|
||||
.map(|checker| lookup::segment(king_square, checker))
|
||||
.unwrap_or(Bitboard(!0));
|
||||
let target_mask = global_mask_to & block_check;
|
||||
|
||||
|
|
@ -1107,7 +1090,7 @@ impl Position {
|
|||
// pawn attacks kingside
|
||||
{
|
||||
let targets =
|
||||
(global_mask_from & ours.pawn() & (!pinned | d.ray(king_square, kside)))
|
||||
(global_mask_from & ours.pawn() & (!pinned | lookup::ray(king_square, kside)))
|
||||
.trans(kside)
|
||||
& them
|
||||
& target_mask;
|
||||
|
|
@ -1129,7 +1112,7 @@ impl Position {
|
|||
// pawn attacks queenside
|
||||
{
|
||||
let targets =
|
||||
(global_mask_from & ours.pawn() & (!pinned | d.ray(king_square, qside)))
|
||||
(global_mask_from & ours.pawn() & (!pinned | lookup::ray(king_square, qside)))
|
||||
.trans(qside)
|
||||
& them
|
||||
& target_mask;
|
||||
|
|
@ -1169,23 +1152,27 @@ impl Position {
|
|||
if block_check.contains(to)
|
||||
|| checker.is_none_or(|checker| checker == capture_square)
|
||||
{
|
||||
let candidates = d.pawn_attack(!turn, to) & ours.pawn();
|
||||
let candidates = lookup::pawn_attack(!turn, to) & ours.pawn();
|
||||
let blockers = blockers ^ capture_square.bitboard();
|
||||
let pinned = pinned
|
||||
| (d.rook(king_square, blockers & !(d.rook(king_square, blockers)))
|
||||
& theirs.rook())
|
||||
.map(|sq| d.segment(king_square, sq))
|
||||
| (lookup::rook(
|
||||
king_square,
|
||||
blockers & !(lookup::rook(king_square, blockers)),
|
||||
) & theirs.rook())
|
||||
.map(|sq| lookup::segment(king_square, sq))
|
||||
.reduce_or();
|
||||
(global_mask_from & candidates & (!pinned | d.segment(king_square, to)))
|
||||
.for_each(|from| {
|
||||
visitor.en_passant_is_legal();
|
||||
visitor.moves(std::iter::once(RawMove {
|
||||
kind: MoveType::EnPassant,
|
||||
from,
|
||||
to,
|
||||
role: Role::Pawn,
|
||||
}))
|
||||
})
|
||||
(global_mask_from
|
||||
& candidates
|
||||
& (!pinned | lookup::segment(king_square, to)))
|
||||
.for_each(|from| {
|
||||
visitor.en_passant_is_legal();
|
||||
visitor.moves(std::iter::once(RawMove {
|
||||
kind: MoveType::EnPassant,
|
||||
from,
|
||||
to,
|
||||
role: Role::Pawn,
|
||||
}))
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1196,11 +1183,13 @@ impl Position {
|
|||
let aux = |visitor: &mut T, role| {
|
||||
for from in global_mask_from & *ours.get(role) & !pinned {
|
||||
visitor.moves(
|
||||
(d.targets(role, from, blockers) & !us & target_mask).map(|to| RawMove {
|
||||
kind: MoveType::PieceMove,
|
||||
from,
|
||||
to,
|
||||
role,
|
||||
(lookup::targets(role, from, blockers) & !us & target_mask).map(|to| {
|
||||
RawMove {
|
||||
kind: MoveType::PieceMove,
|
||||
from,
|
||||
to,
|
||||
role,
|
||||
}
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
|
@ -1229,14 +1218,14 @@ impl Position {
|
|||
let aux = |visitor: &mut T, role, mask| {
|
||||
for from in global_mask_from & *ours.get(role) & mask {
|
||||
visitor.moves(
|
||||
(global_mask_to & !us & pinned & d.line(king_square, from)).map(|to| {
|
||||
RawMove {
|
||||
(global_mask_to & !us & pinned & lookup::line(king_square, from)).map(
|
||||
|to| RawMove {
|
||||
kind: MoveType::PieceMove,
|
||||
from,
|
||||
to,
|
||||
role,
|
||||
}
|
||||
}),
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
};
|
||||
|
|
@ -1254,7 +1243,7 @@ impl Position {
|
|||
|
||||
#[inline]
|
||||
unsafe fn play_unchecked(&mut self, m: RawMove) {
|
||||
let Self { setup, .. } = self;
|
||||
let Self(setup) = self;
|
||||
|
||||
setup.en_passant = OptionSquare::None;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue