diff --git a/src/board.rs b/src/board.rs index 282a46e..115507c 100644 --- a/src/board.rs +++ b/src/board.rs @@ -4,7 +4,7 @@ use crate::{ utils::{ col_to_letter, convert_notation_into_position, convert_position_into_notation, did_piece_already_move, get_cell_paragraph, get_int_from_char, get_king_coordinates, - get_piece_color, get_piece_type, get_player_turn_in_modulo, is_getting_checked, is_valid, + get_piece_color, get_piece_type, get_player_turn_in_modulo, is_getting_checked, }, }; use ratatui::{ @@ -16,12 +16,68 @@ use ratatui::{ }; use uci::Engine; +// only the pure gameboard, no additional information +pub type GameBoard = [[Option<(PieceType, PieceColor)>; 8]; 8]; + +#[derive(PartialEq, Clone, Debug, Eq, PartialOrd, Ord, Copy)] +pub struct Coord { + /// row, line, y + pub row: u8, + /// column, x + pub col: u8, +} +impl Coord { + pub fn new>(row: T, col: T) -> Self { + Coord { + row: row.into(), + col: col.into(), + } + } + /// optional new: try making a valid [`Coord`], if can't, return [`None`] + pub fn opt_new>(row: T, col: T) -> Option { + let row: u8 = row.try_into().ok()?; + let col: u8 = col.try_into().ok()?; + + let ret = Coord { row, col }; + if !ret.is_valid() { + None + } else { + Some(ret) + } + } + /// not yet set position, has to later be set and only used afterwards + pub fn undefined() -> Self { + Coord { + row: UNDEFINED_POSITION, + col: UNDEFINED_POSITION, + } + } + /// checks whether `self` is valid as a chess board coordinate + pub fn is_valid(&self) -> bool { + (0..8).contains(&self.col) && (0..8).contains(&self.row) + } +} + +impl std::ops::Index<&Coord> for GameBoard { + type Output = Option<(PieceType, PieceColor)>; + + fn index(&self, index: &Coord) -> &Self::Output { + &self[index.row as usize][index.col as usize] + } +} + +impl std::ops::IndexMut<&Coord> for GameBoard { + fn index_mut(&mut self, index: &Coord) -> &mut Self::Output { + &mut self[index.row as usize][index.col as usize] + } +} + pub struct Board { - pub board: [[Option<(PieceType, PieceColor)>; 8]; 8], - pub cursor_coordinates: [i8; 2], - pub selected_coordinates: [i8; 2], + pub board: GameBoard, + pub cursor_coordinates: Coord, + pub selected_coordinates: Coord, pub selected_piece_cursor: i8, - pub old_cursor_position: [i8; 2], + pub old_cursor_position: Coord, pub player_turn: PieceColor, pub move_history: Vec, pub is_draw: bool, @@ -83,10 +139,10 @@ impl Default for Board { Some((PieceType::Rook, PieceColor::White)), ], ], - cursor_coordinates: [4, 4], - selected_coordinates: [UNDEFINED_POSITION, UNDEFINED_POSITION], + cursor_coordinates: Coord::new(4, 4), + selected_coordinates: Coord::undefined(), selected_piece_cursor: 0, - old_cursor_position: [UNDEFINED_POSITION, UNDEFINED_POSITION], + old_cursor_position: Coord::undefined(), player_turn: PieceColor::White, move_history: vec![], is_draw: false, @@ -102,17 +158,13 @@ impl Default for Board { } impl Board { - pub fn new( - board: [[Option<(PieceType, PieceColor)>; 8]; 8], - player_turn: PieceColor, - move_history: Vec, - ) -> Self { + pub fn new(board: GameBoard, player_turn: PieceColor, move_history: Vec) -> Self { Self { board, - cursor_coordinates: [4, 4], - selected_coordinates: [UNDEFINED_POSITION, UNDEFINED_POSITION], + cursor_coordinates: Coord::new(4, 4), + selected_coordinates: Coord::undefined(), selected_piece_cursor: 0, - old_cursor_position: [UNDEFINED_POSITION, UNDEFINED_POSITION], + old_cursor_position: Coord::undefined(), player_turn, move_history, is_draw: false, @@ -127,7 +179,7 @@ impl Board { } // Setters - pub fn set_board(&mut self, board: [[Option<(PieceType, PieceColor)>; 8]; 8]) { + pub fn set_board(&mut self, board: GameBoard) { self.board = board; } @@ -146,16 +198,16 @@ impl Board { // Check if a cell has been selected fn is_cell_selected(&self) -> bool { - self.selected_coordinates[0] != UNDEFINED_POSITION - && self.selected_coordinates[1] != UNDEFINED_POSITION + self.selected_coordinates.row != UNDEFINED_POSITION + && self.selected_coordinates.col != UNDEFINED_POSITION } fn get_authorized_positions( &self, piece_type: Option, piece_color: Option, - coordinates: [i8; 2], - ) -> Vec> { + coordinates: &Coord, + ) -> Vec { match (piece_type, piece_color) { (Some(piece_type), Some(piece_color)) => piece_type.authorized_positions( coordinates, @@ -180,8 +232,8 @@ impl Board { if !self.is_checkmate && !self.is_draw && !self.is_promotion { if self.is_cell_selected() { self.move_selected_piece_cursor(false, -1) - } else if self.cursor_coordinates[0] > 0 { - self.cursor_coordinates[0] -= 1 + } else if self.cursor_coordinates.row > 0 { + self.cursor_coordinates.row -= 1 } } } @@ -189,8 +241,8 @@ impl Board { if !self.is_checkmate && !self.is_draw && !self.is_promotion { if self.is_cell_selected() { self.move_selected_piece_cursor(false, 1) - } else if self.cursor_coordinates[0] < 7 { - self.cursor_coordinates[0] += 1 + } else if self.cursor_coordinates.row < 7 { + self.cursor_coordinates.row += 1 } } } @@ -205,8 +257,8 @@ impl Board { } else if !self.is_checkmate && !self.is_draw { if self.is_cell_selected() { self.move_selected_piece_cursor(false, -1) - } else if self.cursor_coordinates[1] > 0 { - self.cursor_coordinates[1] -= 1 + } else if self.cursor_coordinates.col > 0 { + self.cursor_coordinates.col -= 1 } } } @@ -217,8 +269,8 @@ impl Board { } else if !self.is_checkmate && !self.is_draw { if self.is_cell_selected() { self.move_selected_piece_cursor(false, 1) - } else if self.cursor_coordinates[1] < 7 { - self.cursor_coordinates[1] += 1 + } else if self.cursor_coordinates.col < 7 { + self.cursor_coordinates.col += 1 } } } @@ -226,8 +278,7 @@ impl Board { // Method to unselect a cell pub fn unselect_cell(&mut self) { if self.is_cell_selected() { - self.selected_coordinates[0] = UNDEFINED_POSITION; - self.selected_coordinates[1] = UNDEFINED_POSITION; + self.selected_coordinates = Coord::undefined(); self.selected_piece_cursor = 0; self.cursor_coordinates = self.old_cursor_position } @@ -237,11 +288,11 @@ impl Board { We make sure that the cursor is in the authorized positions */ fn move_selected_piece_cursor(&mut self, first_time_moving: bool, direction: i8) { - let piece_color = get_piece_color(self.board, self.selected_coordinates); - let piece_type = get_piece_type(self.board, self.selected_coordinates); + let piece_color = get_piece_color(self.board, &self.selected_coordinates); + let piece_type = get_piece_type(self.board, &self.selected_coordinates); let mut authorized_positions = - self.get_authorized_positions(piece_type, piece_color, self.selected_coordinates); + self.get_authorized_positions(piece_type, piece_color, &self.selected_coordinates); if !authorized_positions.is_empty() { self.selected_piece_cursor = if self.selected_piece_cursor == 0 && first_time_moving { @@ -259,10 +310,10 @@ impl Board { authorized_positions.sort(); if let Some(position) = authorized_positions.get(self.selected_piece_cursor as usize) { - self.cursor_coordinates = [position[0], position[1]]; + self.cursor_coordinates = *position; } } else { - self.cursor_coordinates = [UNDEFINED_POSITION, UNDEFINED_POSITION]; + self.cursor_coordinates = Coord::undefined(); } } @@ -274,16 +325,19 @@ impl Board { } else if !self.is_checkmate && !self.is_draw { if !self.is_cell_selected() { // Check if the piece on the cell can move before selecting it - let piece_color = get_piece_color(self.board, self.cursor_coordinates); - let piece_type = get_piece_type(self.board, self.cursor_coordinates); + let piece_color = get_piece_color(self.board, &self.cursor_coordinates); + let piece_type = get_piece_type(self.board, &self.cursor_coordinates); - let authorized_positions = - self.get_authorized_positions(piece_type, piece_color, self.cursor_coordinates); + let authorized_positions = self.get_authorized_positions( + piece_type, + piece_color, + &self.cursor_coordinates, + ); if authorized_positions.is_empty() { return; } - if let Some(piece_color) = get_piece_color(self.board, self.cursor_coordinates) { + if let Some(piece_color) = get_piece_color(self.board, &self.cursor_coordinates) { if piece_color == self.player_turn { self.selected_coordinates = self.cursor_coordinates; self.old_cursor_position = self.cursor_coordinates; @@ -292,15 +346,9 @@ impl Board { } } else { // We already selected a piece - if is_valid(self.cursor_coordinates) { - let selected_coords_usize: [usize; 2] = [ - self.selected_coordinates[0] as usize, - self.selected_coordinates[1] as usize, - ]; - let cursor_coords_usize: [usize; 2] = [ - self.cursor_coordinates[0] as usize, - self.cursor_coordinates[1] as usize, - ]; + if self.cursor_coordinates.is_valid() { + let selected_coords_usize = &self.selected_coordinates.clone(); + let cursor_coords_usize = &self.cursor_coordinates.clone(); self.move_piece_on_the_board(selected_coords_usize, cursor_coords_usize); self.unselect_cell(); self.switch_player_turn(); @@ -360,8 +408,8 @@ impl Board { let to_x = get_int_from_char(converted_move.chars().nth(3)); self.move_piece_on_the_board( - [from_y as usize, from_x as usize], - [to_y as usize, to_x as usize], + &Coord::new(from_y as u8, from_x as u8), + &Coord::new(to_y as u8, to_x as u8), ); } @@ -370,12 +418,12 @@ impl Board { let mut result = String::new(); // We loop through the board and convert it to a FEN string - for i in 0..8i8 { - for j in 0..8i8 { + for i in 0..8u8 { + for j in 0..8u8 { // We get the piece type and color let (piece_type, piece_color) = ( - get_piece_type(self.board, [i, j]), - get_piece_color(self.board, [i, j]), + get_piece_type(self.board, &Coord::new(i, j)), + get_piece_color(self.board, &Coord::new(i, j)), ); let letter = PieceType::piece_to_fen_enum(piece_type, piece_color); // Pattern match directly on the result of piece_to_fen_enum @@ -413,15 +461,23 @@ impl Board { result.push_str(" b"); // We add the castles availabilities for black - if !did_piece_already_move(&self.move_history, (Some(PieceType::King), [0, 4])) - && !is_getting_checked(self.board, PieceColor::Black, &self.move_history) + if !did_piece_already_move( + &self.move_history, + (Some(PieceType::King), Coord::new(0, 4)), + ) && !is_getting_checked(self.board, PieceColor::Black, &self.move_history) { // king side black castle availability - if !did_piece_already_move(&self.move_history, (Some(PieceType::Rook), [0, 7])) { + if !did_piece_already_move( + &self.move_history, + (Some(PieceType::Rook), Coord::new(0, 7)), + ) { result.push_str(" k"); } // queen side black castle availability - if !did_piece_already_move(&self.move_history, (Some(PieceType::Rook), [0, 0])) { + if !did_piece_already_move( + &self.move_history, + (Some(PieceType::Rook), Coord::new(0, 0)), + ) { result.push('q'); } } else { @@ -434,9 +490,9 @@ impl Board { if let Some(last_move) = self.move_history.last() { let mut converted_move = String::new(); - converted_move += &col_to_letter(last_move.from_x); + converted_move += &col_to_letter(last_move.from.col); // FEN starts counting from 1 not 0 - converted_move += &format!("{}", 8 - last_move.from_y + 1).to_string(); + converted_move += &format!("{}", 8 - last_move.from.row + 1).to_string(); result.push(' '); result.push_str(&converted_move); @@ -459,7 +515,7 @@ impl Board { pub fn did_pawn_move_two_cells(&self) -> bool { match self.move_history.last() { Some(last_move) => { - let distance = (last_move.to_y - last_move.from_y).abs(); + let distance = (last_move.to.row as i8 - last_move.from.row as i8).abs(); if last_move.piece_type == PieceType::Pawn && distance == 2 { return true; @@ -481,10 +537,11 @@ impl Board { _ => unreachable!("Promotion cursor out of boundaries"), }; - let current_piece_color = get_piece_color(self.board, [last_move.to_y, last_move.to_x]); + let current_piece_color = + get_piece_color(self.board, &Coord::new(last_move.to.row, last_move.to.col)); if let Some(piece_color) = current_piece_color { // we replace the piece by the new piece type - self.board[last_move.to_y as usize][last_move.to_x as usize] = + self.board[last_move.to.row as usize][last_move.to.col as usize] = Some((new_piece, piece_color)); } } @@ -493,8 +550,8 @@ impl Board { } // Move a piece from a cell to another - pub fn move_piece_on_the_board(&mut self, from: [usize; 2], to: [usize; 2]) { - if !is_valid([from[0] as i8, from[1] as i8]) || !is_valid([to[0] as i8, to[1] as i8]) { + pub fn move_piece_on_the_board(&mut self, from: &Coord, to: &Coord) { + if !from.is_valid() || !to.is_valid() { return; } let direction_y: i32 = if self.player_turn == PieceColor::White { @@ -503,8 +560,8 @@ impl Board { 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]); + let piece_type_from = get_piece_type(self.board, from); + let piece_type_to = get_piece_type(self.board, to); // Check if moving a piece let piece_type_from = match piece_type_from { @@ -525,17 +582,17 @@ impl Board { // We check for en passant as the latest move if self.is_latest_move_en_passant(from, to) { // we kill the pawn - let row_index = to[0] as i32 - direction_y; + let row_index = to.row as i32 - direction_y; - self.board[row_index as usize][to[1]] = None; + self.board[row_index as usize][to.col as usize] = None; } // We check for castling as the latest move if self.is_latest_move_castling(from, to) { // we set the king 2 cells on where it came from - let from_x: i32 = from[1] as i32; - let mut to_x: i32 = to[1] as i32; + let from_x: i32 = from.col as i32; + let mut to_x: i32 = to.col as i32; let distance = from_x - to_x; let direction_x = if distance > 0 { -1 } else { 1 }; @@ -545,7 +602,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.board[to.row as usize][row_index as usize] = self.board[from]; // 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,38 +622,38 @@ impl Board { } _ => unreachable!("Undefined distance for castling"), } - self.board[to[0]][row_index_rook as usize] = self.board[to[0]][to_x as usize]; + let new_to = Coord::new(to.row, to_x as u8); + self.board[to.row as usize][row_index_rook as usize] = self.board[&new_to]; // We remove the latest rook - self.board[to[0]][to_x as usize] = None; + self.board[&new_to] = None; } else { - self.board[to[0]][to[1]] = self.board[from[0]][from[1]]; + self.board[to] = self.board[from]; } - self.board[from[0]][from[1]] = None; + self.board[from] = None; // 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, + from: *from, + to: *to, }); } // Method to get the number of authorized positions for the current player (used for the end condition) pub fn number_of_authorized_positions(&self) -> usize { - let mut possible_moves: Vec> = vec![]; + let mut possible_moves: Vec = vec![]; for i in 0..8 { for j in 0..8 { - if let Some((piece_type, piece_color)) = self.board[i][j] { + let coord = Coord::new(i, j); + if let Some((piece_type, piece_color)) = self.board[&coord] { if piece_color == self.player_turn { possible_moves.extend(self.get_authorized_positions( Some(piece_type), Some(piece_color), - [i as i8, j as i8], + &coord, )) } } @@ -606,30 +663,30 @@ impl Board { } // Check if the latest move is en passant - fn is_latest_move_en_passant(&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 from_y: i32 = from[0] as i32; - let from_x: i32 = from[1] as i32; - let to_y: i32 = to[0] as i32; - let to_x: i32 = to[1] as i32; + fn is_latest_move_en_passant(&self, from: &Coord, to: &Coord) -> bool { + let piece_type_from = get_piece_type(self.board, from); + let piece_type_to = get_piece_type(self.board, to); + + let from_y: i32 = from.row as i32; + let from_x: i32 = from.col as i32; + let to_y: i32 = to.row as i32; + let to_x: i32 = to.col as i32; match (piece_type_from, piece_type_to) { (Some(PieceType::Pawn), _) => { // Check if it's a diagonal move, and the destination is an empty cell - from_y != to_y && from_x != to_x && self.board[to[0]][to[1]].is_none() + from_y != to_y && from_x != to_x && self.board[to].is_none() } _ => false, } } // 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]); + fn is_latest_move_castling(&self, from: &Coord, to: &Coord) -> bool { + let piece_type_from = get_piece_type(self.board, from); + let piece_type_to = get_piece_type(self.board, to); - let from_x: i32 = from[1] as i32; - let to_x: i32 = to[1] as i32; + let from_x: i32 = from.col as i32; + let to_x: i32 = to.col as i32; let distance = (from_x - to_x).abs(); match (piece_type_from, piece_type_to) { @@ -642,10 +699,10 @@ impl Board { fn is_latest_move_promotion(&self) -> bool { if let Some(last_move) = self.move_history.last() { if let Some(piece_type_to) = - get_piece_type(self.board, [last_move.to_y, last_move.to_x]) + get_piece_type(self.board, &Coord::new(last_move.to.row, last_move.to.col)) { if let Some(piece_color) = - get_piece_color(self.board, [last_move.to_y, last_move.to_x]) + get_piece_color(self.board, &Coord::new(last_move.to.row, last_move.to.col)) { let last_row = if piece_color == PieceColor::White { 0 @@ -653,7 +710,7 @@ impl Board { 7 }; - if last_move.to_y == last_row && piece_type_to == PieceType::Pawn { + if last_move.to.row == last_row && piece_type_to == PieceType::Pawn { return true; } } @@ -722,7 +779,7 @@ impl Board { .split(area); // For each line we set 8 layout - for i in 0..8i8 { + for i in 0..8u8 { let lines = Layout::default() .direction(Direction::Horizontal) .constraints( @@ -741,24 +798,25 @@ impl Board { .as_ref(), ) .split(columns[i as usize + 1]); - for j in 0..8i8 { + for j in 0..8u8 { // 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); + let selected_piece_type = + get_piece_type(self.board, &self.selected_coordinates); let selected_piece_color: Option = - get_piece_color(self.board, self.selected_coordinates); + get_piece_color(self.board, &self.selected_coordinates); let positions = self.get_authorized_positions( selected_piece_type, selected_piece_color, - self.selected_coordinates, + &self.selected_coordinates, ); // Draw grey if the color is in the authorized positions for coords in positions.clone() { - if i == coords[0] && j == coords[1] { + if i == coords.row && j == coords.col { cell_color = Color::Rgb(100, 100, 100) } } @@ -766,11 +824,11 @@ impl Board { let square = lines[j as usize + 1]; // Draw the cell blue if this is the current cursor cell - if i == self.cursor_coordinates[0] && j == self.cursor_coordinates[1] { + if i == self.cursor_coordinates.row && j == self.cursor_coordinates.col { 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) + && Coord::new(i, j) == get_king_coordinates(self.board, self.player_turn) { let cell = Block::default() .bg(Color::Magenta) @@ -778,7 +836,7 @@ 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.row && j == self.selected_coordinates.col { let cell = Block::default().bg(Color::LightGreen); frame.render_widget(cell.clone(), square); } else { @@ -796,7 +854,8 @@ impl Board { } // Get piece and color - let paragraph = get_cell_paragraph(self, [i, j], square); + let coord = Coord::new(i, j); + let paragraph = get_cell_paragraph(self, &coord, square); frame.render_widget(paragraph, square); } @@ -822,10 +881,10 @@ impl Board { PieceType::piece_to_utf_enum(piece_type_from, Some(PieceColor::White)); let move_white = convert_position_into_notation(format!( "{}{}{}{}", - self.move_history[i].from_y, - self.move_history[i].from_x, - self.move_history[i].to_y, - self.move_history[i].to_x + self.move_history[i].from.row, + self.move_history[i].from.col, + self.move_history[i].to.row, + self.move_history[i].to.col )); let mut utf_icon_black = " "; @@ -837,10 +896,10 @@ impl Board { move_black = convert_position_into_notation(format!( "{}{}{}{}", - self.move_history[i + 1].from_y, - self.move_history[i + 1].from_x, - self.move_history[i + 1].to_y, - self.move_history[i + 1].to_x + self.move_history[i + 1].from.row, + self.move_history[i + 1].from.col, + self.move_history[i + 1].to.row, + self.move_history[i + 1].to.col )); utf_icon_black = PieceType::piece_to_utf_enum(piece_type_to, Some(PieceColor::Black)) @@ -884,7 +943,7 @@ impl Board { #[cfg(test)] mod tests { use crate::{ - board::Board, + board::{Board, Coord}, pieces::{PieceColor, PieceMove, PieceType}, utils::is_getting_checked, }; @@ -1370,10 +1429,8 @@ mod tests { vec![ (PieceMove { piece_type: PieceType::Pawn, - from_y: 7, - from_x: 3, - to_y: 6, - to_x: 3, + from: Coord::new(7, 3), + to: Coord::new(6, 3), }), ], ); @@ -1425,10 +1482,8 @@ mod tests { vec![ (PieceMove { piece_type: PieceType::Pawn, - from_y: 1, - from_x: 4, - to_y: 0, - to_x: 4, + from: Coord::new(1, 4), + to: Coord::new(0, 4), }), ], ); @@ -1480,7 +1535,7 @@ mod tests { assert!(!board.is_latest_move_promotion()); // Move the pawn to a promote cell - board.move_piece_on_the_board([1, 4], [0, 4]); + board.move_piece_on_the_board(&Coord::new(1, 4), &Coord::new(0, 4)); assert!(board.is_latest_move_promotion()); // Promote the pawn @@ -1536,10 +1591,8 @@ mod tests { vec![ (PieceMove { piece_type: PieceType::Pawn, - from_y: 6, - from_x: 4, - to_y: 7, - to_x: 4, + from: Coord::new(6, 4), + to: Coord::new(7, 4), }), ], ); @@ -1591,7 +1644,7 @@ mod tests { assert!(!board.is_latest_move_promotion()); // Move the pawn to a promote cell - board.move_piece_on_the_board([6, 5], [7, 5]); + board.move_piece_on_the_board(&Coord::new(6, 5), &Coord::new(7, 5)); assert!(board.is_latest_move_promotion()); // Promote the pawn @@ -1635,7 +1688,7 @@ mod tests { assert!(!board.is_draw()); // Move the pawn to a make the 50th move - board.move_piece_on_the_board([1, 6], [1, 5]); + board.move_piece_on_the_board(&Coord::new(1, 6), &Coord::new(1, 5)); assert!(board.is_draw()); } @@ -1667,59 +1720,43 @@ mod tests { vec![ (PieceMove { piece_type: PieceType::King, - from_y: 0, - from_x: 2, - to_y: 0, - to_x: 1, + from: Coord::new(0, 2), + to: Coord::new(0, 1), }), (PieceMove { piece_type: PieceType::King, - from_y: 0, - from_x: 6, - to_y: 0, - to_x: 5, + from: Coord::new(0, 6), + to: Coord::new(0, 5), }), (PieceMove { piece_type: PieceType::King, - from_y: 0, - from_x: 1, - to_y: 0, - to_x: 2, + from: Coord::new(0, 1), + to: Coord::new(0, 2), }), (PieceMove { piece_type: PieceType::King, - from_y: 0, - from_x: 5, - to_y: 0, - to_x: 6, + from: Coord::new(0, 5), + to: Coord::new(0, 6), }), (PieceMove { piece_type: PieceType::King, - from_y: 0, - from_x: 2, - to_y: 0, - to_x: 1, + from: Coord::new(0, 2), + to: Coord::new(0, 1), }), (PieceMove { piece_type: PieceType::King, - from_y: 0, - from_x: 6, - to_y: 0, - to_x: 5, + from: Coord::new(0, 6), + to: Coord::new(0, 5), }), (PieceMove { piece_type: PieceType::King, - from_y: 0, - from_x: 1, - to_y: 0, - to_x: 2, + from: Coord::new(0, 1), + to: Coord::new(0, 2), }), (PieceMove { piece_type: PieceType::King, - from_y: 0, - from_x: 5, - to_y: 0, - to_x: 6, + from: Coord::new(0, 5), + to: Coord::new(0, 6), }), ], ); @@ -1727,7 +1764,7 @@ mod tests { assert!(!board.is_draw()); // Move the king to replicate a third time the same position - board.move_piece_on_the_board([0, 2], [0, 1]); + board.move_piece_on_the_board(&Coord::new(0, 2), &Coord::new(0, 1)); assert!(board.is_draw()); } @@ -1814,10 +1851,8 @@ mod tests { vec![ (PieceMove { piece_type: PieceType::Pawn, - from_y: 6, - from_x: 2, - to_y: 4, - to_x: 2, + from: Coord::new(6, 2), + to: Coord::new(4, 2), }), ], ); diff --git a/src/constants.rs b/src/constants.rs index 298fa83..2198c63 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -3,7 +3,7 @@ use std::path::PathBuf; use ratatui::style::Color; -pub const UNDEFINED_POSITION: i8 = -1; +pub const UNDEFINED_POSITION: u8 = u8::MAX; pub const WHITE: Color = Color::Rgb(160, 160, 160); pub const BLACK: Color = Color::Rgb(128, 95, 69); diff --git a/src/pieces/bishop.rs b/src/pieces/bishop.rs index 051da99..40998e2 100644 --- a/src/pieces/bishop.rs +++ b/src/pieces/bishop.rs @@ -1,52 +1,50 @@ -use super::{Movable, PieceColor, PieceMove, PieceType, Position}; +use super::{Movable, PieceColor, PieceMove, Position}; +use crate::board::{Coord, GameBoard}; use crate::constants::DisplayMode; use crate::utils::{ cleaned_positions, get_piece_color, impossible_positions_king_checked, is_cell_color_ally, - is_piece_opposite_king, is_valid, + is_piece_opposite_king, }; pub struct Bishop; impl Movable for Bishop { fn piece_move( - coordinates: [i8; 2], + coordinates: &Coord, color: PieceColor, - board: [[Option<(PieceType, PieceColor)>; 8]; 8], + board: GameBoard, allow_move_on_ally_positions: bool, _move_history: &[PieceMove], - ) -> Vec> { - let mut positions: Vec> = vec![]; + ) -> Vec { + let mut positions: Vec = vec![]; - let y = coordinates[0]; - let x = coordinates[1]; + let y = coordinates.row; + let x = coordinates.col; // for diagonal from piece to top left - for i in 1..8i8 { - let new_x = x - i; - let new_y = y - i; - let new_coordinates = [new_y, new_x]; - - // Invalid coords - if !is_valid(new_coordinates) { + for i in 1..8u8 { + let new_x: i8 = x as i8 - i as i8; + let new_y: i8 = y as i8 - i as i8; + let Some(new_coordinates) = Coord::opt_new(new_y, new_x) else { break; - } + }; // Empty cell - if get_piece_color(board, new_coordinates).is_none() { - positions.push(new_coordinates.to_vec()); + if get_piece_color(board, &new_coordinates).is_none() { + positions.push(new_coordinates); continue; } // Ally cell - if is_cell_color_ally(board, new_coordinates, color) { + if is_cell_color_ally(board, &new_coordinates, color) { if !allow_move_on_ally_positions { break; } else { - positions.push(new_coordinates.to_vec()); + positions.push(new_coordinates); break; } } // Enemy cell - positions.push(new_coordinates.to_vec()); + positions.push(new_coordinates); if !allow_move_on_ally_positions || !is_piece_opposite_king(board[new_y as usize][new_x as usize], color) { @@ -55,34 +53,34 @@ impl Movable for Bishop { } // for diagonal from piece to bottom right - for i in 1..8i8 { + for i in 1..8u8 { let new_x = x + i; let new_y = y + i; - let new_coordinates = [new_y, new_x]; + let new_coordinates = Coord::new(new_y, new_x); // Invalid coords - if !is_valid(new_coordinates) { + if !new_coordinates.is_valid() { break; } // Empty cell - if get_piece_color(board, new_coordinates).is_none() { - positions.push(new_coordinates.to_vec()); + if get_piece_color(board, &new_coordinates).is_none() { + positions.push(new_coordinates); continue; } // Ally cell - if is_cell_color_ally(board, new_coordinates, color) { + if is_cell_color_ally(board, &new_coordinates, color) { if !allow_move_on_ally_positions { break; } else { - positions.push(new_coordinates.to_vec()); + positions.push(new_coordinates); break; } } // Enemy cell - positions.push(new_coordinates.to_vec()); + positions.push(new_coordinates); if !allow_move_on_ally_positions || !is_piece_opposite_king(board[new_y as usize][new_x as usize], color) { @@ -91,33 +89,35 @@ impl Movable for Bishop { } // for diagonal from piece to bottom left - for i in 1..8i8 { - let new_x = x - i; - let new_y = y + i; - let new_coordinates = [new_y, new_x]; + for i in 1..8u8 { + let new_x: i8 = x as i8 - i as i8; + let new_y: i8 = y as i8 + i as i8; + let Some(new_coordinates) = Coord::opt_new(new_y, new_x) else { + break; + }; // Invalid coords - if !is_valid(new_coordinates) { + if !new_coordinates.is_valid() { break; } // Empty cell - if get_piece_color(board, new_coordinates).is_none() { - positions.push(new_coordinates.to_vec()); + if get_piece_color(board, &new_coordinates).is_none() { + positions.push(new_coordinates); continue; } // Ally cell - if is_cell_color_ally(board, new_coordinates, color) { + if is_cell_color_ally(board, &new_coordinates, color) { if !allow_move_on_ally_positions { break; } else { - positions.push(new_coordinates.to_vec()); + positions.push(new_coordinates); break; } } // Enemy cell - positions.push(new_coordinates.to_vec()); + positions.push(new_coordinates); if !allow_move_on_ally_positions || !is_piece_opposite_king(board[new_y as usize][new_x as usize], color) { @@ -126,51 +126,51 @@ impl Movable for Bishop { } // for diagonal from piece to top right - for i in 1..8i8 { - let new_x = x + i; - let new_y = y - i; - let new_coordinates = [new_y, new_x]; - - // Invalid coords - if !is_valid(new_coordinates) { + for i in 1..8u8 { + let new_x = x as i8 + i as i8; + let new_y = y as i8 - i as i8; + let Some(new_coordinates) = Coord::opt_new(new_y, new_x) else { break; - } + }; // Empty cell - if get_piece_color(board, new_coordinates).is_none() { - positions.push(new_coordinates.to_vec()); + if get_piece_color(board, &new_coordinates).is_none() { + positions.push(new_coordinates); continue; } // Ally cell - if is_cell_color_ally(board, new_coordinates, color) { + if is_cell_color_ally(board, &new_coordinates, color) { if !allow_move_on_ally_positions { break; } else { - positions.push(new_coordinates.to_vec()); + positions.push(new_coordinates); break; } } // Enemy cell - positions.push(new_coordinates.to_vec()); + positions.push(new_coordinates); if !allow_move_on_ally_positions - || !is_piece_opposite_king(board[new_y as usize][new_x as usize], color) + || !is_piece_opposite_king( + board[new_coordinates.row as usize][new_coordinates.col as usize], + color, + ) { break; } } - cleaned_positions(positions) + cleaned_positions(&positions) } } impl Position for Bishop { fn authorized_positions( - coordinates: [i8; 2], + coordinates: &Coord, color: PieceColor, - board: [[Option<(PieceType, PieceColor)>; 8]; 8], + board: GameBoard, move_history: &[PieceMove], _is_king_checked: bool, - ) -> Vec> { + ) -> Vec { // if the king is checked we clean all the position not resolving the check impossible_positions_king_checked( coordinates, @@ -181,11 +181,11 @@ impl Position for Bishop { ) } fn protected_positions( - coordinates: [i8; 2], + coordinates: &Coord, color: PieceColor, - board: [[Option<(PieceType, PieceColor)>; 8]; 8], + board: GameBoard, move_history: &[PieceMove], - ) -> Vec> { + ) -> Vec { Self::piece_move(coordinates, color, board, true, move_history) } } @@ -210,7 +210,7 @@ impl Bishop { #[cfg(test)] mod tests { use crate::{ - board::Board, + board::{Board, Coord}, pieces::{bishop::Bishop, PieceColor, PieceType, Position}, utils::is_getting_checked, }; @@ -240,24 +240,29 @@ mod tests { board.set_board(custom_board); let mut right_positions = vec![ - vec![0, 0], - vec![1, 1], - vec![2, 2], - vec![3, 3], - vec![5, 5], - vec![6, 6], - vec![7, 7], - vec![1, 7], - vec![2, 6], - vec![3, 5], - vec![5, 3], - vec![6, 2], - vec![7, 1], + Coord::new(0, 0), + Coord::new(1, 1), + Coord::new(2, 2), + Coord::new(3, 3), + Coord::new(5, 5), + Coord::new(6, 6), + Coord::new(7, 7), + Coord::new(1, 7), + Coord::new(2, 6), + Coord::new(3, 5), + Coord::new(5, 3), + Coord::new(6, 2), + Coord::new(7, 1), ]; right_positions.sort(); - let mut positions = - Bishop::authorized_positions([4, 4], PieceColor::White, board.board, &[], false); + let mut positions = Bishop::authorized_positions( + &Coord::new(4, 4), + PieceColor::White, + board.board, + &[], + false, + ); positions.sort(); assert_eq!(right_positions, positions); @@ -297,22 +302,27 @@ mod tests { board.set_board(custom_board); let mut right_positions = vec![ - vec![0, 0], - vec![1, 1], - vec![2, 2], - vec![3, 3], - vec![5, 5], - vec![6, 6], - vec![7, 7], - vec![3, 5], - vec![5, 3], - vec![6, 2], - vec![7, 1], + Coord::new(0, 0), + Coord::new(1, 1), + Coord::new(2, 2), + Coord::new(3, 3), + Coord::new(5, 5), + Coord::new(6, 6), + Coord::new(7, 7), + Coord::new(3, 5), + Coord::new(5, 3), + Coord::new(6, 2), + Coord::new(7, 1), ]; right_positions.sort(); - let mut positions = - Bishop::authorized_positions([4, 4], PieceColor::White, board.board, &[], false); + let mut positions = Bishop::authorized_positions( + &Coord::new(4, 4), + PieceColor::White, + board.board, + &[], + false, + ); positions.sort(); assert_eq!(right_positions, positions); @@ -370,18 +380,23 @@ mod tests { board.set_board(custom_board); let mut right_positions = vec![ - vec![3, 3], - vec![5, 5], - vec![3, 5], - vec![2, 6], - vec![1, 7], - vec![5, 3], - vec![6, 2], + Coord::new(3, 3), + Coord::new(5, 5), + Coord::new(3, 5), + Coord::new(2, 6), + Coord::new(1, 7), + Coord::new(5, 3), + Coord::new(6, 2), ]; right_positions.sort(); - let mut positions = - Bishop::authorized_positions([4, 4], PieceColor::White, board.board, &[], false); + let mut positions = Bishop::authorized_positions( + &Coord::new(4, 4), + PieceColor::White, + board.board, + &[], + false, + ); positions.sort(); assert_eq!(right_positions, positions); @@ -432,11 +447,11 @@ mod tests { let is_king_checked = is_getting_checked(board.board, board.player_turn, &board.move_history); - let mut right_positions = vec![vec![4, 4]]; + let mut right_positions = vec![Coord::new(4, 4)]; right_positions.sort(); let mut positions = Bishop::authorized_positions( - [5, 5], + &Coord::new(5, 5), PieceColor::Black, board.board, &[], @@ -492,11 +507,11 @@ mod tests { let is_king_checked = is_getting_checked(board.board, board.player_turn, &board.move_history); - let mut right_positions: Vec> = vec![]; + let mut right_positions: Vec = vec![]; right_positions.sort(); let mut positions = Bishop::authorized_positions( - [5, 6], + &Coord::new(5, 6), PieceColor::Black, board.board, &[], @@ -552,11 +567,11 @@ mod tests { let is_king_checked = is_getting_checked(board.board, board.player_turn, &board.move_history); - let mut right_positions: Vec> = vec![vec![2, 6], vec![3, 7]]; + let mut right_positions = vec![Coord::new(2, 6), Coord::new(3, 7)]; right_positions.sort(); let mut positions = Bishop::authorized_positions( - [1, 5], + &Coord::new(1, 5), PieceColor::Black, board.board, &[], diff --git a/src/pieces/king.rs b/src/pieces/king.rs index b9cbb4f..3eb8420 100644 --- a/src/pieces/king.rs +++ b/src/pieces/king.rs @@ -1,54 +1,55 @@ use super::{Movable, PieceColor, PieceMove, PieceType, Position}; +use crate::board::{Coord, GameBoard}; use crate::constants::DisplayMode; use crate::utils::{ cleaned_positions, did_piece_already_move, get_all_protected_cells, get_piece_type, - is_cell_color_ally, is_valid, is_vec_in_array, + is_cell_color_ally, }; pub struct King; impl Movable for King { fn piece_move( - coordinates: [i8; 2], + coordinates: &Coord, color: PieceColor, - board: [[Option<(PieceType, PieceColor)>; 8]; 8], + board: GameBoard, allow_move_on_ally_positions: bool, _move_history: &[PieceMove], - ) -> Vec> { - let mut positions: Vec> = vec![]; - let y = coordinates[0]; - let x = coordinates[1]; + ) -> Vec { + let mut positions: Vec = vec![]; + let y = coordinates.row; + let x = coordinates.col; // can move on a complete row // Generate positions in all eight possible directions - for &dy in &[-1, 0, 1] { - for &dx in &[-1, 0, 1] { + for &dy in &[-1i8, 0, 1] { + for &dx in &[-1i8, 0, 1] { // Skip the case where both dx and dy are zero (the current position) - let new_x = x + dx; - let new_y = y + dy; + let new_x = x as i8 + dx; + let new_y = y as i8 + dy; - let new_coordinates = [new_y, new_x]; - if is_valid(new_coordinates) - && (!is_cell_color_ally(board, [new_y, new_x], color) + let new_coordinates = Coord::new(new_y as u8, new_x as u8); + if new_coordinates.is_valid() + && (!is_cell_color_ally(board, &new_coordinates, color) || allow_move_on_ally_positions) { - positions.push(vec![y + dy, x + dx]); + positions.push(new_coordinates); } } } - cleaned_positions(positions) + cleaned_positions(&positions) } } impl Position for King { fn authorized_positions( - coordinates: [i8; 2], + coordinates: &Coord, color: PieceColor, - board: [[Option<(PieceType, PieceColor)>; 8]; 8], + board: GameBoard, move_history: &[PieceMove], is_king_checked: bool, - ) -> Vec> { - let mut positions: Vec> = vec![]; + ) -> Vec { + let mut positions: Vec = vec![]; let checked_cells = get_all_protected_cells(board, color, move_history); let rook_big_castle_x = 0; @@ -57,26 +58,34 @@ impl Position for King { let king_line = if color == PieceColor::White { 7 } else { 0 }; // We check the condition for small and big castling - if !did_piece_already_move(move_history, (Some(PieceType::King), [king_line, king_x])) - && !is_king_checked + if !did_piece_already_move( + move_history, + (Some(PieceType::King), Coord::new(king_line, king_x)), + ) && !is_king_checked { // We check if there is no pieces between tower and king // Big castle check if !did_piece_already_move( move_history, - (Some(PieceType::Rook), [king_line, rook_big_castle_x]), + ( + Some(PieceType::Rook), + Coord::new(king_line, rook_big_castle_x), + ), ) && King::check_castling_condition(board, color, 0, 3, &checked_cells) { - positions.push(vec![king_line, 0]); + positions.push(Coord::new(king_line, 0)); } // Small castle check if !did_piece_already_move( move_history, - (Some(PieceType::Rook), [king_line, rook_small_castle_x]), + ( + Some(PieceType::Rook), + Coord::new(king_line, rook_small_castle_x), + ), ) && King::check_castling_condition(board, color, 5, 7, &checked_cells) { - positions.push(vec![king_line, 7]); + positions.push(Coord::new(king_line, 7)); } } @@ -84,7 +93,7 @@ impl Position for King { let king_cells = King::piece_move(coordinates, color, board, false, move_history); for king_position in king_cells.clone() { - if !is_vec_in_array(checked_cells.clone(), [king_position[0], king_position[1]]) { + if !checked_cells.contains(&king_position) { positions.push(king_position); } } @@ -94,11 +103,11 @@ impl Position for King { // This method is used to calculated the cells the king is actually covering and is used when the other king authorized position is called fn protected_positions( - coordinates: [i8; 2], + coordinates: &Coord, color: PieceColor, - board: [[Option<(PieceType, PieceColor)>; 8]; 8], + board: GameBoard, move_history: &[PieceMove], - ) -> Vec> { + ) -> Vec { Self::piece_move(coordinates, color, board, true, move_history) } } @@ -121,26 +130,26 @@ impl King { // Check if nothing is in between the king and a rook and if none of those cells are getting checked pub fn check_castling_condition( - board: [[Option<(PieceType, PieceColor)>; 8]; 8], + board: GameBoard, color: PieceColor, start: i8, end: i8, - checked_cells: &[Vec], + checked_cells: &[Coord], ) -> bool { let king_line = if color == PieceColor::White { 7 } else { 0 }; let mut valid_for_castling = true; for i in start..=end { - let new_coordinates = [king_line, i]; + let new_coordinates = Coord::new(king_line, i as u8); - if is_vec_in_array(checked_cells.to_owned().clone(), new_coordinates) { + if checked_cells.contains(&new_coordinates) { valid_for_castling = false; } if (i == 7 || i == 0) - && (get_piece_type(board, new_coordinates) != Some(PieceType::Rook) - || !is_cell_color_ally(board, new_coordinates, color)) - || (i != 7 && i != 0 && get_piece_type(board, new_coordinates).is_some()) + && (get_piece_type(board, &new_coordinates) != Some(PieceType::Rook) + || !is_cell_color_ally(board, &new_coordinates, color)) + || (i != 7 && i != 0 && get_piece_type(board, &new_coordinates).is_some()) { valid_for_castling = false; } @@ -153,7 +162,7 @@ impl King { #[cfg(test)] mod tests { use crate::{ - board::Board, + board::{Board, Coord}, pieces::{king::King, PieceColor, PieceMove, PieceType, Position}, utils::is_getting_checked, }; @@ -209,11 +218,16 @@ mod tests { let mut board = Board::default(); board.set_board(custom_board); - let mut right_positions = vec![vec![4, 5], vec![5, 4]]; + let mut right_positions = vec![Coord::new(4, 5), Coord::new(5, 4)]; right_positions.sort(); - let mut positions = - King::authorized_positions([4, 4], PieceColor::White, board.board, &[], false); + let mut positions = King::authorized_positions( + &Coord::new(4, 4), + PieceColor::White, + board.board, + &[], + false, + ); positions.sort(); assert_eq!(right_positions, positions); @@ -270,11 +284,16 @@ mod tests { let mut board = Board::default(); board.set_board(custom_board); - let mut right_positions = vec![vec![3, 4]]; + let mut right_positions = vec![Coord::new(3, 4)]; right_positions.sort(); - let mut positions = - King::authorized_positions([4, 4], PieceColor::White, board.board, &[], false); + let mut positions = King::authorized_positions( + &Coord::new(4, 4), + PieceColor::White, + board.board, + &[], + false, + ); positions.sort(); assert_eq!(right_positions, positions); @@ -331,11 +350,16 @@ mod tests { let mut board = Board::default(); board.set_board(custom_board); - let mut right_positions = vec![vec![4, 5]]; + let mut right_positions = vec![Coord::new(4, 5)]; right_positions.sort(); - let mut positions = - King::authorized_positions([4, 4], PieceColor::White, board.board, &[], false); + let mut positions = King::authorized_positions( + &Coord::new(4, 4), + PieceColor::White, + board.board, + &[], + false, + ); positions.sort(); assert_eq!(right_positions, positions); @@ -392,11 +416,16 @@ mod tests { let mut board = Board::default(); board.set_board(custom_board); - let mut right_positions = vec![vec![7, 3], vec![7, 0]]; + let mut right_positions = vec![Coord::new(7, 3), Coord::new(7, 0)]; right_positions.sort(); - let mut positions = - King::authorized_positions([7, 4], PieceColor::White, board.board, &[], false); + let mut positions = King::authorized_positions( + &Coord::new(7, 4), + PieceColor::White, + board.board, + &[], + false, + ); positions.sort(); assert_eq!(right_positions, positions); @@ -453,11 +482,16 @@ mod tests { let mut board = Board::default(); board.set_board(custom_board); - let mut right_positions = vec![vec![0, 5], vec![0, 7]]; + let mut right_positions = vec![Coord::new(0, 5), Coord::new(0, 7)]; right_positions.sort(); - let mut positions = - King::authorized_positions([0, 4], PieceColor::Black, board.board, &[], false); + let mut positions = King::authorized_positions( + &Coord::new(0, 4), + PieceColor::Black, + board.board, + &[], + false, + ); positions.sort(); assert_eq!(right_positions, positions); @@ -514,11 +548,16 @@ mod tests { let mut board = Board::default(); board.set_board(custom_board); - let mut right_positions = vec![vec![0, 5]]; + let mut right_positions = vec![Coord::new(0, 5)]; right_positions.sort(); - let mut positions = - King::authorized_positions([0, 4], PieceColor::Black, board.board, &[], false); + let mut positions = King::authorized_positions( + &Coord::new(0, 4), + PieceColor::Black, + board.board, + &[], + false, + ); positions.sort(); assert_eq!(right_positions, positions); @@ -578,11 +617,11 @@ mod tests { let is_king_checked = is_getting_checked(board.board, board.player_turn, &board.move_history); - let mut right_positions = vec![vec![0, 5]]; + let mut right_positions = vec![Coord::new(0, 5)]; right_positions.sort(); let mut positions = King::authorized_positions( - [0, 4], + &Coord::new(0, 4), PieceColor::Black, board.board, &[], @@ -644,34 +683,28 @@ mod tests { let mut board = Board::default(); board.set_board(custom_board); - let mut right_positions = vec![vec![0, 5]]; + let mut right_positions = vec![Coord::new(0, 5)]; right_positions.sort(); let mut positions = King::authorized_positions( - [0, 4], + &Coord::new(0, 4), PieceColor::Black, board.board, &[ (PieceMove { piece_type: PieceType::Rook, - from_y: 0, - from_x: 7, - to_y: 4, - to_x: 7, + from: Coord::new(0, 7), + to: Coord::new(4, 7), }), (PieceMove { piece_type: PieceType::Pawn, - from_y: 6, - from_x: 2, - to_y: 5, - to_x: 2, + from: Coord::new(6, 2), + to: Coord::new(5, 2), }), (PieceMove { piece_type: PieceType::Rook, - from_y: 4, - from_x: 7, - to_y: 0, - to_x: 7, + from: Coord::new(4, 7), + to: Coord::new(0, 7), }), ], false, diff --git a/src/pieces/knight.rs b/src/pieces/knight.rs index 9214915..4360313 100644 --- a/src/pieces/knight.rs +++ b/src/pieces/knight.rs @@ -1,24 +1,21 @@ -use super::{Movable, PieceColor, PieceMove, PieceType, Position}; +use super::{Movable, PieceColor, PieceMove, Position}; +use crate::board::{Coord, GameBoard}; use crate::constants::DisplayMode; -use crate::utils::{ - cleaned_positions, impossible_positions_king_checked, is_cell_color_ally, is_valid, -}; +use crate::utils::{cleaned_positions, impossible_positions_king_checked, is_cell_color_ally}; pub struct Knight; impl Movable for Knight { fn piece_move( - coordinates: [i8; 2], + coordinates: &Coord, color: PieceColor, - board: [[Option<(PieceType, PieceColor)>; 8]; 8], + board: GameBoard, allow_move_on_ally_positions: bool, _move_history: &[PieceMove], - ) -> Vec> { - let mut positions: Vec> = Vec::new(); - - let (y, x) = (coordinates[0], coordinates[1]); + ) -> Vec { + let mut positions: Vec = Vec::new(); // Generate knight positions in all eight possible L-shaped moves - let piece_move = [ + let piece_move: [(i8, i8); 8] = [ (-2, -1), (-2, 1), (-1, -2), @@ -30,31 +27,31 @@ impl Movable for Knight { ]; for &(dy, dx) in &piece_move { - let new_coordinates = [y + dy, x + dx]; - - if !is_valid(new_coordinates) { + let Some(new_coordinates) = + Coord::opt_new(coordinates.row as i8 + dy, coordinates.col as i8 + dx) + else { continue; - } + }; - if is_cell_color_ally(board, new_coordinates, color) && !allow_move_on_ally_positions { + if is_cell_color_ally(board, &new_coordinates, color) && !allow_move_on_ally_positions { continue; } - positions.push(new_coordinates.to_vec()); + positions.push(new_coordinates); } - cleaned_positions(positions) + cleaned_positions(&positions) } } impl Position for Knight { fn authorized_positions( - coordinates: [i8; 2], + coordinates: &Coord, color: PieceColor, - board: [[Option<(PieceType, PieceColor)>; 8]; 8], + board: GameBoard, move_history: &[PieceMove], _is_king_checked: bool, - ) -> Vec> { + ) -> Vec { impossible_positions_king_checked( coordinates, Self::piece_move(coordinates, color, board, false, move_history), @@ -65,11 +62,11 @@ impl Position for Knight { } fn protected_positions( - coordinates: [i8; 2], + coordinates: &Coord, color: PieceColor, - board: [[Option<(PieceType, PieceColor)>; 8]; 8], + board: GameBoard, _move_history: &[PieceMove], - ) -> Vec> { + ) -> Vec { Self::piece_move(coordinates, color, board, true, _move_history) } } @@ -94,7 +91,7 @@ impl Knight { #[cfg(test)] mod tests { use crate::{ - board::Board, + board::{Board, Coord}, pieces::{knight::Knight, PieceColor, PieceType, Position}, utils::is_getting_checked, }; @@ -124,19 +121,24 @@ mod tests { board.set_board(custom_board); let mut right_positions = vec![ - vec![2, 3], - vec![2, 5], - vec![3, 2], - vec![3, 6], - vec![5, 2], - vec![5, 6], - vec![6, 3], - vec![6, 5], + Coord::new(2, 3), + Coord::new(2, 5), + Coord::new(3, 2), + Coord::new(3, 6), + Coord::new(5, 2), + Coord::new(5, 6), + Coord::new(6, 3), + Coord::new(6, 5), ]; right_positions.sort(); - let mut positions = - Knight::authorized_positions([4, 4], PieceColor::White, board.board, &[], false); + let mut positions = Knight::authorized_positions( + &Coord::new(4, 4), + PieceColor::White, + board.board, + &[], + false, + ); positions.sort(); assert_eq!(right_positions, positions); @@ -184,11 +186,16 @@ mod tests { let mut board = Board::default(); board.set_board(custom_board); - let mut right_positions = vec![vec![6, 5]]; + let mut right_positions = vec![Coord::new(6, 5)]; right_positions.sort(); - let mut positions = - Knight::authorized_positions([7, 7], PieceColor::White, board.board, &[], false); + let mut positions = Knight::authorized_positions( + &Coord::new(7, 7), + PieceColor::White, + board.board, + &[], + false, + ); positions.sort(); assert_eq!(right_positions, positions); @@ -239,11 +246,11 @@ mod tests { let is_king_checked = is_getting_checked(board.board, board.player_turn, &board.move_history); - let mut right_positions = vec![vec![7, 7]]; + let mut right_positions = vec![Coord::new(7, 7)]; right_positions.sort(); let mut positions = Knight::authorized_positions( - [6, 5], + &Coord::new(6, 5), PieceColor::White, board.board, &[], @@ -299,11 +306,11 @@ mod tests { let is_king_checked = is_getting_checked(board.board, board.player_turn, &board.move_history); - let mut right_positions: Vec> = vec![]; + let mut right_positions: Vec = vec![]; right_positions.sort(); let mut positions = Knight::authorized_positions( - [6, 4], + &Coord::new(6, 4), PieceColor::White, board.board, &[], @@ -358,11 +365,11 @@ mod tests { let is_king_checked = is_getting_checked(board.board, board.player_turn, &board.move_history); - let mut right_positions: Vec> = vec![]; + let mut right_positions: Vec = vec![]; right_positions.sort(); let mut positions = Knight::authorized_positions( - [1, 4], + &Coord::new(1, 4), PieceColor::Black, board.board, &[], diff --git a/src/pieces/mod.rs b/src/pieces/mod.rs index 9b2cc40..5599808 100644 --- a/src/pieces/mod.rs +++ b/src/pieces/mod.rs @@ -1,3 +1,5 @@ +use crate::board::{Coord, GameBoard}; + use self::{bishop::Bishop, king::King, knight::Knight, pawn::Pawn, queen::Queen, rook::Rook}; use super::constants::DisplayMode; @@ -20,12 +22,12 @@ pub enum PieceType { impl PieceType { pub fn authorized_positions( self, - coordinates: [i8; 2], + coordinates: &Coord, color: PieceColor, - board: [[Option<(PieceType, PieceColor)>; 8]; 8], + board: GameBoard, move_history: &[PieceMove], is_king_checked: bool, - ) -> Vec> { + ) -> Vec { match self { PieceType::Pawn => { Pawn::authorized_positions(coordinates, color, board, move_history, is_king_checked) @@ -61,12 +63,12 @@ impl PieceType { } pub fn protected_positions( - selected_coordinates: [i8; 2], + selected_coordinates: &Coord, piece_type: PieceType, color: PieceColor, - board: [[Option<(PieceType, PieceColor)>; 8]; 8], + board: GameBoard, move_history: &[PieceMove], - ) -> Vec> { + ) -> Vec { match piece_type { PieceType::Pawn => { Pawn::protected_positions(selected_coordinates, color, board, move_history) @@ -151,10 +153,8 @@ impl PieceType { #[derive(Debug, Copy, Clone, PartialEq)] pub struct PieceMove { pub piece_type: PieceType, - pub from_x: i8, - pub from_y: i8, - pub to_x: i8, - pub to_y: i8, + pub from: Coord, + pub to: Coord, } #[derive(Debug, Copy, Clone, PartialEq)] @@ -165,27 +165,27 @@ pub enum PieceColor { pub trait Movable { fn piece_move( - coordinates: [i8; 2], + coordinates: &Coord, color: PieceColor, - board: [[Option<(PieceType, PieceColor)>; 8]; 8], + board: GameBoard, allow_move_on_ally_positions: bool, move_history: &[PieceMove], - ) -> Vec>; + ) -> Vec; } pub trait Position { fn authorized_positions( - coordinates: [i8; 2], + coordinates: &Coord, color: PieceColor, - board: [[Option<(PieceType, PieceColor)>; 8]; 8], + board: GameBoard, move_history: &[PieceMove], is_king_checked: bool, - ) -> Vec>; + ) -> Vec; fn protected_positions( - coordinates: [i8; 2], + coordinates: &Coord, color: PieceColor, - board: [[Option<(PieceType, PieceColor)>; 8]; 8], + board: GameBoard, move_history: &[PieceMove], - ) -> Vec>; + ) -> Vec; } diff --git a/src/pieces/pawn.rs b/src/pieces/pawn.rs index d0d50a0..b84bfa2 100644 --- a/src/pieces/pawn.rs +++ b/src/pieces/pawn.rs @@ -1,86 +1,96 @@ -use super::{Movable, PieceColor, PieceMove, PieceType, Position}; +use super::{Movable, PieceColor, PieceMove, Position}; +use crate::board::{Coord, GameBoard}; use crate::constants::DisplayMode; use crate::utils::{ cleaned_positions, get_latest_move, get_piece_color, impossible_positions_king_checked, - is_cell_color_ally, is_valid, + is_cell_color_ally, }; pub struct Pawn; impl Movable for Pawn { fn piece_move( - coordinates: [i8; 2], + coordinates: &Coord, color: PieceColor, - board: [[Option<(PieceType, PieceColor)>; 8]; 8], + board: GameBoard, allow_move_on_ally_positions: bool, move_history: &[PieceMove], - ) -> Vec> { + ) -> Vec { // Pawns can only move in one direction depending of their color // -1 if they are white (go up) +1 if they are black (go down) - let direction = if color == PieceColor::White { -1 } else { 1 }; + let direction: i8 = if color == PieceColor::White { -1 } else { 1 }; - let mut positions: Vec> = vec![]; + let mut positions: Vec = vec![]; - let (y, x) = (coordinates[0], coordinates[1]); + let (y, x) = (coordinates.row, coordinates.col); // move one in front let new_x_front_one = x; - let new_y_front_one = y + direction; - let new_coordinates_front_one = [new_y_front_one, new_x_front_one]; + let new_y_front_one = y as i8 + direction; + let new_coordinates_front_one = Coord::new(new_y_front_one as u8, new_x_front_one); - if is_valid(new_coordinates_front_one) + if new_coordinates_front_one.is_valid() && !allow_move_on_ally_positions - && get_piece_color(board, new_coordinates_front_one).is_none() + && get_piece_color(board, &new_coordinates_front_one).is_none() { // Empty cell - positions.push(new_coordinates_front_one.to_vec()); + positions.push(new_coordinates_front_one); // move front a second cell let new_x_front_two = x; - let new_y_front_two = y + direction * 2; - let new_coordinates_front_two = [new_y_front_two, new_x_front_two]; + let new_y_front_two = y as i8 + direction * 2; + let new_coordinates_front_two = Coord::new(new_y_front_two as u8, new_x_front_two); - if is_valid(new_coordinates_front_two) - && get_piece_color(board, new_coordinates_front_two).is_none() + if new_coordinates_front_two.is_valid() + && get_piece_color(board, &new_coordinates_front_two).is_none() && ((color == PieceColor::White && y == 6) || (color == PieceColor::Black && y == 1)) { - positions.push(new_coordinates_front_two.to_vec()); + positions.push(new_coordinates_front_two); } } // check for enemy piece on the right let new_x_right = x + 1; - let new_y_right = y + direction; - let new_coordinates_right = [new_y_right, new_x_right]; + let new_y_right = y as i8 + direction; + let new_coordinates_right = + if let Some(new_coord) = Coord::opt_new(new_y_right, new_x_right as i8) { + new_coord + } else { + Coord::undefined() + }; // check for enemy piece on the left - let new_x_left = x - 1; - let new_y_left = y + direction; - let new_coordinates_left = [new_y_left, new_x_left]; + let new_x_left = x as i8 - 1; + let new_y_left = y as i8 + direction; + let new_coordinates_left = if let Some(new_coord) = Coord::opt_new(new_y_left, new_x_left) { + new_coord + } else { + Coord::undefined() + }; // If we allow on ally position we push it anyway if allow_move_on_ally_positions { - if is_valid(new_coordinates_right) { - positions.push(new_coordinates_right.to_vec()) + if new_coordinates_right.is_valid() { + positions.push(new_coordinates_right) }; - if is_valid(new_coordinates_left) { - positions.push(new_coordinates_left.to_vec()) + if new_coordinates_left.is_valid() { + positions.push(new_coordinates_left) }; } else { // else we check if it's an ally piece - if is_valid(new_coordinates_right) - && get_piece_color(board, new_coordinates_right).is_some() - && !is_cell_color_ally(board, new_coordinates_right, color) + if new_coordinates_right.is_valid() + && get_piece_color(board, &new_coordinates_right).is_some() + && !is_cell_color_ally(board, &new_coordinates_right, color) { - positions.push(new_coordinates_right.to_vec()); + positions.push(new_coordinates_right); } - if is_valid(new_coordinates_left) - && get_piece_color(board, new_coordinates_left).is_some() - && !is_cell_color_ally(board, new_coordinates_left, color) + if new_coordinates_left.is_valid() + && get_piece_color(board, &new_coordinates_left).is_some() + && !is_cell_color_ally(board, &new_coordinates_left, color) { - positions.push(new_coordinates_left.to_vec()); + positions.push(new_coordinates_left); } } @@ -91,38 +101,38 @@ impl Movable for Pawn { if color == PieceColor::White { valid_y_start = 1; - number_of_cells_move = latest_move.to_y - latest_move.from_y; + number_of_cells_move = latest_move.to.row as i8 - latest_move.from.row as i8; } else { valid_y_start = 6; - number_of_cells_move = latest_move.from_y - latest_move.to_y; + number_of_cells_move = latest_move.from.row as i8 - latest_move.to.row as i8; }; // We check if the latest move was on the right start cell // if it moved 2 cells // and if the current pawn is next to this pawn latest position - if latest_move.from_y == valid_y_start + if latest_move.from.row as i8 == valid_y_start && number_of_cells_move == 2 - && y == latest_move.to_y - && (x == latest_move.to_x - 1 || x == latest_move.to_x + 1) + && y == latest_move.to.row + && (x == latest_move.to.col - 1 || x == latest_move.to.col + 1) { - let new_y = latest_move.from_y + -direction; - let new_x = latest_move.from_x; - positions.push([new_y, new_x].to_vec()); + let new_y = latest_move.from.row as i8 + -direction; + let new_x = latest_move.from.col; + positions.push(Coord::new(new_y as u8, new_x)); } } - cleaned_positions(positions) + cleaned_positions(&positions) } } impl Position for Pawn { fn authorized_positions( - coordinates: [i8; 2], + coordinates: &Coord, color: PieceColor, - board: [[Option<(PieceType, PieceColor)>; 8]; 8], + board: GameBoard, move_history: &[PieceMove], _is_king_checked: bool, - ) -> Vec> { + ) -> Vec { // If the king is not checked we get then normal moves // if the king is checked we clean all the position not resolving the check impossible_positions_king_checked( @@ -135,11 +145,11 @@ impl Position for Pawn { } fn protected_positions( - coordinates: [i8; 2], + coordinates: &Coord, color: PieceColor, - board: [[Option<(PieceType, PieceColor)>; 8]; 8], + board: GameBoard, move_history: &[PieceMove], - ) -> Vec> { + ) -> Vec { Self::piece_move(coordinates, color, board, true, move_history) } } @@ -164,7 +174,7 @@ impl Pawn { #[cfg(test)] mod tests { use crate::{ - board::Board, + board::{Board, Coord}, pieces::{pawn::Pawn, PieceColor, PieceMove, PieceType, Position}, utils::is_getting_checked, }; @@ -193,11 +203,16 @@ mod tests { let mut board = Board::default(); board.set_board(custom_board); - let mut right_positions = vec![vec![3, 4]]; + let mut right_positions = vec![Coord::new(3, 4)]; right_positions.sort(); - let mut positions = - Pawn::authorized_positions([4, 4], PieceColor::White, board.board, &[], false); + let mut positions = Pawn::authorized_positions( + &Coord::new(4, 4), + PieceColor::White, + board.board, + &[], + false, + ); positions.sort(); assert_eq!(right_positions, positions); } @@ -226,11 +241,16 @@ mod tests { let mut board = Board::default(); board.set_board(custom_board); - let mut right_positions = vec![vec![5, 4], vec![4, 4]]; + let mut right_positions = vec![Coord::new(5, 4), Coord::new(4, 4)]; right_positions.sort(); - let mut positions = - Pawn::authorized_positions([6, 4], PieceColor::White, board.board, &[], false); + let mut positions = Pawn::authorized_positions( + &Coord::new(6, 4), + PieceColor::White, + board.board, + &[], + false, + ); positions.sort(); assert_eq!(right_positions, positions); } @@ -268,11 +288,21 @@ mod tests { let mut board = Board::default(); board.set_board(custom_board); - let mut right_positions = vec![vec![2, 3], vec![3, 3], vec![2, 4], vec![2, 2]]; + let mut right_positions = vec![ + Coord::new(2, 3), + Coord::new(3, 3), + Coord::new(2, 4), + Coord::new(2, 2), + ]; right_positions.sort(); - let mut positions = - Pawn::authorized_positions([1, 3], PieceColor::Black, board.board, &[], false); + let mut positions = Pawn::authorized_positions( + &Coord::new(1, 3), + PieceColor::Black, + board.board, + &[], + false, + ); positions.sort(); assert_eq!(right_positions, positions); } @@ -310,11 +340,16 @@ mod tests { let mut board = Board::default(); board.set_board(custom_board); - let mut right_positions = vec![vec![2, 4], vec![2, 2]]; + let mut right_positions = vec![Coord::new(2, 4), Coord::new(2, 2)]; right_positions.sort(); - let mut positions = - Pawn::authorized_positions([1, 3], PieceColor::Black, board.board, &[], false); + let mut positions = Pawn::authorized_positions( + &Coord::new(1, 3), + PieceColor::Black, + board.board, + &[], + false, + ); positions.sort(); assert_eq!(right_positions, positions); } @@ -343,19 +378,17 @@ mod tests { let mut board = Board::default(); board.set_board(custom_board); - let mut right_positions = vec![vec![2, 2], vec![2, 3]]; + let mut right_positions = vec![Coord::new(2, 2), Coord::new(2, 3)]; right_positions.sort(); let mut positions = Pawn::authorized_positions( - [3, 3], + &Coord::new(3, 3), PieceColor::White, board.board, &[(PieceMove { piece_type: PieceType::Pawn, - from_y: 1, - from_x: 2, - to_y: 3, - to_x: 2, + from: Coord::new(1, 2), + to: Coord::new(3, 2), })], false, ); @@ -387,19 +420,17 @@ mod tests { let mut board = Board::default(); board.set_board(custom_board); - let mut right_positions = vec![vec![5, 2], vec![5, 3]]; + let mut right_positions = vec![Coord::new(5, 2), Coord::new(5, 3)]; right_positions.sort(); let mut positions = Pawn::authorized_positions( - [4, 2], + &Coord::new(4, 2), PieceColor::Black, board.board, &[(PieceMove { piece_type: PieceType::Pawn, - from_y: 6, - from_x: 3, - to_y: 4, - to_x: 3, + from: Coord::new(6, 3), + to: Coord::new(4, 3), })], false, ); @@ -440,19 +471,17 @@ mod tests { let mut board = Board::default(); board.set_board(custom_board); - let mut right_positions = vec![vec![2, 1], vec![3, 1]]; + let mut right_positions = vec![Coord::new(2, 1), Coord::new(3, 1)]; right_positions.sort(); let mut positions = Pawn::authorized_positions( - [1, 1], + &Coord::new(1, 1), PieceColor::Black, board.board, &[(PieceMove { piece_type: PieceType::Pawn, - from_y: 6, - from_x: 3, - to_y: 4, - to_x: 3, + from: Coord::new(6, 3), + to: Coord::new(4, 3), })], false, ); @@ -496,11 +525,11 @@ mod tests { let is_king_checked = is_getting_checked(board.board, board.player_turn, &board.move_history); - let mut right_positions = vec![vec![3, 2]]; + let mut right_positions = vec![Coord::new(3, 2)]; right_positions.sort(); let mut positions = Pawn::authorized_positions( - [2, 3], + &Coord::new(2, 3), PieceColor::Black, board.board, &[], @@ -547,11 +576,11 @@ mod tests { let is_king_checked = is_getting_checked(board.board, board.player_turn, &board.move_history); - let mut right_positions: Vec> = vec![]; + let mut right_positions: Vec = vec![]; right_positions.sort(); let mut positions = Pawn::authorized_positions( - [2, 4], + &Coord::new(2, 4), PieceColor::Black, board.board, &[], @@ -607,11 +636,11 @@ mod tests { let is_king_checked = is_getting_checked(board.board, board.player_turn, &board.move_history); - let mut right_positions: Vec> = vec![]; + let mut right_positions: Vec = vec![]; right_positions.sort(); let mut positions = Pawn::authorized_positions( - [1, 5], + &Coord::new(1, 5), PieceColor::Black, board.board, &[], diff --git a/src/pieces/queen.rs b/src/pieces/queen.rs index be143eb..5e18230 100644 --- a/src/pieces/queen.rs +++ b/src/pieces/queen.rs @@ -1,5 +1,6 @@ use super::rook::Rook; -use super::{Movable, PieceColor, PieceMove, PieceType, Position}; +use super::{Movable, PieceColor, PieceMove, Position}; +use crate::board::{Coord, GameBoard}; use crate::constants::DisplayMode; use crate::pieces::bishop::Bishop; use crate::utils::{cleaned_positions, impossible_positions_king_checked}; @@ -8,13 +9,13 @@ pub struct Queen; impl Movable for Queen { fn piece_move( - coordinates: [i8; 2], + coordinates: &Coord, color: PieceColor, - board: [[Option<(PieceType, PieceColor)>; 8]; 8], + board: GameBoard, allow_move_on_ally_positions: bool, move_history: &[PieceMove], - ) -> Vec> { - let mut positions: Vec> = vec![]; + ) -> Vec { + let mut positions = vec![]; // Queen = bishop concat rook positions.extend(Bishop::piece_move( @@ -32,18 +33,18 @@ impl Movable for Queen { move_history, )); - cleaned_positions(positions) + cleaned_positions(&positions) } } impl Position for Queen { fn authorized_positions( - coordinates: [i8; 2], + coordinates: &Coord, color: PieceColor, - board: [[Option<(PieceType, PieceColor)>; 8]; 8], + board: GameBoard, move_history: &[PieceMove], _is_king_checked: bool, - ) -> Vec> { + ) -> Vec { impossible_positions_king_checked( coordinates, Self::piece_move(coordinates, color, board, false, move_history), @@ -53,11 +54,11 @@ impl Position for Queen { ) } fn protected_positions( - coordinates: [i8; 2], + coordinates: &Coord, color: PieceColor, - board: [[Option<(PieceType, PieceColor)>; 8]; 8], + board: GameBoard, move_history: &[PieceMove], - ) -> Vec> { + ) -> Vec { Self::piece_move(coordinates, color, board, true, move_history) } } @@ -82,7 +83,7 @@ impl Queen { #[cfg(test)] mod tests { use crate::{ - board::Board, + board::{Board, Coord}, pieces::{queen::Queen, PieceColor, PieceType, Position}, utils::is_getting_checked, }; @@ -112,38 +113,43 @@ mod tests { board.set_board(custom_board); let mut right_positions = vec![ - vec![0, 0], - vec![0, 4], - vec![1, 1], - vec![1, 4], - vec![1, 7], - vec![2, 2], - vec![2, 4], - vec![2, 6], - vec![3, 3], - vec![3, 4], - vec![3, 5], - vec![4, 0], - vec![4, 1], - vec![4, 2], - vec![4, 3], - vec![4, 5], - vec![4, 6], - vec![4, 7], - vec![5, 3], - vec![5, 4], - vec![5, 5], - vec![6, 2], - vec![6, 4], - vec![6, 6], - vec![7, 1], - vec![7, 4], - vec![7, 7], + Coord::new(0, 0), + Coord::new(0, 4), + Coord::new(1, 1), + Coord::new(1, 4), + Coord::new(1, 7), + Coord::new(2, 2), + Coord::new(2, 4), + Coord::new(2, 6), + Coord::new(3, 3), + Coord::new(3, 4), + Coord::new(3, 5), + Coord::new(4, 0), + Coord::new(4, 1), + Coord::new(4, 2), + Coord::new(4, 3), + Coord::new(4, 5), + Coord::new(4, 6), + Coord::new(4, 7), + Coord::new(5, 3), + Coord::new(5, 4), + Coord::new(5, 5), + Coord::new(6, 2), + Coord::new(6, 4), + Coord::new(6, 6), + Coord::new(7, 1), + Coord::new(7, 4), + Coord::new(7, 7), ]; right_positions.sort(); - let mut positions = - Queen::authorized_positions([4, 4], PieceColor::White, board.board, &[], false); + let mut positions = Queen::authorized_positions( + &Coord::new(4, 4), + PieceColor::White, + board.board, + &[], + false, + ); positions.sort(); assert_eq!(right_positions, positions); @@ -183,36 +189,41 @@ mod tests { board.set_board(custom_board); let mut right_positions = vec![ - vec![0, 0], - vec![0, 4], - vec![1, 1], - vec![1, 4], - vec![2, 2], - vec![2, 4], - vec![3, 3], - vec![3, 4], - vec![3, 5], - vec![4, 0], - vec![4, 1], - vec![4, 2], - vec![4, 3], - vec![4, 5], - vec![4, 6], - vec![4, 7], - vec![5, 3], - vec![5, 4], - vec![5, 5], - vec![6, 2], - vec![6, 4], - vec![6, 6], - vec![7, 1], - vec![7, 4], - vec![7, 7], + Coord::new(0, 0), + Coord::new(0, 4), + Coord::new(1, 1), + Coord::new(1, 4), + Coord::new(2, 2), + Coord::new(2, 4), + Coord::new(3, 3), + Coord::new(3, 4), + Coord::new(3, 5), + Coord::new(4, 0), + Coord::new(4, 1), + Coord::new(4, 2), + Coord::new(4, 3), + Coord::new(4, 5), + Coord::new(4, 6), + Coord::new(4, 7), + Coord::new(5, 3), + Coord::new(5, 4), + Coord::new(5, 5), + Coord::new(6, 2), + Coord::new(6, 4), + Coord::new(6, 6), + Coord::new(7, 1), + Coord::new(7, 4), + Coord::new(7, 7), ]; right_positions.sort(); - let mut positions = - Queen::authorized_positions([4, 4], PieceColor::White, board.board, &[], false); + let mut positions = Queen::authorized_positions( + &Coord::new(4, 4), + PieceColor::White, + board.board, + &[], + false, + ); positions.sort(); assert_eq!(right_positions, positions); @@ -270,28 +281,33 @@ mod tests { board.set_board(custom_board); let mut right_positions = vec![ - vec![1, 7], - vec![2, 6], - vec![3, 3], - vec![3, 5], - vec![4, 1], - vec![4, 2], - vec![4, 3], - vec![4, 5], - vec![4, 6], - vec![4, 7], - vec![5, 3], - vec![5, 4], - vec![5, 5], - vec![6, 2], - vec![6, 4], - vec![7, 4], + Coord::new(1, 7), + Coord::new(2, 6), + Coord::new(3, 3), + Coord::new(3, 5), + Coord::new(4, 1), + Coord::new(4, 2), + Coord::new(4, 3), + Coord::new(4, 5), + Coord::new(4, 6), + Coord::new(4, 7), + Coord::new(5, 3), + Coord::new(5, 4), + Coord::new(5, 5), + Coord::new(6, 2), + Coord::new(6, 4), + Coord::new(7, 4), ]; right_positions.sort(); - let mut positions = - Queen::authorized_positions([4, 4], PieceColor::White, board.board, &[], false); + let mut positions = Queen::authorized_positions( + &Coord::new(4, 4), + PieceColor::White, + board.board, + &[], + false, + ); positions.sort(); assert_eq!(right_positions, positions); @@ -341,11 +357,11 @@ mod tests { let is_king_checked = is_getting_checked(board.board, board.player_turn, &board.move_history); - let mut right_positions = vec![vec![4, 4]]; + let mut right_positions = vec![Coord::new(4, 4)]; right_positions.sort(); let mut positions = Queen::authorized_positions( - [5, 5], + &Coord::new(5, 5), PieceColor::Black, board.board, &[], @@ -400,11 +416,11 @@ mod tests { let is_king_checked = is_getting_checked(board.board, board.player_turn, &board.move_history); - let mut right_positions: Vec> = vec![]; + let mut right_positions: Vec = vec![]; right_positions.sort(); let mut positions = Queen::authorized_positions( - [5, 6], + &Coord::new(5, 6), PieceColor::Black, board.board, &[], @@ -460,11 +476,11 @@ mod tests { let is_king_checked = is_getting_checked(board.board, board.player_turn, &board.move_history); - let mut right_positions: Vec> = vec![vec![2, 6], vec![3, 7]]; + let mut right_positions = vec![Coord::new(2, 6), Coord::new(3, 7)]; right_positions.sort(); let mut positions = Queen::authorized_positions( - [1, 5], + &Coord::new(1, 5), PieceColor::Black, board.board, &[], diff --git a/src/pieces/rook.rs b/src/pieces/rook.rs index 32a2846..1354789 100644 --- a/src/pieces/rook.rs +++ b/src/pieces/rook.rs @@ -1,52 +1,53 @@ -use super::{Movable, PieceColor, PieceMove, PieceType, Position}; +use super::{Movable, PieceColor, PieceMove, Position}; +use crate::board::{Coord, GameBoard}; use crate::constants::DisplayMode; use crate::utils::{ cleaned_positions, get_piece_color, impossible_positions_king_checked, is_cell_color_ally, - is_piece_opposite_king, is_valid, + is_piece_opposite_king, }; pub struct Rook; impl Movable for Rook { fn piece_move( - coordinates: [i8; 2], + coordinates: &Coord, color: PieceColor, - board: [[Option<(PieceType, PieceColor)>; 8]; 8], + board: GameBoard, allow_move_on_ally_positions: bool, _move_history: &[PieceMove], - ) -> Vec> { + ) -> Vec { // Pawns can only move in one direction depending on their color - let mut positions: Vec> = vec![]; + let mut positions = vec![]; - let (y, x) = (coordinates[0], coordinates[1]); + let (y, x) = (coordinates.row, coordinates.col); // RIGHT ROW - for i in 1..8i8 { + for i in 1..8u8 { let new_x = x + i; let new_y = y; - let new_coordinates = [new_y, new_x]; + let new_coordinates = Coord::new(new_y, new_x); // Invalid coords - if !is_valid(new_coordinates) { + if !new_coordinates.is_valid() { break; } // Empty cell - if get_piece_color(board, new_coordinates).is_none() { - positions.push(new_coordinates.to_vec()); + if get_piece_color(board, &new_coordinates).is_none() { + positions.push(new_coordinates); continue; } // Ally cell - if is_cell_color_ally(board, new_coordinates, color) { + if is_cell_color_ally(board, &new_coordinates, color) { if !allow_move_on_ally_positions { break; } else { - positions.push(new_coordinates.to_vec()); + positions.push(new_coordinates); break; } } // Enemy cell - positions.push(new_coordinates.to_vec()); + positions.push(new_coordinates); if !allow_move_on_ally_positions || !is_piece_opposite_king(board[new_y as usize][new_x as usize], color) { @@ -56,33 +57,30 @@ impl Movable for Rook { // LEFT ROW for i in 1..=8 { - let new_x = x - i; - let new_y = y; - let new_coordinates = [new_y, new_x]; - - // Invalid coords - if !is_valid(new_coordinates) { + let new_x: i8 = x as i8 - i as i8; + let new_y: i8 = y as i8; + let Some(new_coordinates) = Coord::opt_new(new_y, new_x) else { break; - } + }; // Empty piece - if get_piece_color(board, new_coordinates).is_none() { - positions.push(new_coordinates.to_vec()); + if get_piece_color(board, &new_coordinates).is_none() { + positions.push(new_coordinates); continue; } // Ally piece - if is_cell_color_ally(board, new_coordinates, color) { + if is_cell_color_ally(board, &new_coordinates, color) { if !allow_move_on_ally_positions { break; } else { - positions.push(new_coordinates.to_vec()); + positions.push(new_coordinates); break; } } // Enemy cell - positions.push(new_coordinates.to_vec()); + positions.push(new_coordinates); if !allow_move_on_ally_positions || !is_piece_opposite_king(board[new_y as usize][new_x as usize], color) { @@ -91,33 +89,33 @@ impl Movable for Rook { } // BOTTOM ROW - for i in 1..8i8 { + for i in 1..8u8 { let new_x = x; let new_y = y + i; - let new_coordinates = [new_y, new_x]; + let new_coordinates = Coord::new(new_y, new_x); // Invalid coords - if !is_valid(new_coordinates) { + if !new_coordinates.is_valid() { break; } // Empty cell - if get_piece_color(board, new_coordinates).is_none() { - positions.push(new_coordinates.to_vec()); + if get_piece_color(board, &new_coordinates).is_none() { + positions.push(new_coordinates); continue; } // Ally cell - if is_cell_color_ally(board, new_coordinates, color) { + if is_cell_color_ally(board, &new_coordinates, color) { if !allow_move_on_ally_positions { break; } else { - positions.push(new_coordinates.to_vec()); + positions.push(new_coordinates); break; } } // Enemy cell - positions.push(new_coordinates.to_vec()); + positions.push(new_coordinates); if !allow_move_on_ally_positions || !is_piece_opposite_king(board[new_y as usize][new_x as usize], color) @@ -127,32 +125,29 @@ impl Movable for Rook { } // UP ROW - for i in 1..8i8 { - let new_x = x; - let new_y = y - i; - let new_coordinates = [new_y, new_x]; - - // Invalid coords - if !is_valid(new_coordinates) { + for i in 1..8u8 { + let new_x = x as i8; + let new_y = y as i8 - i as i8; + let Some(new_coordinates) = Coord::opt_new(new_y, new_x) else { break; - } + }; // Empty cell - if get_piece_color(board, new_coordinates).is_none() { - positions.push(new_coordinates.to_vec()); + if get_piece_color(board, &new_coordinates).is_none() { + positions.push(new_coordinates); continue; } // Ally cell - if is_cell_color_ally(board, new_coordinates, color) { + if is_cell_color_ally(board, &new_coordinates, color) { if !allow_move_on_ally_positions { break; } else { - positions.push(new_coordinates.to_vec()); + positions.push(new_coordinates); break; } } // Enemy cell - positions.push(new_coordinates.to_vec()); + positions.push(new_coordinates); if !allow_move_on_ally_positions || !is_piece_opposite_king(board[new_y as usize][new_x as usize], color) @@ -161,18 +156,18 @@ impl Movable for Rook { } } - cleaned_positions(positions) + cleaned_positions(&positions) } } impl Position for Rook { fn authorized_positions( - coordinates: [i8; 2], + coordinates: &Coord, color: PieceColor, - board: [[Option<(PieceType, PieceColor)>; 8]; 8], + board: GameBoard, move_history: &[PieceMove], _is_king_checked: bool, - ) -> Vec> { + ) -> Vec { // If the king is not checked we get then normal moves // if the king is checked we clean all the position not resolving the check impossible_positions_king_checked( @@ -185,11 +180,11 @@ impl Position for Rook { } fn protected_positions( - coordinates: [i8; 2], + coordinates: &Coord, color: PieceColor, - board: [[Option<(PieceType, PieceColor)>; 8]; 8], + board: GameBoard, move_history: &[PieceMove], - ) -> Vec> { + ) -> Vec { Self::piece_move(coordinates, color, board, true, move_history) } } @@ -214,7 +209,7 @@ impl Rook { #[cfg(test)] mod tests { use crate::{ - board::Board, + board::{Board, Coord}, pieces::{rook::Rook, PieceColor, PieceType, Position}, utils::is_getting_checked, }; @@ -244,25 +239,30 @@ mod tests { board.set_board(custom_board); let mut right_positions = vec![ - vec![7, 4], - vec![6, 4], - vec![5, 4], - vec![3, 4], - vec![2, 4], - vec![1, 4], - vec![0, 4], - vec![4, 0], - vec![4, 1], - vec![4, 2], - vec![4, 3], - vec![4, 5], - vec![4, 6], - vec![4, 7], + Coord::new(7, 4), + Coord::new(6, 4), + Coord::new(5, 4), + Coord::new(3, 4), + Coord::new(2, 4), + Coord::new(1, 4), + Coord::new(0, 4), + Coord::new(4, 0), + Coord::new(4, 1), + Coord::new(4, 2), + Coord::new(4, 3), + Coord::new(4, 5), + Coord::new(4, 6), + Coord::new(4, 7), ]; right_positions.sort(); - let mut positions = - Rook::authorized_positions([4, 4], PieceColor::White, board.board, &[], false); + let mut positions = Rook::authorized_positions( + &Coord::new(4, 4), + PieceColor::White, + board.board, + &[], + false, + ); positions.sort(); assert_eq!(right_positions, positions); } @@ -301,22 +301,27 @@ mod tests { board.set_board(custom_board); let mut right_positions = vec![ - vec![7, 4], - vec![6, 4], - vec![5, 4], - vec![3, 4], - vec![4, 0], - vec![4, 1], - vec![4, 2], - vec![4, 3], - vec![4, 5], - vec![4, 6], - vec![4, 7], + Coord::new(7, 4), + Coord::new(6, 4), + Coord::new(5, 4), + Coord::new(3, 4), + Coord::new(4, 0), + Coord::new(4, 1), + Coord::new(4, 2), + Coord::new(4, 3), + Coord::new(4, 5), + Coord::new(4, 6), + Coord::new(4, 7), ]; right_positions.sort(); - let mut positions = - Rook::authorized_positions([4, 4], PieceColor::White, board.board, &[], false); + let mut positions = Rook::authorized_positions( + &Coord::new(4, 4), + PieceColor::White, + board.board, + &[], + false, + ); positions.sort(); assert_eq!(right_positions, positions); } @@ -364,19 +369,24 @@ mod tests { board.set_board(custom_board); let mut right_positions = vec![ - vec![4, 0], - vec![4, 1], - vec![4, 2], - vec![4, 3], - vec![4, 5], - vec![4, 6], - vec![3, 4], - vec![5, 4], + Coord::new(4, 0), + Coord::new(4, 1), + Coord::new(4, 2), + Coord::new(4, 3), + Coord::new(4, 5), + Coord::new(4, 6), + Coord::new(3, 4), + Coord::new(5, 4), ]; right_positions.sort(); - let mut positions = - Rook::authorized_positions([4, 4], PieceColor::White, board.board, &[], false); + let mut positions = Rook::authorized_positions( + &Coord::new(4, 4), + PieceColor::White, + board.board, + &[], + false, + ); positions.sort(); assert_eq!(right_positions, positions); @@ -427,11 +437,11 @@ mod tests { let is_king_checked = is_getting_checked(board.board, board.player_turn, &board.move_history); - let mut right_positions = vec![vec![4, 2]]; + let mut right_positions = vec![Coord::new(4, 2)]; right_positions.sort(); let mut positions = Rook::authorized_positions( - [5, 2], + &Coord::new(5, 2), PieceColor::Black, board.board, &[], @@ -487,11 +497,11 @@ mod tests { let is_king_checked = is_getting_checked(board.board, board.player_turn, &board.move_history); - let mut right_positions: Vec> = vec![]; + let mut right_positions: Vec = vec![]; right_positions.sort(); let mut positions = Rook::authorized_positions( - [5, 3], + &Coord::new(5, 3), PieceColor::Black, board.board, &[], @@ -546,11 +556,11 @@ mod tests { let is_king_checked = is_getting_checked(board.board, board.player_turn, &board.move_history); - let mut right_positions: Vec> = vec![vec![2, 4], vec![3, 4]]; + let mut right_positions: Vec = vec![Coord::new(2, 4), Coord::new(3, 4)]; right_positions.sort(); let mut positions = Rook::authorized_positions( - [1, 4], + &Coord::new(1, 4), PieceColor::Black, board.board, &[], diff --git a/src/utils.rs b/src/utils.rs index 5208c54..1cfbc61 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,5 +1,5 @@ use crate::{ - board::Board, + board::{Board, Coord, GameBoard}, constants::{DisplayMode, UNDEFINED_POSITION}, pieces::{PieceColor, PieceMove, PieceType}, }; @@ -9,18 +9,18 @@ use ratatui::{ widgets::{Block, Padding, Paragraph}, }; -pub fn get_piece_color( - board: [[Option<(PieceType, PieceColor)>; 8]; 8], - coordinates: [i8; 2], -) -> Option { - board[coordinates[0] as usize][coordinates[1] as usize].map(|(_, piece_color)| piece_color) +pub fn get_piece_color(board: GameBoard, coordinates: &Coord) -> Option { + if !coordinates.is_valid() { + return None; + } + board[coordinates].map(|(_, piece_color)| piece_color) } -pub fn get_piece_type( - board: [[Option<(PieceType, PieceColor)>; 8]; 8], - coordinates: [i8; 2], -) -> Option { - board[coordinates[0] as usize][coordinates[1] as usize].map(|(piece_type, _)| piece_type) +pub fn get_piece_type(board: GameBoard, coordinates: &Coord) -> Option { + if !coordinates.is_valid() { + return None; + } + board[coordinates].map(|(piece_type, _)| piece_type) } pub fn get_opposite_color(color: PieceColor) -> PieceColor { @@ -31,60 +31,41 @@ pub fn get_opposite_color(color: PieceColor) -> PieceColor { } // method to clean the position array to remove impossible positions -pub fn cleaned_positions(positions: Vec>) -> Vec> { - let mut cleaned_array: Vec> = vec![]; +pub fn cleaned_positions(positions: &[Coord]) -> Vec { + let mut cleaned_array: Vec = vec![]; for position in positions { - if is_valid([position[0], position[1]]) { - cleaned_array.push(position); + if position.is_valid() { + cleaned_array.push(*position); } } cleaned_array } // Return true forally cell color; false for enemy -pub fn is_cell_color_ally( - board: [[Option<(PieceType, PieceColor)>; 8]; 8], - coordinates: [i8; 2], - color: PieceColor, -) -> bool { +pub fn is_cell_color_ally(board: GameBoard, coordinates: &Coord, color: PieceColor) -> bool { match get_piece_color(board, coordinates) { Some(cell_color) => cell_color == color, None => false, // Treat empty cell as ally } } -pub fn is_valid(coordinates: [i8; 2]) -> bool { - let (y, x) = (coordinates[0], coordinates[1]); - - (0..8).contains(&y) && (0..8).contains(&x) -} - -pub fn is_vec_in_array(array: Vec>, element: [i8; 2]) -> bool { - for position in array { - if position == element { - return true; - } - } - false -} - // We get all the cells that are getting put in 'check' pub fn get_all_protected_cells( - board: [[Option<(PieceType, PieceColor)>; 8]; 8], + board: GameBoard, player_turn: PieceColor, move_history: &[PieceMove], -) -> Vec> { - let mut check_cells: Vec> = vec![]; - for i in 0..8i8 { - for j in 0..8i8 { - if get_piece_color(board, [i, j]) == Some(player_turn) { +) -> Vec { + let mut check_cells: Vec = vec![]; + for i in 0..8u8 { + for j in 0..8u8 { + if get_piece_color(board, &Coord::new(i, j)) == Some(player_turn) { continue; } // get the current cell piece color and type protecting positions - if let Some(piece_color) = get_piece_color(board, [i, j]) { - if let Some(piece_type) = get_piece_type(board, [i, j]) { + if let Some(piece_color) = get_piece_color(board, &Coord::new(i, j)) { + if let Some(piece_type) = get_piece_type(board, &Coord::new(i, j)) { check_cells.extend(PieceType::protected_positions( - [i, j], + &Coord::new(i, j), piece_type, piece_color, board, @@ -97,7 +78,7 @@ pub fn get_all_protected_cells( check_cells } -pub fn col_to_letter(col: i8) -> String { +pub fn col_to_letter(col: u8) -> String { match col { 0 => "a".to_string(), 1 => "b".to_string(), @@ -143,10 +124,10 @@ pub fn convert_position_into_notation(position: String) -> String { pub fn convert_notation_into_position(notation: String) -> String { let from_x = &letter_to_col(notation.chars().next()); - let from_y = (&get_int_from_char(notation.chars().nth(1)) - 8).abs(); + let from_y = (get_int_from_char(notation.chars().nth(1)) as i8 - 8).abs(); let to_x = &letter_to_col(notation.chars().nth(2)); - let to_y = (&get_int_from_char(notation.chars().nth(3)) - 8).abs(); + let to_y = (get_int_from_char(notation.chars().nth(3)) as i8 - 8).abs(); format!("{}{}{}{}", from_y, from_x, to_y, to_x) } @@ -158,10 +139,10 @@ pub fn get_player_turn_in_modulo(color: PieceColor) -> usize { } } -pub fn get_int_from_char(ch: Option) -> i8 { +pub fn get_int_from_char(ch: Option) -> u8 { match ch { - Some(ch) => ch.to_digit(10).unwrap() as i8, - _ => -1, + Some(ch) => ch.to_digit(10).unwrap() as u8, + _ => UNDEFINED_POSITION, } } @@ -174,11 +155,11 @@ pub fn get_latest_move(move_history: &[PieceMove]) -> Option { pub fn did_piece_already_move( move_history: &[PieceMove], - original_piece: (Option, [i8; 2]), + original_piece: (Option, Coord), ) -> bool { for entry in move_history { if Some(entry.piece_type) == original_piece.0 - && [entry.from_y, entry.from_x] == original_piece.1 + && Coord::new(entry.from.row, entry.from.col) == original_piece.1 { return true; } @@ -186,25 +167,22 @@ pub fn did_piece_already_move( false } // Method returning the coordinates of the king of a certain color -pub fn get_king_coordinates( - board: [[Option<(PieceType, PieceColor)>; 8]; 8], - player_turn: PieceColor, -) -> [i8; 2] { - for i in 0..8i32 { - for j in 0..8i32 { +pub fn get_king_coordinates(board: GameBoard, player_turn: PieceColor) -> Coord { + for i in 0..8u8 { + for j in 0..8u8 { if let Some((piece_type, piece_color)) = board[i as usize][j as usize] { if piece_type == PieceType::King && piece_color == player_turn { - return [i as i8, j as i8]; + return Coord::new(i, j); } } } } - [UNDEFINED_POSITION, UNDEFINED_POSITION] + Coord::undefined() } // Is getting checked pub fn is_getting_checked( - board: [[Option<(PieceType, PieceColor)>; 8]; 8], + board: GameBoard, player_turn: PieceColor, move_history: &[PieceMove], ) -> bool { @@ -221,27 +199,20 @@ pub fn is_getting_checked( } pub fn impossible_positions_king_checked( - original_coordinates: [i8; 2], - positions: Vec>, - board: [[Option<(PieceType, PieceColor)>; 8]; 8], + original_coordinates: &Coord, + positions: Vec, + board: GameBoard, color: PieceColor, move_history: &[PieceMove], -) -> Vec> { - let mut cleaned_position: Vec> = vec![]; +) -> Vec { + let mut cleaned_position: Vec = vec![]; for position in positions { // We create a new board let mut new_board = Board::new(board, color, move_history.to_owned().clone()); // We simulate the move - Board::move_piece_on_the_board( - &mut new_board, - [ - original_coordinates[0] as usize, - original_coordinates[1] as usize, - ], - [position[0] as usize, position[1] as usize], - ); + Board::move_piece_on_the_board(&mut new_board, original_coordinates, &position); // We check if the board is still checked with this move meaning it didn't resolve the problem if !is_getting_checked( @@ -272,11 +243,11 @@ pub fn color_to_ratatui_enum(piece_color: Option) -> Color { } } -pub fn get_cell_paragraph( - board: &Board, - cell_coordinates: [i8; 2], +pub fn get_cell_paragraph<'a>( + board: &'a Board, + cell_coordinates: &'a Coord, bounding_rect: Rect, -) -> Paragraph<'_> { +) -> Paragraph<'a> { // Get piece and color let piece_color = get_piece_color(board.board, cell_coordinates); let piece_type = get_piece_type(board.board, cell_coordinates);