eschac 0.6.2

computing chess moves
Documentation
//! UCI notation.
//!
//! Move notation as defined by the Universal Chess Interface standard used for most chess engines
//! and chess servers.
//!
//! A move is described by its origin and target squares. Castling is described as the move done by
//! the king. For promotion, a lowercase letter is added at the end of the move.
//!
//! Examples: *`e2e4`*, *`d1d8`*, *`e1g1`* (short castling), *`h7h8q`* (promotion)

use crate::board::*;
use crate::position::*;

/// **The UCI notation of a move.**
///
/// UCI notation can be obtained from a legal move using the [`Move::to_uci`] method.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct UciMove {
	pub from: Square,
	pub to: Square,
	pub promotion: Option<Role>,
}

impl UciMove {
	/// Tries to convert UCI notation to a playable move.
	#[inline]
	pub fn to_move<'l>(self, position: &'l Position) -> Result<Move<'l>, InvalidUciMove> {
		let Self {
			from,
			to,
			promotion,
		} = self;
		position
			.check_move(from, to, promotion)
			.ok_or(InvalidUciMove::Illegal)
	}

	/// Tries to read UCI notation from ascii text.
	#[inline]
	pub fn from_ascii(s: &[u8]) -> Option<Self> {
		match s {
			[a, b, c, d, s @ ..] => Some(Self {
				from: Square::from_coords(File::from_ascii(*a)?, Rank::from_ascii(*b)?),
				to: Square::from_coords(File::from_ascii(*c)?, Rank::from_ascii(*d)?),
				promotion: match s {
					[] => None,
					[c] => Some(Role::from_ascii(*c)?),
					_ => return None,
				},
			}),
			_ => None,
		}
	}
}

impl core::fmt::Display for UciMove {
	#[inline]
	fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
		write!(f, "{}{}", self.from, self.to)?;
		if let Some(promotion) = self.promotion {
			write!(f, "{}", promotion.to_char_lowercase())?;
		}
		Ok(())
	}
}

impl core::fmt::Debug for UciMove {
	fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
		write!(f, "{self}")
	}
}

impl core::str::FromStr for UciMove {
	type Err = ParseUciMoveError;
	#[inline]
	fn from_str(s: &str) -> Result<Self, Self::Err> {
		Self::from_ascii(s.as_bytes()).ok_or(ParseUciMoveError)
	}
}

/// A syntax error when parsing a [`UciMove`].
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ParseUciMoveError;
impl core::fmt::Display for ParseUciMoveError {
	fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
		f.write_str("invalid UCI notation")
	}
}
impl core::error::Error for ParseUciMoveError {}

/// An error when converting [`UciMove`] notation to a playable [`Move`].
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum InvalidUciMove {
	/// The is no move on the position that matches the UCI notation.
	Illegal,
}
impl core::fmt::Display for InvalidUciMove {
	fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
		f.write_str("illegal UCI move")
	}
}
impl core::error::Error for InvalidUciMove {}