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;
|
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> {
|
||||||
|
|
|
||||||
295
src/setup.rs
295
src/setup.rs
|
|
@ -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> {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue