Skip to content

Commit

Permalink
optimize algorithm to estimate move value
Browse files Browse the repository at this point in the history
  • Loading branch information
brunocodutra committed Jan 16, 2025
1 parent 33699e9 commit 63a8141
Show file tree
Hide file tree
Showing 8 changed files with 178 additions and 124 deletions.
62 changes: 18 additions & 44 deletions lib/chess/board.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ use crate::chess::*;
use crate::util::{Assume, Integer};
use derive_more::{Debug, Display, Error};
use std::fmt::{self, Formatter, Write};
use std::io::Write as _;
use std::str::{self, FromStr};
use std::{io::Write as _, ops::Index};

/// The chess board.
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
Expand Down Expand Up @@ -133,36 +133,12 @@ impl Board {
/// Toggles a piece on a square.
#[inline(always)]
pub fn toggle(&mut self, p: Piece, sq: Square) {
debug_assert!(self[sq].is_none_or(|q| p == q));
debug_assert!(self.piece_on(sq).is_none_or(|q| p == q));
self.colors[p.color() as usize] ^= sq.bitboard();
self.roles[p.role() as usize] ^= sq.bitboard();
}
}

/// Retrieves the [`Piece`] at a given [`Square`], if any.
impl Index<Square> for Board {
type Output = Option<Piece>;

#[inline(always)]
fn index(&self, sq: Square) -> &Self::Output {
match self.piece_on(sq) {
Some(Piece::WhitePawn) => &Some(Piece::WhitePawn),
Some(Piece::WhiteKnight) => &Some(Piece::WhiteKnight),
Some(Piece::WhiteBishop) => &Some(Piece::WhiteBishop),
Some(Piece::WhiteRook) => &Some(Piece::WhiteRook),
Some(Piece::WhiteQueen) => &Some(Piece::WhiteQueen),
Some(Piece::WhiteKing) => &Some(Piece::WhiteKing),
Some(Piece::BlackPawn) => &Some(Piece::BlackPawn),
Some(Piece::BlackKnight) => &Some(Piece::BlackKnight),
Some(Piece::BlackBishop) => &Some(Piece::BlackBishop),
Some(Piece::BlackRook) => &Some(Piece::BlackRook),
Some(Piece::BlackQueen) => &Some(Piece::BlackQueen),
Some(Piece::BlackKing) => &Some(Piece::BlackKing),
None => &None,
}
}
}

impl Display for Board {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let mut skip = 0;
Expand All @@ -173,7 +149,7 @@ impl Display for Board {
buffer[0] = if sq.rank() == Rank::First { b' ' } else { b'/' };
}

match self[sq] {
match self.piece_on(sq) {
None => skip += 1,
Some(p) => {
buffer[1] = buffer[0];
Expand Down Expand Up @@ -321,35 +297,35 @@ mod tests {
#[proptest]
fn iter_returns_pieces_and_squares(b: Board) {
for (p, sq) in b.iter() {
assert_eq!(b[sq], Some(p));
assert_eq!(b.piece_on(sq), Some(p));
}
}

#[proptest]
fn by_color_returns_squares_occupied_by_pieces_of_a_color(b: Board, c: Color) {
for sq in b.by_color(c) {
assert_eq!(b[sq].map(|p| p.color()), Some(c));
assert_eq!(b.piece_on(sq).map(|p| p.color()), Some(c));
}
}

#[proptest]
fn by_color_returns_squares_occupied_by_pieces_of_a_role(b: Board, r: Role) {
for sq in b.by_role(r) {
assert_eq!(b[sq].map(|p| p.role()), Some(r));
assert_eq!(b.piece_on(sq).map(|p| p.role()), Some(r));
}
}

#[proptest]
fn by_piece_returns_squares_occupied_by_a_piece(b: Board, p: Piece) {
for sq in b.by_piece(p) {
assert_eq!(b[sq], Some(p));
assert_eq!(b.piece_on(sq), Some(p));
}
}

#[proptest]
fn king_returns_square_occupied_by_a_king(b: Board, c: Color) {
if let Some(sq) = b.king(c) {
assert_eq!(b[sq], Some(Piece::new(Role::King, c)));
assert_eq!(b.piece_on(sq), Some(Piece::new(Role::King, c)));
}
}

Expand All @@ -362,37 +338,35 @@ mod tests {
}

#[proptest]
fn toggle_removes_piece_from_square(mut b: Board, #[filter(#b[#sq].is_some())] sq: Square) {
let p = b[sq].unwrap();
fn toggle_removes_piece_from_square(
mut b: Board,
#[filter(#b.piece_on(#sq).is_some())] sq: Square,
) {
let p = b.piece_on(sq).unwrap();
b.toggle(p, sq);
assert_eq!(b[sq], None);
assert_eq!(b.piece_on(sq), None);
}

#[proptest]
fn toggle_places_piece_on_square(
mut b: Board,
#[filter(#b[#sq].is_none())] sq: Square,
#[filter(#b.piece_on(#sq).is_none())] sq: Square,
p: Piece,
) {
b.toggle(p, sq);
assert_eq!(b[sq], Some(p));
assert_eq!(b.piece_on(sq), Some(p));
}

#[proptest]
#[should_panic]
fn toggle_panics_if_square_occupied_by_other_piece(
mut b: Board,
#[filter(#b[#sq].is_some())] sq: Square,
#[filter(Some(#p) != #b[#sq])] p: Piece,
#[filter(#b.piece_on(#sq).is_some())] sq: Square,
#[filter(Some(#p) != #b.piece_on(#sq))] p: Piece,
) {
b.toggle(p, sq);
}

#[proptest]
fn board_can_be_indexed_by_square(b: Board, sq: Square) {
assert_eq!(b[sq], b.piece_on(sq));
}

#[proptest]
fn parsing_printed_board_is_an_identity(b: Board) {
assert_eq!(b.to_string().parse(), Ok(b));
Expand Down
37 changes: 30 additions & 7 deletions lib/chess/position.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ use arrayvec::{ArrayVec, CapacityError};
use derive_more::{Debug, Display, Error, From};
use std::fmt::{self, Formatter};
use std::hash::{Hash, Hasher};
use std::num::NonZeroU32;
use std::str::FromStr;
use std::{num::NonZeroU32, ops::Index, str::FromStr};

#[cfg(test)]
use proptest::{prelude::*, sample::*};
Expand Down Expand Up @@ -652,6 +651,30 @@ impl Position {
}
}

/// Retrieves the [`Piece`] at a given [`Square`], if any.
impl Index<Square> for Position {
type Output = Option<Piece>;

#[inline(always)]
fn index(&self, sq: Square) -> &Self::Output {
match self.board.piece_on(sq) {
Some(Piece::WhitePawn) => &Some(Piece::WhitePawn),
Some(Piece::WhiteKnight) => &Some(Piece::WhiteKnight),
Some(Piece::WhiteBishop) => &Some(Piece::WhiteBishop),
Some(Piece::WhiteRook) => &Some(Piece::WhiteRook),
Some(Piece::WhiteQueen) => &Some(Piece::WhiteQueen),
Some(Piece::WhiteKing) => &Some(Piece::WhiteKing),
Some(Piece::BlackPawn) => &Some(Piece::BlackPawn),
Some(Piece::BlackKnight) => &Some(Piece::BlackKnight),
Some(Piece::BlackBishop) => &Some(Piece::BlackBishop),
Some(Piece::BlackRook) => &Some(Piece::BlackRook),
Some(Piece::BlackQueen) => &Some(Piece::BlackQueen),
Some(Piece::BlackKing) => &Some(Piece::BlackKing),
None => &None,
}
}
}

impl Display for Position {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
Display::fmt(&self.board, f)
Expand Down Expand Up @@ -736,7 +759,7 @@ mod tests {
#[proptest]
fn occupied_returns_non_empty_squares(pos: Position) {
for sq in pos.occupied() {
assert_ne!(pos.board[sq], None);
assert_ne!(pos[sq], None);
}
}

Expand All @@ -747,7 +770,7 @@ mod tests {

#[proptest]
fn king_returns_square_occupied_by_a_king(pos: Position, c: Color) {
assert_eq!(pos.board[pos.king(c)], Some(Piece::new(Role::King, c)));
assert_eq!(pos[pos.king(c)], Some(Piece::new(Role::King, c)));
}

#[proptest]
Expand Down Expand Up @@ -832,12 +855,12 @@ mod tests {
assert_ne!(pos, prev);
assert_ne!(pos.turn(), prev.turn());

assert_eq!(pos.board[m.whence()], None);
assert_eq!(pos[m.whence()], None);
assert_eq!(
pos.board[m.whither()],
pos[m.whither()],
m.promotion()
.map(|r| Piece::new(r, prev.turn()))
.or_else(|| prev.board[m.whence()])
.or_else(|| prev[m.whence()])
);

assert_eq!(
Expand Down
8 changes: 4 additions & 4 deletions lib/nnue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ pub use value::*;
/// [NNUE]: https://www.chessprogramming.org/NNUE
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
struct Nnue {
ft: Transformer<i16, { Positional::LEN }>,
psqt: Transformer<i32, { Material::LEN }>,
ft: Affine<i16, { Positional::LEN }>,
psqt: Linear<i32, { Material::LEN }>,
hidden: [Hidden<{ Positional::LEN }>; Material::LEN],
}

Expand Down Expand Up @@ -74,12 +74,12 @@ impl Nnue {
}

#[inline(always)]
fn psqt() -> &'static Transformer<i32, { Material::LEN }> {
fn psqt() -> &'static Linear<i32, { Material::LEN }> {
unsafe { &NNUE.get().as_ref_unchecked().psqt }
}

#[inline(always)]
fn ft() -> &'static Transformer<i16, { Positional::LEN }> {
fn ft() -> &'static Affine<i16, { Positional::LEN }> {
unsafe { &NNUE.get().as_ref_unchecked().ft }
}

Expand Down
Loading

0 comments on commit 63a8141

Please sign in to comment.