chess_notation_parser/
move.rs

1//! A simple Move representation - a turn which moves a piece on a chessboard
2
3use crate::flag::{Flag, FlagCheck};
4use crate::parser;
5use crate::piece::Piece;
6use crate::square::Square;
7use std::fmt;
8
9/// # Move turn
10///
11/// `Move` struct represents any chess turn in which piece is moved from *square
12/// A* to *square B*, with some additional information.
13///
14/// `Move` can represent any chess turn expect for castling. Castling is
15/// described separately within 'Castling' struct.
16///
17/// ## Example
18/// ```
19/// use chess_notation_parser::{Turn, Move, Square, Piece, Flag};
20///
21/// let turn = Turn::Move(
22///     Move {
23///         who: Piece::Pawn,
24///         dst: Square::C3,
25///         flags: Flag::NONE,
26///         src: None,
27///         promotion: None,
28///     }
29/// );
30/// assert_eq!(turn, Turn::try_from("c3").unwrap());
31/// ```
32#[derive(Clone, Debug, PartialEq)]
33pub struct Move {
34    /// The piece which is moving from *square A* to *square B*
35    pub who: Piece,
36
37    /// Destination square (*square B*)
38    pub dst: Square,
39
40    /// Extra bits of information stored in a bitmask about the turn, check
41    /// `Flag` for more info
42    pub flags: u8,
43
44    /// Description of a *square A*
45    /// - Can be a specific square
46    /// - Can be a rank or a file. In that case it is a set of 8 squares
47    /// - Can be `None` when `src` doesn't need to be provided
48    pub src: Option<Vec<Square>>,
49
50    /// The `Piece` to which pawns are promoted to
51    pub promotion: Option<Piece>,
52}
53
54impl Move {
55    fn constuct_src_string(src: &[Square]) -> String {
56        match src.len() {
57            1 => return src[0].to_string(),
58            8 => (),
59            _ => panic!("'Source' should contain a set of 8 ranks/files"),
60        }
61
62        // Get rank/file chars for the first square
63        let (file, rank) = {
64            let s = src[0].to_string();
65            let mut square_chars = s.chars();
66            (square_chars.next().unwrap(), square_chars.next().unwrap())
67        };
68
69        // Return whatever rank/file the second square can match
70        match src[1].to_string().contains(file) {
71            true => String::from(file),
72            false => String::from(rank),
73        }
74    }
75}
76
77impl FlagCheck for Move {
78    fn get_flags(&self) -> u8 {
79        self.flags
80    }
81}
82
83impl fmt::Display for Move {
84    // Stringification goes in this order:
85    //   <who><src><capture><dst><promotion><check/checkmate>
86    //
87    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
88        // This capacity should be more than enough
89        let mut s = String::with_capacity(16);
90
91        // Pawns are not annotated in chess notation
92        if self.who != Piece::Pawn {
93            s.push(parser::get_piece_char(self.who));
94        }
95
96        // Append source if any
97        if let Some(ref src) = self.src {
98            s.push_str(Self::constuct_src_string(src).as_str());
99        }
100
101        if self.check_flag(Flag::CAPTURE) {
102            s.push('x');
103        }
104
105        s.push_str(self.dst.to_string().as_str());
106
107        if let Some(promotion_piece) = self.promotion {
108            s.push('=');
109            s.push(parser::get_piece_char(promotion_piece));
110        }
111
112        if self.check_flag(Flag::CHECK) {
113            s.push('+');
114        } else if self.check_flag(Flag::CHECKMATE) {
115            s.push('#');
116        }
117        write!(f, "{}", s)
118    }
119}