rg-chess 0.2.2

Chess Game with graphical interface.
Documentation
use std::fmt;

use crate::Square;

/// Represent a ChessMove in memory.
#[derive(Clone, Copy, Eq, PartialEq, Debug)]
pub struct ChessMove {
    pub from: Square,
    pub to: Square,
}

impl ChessMove {
    /// Create a new chess move.
    #[inline]
    pub fn new(from: Square, to: Square) -> Self {
        ChessMove { from, to }
    }

    /*
    /// Convert a SAN (Standard Algebraic Notation) move into a [`ChessMove`].
    ///
    /// TODO
    ///
    /// ```
    /// use chess::{Board, ChessMove, Square};
    ///
    /// let board = Board::default();
    /// assert_eq!(
    ///     ChessMove::from_san(&board, "e4").expect("e4 is valid in the initial position"),
    ///     ChessMove::new(Square::E2, Square::E4)
    /// );
    /// ```
    pub fn from_san(board: &Board, move_text: &str) -> Result<ChessMove, Error> {
        // Castles first...
        if move_text == "O-O" || move_text == "O-O-O" {
            let rank = match board.side_to_move() {
                Color::White => Rank::First,
                Color::Black => Rank::Eighth,
            };
            let source_file = File::E;
            let dest_file = if move_text == "O-O" { File::G } else { File::C };

            let m = ChessMove::new(
                Square::make_square(source_file, rank),
                Square::make_square(dest_file, rank),
            );
            return if board.is_legal(m) {
                Ok(m)
            } else {
                Err(Error::InvalidSanMove)
            };
        }

        // forms of SAN moves
        // a4 (Pawn moves to a4)
        // exd4 (Pawn on e file takes on d4)
        // xd4 (Illegal, source file must be specified)
        // 1xd4 (Illegal, source file (not rank) must be specified)
        // Nc3 (Knight (or any piece) on *some square* to c3
        // Nb1c3 (Knight (or any piece) on b1 to c3
        // Nbc3 (Knight on b file to c3)
        // N1c3 (Knight on first rank to c3)
        // Nb1xc3 (Knight on b1 takes on c3)
        // Nbxc3 (Knight on b file takes on c3)
        // N1xc3 (Knight on first rank takes on c3)
        // Nc3+ (Knight moves to c3 with check)
        // Nc3# (Knight moves to c3 with checkmate)

        let error = Error::InvalidSanMove;
        let mut cur_index: usize = 0;
        let moving_piece = match move_text
            .get(cur_index..(cur_index + 1))
            .ok_or(error.clone())?
        {
            "N" => {
                cur_index += 1;
                Piece::Knight
            }
            "B" => {
                cur_index += 1;
                Piece::Bishop
            }
            "Q" => {
                cur_index += 1;
                Piece::Queen
            }
            "R" => {
                cur_index += 1;
                Piece::Rook
            }
            "K" => {
                cur_index += 1;
                Piece::King
            }
            _ => Piece::Pawn,
        };

        let mut source_file = match move_text
            .get(cur_index..(cur_index + 1))
            .ok_or(error.clone())?
        {
            "a" => {
                cur_index += 1;
                Some(File::A)
            }
            "b" => {
                cur_index += 1;
                Some(File::B)
            }
            "c" => {
                cur_index += 1;
                Some(File::C)
            }
            "d" => {
                cur_index += 1;
                Some(File::D)
            }
            "e" => {
                cur_index += 1;
                Some(File::E)
            }
            "f" => {
                cur_index += 1;
                Some(File::F)
            }
            "g" => {
                cur_index += 1;
                Some(File::G)
            }
            "h" => {
                cur_index += 1;
                Some(File::H)
            }
            _ => None,
        };

        let mut source_rank = match move_text
            .get(cur_index..(cur_index + 1))
            .ok_or(error.clone())?
        {
            "1" => {
                cur_index += 1;
                Some(Rank::First)
            }
            "2" => {
                cur_index += 1;
                Some(Rank::Second)
            }
            "3" => {
                cur_index += 1;
                Some(Rank::Third)
            }
            "4" => {
                cur_index += 1;
                Some(Rank::Fourth)
            }
            "5" => {
                cur_index += 1;
                Some(Rank::Fifth)
            }
            "6" => {
                cur_index += 1;
                Some(Rank::Sixth)
            }
            "7" => {
                cur_index += 1;
                Some(Rank::Seventh)
            }
            "8" => {
                cur_index += 1;
                Some(Rank::Eighth)
            }
            _ => None,
        };

        let takes = if let Some(s) = move_text.get(cur_index..(cur_index + 1)) {
            match s {
                "x" => {
                    cur_index += 1;
                    true
                }
                _ => false,
            }
        } else {
            false
        };

        let dest = if let Some(s) = move_text.get(cur_index..(cur_index + 2)) {
            if let Ok(q) = Square::from_str(s) {
                cur_index += 2;
                q
            } else {
                let sq = Square::make_square(
                    source_file.ok_or(error.clone())?,
                    source_rank.ok_or(error.clone())?,
                );
                source_rank = None;
                source_file = None;
                sq
            }
        } else {
            let sq = Square::make_square(
                source_file.ok_or(error.clone())?,
                source_rank.ok_or(error.clone())?,
            );
            source_rank = None;
            source_file = None;
            sq
        };

        let promotion = if let Some(s) = move_text.get(cur_index..(cur_index + 1)) {
            match s {
                "N" => {
                    cur_index += 1;
                    Some(Piece::Knight)
                }
                "B" => {
                    cur_index += 1;
                    Some(Piece::Bishop)
                }
                "R" => {
                    cur_index += 1;
                    Some(Piece::Rook)
                }
                "Q" => {
                    cur_index += 1;
                    Some(Piece::Queen)
                }
                _ => None,
            }
        } else {
            None
        };

        if let Some(s) = move_text.get(cur_index..(cur_index + 1)) {
            let _maybe_check_or_mate = match s {
                "+" => {
                    cur_index += 1;
                    Some(false)
                }
                "#" => {
                    cur_index += 1;
                    Some(true)
                }
                _ => None,
            };
        }

        let ep = if let Some(s) = move_text.get(cur_index..) {
            s == " e.p."
        } else {
            false
        };

        //if ep {
        //    cur_index += 5;
        //}

        // Ok, now we have all the data from the SAN move, in the following structures
        // moveing_piece, source_rank, source_file, taks, dest, promotion, maybe_check_or_mate, and
        // ep

        todo!()
        /*
        let mut found_move: Option<ChessMove> = None;
        for m in &mut MoveGen::new_legal(board) {
            // check that the move has the properties specified
            if board.piece_on(m.get_source()) != Some(moving_piece) {
                continue;
            }

            if let Some(rank) = source_rank {
                if m.get_source().get_rank() != rank {
                    continue;
                }
            }

            if let Some(file) = source_file {
                if m.get_source().get_file() != file {
                    continue;
                }
            }

            if m.get_dest() != dest {
                continue;
            }

            if m.get_promotion() != promotion {
                continue;
            }

            if found_move.is_some() {
                return Err(error);
            }

            // takes is complicated, because of e.p.
            if !takes {
                if board.piece_on(m.get_dest()).is_some() {
                    continue;
                }
            }

            if !ep && takes {
                if board.piece_on(m.get_dest()).is_none() {
                    continue;
                }
            }

            found_move = Some(m);
        }

        found_move.ok_or(error.clone())

        */
    } */

    /// The distance between the two [`Square`] of the move.
    ///
    /// ```
    /// use chess::{ChessMove, Square};
    ///
    /// let m = ChessMove::new(Square::A1, Square::H8);
    ///
    /// assert_eq!(m.distance(), 7);
    /// ```
    pub fn distance(&self) -> u32 {
        self.from.distance(self.to)
    }
}

impl fmt::Display for ChessMove {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}{}", self.from, self.to)
    }
}