1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198
//! Data types for text representations of game positions and moves, which may be used for [Portable game notation][1].
//!
//! The terminology used in this module is specific to chess and chess variants, but it can be implemented for any game.
//!
//! [1]: https://en.wikipedia.org/wiki/Portable_Game_Notation
extern crate board_game_traits;
use board_game_traits::{GameResult, Position};
use std::error;
use std::fmt;
/// A list of general categories of errors related to pgn parsing.
///
/// This list is intended to grow over time and it is not recommended to exhaustively match against it.
///
/// It is used with the [`Error`] type.
///
/// [`Error`]: struct.Error.html
#[derive(Clone, Copy, Eq, PartialEq, Debug, PartialOrd, Ord)]
pub enum ErrorKind {
ParseError,
AmbiguousMove,
IllegalMove,
IllegalPosition,
IoError,
Other,
}
/// The error type for operations on a `PgnPosition`.
///
/// The error can be created with an arbitrary payload and optionally an underlying source error for error chaining.
#[derive(Debug)]
pub struct Error {
kind: ErrorKind,
error: Box<dyn error::Error + Send + Sync>,
source: Option<Box<dyn error::Error + Send + Sync>>,
}
impl Error {
/// Returns a new error of the specific `ErrorKind` with an arbitrary payload.
pub fn new<E>(kind: ErrorKind, error: E) -> Error
where
E: Into<Box<dyn error::Error + Send + Sync>>,
{
Error {
kind,
error: error.into(),
source: None,
}
}
/// Returns a new error of the specific `ErrorKind` with an arbitrary payload and source error.
pub fn new_caused_by<E, F>(kind: ErrorKind, error: E, source: F) -> Error
where
E: Into<Box<dyn error::Error + Send + Sync>>,
F: Into<Box<dyn error::Error + Send + Sync>>,
{
Error {
kind,
error: error.into(),
source: Some(source.into()),
}
}
/// Convenience function that returns a `ParseError` with an arbitrary payload. Equivalent to calling `Error::new(ErrorKind::ParseError, error)`
pub fn new_parse_error<E>(error: E) -> Error
where
E: Into<Box<dyn error::Error + Send + Sync>>,
{
Error {
kind: ErrorKind::ParseError,
error: error.into(),
source: None,
}
}
}
impl error::Error for Error {}
impl fmt::Display for Error {
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match self.kind {
ErrorKind::ParseError => write!(fmt, "Parse error. "),
ErrorKind::AmbiguousMove => write!(fmt, "Ambiguous move. "),
ErrorKind::IllegalMove => write!(fmt, "Illegal move. "),
ErrorKind::IllegalPosition => write!(fmt, "Illegal position. "),
ErrorKind::IoError => write!(fmt, "IO error. "),
ErrorKind::Other => Ok(()),
}?;
write!(fmt, "{}", self.error)?;
if let Some(ref source) = self.source {
write!(fmt, "\nCaused by: {}", source)?;
}
Ok(())
}
}
/// Trait for text representations of game positions and moves.
///
/// The terminology used in this trait is specific to chess and chess variants, but it can be implemented for any game.
pub trait PgnPosition: Sized + Position + PartialEq {
/// The required tags, and their default values, for pgn files
const REQUIRED_TAGS: &'static [(&'static str, &'static str)];
/// The name of a special tag that allows non-standard start positions for a game
/// For such games, parsers need to handle this tag to work correctly
/// In standard chess PGN, this would be `Some("FEN")`
/// Can be `None` to disable its usage
const START_POSITION_TAG_NAME: Option<&'static str>;
/// Each possible game result in the pgn
const POSSIBLE_GAME_RESULTS: &'static [(&'static str, Option<GameResult>)] = &[
("*", None),
("1-0", Some(GameResult::WhiteWin)),
("0-1", Some(GameResult::BlackWin)),
("1/2-1/2", Some(GameResult::Draw)),
];
/// Returns a more detailed game result string, for games that use these.
/// Must correspond with `POSSIBLE_GAME_RESULTS`
#[inline]
fn pgn_game_result(&self) -> Option<&'static str> {
self.game_result().map(|game_result| match game_result {
GameResult::WhiteWin => "1-0",
GameResult::BlackWin => "0-1",
GameResult::Draw => "1/2-1/2",
})
}
/// Each possible move annotation that can appear at the end of a move
/// A move can have multiple annotations.
/// If one annotation is a substring of another, the longer one must be written first
const POSSIBLE_MOVE_ANNOTATIONS: &'static [&'static str] = &["!!", "!?", "?!", "??", "!", "?"];
/// Constructs a position from [Forsyth–Edwards Notation][1].
///
/// Extensions to this notation exist for all large chess variants
///
/// [1]: https://en.wikipedia.org/wiki/Forsyth%E2%80%93Edwards_Notation
#[inline]
fn from_fen(fen: &str) -> Result<Self, Error> {
Self::from_fen_with_settings(fen, &Self::Settings::default())
}
/// The number of full moves in the position
/// It starts at 1 and is incremented after the second player's move
#[inline]
fn full_move_number(&self) -> Option<u32> {
None
}
/// Constructs a position from [Forsyth–Edwards Notation][1] with the given settings.
///
/// Extensions to this notation exist for all large chess variants
///
/// [1]: https://en.wikipedia.org/wiki/Forsyth%E2%80%93Edwards_Notation
fn from_fen_with_settings(fen: &str, settings: &Self::Settings) -> Result<Self, Error>;
/// Returns a string representation of the position in [Forsyth–Edwards Notation][1].
///
/// Extensions to this notation exist for all large chess variants.
///
/// [1]: https://en.wikipedia.org/wiki/Forsyth%E2%80%93Edwards_Notation
fn to_fen(&self) -> String;
/// Construct a game move from [Standard Algebraic Notation][1], specifically the format used in [pgn notation][2].
///
/// Extensions to this notation exist for all large chess variants.
///
/// [1]: https://en.wikipedia.org/wiki/Algebraic_notation_(chess)
/// [2]: https://en.wikipedia.org/wiki/Portable_Game_Notation
fn move_from_san(&self, input: &str) -> Result<Self::Move, Error>;
/// Returns a string representation of the move in [Standard Algebraic Notation][1], specifically the format used in [pgn notation][2].
///
/// Extensions to this notation exist for all large chess variants.
///
/// [1]: https://en.wikipedia.org/wiki/Algebraic_notation_(chess)
/// [2]: https://en.wikipedia.org/wiki/Portable_Game_Notation
fn move_to_san(&self, mv: &Self::Move) -> String;
/// Construct a move from an alternative, [long algebraic notation][1].
///
/// This is mostly used for chess and chess variations in the uci interface, or for convenient debugging.
/// Implementations may simply wrap this function around `move_from_san` where appropriate.
///
/// [1]: https://en.wikipedia.org/wiki/Algebraic_notation_(chess)#Long_algebraic_notation
fn move_from_lan(&self, input: &str) -> Result<Self::Move, Error>;
/// Returns a string representation of the move in an alternative, [long algebraic notation][1].
///
/// This is mostly used for chess and chess variations in the uci interface, or for convenient debugging.
/// Implementations may simply wrap this function around `move_to_san` where appropriate.
///
/// [1]: https://en.wikipedia.org/wiki/Algebraic_notation_(chess)#Long_algebraic_notation
fn move_to_lan(&self, mv: &Self::Move) -> String;
}