setup readers
This commit is contained in:
parent
d9e3879937
commit
e547d7596f
2 changed files with 192 additions and 194 deletions
|
|
@ -59,14 +59,6 @@ pub struct Position(Setup);
|
|||
|
||||
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 {
|
||||
/// Returns the initial position of a chess game.
|
||||
///
|
||||
|
|
@ -100,18 +92,6 @@ impl Position {
|
|||
.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.
|
||||
#[inline]
|
||||
pub fn legal_moves<'l>(&'l self) -> Moves<'l> {
|
||||
|
|
@ -179,33 +159,6 @@ impl Position {
|
|||
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.
|
||||
#[inline]
|
||||
pub fn remove_castling_rights(&mut self, color: Color, side: CastlingSide) {
|
||||
|
|
@ -283,7 +236,9 @@ impl Position {
|
|||
Some(depth) => aux(self, depth),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Position {
|
||||
pub(crate) fn move_from_uci<'l>(&'l self, uci: UciMove) -> Result<Move<'l>, InvalidUciMove> {
|
||||
struct VisitorImpl<const ROLE: u8> {
|
||||
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.
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Move<'l> {
|
||||
|
|
|
|||
295
src/setup.rs
295
src/setup.rs
|
|
@ -140,119 +140,6 @@ impl 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.
|
||||
#[inline]
|
||||
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`].
|
||||
///
|
||||
/// 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 {
|
||||
type Error = IllegalPosition;
|
||||
fn try_from(setup: Setup) -> Result<Position, IllegalPosition> {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue