use game_board::get_square_name_from_row_and_col;
use crate::{ChessGame, ChessMoveType};
use crate::chess_game_state_analyzer::GameState;
use crate::piece::{ChessPiece, PieceType};
pub fn encode_move_as_long_algebraic_notation(
chess_move_type: &ChessMoveType
) -> String {
match chess_move_type {
ChessMoveType::Move {
original_position,
new_position,
piece,
taken_piece,
promotion
} => encode_move(
original_position,
new_position,
piece,
taken_piece,
promotion
),
ChessMoveType::EnPassant {
original_position,
new_position,
promotion,
..
} => encode_en_passant(
original_position,
new_position,
promotion,
),
ChessMoveType::Castle {
rook_original_position,
..
} => encode_castle(
rook_original_position
),
}
}
pub fn encode_game(game: &ChessGame) -> String {
game
.get_moves()
.iter()
.map(|m| encode_move_as_long_algebraic_notation(&m))
.collect::<Vec<String>>()
.join(" ")
}
pub fn build_game_from_long_algebraic_notation(long_algebraic_notation_string: &str) -> Result<ChessGame, LongAlgebraicNotationError> {
let mut game = ChessGame::new();
let normalized_string = long_algebraic_notation_string.trim().replace("\n", " ").to_lowercase();
let moves = normalized_string.split(" ");
for mv in moves {
let available_moves = match game.get_game_state() {
GameState::InProgress { legal_moves, .. } => legal_moves,
GameState::Check { legal_moves, .. } => legal_moves,
GameState::Checkmate { .. } => Vec::new(),
GameState::Stalemate => Vec::new(),
};
let next_move = available_moves.iter().copied().find(|m| {
encode_move_as_long_algebraic_notation(m).to_lowercase() == mv
});
match next_move {
Some(m) => game.make_move(m),
None => return Err(LongAlgebraicNotationError{reason: format!("Unable to make move {}", mv)})
}
}
Ok(game)
}
fn encode_move(
original_position: &(usize, usize),
new_position: &(usize, usize),
piece: &ChessPiece,
taken_piece: &Option<ChessPiece>,
promotion: &Option<ChessPiece>
) -> String {
let moving_piece_str = get_piece_as_char(piece.get_piece_type()).unwrap_or(String::new());
let original_square_str = get_square_name_from_row_and_col(original_position.0, original_position.1);
let taken_str = match taken_piece {
Some( .. ) => "x",
None => ""
};
let new_square_str = get_square_name_from_row_and_col(new_position.0, new_position.1);
let promotion_str = match promotion {
Some(piece) => {
format!("={}", get_piece_as_char(piece.get_piece_type()).unwrap())
},
None => String::new()
};
format!(
"{}{}{}{}{}",
moving_piece_str,
original_square_str,
taken_str,
new_square_str,
promotion_str
)
}
fn encode_en_passant(
original_position: &(usize, usize),
new_position: &(usize, usize),
promotion: &Option<ChessPiece>,
) -> String {
let original_square_str = get_square_name_from_row_and_col(original_position.0, original_position.1);
let new_square_str = get_square_name_from_row_and_col(new_position.0, new_position.1);
let promotion_str = match promotion {
None => String::new(),
Some(piece) => format!("={}", get_piece_as_char(piece.get_piece_type()).unwrap())
};
format!("{}x{}{} e.p.",
original_square_str,
new_square_str,
promotion_str,
)
}
fn encode_castle(
rook_original_position: &(usize, usize)
) -> String {
if rook_original_position.0 == 0 {
String::from("O-O-O")
} else {
String::from("O-O")
}
}
fn get_piece_as_char(piece_type: PieceType) -> Option<String> {
match piece_type {
PieceType::Pawn => None,
PieceType::Rook => Some(String::from("R")),
PieceType::Knight => Some(String::from("N")),
PieceType::Bishop => Some(String::from("B")),
PieceType::Queen => Some(String::from("Q")),
PieceType::King => Some(String::from("K")),
}
}
pub struct LongAlgebraicNotationError {
pub reason: String,
}
#[cfg(test)]
mod tests {
use crate::codec::forsyth_edwards_notation::encode_game_as_string;
use super::*;
#[test]
fn decode_string_to_game() {
let string_to_decode = "h2h3 f7f6 Nb1a3 Ng8h6 g2g3 f6f5 c2c4 g7g6 g3g4 Nh6f7 g4xf5 ";
match build_game_from_long_algebraic_notation(string_to_decode) {
Ok(mut game) => {
let current_state = &game.get_game_state();
let game_as_fen_string = encode_game_as_string(&game);
println!("{game_as_fen_string}");
match current_state {
GameState::InProgress { .. } => assert!(true, "Game is in Progress"),
_ => panic!("Game should be in progress"),
}
}
Err(_) => {
panic!();
}
}
}
#[test]
fn encode_game_to_string() {
let mut game = ChessGame::new();
let expected_encoded_string = String::from("Nb1c3 a7a6 Ra1b1 a6a5 Rb1a1 a5a4 Ra1b1 a4a3 Rb1a1 a3xb2");
let mut expected_moves_as_encoded = expected_encoded_string.split(" ");
for _ in 0..10 {
let game_state = game.get_game_state();
let next_move = match game_state {
GameState::InProgress { legal_moves, .. } => legal_moves,
GameState::Check { legal_moves, .. } => legal_moves,
_ => panic!("Game has ended prematurely")
}.first().unwrap().clone();
let encoded_move = encode_move_as_long_algebraic_notation(&next_move);
assert_eq!(expected_moves_as_encoded.next().unwrap(), encoded_move);
game.make_move(next_move);
}
assert_eq!(expected_encoded_string, encode_game(&game))
}
}