Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve quality of code generated #568

Merged
merged 8 commits into from
Nov 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
@@ -1,2 +1,8 @@
[build]
rustflags = ["-Ctarget-cpu=native"]

[unstable]
profile-rustflags = true

[profile.release]
rustflags = ["-Zlocation-detail=none"]
2 changes: 2 additions & 0 deletions lib/chess/bitboard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,13 +96,15 @@ impl IntoIterator for Bitboard {

#[doc(hidden)]
impl From<cc::BitBoard> for Bitboard {
#[inline(always)]
fn from(bb: cc::BitBoard) -> Self {
Bitboard(bb)
}
}

#[doc(hidden)]
impl From<Bitboard> for cc::BitBoard {
#[inline(always)]
fn from(bb: Bitboard) -> Self {
bb.0
}
Expand Down
2 changes: 2 additions & 0 deletions lib/chess/color.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ impl Not for Color {

#[doc(hidden)]
impl From<cc::Color> for Color {
#[inline(always)]
fn from(c: cc::Color) -> Self {
match c {
cc::Color::White => Color::White,
Expand All @@ -36,6 +37,7 @@ impl From<cc::Color> for Color {

#[doc(hidden)]
impl From<Color> for cc::Color {
#[inline(always)]
fn from(c: Color) -> Self {
match c {
Color::White => cc::Color::White,
Expand Down
2 changes: 2 additions & 0 deletions lib/chess/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,13 +72,15 @@ impl Sub for File {

#[doc(hidden)]
impl From<cc::File> for File {
#[inline(always)]
fn from(f: cc::File) -> Self {
File::from_index(f as _)
}
}

#[doc(hidden)]
impl From<File> for cc::File {
#[inline(always)]
fn from(f: File) -> Self {
cc::File::index_const(f as _)
}
Expand Down
12 changes: 12 additions & 0 deletions lib/chess/move.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@ use vampirc_uci::UciMove;
pub struct Move(NonZeroU16);

impl Move {
#[inline(always)]
fn bits<R: RangeBounds<u32>>(&self, r: R) -> Bits<u16, 16> {
Bits::new(self.0.get()).slice(r)
}

/// Constructs a castling move.
#[inline(always)]
pub fn castling(whence: Square, whither: Square) -> Self {
let mut bits = Bits::<_, 16>::default();
bits.push(whence.encode());
Expand All @@ -24,6 +26,7 @@ impl Move {
}

/// Constructs an en passant move.
#[inline(always)]
pub fn en_passant(whence: Square, whither: Square) -> Self {
let mut bits = Bits::<_, 16>::default();
bits.push(whence.encode());
Expand All @@ -33,6 +36,7 @@ impl Move {
}

/// Constructs a regular move.
#[inline(always)]
pub fn regular(
whence: Square,
whither: Square,
Expand All @@ -57,16 +61,19 @@ impl Move {
}

/// The source [`Square`].
#[inline(always)]
pub fn whence(&self) -> Square {
Square::decode(self.bits(10..).pop()).assume()
}

/// The destination [`Square`].
#[inline(always)]
pub fn whither(&self) -> Square {
Square::decode(self.bits(4..).pop()).assume()
}

/// The promotion specifier.
#[inline(always)]
pub fn promotion(&self) -> Option<Role> {
if self.is_promotion() {
Some(Role::from_index(self.bits(..2).get() as u8 + 1))
Expand All @@ -76,26 +83,31 @@ impl Move {
}

/// Whether this is a promotion move.
#[inline(always)]
pub fn is_promotion(&self) -> bool {
self.bits(3..=3).get() != 0
}

/// Whether this is a castling move.
#[inline(always)]
pub fn is_castling(&self) -> bool {
self.bits(..4).get() == 0b0001
}

/// Whether this is an en passant capture move.
#[inline(always)]
pub fn is_en_passant(&self) -> bool {
self.bits(..4).get() == 0b0011
}

/// Whether this is a capture move.
#[inline(always)]
pub fn is_capture(&self) -> bool {
self.bits(2..=2).get() != 0 || (self.bits(1..=1).get() != 0 && !self.is_promotion())
}

/// Whether this move is neither a capture nor a promotion.
#[inline(always)]
pub fn is_quiet(&self) -> bool {
!(self.is_capture() || self.is_promotion())
}
Expand Down
38 changes: 35 additions & 3 deletions lib/chess/position.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,18 @@
}

impl Hash for Position {
#[inline(always)]
fn hash<H: Hasher>(&self, state: &mut H) {
state.write_u64(self.zobrist().get())
self.zobrist().hash(state)
}
}

impl Eq for Position {}

impl PartialEq for Position {
#[inline(always)]
fn eq(&self, other: &Self) -> bool {
self.board == other.board
self.board.eq(&other.board)
}
}

Expand Down Expand Up @@ -90,25 +92,29 @@

impl Position {
/// The side to move.
#[inline(always)]
pub fn turn(&self) -> Color {
self.board.side_to_move().into()
}

/// The number of halfmoves since the last capture or pawn advance.
///
/// It resets to 0 whenever a piece is captured or a pawn is moved.
#[inline(always)]
pub fn halfmoves(&self) -> u8 {
self.board.halfmove_clock()
}

/// The current move number since the start of the game.
///
/// It starts at 1, and is incremented after every move by black.
#[inline(always)]
pub fn fullmoves(&self) -> NonZeroU32 {
NonZeroU32::new(self.board.fullmove_number() as _).assume()
}

/// The en passant square.
#[inline(always)]
pub fn en_passant(&self) -> Option<Square> {
self.board.en_passant().map(|f| match self.turn() {
Color::White => Square::new(f.into(), Rank::Sixth),
Expand All @@ -119,56 +125,67 @@
/// This position's [zobrist hash].
///
/// [zobrist hash]: https://www.chessprogramming.org/Zobrist_Hashing
#[inline(always)]
pub fn zobrist(&self) -> Zobrist {
Bits::new(self.board.hash())
}

/// An iterator over all pieces on the board.
#[inline(always)]
pub fn iter(&self) -> impl ExactSizeIterator<Item = (Piece, Square)> + '_ {
self.occupied().into_iter().map(|s| (self[s].assume(), s))
}

/// [`Square`]s occupied.
#[inline(always)]
pub fn occupied(&self) -> Bitboard {
self.board.occupied().into()
}

/// [`Square`]s occupied by a [`Color`].
#[inline(always)]
pub fn by_color(&self, c: Color) -> Bitboard {
self.board.colors(c.into()).into()
}

/// [`Square`]s occupied by a [`Role`].
#[inline(always)]
pub fn by_role(&self, r: Role) -> Bitboard {
self.board.pieces(r.into()).into()
}

/// [`Square`]s occupied by a [`Piece`].
#[inline(always)]
pub fn by_piece(&self, Piece(c, r): Piece) -> Bitboard {
self.board.colored_pieces(c.into(), r.into()).into()
}

/// [`Square`] occupied by a the king of the given color.
#[inline(always)]
pub fn king(&self, side: Color) -> Square {
self.board.king(side.into()).into()
}

/// The [`Role`] of the piece on the given [`Square`], if any.
#[inline(always)]
pub fn role_on(&self, s: Square) -> Option<Role> {
self.board.piece_on(s.into()).map(Role::from)
}

/// The [`Color`] of the piece on the given [`Square`], if any.
#[inline(always)]
pub fn color_on(&self, s: Square) -> Option<Color> {
self.board.color_on(s.into()).map(Color::from)
}

/// The [`Piece`] on the given [`Square`], if any.
#[inline(always)]
pub fn piece_on(&self, s: Square) -> Option<Piece> {
Option::zip(self.color_on(s), self.role_on(s)).map(|(c, r)| Piece(c, r))
}

/// Into where a [`Piece`] on this [`Square`] can attack.
#[inline(always)]
pub fn attacks(&self, s: Square, Piece(c, r): Piece) -> Bitboard {
let blk = self.occupied().into();

Expand All @@ -183,11 +200,13 @@
}

/// From where a [`Piece`] can attack into this [`Square`].
#[inline(always)]
pub fn attackers(&self, s: Square, p: Piece) -> Bitboard {
self.attacks(s, p.mirror())
}

/// How many other times this position has repeated.
#[inline(always)]
pub fn repetitions(&self) -> usize {
let zobrist = self.zobrist().pop();
let history = &self.history[self.turn() as usize];
Expand All @@ -197,41 +216,47 @@
/// Whether this position is a [check].
///
/// [check]: https://www.chessprogramming.org/Check
#[inline(always)]
pub fn is_check(&self) -> bool {
!self.board.checkers().is_empty()
}

/// Whether this position is a [checkmate].
///
/// [checkmate]: https://www.chessprogramming.org/Checkmate
#[inline(always)]
pub fn is_checkmate(&self) -> bool {
self.is_check() & !self.board.generate_moves(|_| true)
}

/// Whether this position is a [stalemate].
///
/// [stalemate]: https://www.chessprogramming.org/Stalemate
#[inline(always)]
pub fn is_stalemate(&self) -> bool {
!self.is_check() & !self.board.generate_moves(|_| true)
}

/// Whether the game is a draw by [Threefold repetition].
///
/// [Threefold repetition]: https://en.wikipedia.org/wiki/Threefold_repetition
#[inline(always)]
pub fn is_draw_by_threefold_repetition(&self) -> bool {
self.repetitions() > 1
}

/// Whether the game is a draw by the [50-move rule].
///
/// [50-move rule]: https://en.wikipedia.org/wiki/Fifty-move_rule
#[inline(always)]
pub fn is_draw_by_50_move_rule(&self) -> bool {
self.halfmoves() >= 100
}

/// Whether this position has [insufficient material].
///
/// [insufficient material]: https://www.chessprogramming.org/Material#InsufficientMaterial
#[inline(always)]
pub fn is_material_insufficient(&self) -> bool {
match self.occupied().len() {
2 => true,
Expand All @@ -246,6 +271,7 @@
}

/// The [`Outcome`] of the game in case this position is final.
#[inline(always)]
pub fn outcome(&self) -> Option<Outcome> {
if self.is_checkmate() {
Some(Outcome::Checkmate(!self.turn()))
Expand All @@ -263,6 +289,7 @@
}

/// An iterator over the legal [`Move`]s that can be played from a subset of squares in this position.
#[inline(always)]
pub fn moves(&self, whence: Bitboard) -> impl Iterator<Item = Move> + '_ {
let mut moves = Buffer::<_, 18>::new();
self.board.generate_moves_for(whence.into(), |ms| {
Expand Down Expand Up @@ -298,6 +325,7 @@
}

/// Play a [`Move`].
#[inline(always)]
pub fn play(&mut self, m: Move) {
let from = m.whence();
let to = if !m.is_castling() {
Expand Down Expand Up @@ -330,13 +358,15 @@
/// Play a [null-move].
///
/// [null-move]: https://www.chessprogramming.org/Null_Move
#[inline(always)]
pub fn pass(&mut self) {
debug_assert!(!self.is_check(), "null move is illegal in `{self}`");
self.board = self.board.null_move().assume();
self.history = Default::default();
}

/// Exchange a piece on [`Square`] by the attacker of least value.
#[inline(always)]
pub fn exchange(&mut self, whither: Square) -> Result<Move, ImpossibleExchange> {
let turn = self.turn();
let capture = match self[whither] {
Expand All @@ -354,7 +384,8 @@
};

if let Some(m) = ms.into_iter().max_by_key(|m| m.promotion) {
if self.board.try_play(m).is_ok() {
if self.board.is_legal(m) {

Check warning on line 387 in lib/chess/position.rs

View check run for this annotation

Codecov / codecov/patch

lib/chess/position.rs#L387

Added line #L387 was not covered by tests
self.board.play_unchecked(m);
self.history = Default::default();
let promotion = m.promotion.map(Role::from);
return Ok(Move::regular(whence, whither, promotion, capture));
Expand All @@ -371,6 +402,7 @@
impl Index<Square> for Position {
type Output = Option<Piece>;

#[inline(always)]
fn index(&self, s: Square) -> &Self::Output {
use {Color::*, Role::*};
match self.piece_on(s) {
Expand Down
2 changes: 2 additions & 0 deletions lib/chess/rank.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,13 +72,15 @@ impl Sub for Rank {

#[doc(hidden)]
impl From<cc::Rank> for Rank {
#[inline(always)]
fn from(r: cc::Rank) -> Self {
Rank::from_index(r as _)
}
}

#[doc(hidden)]
impl From<Rank> for cc::Rank {
#[inline(always)]
fn from(r: Rank) -> Self {
cc::Rank::index_const(r as _)
}
Expand Down
Loading