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
//! A simple Move representation - a turn which moves a piece on a chessboard

use crate::flag::{Flag, FlagCheck};
use crate::parser;
use crate::piece::Piece;
use crate::square::Square;
use std::fmt;

/// # Move turn
///
/// `Move` struct represents any chess turn in which piece is moved from *square
/// A* to *square B*, with some additional information.
///
/// `Move` can represent any chess turn expect for castling. Castling is
/// described separately within 'Castling' struct.
///
/// ## Example
/// ```
/// use chess_notation_parser::{Turn, Move, Square, Piece, Flag};
///
/// let turn = Turn::Move(
///     Move {
///         who: Piece::Pawn,
///         dst: Square::C3,
///         flags: Flag::NONE,
///         src: None,
///         promotion: None,
///     }
/// );
/// assert_eq!(turn, Turn::try_from("c3").unwrap());
/// ```
#[derive(Clone, Debug, PartialEq)]
pub struct Move {
    /// The piece which is moving from *square A* to *square B*
    pub who: Piece,

    /// Destination square (*square B*)
    pub dst: Square,

    /// Extra bits of information stored in a bitmask about the turn, check
    /// `Flag` for more info
    pub flags: u8,

    /// Description of a *square A*
    /// - Can be a specific square
    /// - Can be a rank or a file. In that case it is a set of 8 squares
    /// - Can be `None` when `src` doesn't need to be provided
    pub src: Option<Vec<Square>>,

    /// The `Piece` to which pawns are promoted to
    pub promotion: Option<Piece>,
}

impl Move {
    fn constuct_src_string(src: &Vec<Square>) -> String {
        match src.len() {
            1 => return src[0].to_string(),
            8 => (),
            _ => panic!("'Source' should contain a set of 8 ranks/files"),
        }

        // Get rank/file chars for the first square
        let (file, rank) = {
            let s = src[0].to_string();
            let mut square_chars = s.chars();
            (square_chars.next().unwrap(), square_chars.next().unwrap())
        };

        // Return whatever rank/file the second square can match
        match src[1].to_string().contains(file) {
            true => String::from(file),
            false => String::from(rank),
        }
    }
}

impl FlagCheck for Move {
    fn get_flags(&self) -> u8 {
        self.flags
    }
}

impl fmt::Display for Move {
    // Stringification goes in this order:
    //   <who><src><capture><dst><promotion><check/checkmate>
    //
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        // This capacity should be more than enough
        let mut s = String::with_capacity(16);

        // Pawns are not annotated in chess notation
        if self.who != Piece::Pawn {
            s.push(parser::get_piece_char(self.who));
        }

        // Append source if any
        if let Some(ref src) = self.src {
            s.push_str(Self::constuct_src_string(src).as_str());
        }

        if self.check_flag(Flag::CAPTURE) {
            s.push('x');
        }

        s.push_str(self.dst.to_string().as_str());

        if let Some(promotion_piece) = self.promotion {
            s.push('=');
            s.push(parser::get_piece_char(promotion_piece));
        }

        if self.check_flag(Flag::CHECK) {
            s.push('+');
        } else if self.check_flag(Flag::CHECKMATE) {
            s.push('#');
        }
        write!(f, "{}", s)
    }
}