From 652745311bfd870b94e0be5c341e31aa99328110 Mon Sep 17 00:00:00 2001 From: Nick Mello Date: Sat, 20 Apr 2024 18:57:36 -0500 Subject: [PATCH 1/2] Add movement through history Adds the ability to move through history to view the moves that have been played. This is NOT equivalent to an analysis board where you can undo moves or look at other lines. Signed-off-by: Nick Mello --- src/board.rs | 296 +++++++++++++++++++++++++++++++++++++++------- src/handler.rs | 2 + src/pieces/mod.rs | 3 + src/popups.rs | 2 + src/utils.rs | 4 +- 5 files changed, 262 insertions(+), 45 deletions(-) diff --git a/src/board.rs b/src/board.rs index 282a46e..8f6a9ff 100644 --- a/src/board.rs +++ b/src/board.rs @@ -18,12 +18,14 @@ use uci::Engine; pub struct Board { pub board: [[Option<(PieceType, PieceColor)>; 8]; 8], + pub display_board: [[Option<(PieceType, PieceColor)>; 8]; 8], pub cursor_coordinates: [i8; 2], pub selected_coordinates: [i8; 2], pub selected_piece_cursor: i8, pub old_cursor_position: [i8; 2], pub player_turn: PieceColor, pub move_history: Vec, + pub history_position: usize, pub is_draw: bool, pub is_checkmate: bool, pub is_promotion: bool, @@ -83,12 +85,59 @@ impl Default for Board { Some((PieceType::Rook, PieceColor::White)), ], ], + display_board: [ + [ + Some((PieceType::Rook, PieceColor::Black)), + Some((PieceType::Knight, PieceColor::Black)), + Some((PieceType::Bishop, PieceColor::Black)), + Some((PieceType::Queen, PieceColor::Black)), + Some((PieceType::King, PieceColor::Black)), + Some((PieceType::Bishop, PieceColor::Black)), + Some((PieceType::Knight, PieceColor::Black)), + Some((PieceType::Rook, PieceColor::Black)), + ], + [ + Some((PieceType::Pawn, PieceColor::Black)), + Some((PieceType::Pawn, PieceColor::Black)), + Some((PieceType::Pawn, PieceColor::Black)), + Some((PieceType::Pawn, PieceColor::Black)), + Some((PieceType::Pawn, PieceColor::Black)), + Some((PieceType::Pawn, PieceColor::Black)), + Some((PieceType::Pawn, PieceColor::Black)), + Some((PieceType::Pawn, PieceColor::Black)), + ], + [None, None, None, None, None, None, None, None], + [None, None, None, None, None, None, None, None], + [None, None, None, None, None, None, None, None], + [None, None, None, None, None, None, None, None], + [ + Some((PieceType::Pawn, PieceColor::White)), + Some((PieceType::Pawn, PieceColor::White)), + Some((PieceType::Pawn, PieceColor::White)), + Some((PieceType::Pawn, PieceColor::White)), + Some((PieceType::Pawn, PieceColor::White)), + Some((PieceType::Pawn, PieceColor::White)), + Some((PieceType::Pawn, PieceColor::White)), + Some((PieceType::Pawn, PieceColor::White)), + ], + [ + Some((PieceType::Rook, PieceColor::White)), + Some((PieceType::Knight, PieceColor::White)), + Some((PieceType::Bishop, PieceColor::White)), + Some((PieceType::Queen, PieceColor::White)), + Some((PieceType::King, PieceColor::White)), + Some((PieceType::Bishop, PieceColor::White)), + Some((PieceType::Knight, PieceColor::White)), + Some((PieceType::Rook, PieceColor::White)), + ], + ], cursor_coordinates: [4, 4], selected_coordinates: [UNDEFINED_POSITION, UNDEFINED_POSITION], selected_piece_cursor: 0, old_cursor_position: [UNDEFINED_POSITION, UNDEFINED_POSITION], player_turn: PieceColor::White, move_history: vec![], + history_position: 0, is_draw: false, is_checkmate: false, is_promotion: false, @@ -109,12 +158,14 @@ impl Board { ) -> Self { Self { board, + display_board: board, cursor_coordinates: [4, 4], selected_coordinates: [UNDEFINED_POSITION, UNDEFINED_POSITION], selected_piece_cursor: 0, old_cursor_position: [UNDEFINED_POSITION, UNDEFINED_POSITION], player_turn, - move_history, + move_history: move_history.clone(), + history_position: move_history.len(), is_draw: false, is_checkmate: false, is_promotion: false, @@ -268,6 +319,13 @@ impl Board { // Methods to select a cell on the board pub fn select_cell(&mut self) { + // If looking at history, bring back to the present + if self.is_history_mode() { + self.display_board = self.board; + self.history_position = self.move_history.len(); + return; + } + // If we are doing a promotion the cursor is used for the popup if self.is_promotion { self.promote_piece(); @@ -486,6 +544,11 @@ impl Board { // we replace the piece by the new piece type self.board[last_move.to_y as usize][last_move.to_x as usize] = Some((new_piece, piece_color)); + self.display_board[last_move.to_y as usize][last_move.to_x as usize] = + Some((new_piece, piece_color)); + + // Promotion happened, update the previous record + self.move_history[self.history_position - 1].promotion_piece = Some(new_piece); } } self.is_promotion = false; @@ -497,14 +560,13 @@ impl Board { if !is_valid([from[0] as i8, from[1] as i8]) || !is_valid([to[0] as i8, to[1] as i8]) { return; } - let direction_y: i32 = if self.player_turn == PieceColor::White { - -1 - } else { - 1 - }; - let piece_type_from = get_piece_type(self.board, [from[0] as i8, from[1] as i8]); - let piece_type_to = get_piece_type(self.board, [to[0] as i8, to[1] as i8]); + // Check if we are in the history, this will be used to know if we need + // to update the real board later on + let history = self.is_history_mode(); + + let piece_type_from = get_piece_type(self.display_board, [from[0] as i8, from[1] as i8]); + let mut piece_type_to = get_piece_type(self.display_board, [to[0] as i8, to[1] as i8]); // Check if moving a piece let piece_type_from = match piece_type_from { @@ -512,22 +574,32 @@ impl Board { None => return, }; - // We increment the consecutive_non_pawn_or_capture if the piece type is a pawn or if there is no capture - match (piece_type_from, piece_type_to) { - (PieceType::Pawn, _) | (_, Some(_)) => { - self.consecutive_non_pawn_or_capture = 0; - } - _ => { - self.consecutive_non_pawn_or_capture += 1; + // piece_type_to is used for captures, ignore it if castling + if self.is_latest_move_castling(from, to) { + piece_type_to = None; + } + + // We increment the consecutive_non_pawn_or_capture if the piece type is + // a pawn or if there is no capture and we are not looking at the + // history + if !history { + match (piece_type_from, piece_type_to) { + (PieceType::Pawn, _) | (_, Some(_)) => { + self.consecutive_non_pawn_or_capture = 0; + } + _ => { + self.consecutive_non_pawn_or_capture += 1; + } } } // We check for en passant as the latest move + let mut is_en_passant = false; if self.is_latest_move_en_passant(from, to) { - // we kill the pawn - let row_index = to[0] as i32 - direction_y; + is_en_passant = true; - self.board[row_index as usize][to[1]] = None; + // we kill the pawn + self.display_board[from[0]][to[1]] = None; } // We check for castling as the latest move @@ -545,7 +617,7 @@ impl Board { let row_index = from_x + direction_x * 2; // We put move the king 2 cells - self.board[to[0]][row_index as usize] = self.board[from[0]][from[1]]; + self.display_board[to[0]][row_index as usize] = self.display_board[from[0]][from[1]]; // We put the rook 3 cells from it's position if it's a big castling else 2 cells // If it is playing against a bot we will receive 4 -> 6 and 4 -> 2 for to_x instead of 4 -> 7 and 4 -> 0 @@ -565,24 +637,50 @@ impl Board { } _ => unreachable!("Undefined distance for castling"), } - self.board[to[0]][row_index_rook as usize] = self.board[to[0]][to_x as usize]; + + self.display_board[to[0]][row_index_rook as usize] = + self.display_board[to[0]][to_x as usize]; // We remove the latest rook - self.board[to[0]][to_x as usize] = None; + self.display_board[to[0]][to_x as usize] = None; } else { - self.board[to[0]][to[1]] = self.board[from[0]][from[1]]; + self.display_board[to[0]][to[1]] = self.display_board[from[0]][from[1]]; } - self.board[from[0]][from[1]] = None; + self.display_board[from[0]][from[1]] = None; + + // History mode checks for en passant and promotions + if history { + if self.move_history[self.history_position].is_en_passant { + // we kill the pawn + self.display_board[from[0]][to[1]] = None; + } + + if let Some(piece) = self.move_history[self.history_position].promotion_piece { + let mut color = PieceColor::Black; + if to[0] == 0 { + color = PieceColor::White; + } - // We store it in the history - self.move_history.push(PieceMove { - piece_type: piece_type_from, - from_y: from[0] as i8, - from_x: from[1] as i8, - to_y: to[0] as i8, - to_x: to[1] as i8, - }); + self.display_board[to[0]][to[1]] = Some((piece, color)); + } + } + + // We store it in the history if not looking at history and update board + self.history_position += 1; + if !history { + self.board = self.display_board; + self.move_history.push(PieceMove { + piece_type: piece_type_from, + piece_captured: piece_type_to, + from_y: from[0] as i8, + from_x: from[1] as i8, + to_y: to[0] as i8, + to_x: to[1] as i8, + is_en_passant, + promotion_piece: None, + }); + } } // Method to get the number of authorized positions for the current player (used for the end condition) @@ -625,8 +723,8 @@ impl Board { // Check if the latest move is castling fn is_latest_move_castling(&self, from: [usize; 2], to: [usize; 2]) -> bool { - let piece_type_from = get_piece_type(self.board, [from[0] as i8, from[1] as i8]); - let piece_type_to = get_piece_type(self.board, [to[0] as i8, to[1] as i8]); + let piece_type_from = get_piece_type(self.display_board, [from[0] as i8, from[1] as i8]); + let piece_type_to = get_piece_type(self.display_board, [to[0] as i8, to[1] as i8]); let from_x: i32 = from[1] as i32; let to_x: i32 = to[1] as i32; @@ -745,11 +843,13 @@ impl Board { // Color of the cell to draw the board let mut cell_color: Color = if (i + j) % 2 == 0 { WHITE } else { BLACK }; - // Draw the available moves for the selected piece - if self.is_cell_selected() { - let selected_piece_type = get_piece_type(self.board, self.selected_coordinates); + // Draw the available moves for the selected piece and not + // looking at history + if self.is_cell_selected() && !self.is_history_mode() { + let selected_piece_type = + get_piece_type(self.display_board, self.selected_coordinates); let selected_piece_color: Option = - get_piece_color(self.board, self.selected_coordinates); + get_piece_color(self.display_board, self.selected_coordinates); let positions = self.get_authorized_positions( selected_piece_type, selected_piece_color, @@ -769,8 +869,11 @@ impl Board { if i == self.cursor_coordinates[0] && j == self.cursor_coordinates[1] { let cell = Block::default().bg(Color::LightBlue); frame.render_widget(cell.clone(), square); - } else if is_getting_checked(self.board, self.player_turn, &self.move_history) - && [i, j] == get_king_coordinates(self.board, self.player_turn) + } else if is_getting_checked( + self.display_board, + self.player_turn, + &self.move_history, + ) && [i, j] == get_king_coordinates(self.display_board, self.player_turn) { let cell = Block::default() .bg(Color::Magenta) @@ -778,7 +881,10 @@ impl Board { frame.render_widget(cell.clone(), square); } // Draw the cell green if this is the selected cell - else if i == self.selected_coordinates[0] && j == self.selected_coordinates[1] { + else if i == self.selected_coordinates[0] + && j == self.selected_coordinates[1] + && !self.is_history_mode() + { let cell = Block::default().bg(Color::LightGreen); frame.render_widget(cell.clone(), square); } else { @@ -846,13 +952,28 @@ impl Board { PieceType::piece_to_utf_enum(piece_type_to, Some(PieceColor::Black)) } + // Highlighting for history, i=0 on the first move but history_position=0 means 0 moves + // have been played (representing the beginning of the game) + let white_string = if i + 1 == self.history_position { + Span::styled(move_white.to_string(), Style::default().bg(WHITE)) + } else { + Span::raw(move_white.to_string()) + }; + + // i+1 is the move in history and we need to add 1 for blacks move as well + let black_string = if i + 2 == self.history_position { + Span::styled(move_black.to_string(), Style::default().bg(WHITE)) + } else { + Span::raw(move_black.to_string()) + }; + lines.push(Line::from(vec![ Span::raw(format!("{}. ", i / 2 + 1)), // line number Span::styled(format!("{} ", utf_icon_white), Style::default().fg(WHITE)), // white symbol - Span::raw(move_white.to_string()), // white move - Span::raw(" "), // separator + white_string, // white move + Span::raw(" "), // separator Span::styled(format!("{} ", utf_icon_black), Style::default().fg(WHITE)), // white symbol - Span::raw(move_black.to_string()), // black move + black_string, // black move ])); } @@ -879,6 +1000,95 @@ impl Board { .alignment(Alignment::Center); frame.render_widget(help_paragraph, right_panel_layout[1]); } + + pub fn is_history_mode(&self) -> bool { + if self.board == self.display_board { + return false; + } + true + } + + pub fn history_forward(&mut self) { + // Check if already up to date + if !self.is_history_mode() { + return; + } + + self.move_piece_on_the_board( + [ + self.move_history[self.history_position].from_y as usize, + self.move_history[self.history_position].from_x as usize, + ], + [ + self.move_history[self.history_position].to_y as usize, + self.move_history[self.history_position].to_x as usize, + ], + ); + } + + pub fn history_backward(&mut self) { + // Check if at the beginning of the game + if self.history_position == 0 { + return; + } + + let previous_move = self.move_history[self.history_position - 1]; + + // If it is whites turn we are undoing black's move + let color_move = if self.history_position % 2 == 0 { + PieceColor::Black + } else { + PieceColor::White + }; + let color_not_move = if self.history_position % 2 == 0 { + PieceColor::White + } else { + PieceColor::Black + }; + + // Move the piece back to where it was using the color of the turn + self.display_board[previous_move.from_y as usize][previous_move.from_x as usize] = + Some((previous_move.piece_type, color_move)); + + // Replace with what was captured (opposite color of the turn), even if + // it was none + match previous_move.piece_captured { + Some(previous_piece) => { + self.display_board[previous_move.to_y as usize][previous_move.to_x as usize] = + Some((previous_piece, color_not_move)); + } + None => { + self.display_board[previous_move.to_y as usize][previous_move.to_x as usize] = None + } + } + + // Check for castling + let distance = (previous_move.from_x - previous_move.to_x).abs(); + + if previous_move.piece_type == PieceType::King && distance > 1 { + // Add rook back where it was + self.display_board[previous_move.from_y as usize][previous_move.to_x as usize] = + Some((PieceType::Rook, color_move)); + + // Remove rook and king from previous spot, 0 is long castle + if previous_move.to_x == 0 { + self.display_board[previous_move.from_y as usize][2] = None; // King + self.display_board[previous_move.from_y as usize][3] = None; // Rook + } else { + self.display_board[previous_move.from_y as usize][5] = None; // Rook + self.display_board[previous_move.from_y as usize][6] = None; // King + } + } + + // Check for en passant and replace pawn + if previous_move.is_en_passant { + self.display_board[previous_move.from_y as usize][previous_move.to_x as usize] = + Some((PieceType::Pawn, color_not_move)); + } + + // Decrement history position + self.history_position -= 1; + } } #[cfg(test)] diff --git a/src/handler.rs b/src/handler.rs index 00c5cfc..74248e6 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -59,6 +59,8 @@ pub fn handle_key_events(key_event: KeyEvent, app: &mut App) -> AppResult<()> { } app.board.unselect_cell(); } + KeyCode::Char('N') => app.board.history_forward(), + KeyCode::Char('P') => app.board.history_backward(), // Other handlers you could add here. _ => {} } diff --git a/src/pieces/mod.rs b/src/pieces/mod.rs index 9b2cc40..adc9762 100644 --- a/src/pieces/mod.rs +++ b/src/pieces/mod.rs @@ -151,6 +151,9 @@ impl PieceType { #[derive(Debug, Copy, Clone, PartialEq)] pub struct PieceMove { pub piece_type: PieceType, + pub piece_captured: Option, + pub is_en_passant: bool, + pub promotion_piece: Option, pub from_x: i8, pub from_y: i8, pub to_x: i8, diff --git a/src/popups.rs b/src/popups.rs index a1f800b..326c2ac 100644 --- a/src/popups.rs +++ b/src/popups.rs @@ -220,6 +220,8 @@ pub fn render_help_popup(frame: &mut Frame) { Line::from(""), Line::from("`Esc`: Deselect a piece / hide popups"), Line::from(""), + Line::from("`P` for previous position, `N` for next"), + Line::from(""), Line::from("q: Quit the game"), Line::from(""), Line::from(""), diff --git a/src/utils.rs b/src/utils.rs index 5208c54..e27d958 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -278,8 +278,8 @@ pub fn get_cell_paragraph( bounding_rect: Rect, ) -> Paragraph<'_> { // Get piece and color - let piece_color = get_piece_color(board.board, cell_coordinates); - let piece_type = get_piece_type(board.board, cell_coordinates); + let piece_color = get_piece_color(board.display_board, cell_coordinates); + let piece_type = get_piece_type(board.display_board, cell_coordinates); let piece_enum = PieceType::piece_type_to_string_enum(piece_type, &board.display_mode); let paragraph = match board.display_mode { From 4b1d6f3a07f9810dc881cee56699b582f5257054 Mon Sep 17 00:00:00 2001 From: Nick Mello Date: Sun, 30 Jun 2024 01:38:23 -0500 Subject: [PATCH 2/2] Add tests to history movement features Signed-off-by: Nicholas Mello --- src/board.rs | 374 +++++++++++++++++++++++++++++++++++++++++++++ src/pieces/king.rs | 9 ++ src/pieces/pawn.rs | 9 ++ 3 files changed, 392 insertions(+) diff --git a/src/board.rs b/src/board.rs index 8f6a9ff..863ccb7 100644 --- a/src/board.rs +++ b/src/board.rs @@ -1584,6 +1584,9 @@ mod tests { from_x: 3, to_y: 6, to_x: 3, + is_en_passant: false, + piece_captured: None, + promotion_piece: None, }), ], ); @@ -1639,6 +1642,9 @@ mod tests { from_x: 4, to_y: 0, to_x: 4, + is_en_passant: false, + piece_captured: None, + promotion_piece: Some(PieceType::Queen), }), ], ); @@ -1750,6 +1756,9 @@ mod tests { from_x: 4, to_y: 7, to_x: 4, + is_en_passant: false, + piece_captured: None, + promotion_piece: Some(PieceType::Queen), }), ], ); @@ -1881,6 +1890,9 @@ mod tests { from_x: 2, to_y: 0, to_x: 1, + is_en_passant: false, + piece_captured: None, + promotion_piece: None, }), (PieceMove { piece_type: PieceType::King, @@ -1888,6 +1900,9 @@ mod tests { from_x: 6, to_y: 0, to_x: 5, + is_en_passant: false, + piece_captured: None, + promotion_piece: None, }), (PieceMove { piece_type: PieceType::King, @@ -1895,6 +1910,9 @@ mod tests { from_x: 1, to_y: 0, to_x: 2, + is_en_passant: false, + piece_captured: None, + promotion_piece: None, }), (PieceMove { piece_type: PieceType::King, @@ -1902,6 +1920,9 @@ mod tests { from_x: 5, to_y: 0, to_x: 6, + is_en_passant: false, + piece_captured: None, + promotion_piece: None, }), (PieceMove { piece_type: PieceType::King, @@ -1909,6 +1930,9 @@ mod tests { from_x: 2, to_y: 0, to_x: 1, + is_en_passant: false, + piece_captured: None, + promotion_piece: None, }), (PieceMove { piece_type: PieceType::King, @@ -1916,6 +1940,9 @@ mod tests { from_x: 6, to_y: 0, to_x: 5, + is_en_passant: false, + piece_captured: None, + promotion_piece: None, }), (PieceMove { piece_type: PieceType::King, @@ -1923,6 +1950,9 @@ mod tests { from_x: 1, to_y: 0, to_x: 2, + is_en_passant: false, + piece_captured: None, + promotion_piece: None, }), (PieceMove { piece_type: PieceType::King, @@ -1930,6 +1960,9 @@ mod tests { from_x: 5, to_y: 0, to_x: 6, + is_en_passant: false, + piece_captured: None, + promotion_piece: None, }), ], ); @@ -2028,6 +2061,9 @@ mod tests { from_x: 2, to_y: 4, to_x: 2, + is_en_passant: false, + piece_captured: None, + promotion_piece: None, }), ], ); @@ -2092,4 +2128,342 @@ mod tests { "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR b kq - 0 0" ); } + + #[test] + fn history_forward_backward() { + let custom_board = [ + [ + Some((PieceType::Rook, PieceColor::Black)), + Some((PieceType::Knight, PieceColor::Black)), + Some((PieceType::Bishop, PieceColor::Black)), + Some((PieceType::Queen, PieceColor::Black)), + Some((PieceType::King, PieceColor::Black)), + Some((PieceType::Bishop, PieceColor::Black)), + Some((PieceType::Knight, PieceColor::Black)), + Some((PieceType::Rook, PieceColor::Black)), + ], + [ + Some((PieceType::Pawn, PieceColor::Black)), + Some((PieceType::Pawn, PieceColor::Black)), + Some((PieceType::Pawn, PieceColor::Black)), + Some((PieceType::Pawn, PieceColor::Black)), + Some((PieceType::Pawn, PieceColor::Black)), + Some((PieceType::Pawn, PieceColor::Black)), + Some((PieceType::Pawn, PieceColor::Black)), + Some((PieceType::Pawn, PieceColor::Black)), + ], + [None, None, None, None, None, None, None, None], + [None, None, None, None, None, None, None, None], + [None, None, None, None, None, None, None, None], + [None, None, None, None, None, None, None, None], + [ + Some((PieceType::Pawn, PieceColor::White)), + Some((PieceType::Pawn, PieceColor::White)), + Some((PieceType::Pawn, PieceColor::White)), + Some((PieceType::Pawn, PieceColor::White)), + Some((PieceType::Pawn, PieceColor::White)), + Some((PieceType::Pawn, PieceColor::White)), + Some((PieceType::Pawn, PieceColor::White)), + Some((PieceType::Pawn, PieceColor::White)), + ], + [ + Some((PieceType::Rook, PieceColor::White)), + Some((PieceType::Knight, PieceColor::White)), + Some((PieceType::Bishop, PieceColor::White)), + Some((PieceType::Queen, PieceColor::White)), + Some((PieceType::King, PieceColor::White)), + Some((PieceType::Bishop, PieceColor::White)), + Some((PieceType::Knight, PieceColor::White)), + Some((PieceType::Rook, PieceColor::White)), + ], + ]; + + let mut board = Board::new(custom_board, PieceColor::White, vec![]); + board.move_piece_on_the_board([6, 0], [5, 0]); + board.history_backward(); + board.history_forward(); + } + + // Checking overflow and underflow cases by going back and forth through + // history in many directions + #[test] + fn history_forward_backward_overflow_underflow() { + let custom_board = [ + [ + Some((PieceType::Rook, PieceColor::Black)), + Some((PieceType::Knight, PieceColor::Black)), + Some((PieceType::Bishop, PieceColor::Black)), + Some((PieceType::Queen, PieceColor::Black)), + Some((PieceType::King, PieceColor::Black)), + Some((PieceType::Bishop, PieceColor::Black)), + Some((PieceType::Knight, PieceColor::Black)), + Some((PieceType::Rook, PieceColor::Black)), + ], + [ + Some((PieceType::Pawn, PieceColor::Black)), + Some((PieceType::Pawn, PieceColor::Black)), + Some((PieceType::Pawn, PieceColor::Black)), + Some((PieceType::Pawn, PieceColor::Black)), + Some((PieceType::Pawn, PieceColor::Black)), + Some((PieceType::Pawn, PieceColor::Black)), + Some((PieceType::Pawn, PieceColor::Black)), + Some((PieceType::Pawn, PieceColor::Black)), + ], + [None, None, None, None, None, None, None, None], + [None, None, None, None, None, None, None, None], + [None, None, None, None, None, None, None, None], + [None, None, None, None, None, None, None, None], + [ + Some((PieceType::Pawn, PieceColor::White)), + Some((PieceType::Pawn, PieceColor::White)), + Some((PieceType::Pawn, PieceColor::White)), + Some((PieceType::Pawn, PieceColor::White)), + Some((PieceType::Pawn, PieceColor::White)), + Some((PieceType::Pawn, PieceColor::White)), + Some((PieceType::Pawn, PieceColor::White)), + Some((PieceType::Pawn, PieceColor::White)), + ], + [ + Some((PieceType::Rook, PieceColor::White)), + Some((PieceType::Knight, PieceColor::White)), + Some((PieceType::Bishop, PieceColor::White)), + Some((PieceType::Queen, PieceColor::White)), + Some((PieceType::King, PieceColor::White)), + Some((PieceType::Bishop, PieceColor::White)), + Some((PieceType::Knight, PieceColor::White)), + Some((PieceType::Rook, PieceColor::White)), + ], + ]; + + let mut board = Board::new(custom_board, PieceColor::White, vec![]); + board.move_piece_on_the_board([6, 0], [5, 0]); + board.move_piece_on_the_board([1, 0], [2, 0]); + board.history_backward(); + board.history_forward(); + board.history_backward(); + board.history_backward(); + board.history_backward(); + board.history_backward(); + board.history_forward(); + board.history_forward(); + board.history_forward(); + board.history_forward(); + } + + #[test] + fn history_promition_white() { + let custom_board = [ + [None, None, None, None, None, None, None, None], + [ + None, + Some((PieceType::Pawn, PieceColor::White)), + None, + None, + None, + None, + Some((PieceType::King, PieceColor::Black)), + None, + ], + [None, None, None, None, None, None, None, None], + [None, None, None, None, None, None, None, None], + [None, None, None, None, None, None, None, None], + [None, None, None, None, None, None, None, None], + [ + None, + None, + None, + None, + Some((PieceType::King, PieceColor::White)), + None, + None, + None, + ], + [None, None, None, None, None, None, None, None], + ]; + let mut board = Board::new(custom_board, PieceColor::White, vec![]); + board.move_piece_on_the_board([1, 1], [0, 1]); + board.promotion_cursor = 0; + board.promote_piece(); + board.history_backward(); + board.history_backward(); + board.history_forward(); + board.history_forward(); + assert_eq!(board.display_board, board.board); + } + + #[test] + fn history_promition_black() { + let custom_board = [ + [None, None, None, None, None, None, None, None], + [ + None, + None, + None, + None, + None, + None, + Some((PieceType::King, PieceColor::Black)), + None, + ], + [None, None, None, None, None, None, None, None], + [None, None, None, None, None, None, None, None], + [None, None, None, None, None, None, None, None], + [None, None, None, None, None, None, None, None], + [ + None, + Some((PieceType::Pawn, PieceColor::Black)), + None, + None, + Some((PieceType::King, PieceColor::White)), + None, + None, + None, + ], + [None, None, None, None, None, None, None, None], + ]; + let mut board = Board::new(custom_board, PieceColor::Black, vec![]); + board.move_piece_on_the_board([6, 1], [7, 1]); + board.promotion_cursor = 0; + board.promote_piece(); + board.history_backward(); + board.history_backward(); + board.history_forward(); + board.history_forward(); + assert_eq!(board.display_board, board.board); + } + + #[test] + fn history_en_passant() { + let custom_board = [ + [ + Some((PieceType::Rook, PieceColor::Black)), + Some((PieceType::Knight, PieceColor::Black)), + Some((PieceType::Bishop, PieceColor::Black)), + Some((PieceType::Queen, PieceColor::Black)), + Some((PieceType::King, PieceColor::Black)), + Some((PieceType::Bishop, PieceColor::Black)), + Some((PieceType::Knight, PieceColor::Black)), + Some((PieceType::Rook, PieceColor::Black)), + ], + [ + None, + Some((PieceType::Pawn, PieceColor::Black)), + Some((PieceType::Pawn, PieceColor::Black)), + Some((PieceType::Pawn, PieceColor::Black)), + Some((PieceType::Pawn, PieceColor::Black)), + Some((PieceType::Pawn, PieceColor::Black)), + Some((PieceType::Pawn, PieceColor::Black)), + Some((PieceType::Pawn, PieceColor::Black)), + ], + [None, None, None, None, None, None, None, None], + [None, None, None, None, None, None, None, None], + [ + Some((PieceType::Pawn, PieceColor::Black)), + None, + None, + None, + None, + None, + None, + None, + ], + [None, None, None, None, None, None, None, None], + [ + Some((PieceType::Pawn, PieceColor::White)), + Some((PieceType::Pawn, PieceColor::White)), + Some((PieceType::Pawn, PieceColor::White)), + Some((PieceType::Pawn, PieceColor::White)), + Some((PieceType::Pawn, PieceColor::White)), + Some((PieceType::Pawn, PieceColor::White)), + Some((PieceType::Pawn, PieceColor::White)), + Some((PieceType::Pawn, PieceColor::White)), + ], + [ + Some((PieceType::Rook, PieceColor::White)), + Some((PieceType::Knight, PieceColor::White)), + Some((PieceType::Bishop, PieceColor::White)), + Some((PieceType::Queen, PieceColor::White)), + Some((PieceType::King, PieceColor::White)), + Some((PieceType::Bishop, PieceColor::White)), + Some((PieceType::Knight, PieceColor::White)), + Some((PieceType::Rook, PieceColor::White)), + ], + ]; + + let mut board = Board::new(custom_board, PieceColor::White, vec![]); + board.move_piece_on_the_board([6, 1], [4, 1]); + board.move_piece_on_the_board([4, 0], [3, 1]); + board.move_piece_on_the_board([2, 2], [3, 1]); + board.history_backward(); + board.history_backward(); + board.history_backward(); + board.history_backward(); + board.history_forward(); + board.history_forward(); + board.history_forward(); + board.history_forward(); + assert_eq!(board.display_board, board.board); + } + + #[test] + fn history_castling() { + let custom_board = [ + [ + Some((PieceType::Rook, PieceColor::Black)), + Some((PieceType::Knight, PieceColor::Black)), + Some((PieceType::Bishop, PieceColor::Black)), + Some((PieceType::Queen, PieceColor::Black)), + Some((PieceType::King, PieceColor::Black)), + Some((PieceType::Bishop, PieceColor::Black)), + Some((PieceType::Knight, PieceColor::Black)), + Some((PieceType::Rook, PieceColor::Black)), + ], + [ + Some((PieceType::Pawn, PieceColor::Black)), + Some((PieceType::Pawn, PieceColor::Black)), + Some((PieceType::Pawn, PieceColor::Black)), + Some((PieceType::Pawn, PieceColor::Black)), + Some((PieceType::Pawn, PieceColor::Black)), + Some((PieceType::Pawn, PieceColor::Black)), + Some((PieceType::Pawn, PieceColor::Black)), + Some((PieceType::Pawn, PieceColor::Black)), + ], + [None, None, None, None, None, None, None, None], + [None, None, None, None, None, None, None, None], + [None, None, None, None, None, None, None, None], + [None, None, None, None, None, None, None, None], + [ + Some((PieceType::Pawn, PieceColor::White)), + Some((PieceType::Pawn, PieceColor::White)), + Some((PieceType::Pawn, PieceColor::White)), + Some((PieceType::Pawn, PieceColor::White)), + Some((PieceType::Pawn, PieceColor::White)), + Some((PieceType::Pawn, PieceColor::White)), + Some((PieceType::Pawn, PieceColor::White)), + Some((PieceType::Pawn, PieceColor::White)), + ], + [ + Some((PieceType::Rook, PieceColor::White)), + Some((PieceType::Knight, PieceColor::White)), + Some((PieceType::Bishop, PieceColor::White)), + Some((PieceType::Queen, PieceColor::White)), + Some((PieceType::King, PieceColor::White)), + None, + None, + Some((PieceType::Rook, PieceColor::White)), + ], + ]; + + let mut board = Board::new(custom_board, PieceColor::White, vec![]); + board.move_piece_on_the_board([7, 4], [7, 7]); + board.history_backward(); + board.history_backward(); + board.history_backward(); + board.history_backward(); + board.history_forward(); + board.history_forward(); + board.history_forward(); + board.history_forward(); + assert_eq!(board.display_board, board.board); + } } diff --git a/src/pieces/king.rs b/src/pieces/king.rs index b9cbb4f..4a9e004 100644 --- a/src/pieces/king.rs +++ b/src/pieces/king.rs @@ -658,6 +658,9 @@ mod tests { from_x: 7, to_y: 4, to_x: 7, + is_en_passant: false, + piece_captured: None, + promotion_piece: None, }), (PieceMove { piece_type: PieceType::Pawn, @@ -665,6 +668,9 @@ mod tests { from_x: 2, to_y: 5, to_x: 2, + is_en_passant: false, + piece_captured: None, + promotion_piece: None, }), (PieceMove { piece_type: PieceType::Rook, @@ -672,6 +678,9 @@ mod tests { from_x: 7, to_y: 0, to_x: 7, + is_en_passant: false, + piece_captured: None, + promotion_piece: None, }), ], false, diff --git a/src/pieces/pawn.rs b/src/pieces/pawn.rs index d0d50a0..1390474 100644 --- a/src/pieces/pawn.rs +++ b/src/pieces/pawn.rs @@ -356,6 +356,9 @@ mod tests { from_x: 2, to_y: 3, to_x: 2, + is_en_passant: false, + piece_captured: None, + promotion_piece: None, })], false, ); @@ -400,6 +403,9 @@ mod tests { from_x: 3, to_y: 4, to_x: 3, + is_en_passant: false, + piece_captured: None, + promotion_piece: None, })], false, ); @@ -453,6 +459,9 @@ mod tests { from_x: 3, to_y: 4, to_x: 3, + is_en_passant: false, + piece_captured: None, + promotion_piece: None, })], false, );