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}