1
0
Fork 0

setup readers

This commit is contained in:
Paul-Nicolas Madelaine 2025-10-22 02:06:15 +02:00
parent d9e3879937
commit e547d7596f
2 changed files with 192 additions and 194 deletions

View file

@ -59,14 +59,6 @@ pub struct Position(Setup);
const MAX_LEGAL_MOVES: usize = 218; const MAX_LEGAL_MOVES: usize = 218;
impl std::fmt::Debug for Position {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
f.debug_tuple("Position")
.field(&self.as_setup().to_text_record())
.finish()
}
}
impl Position { impl Position {
/// Returns the initial position of a chess game. /// Returns the initial position of a chess game.
/// ///
@ -100,18 +92,6 @@ impl Position {
.and_then(|setup| setup.try_into().ok()) .and_then(|setup| setup.try_into().ok())
} }
/// Returns the text record of the position.
///
/// This is a shortcut for:
/// ```
/// # |position: eschac::position::Position| {
/// position.as_setup().to_text_record()
/// # };
#[inline]
pub fn to_text_record(&self) -> String {
self.as_setup().to_text_record()
}
/// Returns all the legal moves on the position. /// Returns all the legal moves on the position.
#[inline] #[inline]
pub fn legal_moves<'l>(&'l self) -> Moves<'l> { pub fn legal_moves<'l>(&'l self) -> Moves<'l> {
@ -179,33 +159,6 @@ impl Position {
self.0.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.0.get(square)
}
/// Returns the color whose turn it is to play.
#[inline]
pub fn turn(&self) -> Color {
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.0.castling_rights(color, side)
}
/// Returns the en passant target square if it exists.
///
/// Note that if an en passant target square exists, it does not mean that taking en passant is
/// legal or even pseudo-legal.
#[inline]
pub fn en_passant_target_square(&self) -> Option<Square> {
self.0.en_passant_target_square()
}
/// Discards the castling rights for the given color and side. /// Discards the castling rights for the given color and side.
#[inline] #[inline]
pub fn remove_castling_rights(&mut self, color: Color, side: CastlingSide) { pub fn remove_castling_rights(&mut self, color: Color, side: CastlingSide) {
@ -283,7 +236,9 @@ impl Position {
Some(depth) => aux(self, depth), Some(depth) => aux(self, depth),
} }
} }
}
impl Position {
pub(crate) fn move_from_uci<'l>(&'l self, uci: UciMove) -> Result<Move<'l>, InvalidUciMove> { pub(crate) fn move_from_uci<'l>(&'l self, uci: UciMove) -> Result<Move<'l>, InvalidUciMove> {
struct VisitorImpl<const ROLE: u8> { struct VisitorImpl<const ROLE: u8> {
role: Role, role: Role,
@ -487,6 +442,48 @@ impl Position {
} }
} }
impl Position {
#[inline]
pub fn to_text_record(&self) -> String {
self.0.to_text_record()
}
#[inline]
pub fn write_text_record<W>(&self, w: &mut W) -> std::fmt::Result
where
W: std::fmt::Write,
{
self.0.write_text_record(w)
}
#[inline]
pub fn get(&self, square: Square) -> Option<Piece> {
self.0.get(square)
}
#[inline]
pub fn turn(&self) -> Color {
self.0.turn()
}
#[inline]
pub fn castling_rights(&self, color: Color, side: CastlingSide) -> bool {
self.0.castling_rights(color, side)
}
#[inline]
pub fn en_passant_target_square(&self) -> Option<Square> {
self.0.en_passant_target_square()
}
#[inline]
pub fn pieces(&self) -> ByColor<ByRole<Bitboard>> {
self.0.pieces()
}
}
impl std::fmt::Debug for Position {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
f.debug_tuple("Position")
.field(&self.to_text_record())
.finish()
}
}
/// A legal move. /// A legal move.
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub struct Move<'l> { pub struct Move<'l> {

View file

@ -140,119 +140,6 @@ impl Setup {
Ok(setup) Ok(setup)
} }
/// Returns the text record of the position.
pub fn to_text_record(&self) -> String {
let mut record = String::with_capacity(81);
self.write_text_record(&mut record).unwrap();
record
}
/// Writes the text record of the position.
pub fn write_text_record<W>(&self, w: &mut W) -> std::fmt::Result
where
W: std::fmt::Write,
{
for rank in Rank::all().into_iter().rev() {
let mut count = 0;
for file in File::all() {
match self.get(Square::from_coords(file, rank)) {
Some(piece) => {
if count > 0 {
w.write_char(char::from_u32('0' as u32 + count).unwrap())?;
}
count = 0;
w.write_char(match piece.color {
Color::White => piece.role.to_char_uppercase(),
Color::Black => piece.role.to_char_lowercase(),
})?;
}
None => {
count += 1;
}
}
}
if count > 0 {
w.write_char(char::from_u32('0' as u32 + count).unwrap())?;
}
if rank != Rank::First {
w.write_char('/')?;
}
}
w.write_char(' ')?;
w.write_char(match self.turn {
Color::White => 'w',
Color::Black => 'b',
})?;
w.write_char(' ')?;
let mut no_castle_available = true;
if self.castling_rights(Color::White, CastlingSide::Short) {
w.write_char('K')?;
no_castle_available = false;
}
if self.castling_rights(Color::White, CastlingSide::Long) {
w.write_char('Q')?;
no_castle_available = false;
}
if self.castling_rights(Color::Black, CastlingSide::Short) {
w.write_char('k')?;
no_castle_available = false;
}
if self.castling_rights(Color::Black, CastlingSide::Long) {
w.write_char('q')?;
no_castle_available = false;
}
if no_castle_available {
w.write_char('-')?;
}
w.write_char(' ')?;
match self.en_passant.try_into_square() {
Some(sq) => {
w.write_str(sq.to_str())?;
}
None => {
w.write_char('-')?;
}
}
Ok(())
}
/// Returns the occupancy of a square.
#[inline]
pub fn get(&self, square: Square) -> Option<Piece> {
Some(Piece {
role: self.get_role(square)?,
color: match (self.w & square.bitboard()).is_empty() {
false => Color::White,
true => Color::Black,
},
})
}
/// Returns the color to play.
#[inline]
pub fn turn(&self) -> Color {
self.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.castling_rights.get(color, side)
}
/// Returns the optional en passant target square.
#[inline]
pub fn en_passant_target_square(&self) -> Option<Square> {
self.en_passant.try_into_square()
}
/// Sets the occupancy of a square. /// Sets the occupancy of a square.
#[inline] #[inline]
pub fn set(&mut self, square: Square, piece: Option<Piece>) { pub fn set(&mut self, square: Square, piece: Option<Piece>) {
@ -339,40 +226,6 @@ impl Setup {
} }
} }
/// Returns the bitboard of each kind of piece.
#[inline]
pub fn pieces(&self) -> ByColor<ByRole<Bitboard>> {
let Self {
w,
p_b_q,
n_b_k,
r_q_k,
..
} = self.clone();
let k = n_b_k & r_q_k;
let q = p_b_q & r_q_k;
let b = p_b_q & n_b_k;
let n = n_b_k ^ b ^ k;
let r = r_q_k ^ q ^ k;
let p = p_b_q ^ b ^ q;
ByColor::new(|color| {
let mask = match color {
Color::White => w,
Color::Black => !w,
};
ByRole::new(|kind| {
mask & match kind {
Role::Pawn => p,
Role::Knight => n,
Role::Bishop => b,
Role::Rook => r,
Role::Queen => q,
Role::King => k,
}
})
})
}
/// Tries to validate the position, i.e. converting it to a [`Position`]. /// Tries to validate the position, i.e. converting it to a [`Position`].
/// ///
/// See [`IllegalPositionReason`] for details. /// See [`IllegalPositionReason`] for details.
@ -514,6 +367,154 @@ impl Setup {
} }
} }
impl Setup {
#[inline]
pub fn to_text_record(&self) -> String {
let mut record = String::with_capacity(81);
self.write_text_record(&mut record).unwrap();
record
}
pub fn write_text_record<W>(&self, w: &mut W) -> std::fmt::Result
where
W: std::fmt::Write,
{
for rank in Rank::all().into_iter().rev() {
let mut count = 0;
for file in File::all() {
match self.get(Square::from_coords(file, rank)) {
Some(piece) => {
if count > 0 {
w.write_char(char::from_u32('0' as u32 + count).unwrap())?;
}
count = 0;
w.write_char(match piece.color {
Color::White => piece.role.to_char_uppercase(),
Color::Black => piece.role.to_char_lowercase(),
})?;
}
None => {
count += 1;
}
}
}
if count > 0 {
w.write_char(char::from_u32('0' as u32 + count).unwrap())?;
}
if rank != Rank::First {
w.write_char('/')?;
}
}
w.write_char(' ')?;
w.write_char(match self.turn {
Color::White => 'w',
Color::Black => 'b',
})?;
w.write_char(' ')?;
let mut no_castle_available = true;
if self.castling_rights(Color::White, CastlingSide::Short) {
w.write_char('K')?;
no_castle_available = false;
}
if self.castling_rights(Color::White, CastlingSide::Long) {
w.write_char('Q')?;
no_castle_available = false;
}
if self.castling_rights(Color::Black, CastlingSide::Short) {
w.write_char('k')?;
no_castle_available = false;
}
if self.castling_rights(Color::Black, CastlingSide::Long) {
w.write_char('q')?;
no_castle_available = false;
}
if no_castle_available {
w.write_char('-')?;
}
w.write_char(' ')?;
match self.en_passant.try_into_square() {
Some(sq) => {
w.write_str(sq.to_str())?;
}
None => {
w.write_char('-')?;
}
}
Ok(())
}
/// Returns the occupancy of a square.
#[inline]
pub fn get(&self, square: Square) -> Option<Piece> {
Some(Piece {
role: self.get_role(square)?,
color: match (self.w & square.bitboard()).is_empty() {
false => Color::White,
true => Color::Black,
},
})
}
/// Returns the color to play.
#[inline]
pub fn turn(&self) -> Color {
self.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.castling_rights.get(color, side)
}
/// Returns the optional en passant target square.
#[inline]
pub fn en_passant_target_square(&self) -> Option<Square> {
self.en_passant.try_into_square()
}
/// Returns the bitboard of each kind of piece.
#[inline]
pub fn pieces(&self) -> ByColor<ByRole<Bitboard>> {
let Setup {
w,
p_b_q,
n_b_k,
r_q_k,
..
} = self.clone();
let k = n_b_k & r_q_k;
let q = p_b_q & r_q_k;
let b = p_b_q & n_b_k;
let n = n_b_k ^ b ^ k;
let r = r_q_k ^ q ^ k;
let p = p_b_q ^ b ^ q;
ByColor::new(|color| {
let mask = match color {
Color::White => w,
Color::Black => !w,
};
ByRole::new(|kind| {
mask & match kind {
Role::Pawn => p,
Role::Knight => n,
Role::Bishop => b,
Role::Rook => r,
Role::Queen => q,
Role::King => k,
}
})
})
}
}
impl TryFrom<Setup> for Position { impl TryFrom<Setup> for Position {
type Error = IllegalPosition; type Error = IllegalPosition;
fn try_from(setup: Setup) -> Result<Position, IllegalPosition> { fn try_from(setup: Setup) -> Result<Position, IllegalPosition> {