Repository: thomas-mauran/chess-tui (language: rust)

Issue:
Material difference
## Problem

Material difference is not shown.

## Solution

Display material difference in History.

## Additional context

I guess Lichess has support for this too.


Discussion / hints:
Hello, can I try to take this issue ?

Fix patch:
diff --git a/src/app.rs b/src/app.rs
index b7fd5e0..1676315 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -52,6 +52,11 @@ impl App {
         }
     }
 
+    pub fn go_to_home(&mut self) {
+        self.current_page = Pages::Home;
+        self.board = Board::default();
+    }
+
     /// Handles the tick event of the terminal.
     pub fn tick(&self) {}
 
diff --git a/src/board.rs b/src/board.rs
index fb5e404..f588e2e 100644
--- a/src/board.rs
+++ b/src/board.rs
@@ -115,6 +115,9 @@ pub struct Board {
     pub engine: Option<Engine>,
     pub is_game_against_bot: bool,
     pub display_mode: DisplayMode,
+
+    pub white_taken_pieces: Vec<PieceType>,
+    pub black_taken_pieces: Vec<PieceType>,
 }
 
 impl Default for Board {
@@ -180,6 +183,8 @@ impl Default for Board {
             engine: None,
             is_game_against_bot: false,
             display_mode: DisplayMode::DEFAULT,
+            white_taken_pieces: vec![],
+            black_taken_pieces: vec![],
         }
     }
 }
@@ -202,6 +207,8 @@ impl Board {
             engine: None,
             is_game_against_bot: false,
             display_mode: DisplayMode::DEFAULT,
+            white_taken_pieces: vec![],
+            black_taken_pieces: vec![],
         }
     }
 
@@ -600,6 +607,26 @@ impl Board {
             }
         }
 
+        // We check if the move is a capture and add the piece to the taken pieces
+        match (piece_type_from, piece_type_to) {
+            (_, None) => {}
+            (_, Some(piece)) => {
+                let piece_color = get_piece_color(self.board, to);
+                if let Some(piece_color) = piece_color {
+                    match piece_color {
+                        PieceColor::Black => {
+                            self.white_taken_pieces.push(piece);
+                            self.white_taken_pieces.sort();
+                        }
+                        PieceColor::White => {
+                            self.black_taken_pieces.push(piece);
+                            self.black_taken_pieces.sort();
+                        }
+                    }
+                }
+            }
+        }
+
         // We check for en passant as the latest move
         if self.is_latest_move_en_passant(*from, *to) {
             // we kill the pawn
@@ -950,7 +977,40 @@ impl Board {
             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)];
 
@@ -959,6 +1019,40 @@ impl Board {
             .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]),
+        );
+    }
 }
 
 #[cfg(test)]
diff --git a/src/handler.rs b/src/handler.rs
index 46cf351..b09853f 100644
--- a/src/handler.rs
+++ b/src/handler.rs
@@ -59,6 +59,9 @@ pub fn handle_key_events(key_event: KeyEvent, app: &mut App) -> AppResult<()> {
             }
             app.board.unselect_cell();
         }
+        KeyCode::Char('b') => {
+            app.go_to_home();
+        }
         // Other handlers you could add here.
         _ => {}
     }
diff --git a/src/pieces/mod.rs b/src/pieces/mod.rs
index eba18de..2344355 100644
--- a/src/pieces/mod.rs
+++ b/src/pieces/mod.rs
@@ -1,3 +1,5 @@
+use std::cmp::Ordering;
+
 use crate::board::{Coord, GameBoard};
 
 use self::{bishop::Bishop, king::King, knight::Knight, pawn::Pawn, queen::Queen, rook::Rook};
@@ -150,6 +152,36 @@ impl PieceType {
     }
 }
 
+// Implementing the PartialOrd trait for PieceType to allow comparison between PieceType
+#[allow(clippy::non_canonical_partial_ord_impl)]
+impl PartialOrd for PieceType {
+    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+        if self == other {
+            return Some(Ordering::Equal);
+        }
+        match (self, other) {
+            (PieceType::Pawn, _) => Some(Ordering::Less),
+            (PieceType::Queen, _) => Some(Ordering::Greater),
+            (_, PieceType::Pawn) => Some(Ordering::Greater),
+            (_, PieceType::Queen) => Some(Ordering::Less),
+            (PieceType::Rook, PieceType::Bishop) => Some(Ordering::Greater),
+            (PieceType::Rook, PieceType::Knight) => Some(Ordering::Greater),
+            (PieceType::Bishop, PieceType::Knight) => Some(Ordering::Greater), // just for visual purpose
+            (PieceType::Bishop, PieceType::Rook) => Some(Ordering::Less),
+            (PieceType::Knight, PieceType::Rook) => Some(Ordering::Less),
+            (PieceType::Knight, PieceType::Bishop) => Some(Ordering::Less), // just for visual purpose
+            _ => Some(Ordering::Equal),
+        }
+    }
+}
+impl Ord for PieceType {
+    fn cmp(&self, other: &Self) -> Ordering {
+        self.partial_cmp(other).unwrap()
+    }
+}
+
+impl Eq for PieceType {}
+
 #[derive(Debug, Copy, Clone, PartialEq)]
 pub struct PieceMove {
     pub piece_type: PieceType,
diff --git a/src/popups.rs b/src/popups.rs
index 8a66fb7..e1ebaf0 100644
--- a/src/popups.rs
+++ b/src/popups.rs
@@ -222,6 +222,8 @@ pub fn render_help_popup(frame: &mut Frame) {
         Line::from(""),
         Line::from("q: Quit the game"),
         Line::from(""),
+        Line::from("b: Go to the home menu / reset the game"),
+        Line::from(""),
         Line::from(""),
         Line::from("Color codes:".underlined().bold()),
         Line::from(""),
diff --git a/src/ui.rs b/src/ui.rs
index de03ac8..1512dc7 100644
--- a/src/ui.rs
+++ b/src/ui.rs
@@ -160,6 +160,17 @@ pub fn render_game_ui(frame: &mut Frame, app: &App, main_area: Rect) {
         )
         .split(main_layout_horizontal[1]);
 
+    let right_box_layout = Layout::default()
+        .direction(Direction::Vertical)
+        .constraints(
+            [
+                Constraint::Ratio(2, 15),
+                Constraint::Ratio(11, 15),
+                Constraint::Ratio(2, 15),
+            ]
+            .as_ref(),
+        )
+        .split(main_layout_vertical[3]);
     // Board block representing the full board div
     let board_block = Block::default().style(Style::default());
 
@@ -170,9 +181,17 @@ pub fn render_game_ui(frame: &mut Frame, app: &App, main_area: Rect) {
     app.board
         .board_render(board_block.inner(main_layout_vertical[1]), frame);
 
+    //top box for white material
+    app.board
+        .black_material_render(board_block.inner(right_box_layout[0]), frame);
+
     // We make the inside of the board
     app.board
-        .history_render(board_block.inner(main_layout_vertical[3]), frame);
+        .history_render(board_block.inner(right_box_layout[1]), frame);
+
+    //bottom box for black matetrial
+    app.board
+        .white_material_render(board_block.inner(right_box_layout[2]), frame);
 
     if app.board.is_promotion {
         render_promotion_popup(frame, app);


Test patch: