diff --git a/src/app.rs b/src/app.rs index d40eb68..1489571 100644 --- a/src/app.rs +++ b/src/app.rs @@ -64,6 +64,7 @@ impl App { pub fn go_to_home(&mut self) { self.current_page = Pages::Home; + self.restart(); } /// Handles the tick event of the terminal. @@ -124,18 +125,16 @@ impl App { } pub fn restart(&mut self) { - if self.game.is_draw || self.game.is_checkmate { - let is_bot_starting = self.game.is_bot_starting; - let engine = self.game.engine.clone(); - let game_is_against_bot = self.game.is_game_against_bot; - self.game = Game::default(); - self.game.engine = engine; - self.game.is_game_against_bot = game_is_against_bot; - if is_bot_starting { - self.game.is_bot_starting = true; - self.game.bot_move(); - self.game.player_turn = PieceColor::Black; - } + let is_bot_starting = self.game.is_bot_starting; + let engine = self.game.engine.clone(); + let game_is_against_bot = self.game.is_game_against_bot; + self.game = Game::default(); + self.game.engine = engine; + self.game.is_game_against_bot = game_is_against_bot; + if is_bot_starting { + self.game.is_bot_starting = true; + self.game.bot_move(); + self.game.player_turn = PieceColor::Black; } } diff --git a/src/constants.rs b/src/constants.rs index 2bdb29c..30938c8 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -16,6 +16,7 @@ pub const TITLE: &str = r" ╚═════╝╚═╝ ╚═╝╚══════╝╚══════╝╚══════╝ ╚═╝ ╚═════╝ ╚═╝ "; +#[derive(Debug, Clone, Copy)] pub enum DisplayMode { DEFAULT, ASCII, diff --git a/src/game/game.rs b/src/game/game.rs index a7af2d0..d7a2cfd 100644 --- a/src/game/game.rs +++ b/src/game/game.rs @@ -1,18 +1,8 @@ use super::{coord::Coord, game_board::GameBoard, ui::UI}; use crate::{ - constants::{DisplayMode, BLACK, WHITE}, + constants::DisplayMode, pieces::{PieceColor, PieceMove, PieceType}, - utils::{ - convert_notation_into_position, convert_position_into_notation, get_cell_paragraph, - get_int_from_char, invert_position, - }, -}; -use ratatui::{ - layout::{Alignment, Constraint, Direction, Layout, Rect}, - style::{Color, Modifier, Style, Stylize}, - text::{Line, Span}, - widgets::{Block, BorderType, Borders, Padding, Paragraph}, - Frame, + utils::{convert_notation_into_position, get_int_from_char}, }; use uci::Engine; @@ -37,7 +27,6 @@ pub struct Game { pub display_mode: DisplayMode, /// Used to indicate if a bot move is following pub bot_will_move: bool, - // if the bot is starting, meaning the player is black pub is_bot_starting: bool, // The white piece that got taken @@ -85,6 +74,26 @@ impl Game { } } + // Clone the game + + pub fn clone(&self) -> Self { + Self { + game_board: self.game_board.clone(), + ui: self.ui.clone(), + player_turn: self.player_turn, + is_draw: self.is_draw, + is_checkmate: self.is_checkmate, + is_promotion: self.is_promotion, + engine: self.engine.clone(), + is_game_against_bot: self.is_game_against_bot, + display_mode: self.display_mode, + bot_will_move: self.bot_will_move, + is_bot_starting: self.is_bot_starting, + white_taken_pieces: self.white_taken_pieces.clone(), + black_taken_pieces: self.black_taken_pieces.clone(), + } + } + // Setters pub fn set_board(&mut self, game_board: GameBoard) { self.game_board = game_board; @@ -382,313 +391,4 @@ impl Game { // We store the current position of the board self.game_board.board_history.push(self.game_board.board); } - - // Method to render the board - pub fn board_render(&mut self, area: Rect, frame: &mut Frame) { - let width = area.width / 8; - let height = area.height / 8; - let border_height = area.height / 2 - (4 * height); - let border_width = area.width / 2 - (4 * width); - - // we update the starting coordinates - self.ui.top_x = area.x + border_width; - self.ui.top_y = area.y + border_height; - self.ui.width = width; - self.ui.height = height; - // We have 8 vertical lines - let columns = Layout::default() - .direction(Direction::Vertical) - .constraints( - [ - // spread the excess border - Constraint::Length(border_height), - Constraint::Length(height), - Constraint::Length(height), - Constraint::Length(height), - Constraint::Length(height), - Constraint::Length(height), - Constraint::Length(height), - Constraint::Length(height), - Constraint::Length(height), - Constraint::Length(border_height), - ] - .as_ref(), - ) - .split(area); - - // For each line we set 8 layout - for i in 0..8u8 { - let lines = Layout::default() - .direction(Direction::Horizontal) - .constraints( - [ - Constraint::Length(border_width), - Constraint::Length(width), - Constraint::Length(width), - Constraint::Length(width), - Constraint::Length(width), - Constraint::Length(width), - Constraint::Length(width), - Constraint::Length(width), - Constraint::Length(width), - Constraint::Length(border_width), - ] - .as_ref(), - ) - .split(columns[i as usize + 1]); - for j in 0..8u8 { - // Color of the cell to draw the board - let cell_color: Color = if (i + j) % 2 == 0 { WHITE } else { BLACK }; - - let last_move; - let mut last_move_from = Coord::undefined(); - let mut last_move_to = Coord::undefined(); - if !self.game_board.move_history.is_empty() { - last_move = self.game_board.move_history.last(); - if self.is_game_against_bot && !self.is_bot_starting { - last_move_from = last_move.map(|m| m.from).unwrap(); - last_move_to = last_move.map(|m| m.to).unwrap(); - } else { - last_move_from = invert_position(&last_move.map(|m| m.from).unwrap()); - last_move_to = invert_position(&last_move.map(|m| m.to).unwrap()); - } - } - - let mut positions: Vec = vec![]; - let is_cell_in_positions = |positions: &Vec, i: u8, j: u8| { - positions.iter().any(|&coord| coord == Coord::new(i, j)) - }; - // Draw the available moves for the selected piece - if self.ui.is_cell_selected() { - let selected_piece_color: Option = self - .game_board - .get_piece_color(&self.ui.selected_coordinates); - // only draw available moves if it is the right players turn - if match selected_piece_color { - Some(color) => color == self.player_turn, - None => false, - } { - positions = self.game_board.get_authorized_positions( - self.player_turn, - self.ui.selected_coordinates, - ); - - // Draw grey if the color is in the authorized positions - for coords in positions.clone() { - if i == coords.row && j == coords.col { - // cell_color = Color::Rgb(100, 100, 100); - } - } - } - } - - let square = lines[j as usize + 1]; - // Here we have all the possibilities for a cell: - // - selected cell: green - // - cursor cell: blue - // - available move cell: grey - // - checked king cell: magenta - // - last move cell: green - // - default cell: white or black - // Draw the cell blue if this is the current cursor cell - if i == self.ui.cursor_coordinates.row - && j == self.ui.cursor_coordinates.col - && !self.ui.mouse_used - { - Game::render_cell(frame, square, Color::LightBlue, None); - } - // Draw the cell magenta if the king is getting checked - else if self - .game_board - .is_getting_checked(self.game_board.board, self.player_turn) - && Coord::new(i, j) - == self - .game_board - .get_king_coordinates(self.game_board.board, self.player_turn) - { - Game::render_cell(frame, square, Color::Magenta, Some(Modifier::SLOW_BLINK)); - } - // Draw the cell green if this is the selected cell or if the cell is part of the last move - else if (i == self.ui.selected_coordinates.row - && j == self.ui.selected_coordinates.col) - || (last_move_from == Coord::new(i, j) // If the last move from - || (last_move_to == Coord::new(i, j) // If last move to - && !is_cell_in_positions(&positions, i, j))) - // and not in the authorized positions (grey instead of green) - { - Game::render_cell(frame, square, Color::LightGreen, None); - } else if is_cell_in_positions(&positions, i, j) { - Game::render_cell(frame, square, Color::Rgb(100, 100, 100), None); - } - // else as a last resort we draw the cell with the default color either white or black - else { - let mut cell = Block::default(); - cell = match self.display_mode { - DisplayMode::DEFAULT => cell.bg(cell_color), - DisplayMode::ASCII => match cell_color { - WHITE => cell.bg(Color::White).fg(Color::Black), - BLACK => cell.bg(Color::Black).fg(Color::White), - _ => cell.bg(cell_color), - }, - }; - frame.render_widget(cell.clone(), square); - } - - // Get piece and color - let coord = Coord::new(i, j); - let paragraph = get_cell_paragraph(self, &coord, square); - - frame.render_widget(paragraph, square); - } - } - } - fn render_cell(frame: &mut Frame, square: Rect, color: Color, modifier: Option) { - let mut cell = Block::default().bg(color); - if let Some(modifier) = modifier { - cell = cell.add_modifier(modifier); - } - frame.render_widget(cell, square); - } - - // Method to render the right panel history - pub fn history_render(&self, area: Rect, frame: &mut Frame) { - // We write the history board on the side - let history_block = Block::default() - .title("History") - .borders(Borders::ALL) - .border_style(Style::default().fg(WHITE)) - .border_type(BorderType::Rounded) - .padding(Padding::new(5, 10, 1, 2)); - - let mut lines: Vec = vec![]; - - for i in (0..self.game_board.move_history.len()).step_by(2) { - let piece_type_from = self.game_board.move_history[i].piece_type; - - let utf_icon_white = - PieceType::piece_to_utf_enum(piece_type_from, Some(PieceColor::White)); - let move_white = convert_position_into_notation(&format!( - "{}{}{}{}", - self.game_board.move_history[i].from.row, - self.game_board.move_history[i].from.col, - self.game_board.move_history[i].to.row, - self.game_board.move_history[i].to.col - )); - - let mut utf_icon_black = " "; - let mut move_black: String = " ".to_string(); - - // If there is something for black - if i + 1 < self.game_board.move_history.len() { - let piece_type_to = self.game_board.move_history[i + 1].piece_type; - - move_black = convert_position_into_notation(&format!( - "{}{}{}{}", - self.game_board.move_history[i + 1].from.row, - self.game_board.move_history[i + 1].from.col, - self.game_board.move_history[i + 1].to.row, - self.game_board.move_history[i + 1].to.col - )); - utf_icon_black = - PieceType::piece_to_utf_enum(piece_type_to, Some(PieceColor::Black)); - } - - 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 - Span::styled(format!("{utf_icon_black} "), Style::default().fg(WHITE)), // white symbol - Span::raw(move_black.to_string()), // black move - ])); - } - - let history_paragraph = Paragraph::new(lines).alignment(Alignment::Center); - - let height = area.height; - - let right_panel_layout = Layout::default() - .direction(Direction::Vertical) - .constraints([Constraint::Length(height - 1), Constraint::Length(1)].as_ref()) - .split(area); - - frame.render_widget(history_block.clone(), right_panel_layout[0]); - frame.render_widget( - history_paragraph, - history_block.inner(right_panel_layout[0]), - ); - } - - pub fn white_material_render(&self, area: Rect, frame: &mut Frame) { - let white_block = Block::default() - .title("White material") - .borders(Borders::ALL) - .border_style(Style::default().fg(WHITE)) - .border_type(BorderType::Rounded); - - let mut pieces: String = String::new(); - - for i in 0..self.white_taken_pieces.len() { - let utf_icon_white = - PieceType::piece_to_utf_enum(self.white_taken_pieces[i], Some(PieceColor::Black)); - - pieces.push_str(&format!("{utf_icon_white} ")); - } - let white_material_paragraph = Paragraph::new(pieces) - .alignment(Alignment::Center) - .add_modifier(Modifier::BOLD); - - let height = area.height; - - let right_panel_layout = Layout::default() - .direction(Direction::Vertical) - .constraints([Constraint::Length(height - 1), Constraint::Length(1)].as_ref()) - .split(area); - frame.render_widget(white_block.clone(), right_panel_layout[0]); - frame.render_widget( - white_material_paragraph, - white_block.inner(right_panel_layout[0]), - ); - // Bottom paragraph help text - let text = vec![Line::from("Press ? for help").alignment(Alignment::Center)]; - - let help_paragraph = Paragraph::new(text) - .block(Block::new()) - .alignment(Alignment::Center); - frame.render_widget(help_paragraph, right_panel_layout[1]); - } - - pub fn black_material_render(&self, area: Rect, frame: &mut Frame) { - let black_block = Block::default() - .title("Black material") - .borders(Borders::ALL) - .border_style(Style::default().fg(WHITE)) - .border_type(BorderType::Rounded); - - let mut pieces: String = String::new(); - - for i in 0..self.black_taken_pieces.len() { - let utf_icon_black = - PieceType::piece_to_utf_enum(self.black_taken_pieces[i], Some(PieceColor::White)); - - pieces.push_str(&format!("{utf_icon_black} ")); - } - - let black_material_paragraph = Paragraph::new(pieces) - .alignment(Alignment::Center) - .add_modifier(Modifier::BOLD); - - let height = area.height; - - let right_panel_layout = Layout::default() - .direction(Direction::Vertical) - .constraints([Constraint::Length(height - 1), Constraint::Length(1)].as_ref()) - .split(area); - - frame.render_widget(black_block.clone(), right_panel_layout[0]); - frame.render_widget( - black_material_paragraph, - black_block.inner(right_panel_layout[0]), - ); - } } diff --git a/src/game/game_board.rs b/src/game/game_board.rs index 75de109..f728012 100644 --- a/src/game/game_board.rs +++ b/src/game/game_board.rs @@ -70,6 +70,14 @@ impl GameBoard { } } + pub fn reset(&mut self) { + self.board = init_board(); + self.move_history.clear(); + self.board_history.clear(); + self.board_history.push(init_board()); + self.consecutive_non_pawn_or_capture = 0; + } + // Method to get the authorized positions for a piece pub fn get_authorized_positions( &self, diff --git a/src/game/ui.rs b/src/game/ui.rs index 5e1b813..ae49588 100644 --- a/src/game/ui.rs +++ b/src/game/ui.rs @@ -1,5 +1,17 @@ -use super::coord::Coord; -use crate::constants::UNDEFINED_POSITION; +use super::{coord::Coord, game::Game}; +use crate::{ + constants::{DisplayMode, BLACK, UNDEFINED_POSITION, WHITE}, + pieces::{PieceColor, PieceMove, PieceType}, + ui::ui::render_cell, + utils::{convert_position_into_notation, get_cell_paragraph, invert_position}, +}; +use ratatui::{ + layout::{Alignment, Constraint, Direction, Layout, Rect}, + style::{Color, Modifier, Style, Stylize}, + text::{Line, Span}, + widgets::{Block, BorderType, Borders, Padding, Paragraph}, + Frame, +}; pub struct UI { /// the cursor position @@ -40,6 +52,35 @@ impl Default for UI { } impl UI { + /// Clone the UI + pub fn clone(&self) -> UI { + UI { + cursor_coordinates: self.cursor_coordinates, + selected_coordinates: self.selected_coordinates, + selected_piece_cursor: self.selected_piece_cursor, + promotion_cursor: self.promotion_cursor, + old_cursor_position: self.old_cursor_position, + top_x: self.top_x, + top_y: self.top_y, + width: self.width, + height: self.height, + mouse_used: self.mouse_used, + } + } + + pub fn reset(&mut self) { + self.cursor_coordinates = Coord::new(4, 4); + self.selected_coordinates = Coord::undefined(); + self.selected_piece_cursor = 0; + self.promotion_cursor = 0; + self.old_cursor_position = Coord::undefined(); + self.top_x = 0; + self.top_y = 0; + self.width = 0; + self.height = 0; + self.mouse_used = false; + } + /// Check if a cell has been selected pub fn is_cell_selected(&self) -> bool { self.selected_coordinates.row != UNDEFINED_POSITION @@ -130,4 +171,313 @@ impl UI { self.cursor_coordinates = self.old_cursor_position; } } + + // Method to render the right panel history + pub fn history_render(&self, area: Rect, frame: &mut Frame, move_history: &Vec) { + // We write the history board on the side + let history_block = Block::default() + .title("History") + .borders(Borders::ALL) + .border_style(Style::default().fg(WHITE)) + .border_type(BorderType::Rounded) + .padding(Padding::new(5, 10, 1, 2)); + + let mut lines: Vec = vec![]; + + for i in (0..move_history.len()).step_by(2) { + let piece_type_from = move_history[i].piece_type; + + let utf_icon_white = + PieceType::piece_to_utf_enum(piece_type_from, Some(PieceColor::White)); + let move_white = convert_position_into_notation(&format!( + "{}{}{}{}", + move_history[i].from.row, + move_history[i].from.col, + move_history[i].to.row, + move_history[i].to.col + )); + + let mut utf_icon_black = " "; + let mut move_black: String = " ".to_string(); + + // If there is something for black + if i + 1 < move_history.len() { + let piece_type_to = move_history[i + 1].piece_type; + + move_black = convert_position_into_notation(&format!( + "{}{}{}{}", + move_history[i + 1].from.row, + move_history[i + 1].from.col, + move_history[i + 1].to.row, + move_history[i + 1].to.col + )); + utf_icon_black = + PieceType::piece_to_utf_enum(piece_type_to, Some(PieceColor::Black)); + } + + 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 + Span::styled(format!("{utf_icon_black} "), Style::default().fg(WHITE)), // white symbol + Span::raw(move_black.to_string()), // black move + ])); + } + + let history_paragraph = Paragraph::new(lines).alignment(Alignment::Center); + + let height = area.height; + + let right_panel_layout = Layout::default() + .direction(Direction::Vertical) + .constraints([Constraint::Length(height - 1), Constraint::Length(1)].as_ref()) + .split(area); + + frame.render_widget(history_block.clone(), right_panel_layout[0]); + frame.render_widget( + history_paragraph, + history_block.inner(right_panel_layout[0]), + ); + } + + pub fn white_material_render( + &self, + area: Rect, + frame: &mut Frame, + white_taken_pieces: &Vec, + ) { + let white_block = Block::default() + .title("White material") + .borders(Borders::ALL) + .border_style(Style::default().fg(WHITE)) + .border_type(BorderType::Rounded); + + let mut pieces: String = String::new(); + + for i in 0..white_taken_pieces.len() { + let utf_icon_white = + PieceType::piece_to_utf_enum(white_taken_pieces[i], Some(PieceColor::Black)); + + pieces.push_str(&format!("{utf_icon_white} ")); + } + let white_material_paragraph = Paragraph::new(pieces) + .alignment(Alignment::Center) + .add_modifier(Modifier::BOLD); + + let height = area.height; + + let right_panel_layout = Layout::default() + .direction(Direction::Vertical) + .constraints([Constraint::Length(height - 1), Constraint::Length(1)].as_ref()) + .split(area); + frame.render_widget(white_block.clone(), right_panel_layout[0]); + frame.render_widget( + white_material_paragraph, + white_block.inner(right_panel_layout[0]), + ); + // Bottom paragraph help text + let text = vec![Line::from("Press ? for help").alignment(Alignment::Center)]; + + let help_paragraph = Paragraph::new(text) + .block(Block::new()) + .alignment(Alignment::Center); + frame.render_widget(help_paragraph, right_panel_layout[1]); + } + + pub fn black_material_render( + &self, + area: Rect, + frame: &mut Frame, + black_taken_pieces: &Vec, + ) { + let black_block = Block::default() + .title("Black material") + .borders(Borders::ALL) + .border_style(Style::default().fg(WHITE)) + .border_type(BorderType::Rounded); + + let mut pieces: String = String::new(); + + for i in 0..black_taken_pieces.len() { + let utf_icon_black = + PieceType::piece_to_utf_enum(black_taken_pieces[i], Some(PieceColor::White)); + + pieces.push_str(&format!("{utf_icon_black} ")); + } + + let black_material_paragraph = Paragraph::new(pieces) + .alignment(Alignment::Center) + .add_modifier(Modifier::BOLD); + + let height = area.height; + + let right_panel_layout = Layout::default() + .direction(Direction::Vertical) + .constraints([Constraint::Length(height - 1), Constraint::Length(1)].as_ref()) + .split(area); + + frame.render_widget(black_block.clone(), right_panel_layout[0]); + frame.render_widget( + black_material_paragraph, + black_block.inner(right_panel_layout[0]), + ); + } + + // Method to render the board + pub fn board_render(&mut self, area: Rect, frame: &mut Frame, game: &Game) { + let width = area.width / 8; + let height = area.height / 8; + let border_height = area.height / 2 - (4 * height); + let border_width = area.width / 2 - (4 * width); + + // we update the starting coordinates + self.top_x = area.x + border_width; + self.top_y = area.y + border_height; + self.width = width; + self.height = height; + // We have 8 vertical lines + let columns = Layout::default() + .direction(Direction::Vertical) + .constraints( + [ + // spread the excess border + Constraint::Length(border_height), + Constraint::Length(height), + Constraint::Length(height), + Constraint::Length(height), + Constraint::Length(height), + Constraint::Length(height), + Constraint::Length(height), + Constraint::Length(height), + Constraint::Length(height), + Constraint::Length(border_height), + ] + .as_ref(), + ) + .split(area); + + // For each line we set 8 layout + for i in 0..8u8 { + let lines = Layout::default() + .direction(Direction::Horizontal) + .constraints( + [ + Constraint::Length(border_width), + Constraint::Length(width), + Constraint::Length(width), + Constraint::Length(width), + Constraint::Length(width), + Constraint::Length(width), + Constraint::Length(width), + Constraint::Length(width), + Constraint::Length(width), + Constraint::Length(border_width), + ] + .as_ref(), + ) + .split(columns[i as usize + 1]); + for j in 0..8u8 { + // Color of the cell to draw the board + let cell_color: Color = if (i + j) % 2 == 0 { WHITE } else { BLACK }; + + let last_move; + let mut last_move_from = Coord::undefined(); + let mut last_move_to = Coord::undefined(); + if !game.game_board.move_history.is_empty() { + last_move = game.game_board.move_history.last(); + if game.is_game_against_bot && !game.is_bot_starting { + last_move_from = last_move.map(|m| m.from).unwrap(); + last_move_to = last_move.map(|m| m.to).unwrap(); + } else { + last_move_from = invert_position(&last_move.map(|m| m.from).unwrap()); + last_move_to = invert_position(&last_move.map(|m| m.to).unwrap()); + } + } + + let mut positions: Vec = vec![]; + let is_cell_in_positions = |positions: &Vec, i: u8, j: u8| { + positions.iter().any(|&coord| coord == Coord::new(i, j)) + }; + // Draw the available moves for the selected piece + if self.is_cell_selected() { + let selected_piece_color: Option = + game.game_board.get_piece_color(&self.selected_coordinates); + // only draw available moves if it is the right players turn + if match selected_piece_color { + Some(color) => color == game.player_turn, + None => false, + } { + positions = game + .game_board + .get_authorized_positions(game.player_turn, self.selected_coordinates); + + // Draw grey if the color is in the authorized positions + for coords in positions.clone() { + if i == coords.row && j == coords.col { + // cell_color = Color::Rgb(100, 100, 100); + } + } + } + } + + let square = lines[j as usize + 1]; + // Here we have all the possibilities for a cell: + // - selected cell: green + // - cursor cell: blue + // - available move cell: grey + // - checked king cell: magenta + // - last move cell: green + // - default cell: white or black + // Draw the cell blue if this is the current cursor cell + if i == self.cursor_coordinates.row + && j == self.cursor_coordinates.col + && !self.mouse_used + { + render_cell(frame, square, Color::LightBlue, None); + } + // Draw the cell magenta if the king is getting checked + else if game + .game_board + .is_getting_checked(game.game_board.board, game.player_turn) + && Coord::new(i, j) + == game + .game_board + .get_king_coordinates(game.game_board.board, game.player_turn) + { + render_cell(frame, square, Color::Magenta, Some(Modifier::SLOW_BLINK)); + } + // Draw the cell green if this is the selected cell or if the cell is part of the last move + else if (i == self.selected_coordinates.row && j == self.selected_coordinates.col) + || (last_move_from == Coord::new(i, j) // If the last move from + || (last_move_to == Coord::new(i, j) // If last move to + && !is_cell_in_positions(&positions, i, j))) + // and not in the authorized positions (grey instead of green) + { + render_cell(frame, square, Color::LightGreen, None); + } else if is_cell_in_positions(&positions, i, j) { + render_cell(frame, square, Color::Rgb(100, 100, 100), None); + } + // else as a last resort we draw the cell with the default color either white or black + else { + let mut cell = Block::default(); + cell = match game.display_mode { + DisplayMode::DEFAULT => cell.bg(cell_color), + DisplayMode::ASCII => match cell_color { + WHITE => cell.bg(Color::White).fg(Color::Black), + BLACK => cell.bg(Color::Black).fg(Color::White), + _ => cell.bg(cell_color), + }, + }; + frame.render_widget(cell.clone(), square); + } + + // Get piece and color + let coord = Coord::new(i, j); + let paragraph = get_cell_paragraph(game, &coord, square); + + frame.render_widget(paragraph, square); + } + } + } } diff --git a/src/handler.rs b/src/handler.rs index db858f6..600e0ec 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -110,6 +110,9 @@ pub fn handle_key_events(key_event: KeyEvent, app: &mut App) -> AppResult<()> { KeyCode::Esc => { if app.show_help_popup { app.show_help_popup = false; + } else if app.show_color_popup { + app.show_color_popup = false; + app.current_page = Pages::Home; } else if app.current_page == Pages::Credit { app.current_page = Pages::Home; } else if app.current_page == Pages::Bot && app.selected_color.is_none() { @@ -120,7 +123,16 @@ pub fn handle_key_events(key_event: KeyEvent, app: &mut App) -> AppResult<()> { app.game.ui.unselect_cell(); } KeyCode::Char('b') => { + let display_mode = app.game.display_mode; + app.selected_color = None; + if app.game.is_game_against_bot { + app.game.is_game_against_bot = false; + app.game.is_bot_starting = false; + } app.go_to_home(); + app.game.game_board.reset(); + app.game.ui.reset(); + app.game.display_mode = display_mode; } // Other handlers you could add here. _ => {} diff --git a/src/ui/ui.rs b/src/ui/ui.rs index cb0cc87..7a1683c 100644 --- a/src/ui/ui.rs +++ b/src/ui/ui.rs @@ -1,7 +1,7 @@ use ratatui::{ layout::{Constraint, Direction, Layout}, prelude::{Alignment, Rect}, - style::{Style, Stylize}, + style::{Color, Modifier, Style, Stylize}, text::Line, widgets::{Block, Paragraph}, Frame, @@ -79,6 +79,14 @@ pub fn centered_rect(percent_x: u16, percent_y: u16, r: Rect) -> Rect { .split(popup_layout[1])[1] } +pub fn render_cell(frame: &mut Frame, square: Rect, color: Color, modifier: Option) { + let mut cell = Block::default().bg(color); + if let Some(modifier) = modifier { + cell = cell.add_modifier(modifier); + } + frame.render_widget(cell, square); +} + // Method to render the home menu and the options pub fn render_menu_ui(frame: &mut Frame, app: &App, main_area: Rect) { let main_layout_horizontal = Layout::default() @@ -188,20 +196,33 @@ pub fn render_game_ui(frame: &mut Frame, app: &mut App, main_area: Rect) { frame.render_widget(board_block.clone(), main_layout_vertical[1]); // We make the inside of the board - app.game - .board_render(board_block.inner(main_layout_vertical[1]), frame); + let inner_block = { + let temp = board_block.inner(main_layout_vertical[1]); + temp // Resolve immutable borrow here + }; + let game_clone = app.game.clone(); + app.game.ui.board_render(inner_block, frame, &game_clone); // Mutable borrow now allowed //top box for white material - app.game - .black_material_render(board_block.inner(right_box_layout[0]), frame); + app.game.ui.black_material_render( + board_block.inner(right_box_layout[0]), + frame, + &app.game.black_taken_pieces, + ); // We make the inside of the board - app.game - .history_render(board_block.inner(right_box_layout[1]), frame); + app.game.ui.history_render( + board_block.inner(right_box_layout[1]), + frame, + &app.game.game_board.move_history, + ); //bottom box for black matetrial - app.game - .white_material_render(board_block.inner(right_box_layout[2]), frame); + app.game.ui.white_material_render( + board_block.inner(right_box_layout[2]), + frame, + &app.game.white_taken_pieces, + ); if app.game.is_promotion { render_promotion_popup(frame, app);