diff --git a/Cargo.toml b/Cargo.toml index 7809ba5..2aa388f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = ["rdraught", "rdraught-cli", "rdraught-ui", "rdraught-w4"] +members = ["rdraught", "rdraught-cli", "rdraught-gtk", "rdraught-w4"] resolver = "3" [workspace.package] diff --git a/rdraught-ui/Cargo.toml b/rdraught-gtk/Cargo.toml similarity index 87% rename from rdraught-ui/Cargo.toml rename to rdraught-gtk/Cargo.toml index 4ce4f05..7a5188a 100644 --- a/rdraught-ui/Cargo.toml +++ b/rdraught-gtk/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "rdraught-ui" +name = "rdraught-gtk" authors.workspace = true edition.workspace = true homepage.workspace = true @@ -18,10 +18,10 @@ gio.workspace = true glib.workspace = true [lib] -name = "rdraught_ui" +name = "rdraught_gtk" crate-type = ["lib"] bench = false [[bin]] -name = "rdraught-ui" +name = "rdraught-gtk" path = "src/main.rs" diff --git a/rdraught-ui/Chess_Pieces_Sprite.svg b/rdraught-gtk/Chess_Pieces_Sprite.svg similarity index 100% rename from rdraught-ui/Chess_Pieces_Sprite.svg rename to rdraught-gtk/Chess_Pieces_Sprite.svg diff --git a/rdraught-ui/examples/ai_debugger.rs b/rdraught-gtk/examples/ai_debugger.rs similarity index 100% rename from rdraught-ui/examples/ai_debugger.rs rename to rdraught-gtk/examples/ai_debugger.rs diff --git a/rdraught-ui/src/crown_red.svg b/rdraught-gtk/src/crown_red.svg similarity index 100% rename from rdraught-ui/src/crown_red.svg rename to rdraught-gtk/src/crown_red.svg diff --git a/rdraught-ui/src/crown_white.svg b/rdraught-gtk/src/crown_white.svg similarity index 100% rename from rdraught-ui/src/crown_white.svg rename to rdraught-gtk/src/crown_white.svg diff --git a/rdraught-ui/src/final_dialog.rs b/rdraught-gtk/src/final_dialog.rs similarity index 100% rename from rdraught-ui/src/final_dialog.rs rename to rdraught-gtk/src/final_dialog.rs diff --git a/rdraught-ui/src/geo2d.rs b/rdraught-gtk/src/geo2d.rs similarity index 100% rename from rdraught-ui/src/geo2d.rs rename to rdraught-gtk/src/geo2d.rs diff --git a/rdraught-ui/src/greeting_dialog.rs b/rdraught-gtk/src/greeting_dialog.rs similarity index 100% rename from rdraught-ui/src/greeting_dialog.rs rename to rdraught-gtk/src/greeting_dialog.rs diff --git a/rdraught-ui/src/lib.rs b/rdraught-gtk/src/lib.rs similarity index 100% rename from rdraught-ui/src/lib.rs rename to rdraught-gtk/src/lib.rs diff --git a/rdraught-ui/src/main.rs b/rdraught-gtk/src/main.rs similarity index 81% rename from rdraught-ui/src/main.rs rename to rdraught-gtk/src/main.rs index c9fc248..5c402ec 100644 --- a/rdraught-ui/src/main.rs +++ b/rdraught-gtk/src/main.rs @@ -3,5 +3,5 @@ use rdraught::draughts::DraughtsGame; fn main() -> ExitCode { let game = DraughtsGame::default(); - rdraught_ui::run(game) + rdraught_gtk::run(game) } diff --git a/rdraught-ui/src/rdraught_application.rs b/rdraught-gtk/src/rdraught_application.rs similarity index 100% rename from rdraught-ui/src/rdraught_application.rs rename to rdraught-gtk/src/rdraught_application.rs diff --git a/rdraught-ui/src/types.rs b/rdraught-gtk/src/types.rs similarity index 100% rename from rdraught-ui/src/types.rs rename to rdraught-gtk/src/types.rs diff --git a/rdraught/src/draughts.rs b/rdraught/src/draughts.rs index 15a2de3..095dbda 100644 --- a/rdraught/src/draughts.rs +++ b/rdraught/src/draughts.rs @@ -1,6 +1,6 @@ use super::circular_buffer::CircularBuffer; use super::constants::{BITS_PER_POSITION, POSITIONS, POSITIONS_PER_ROW}; -use super::movement::{Move, MoveDirection}; +use super::movement::{LoggedMove, Move, MoveDirection}; use super::position::Position; use crate::piece::Piece; use crate::player::Player; @@ -89,7 +89,7 @@ impl DraughtsBoard { } fn check_move_valid(&self, mv: &Move) -> Result<(), Error> { - let start = mv.start_position(); + let start = mv.get_start_position(); if mv.is_movement() { let mut move_is_possible = false; for possible_move in self.moves_for_piece(start, false) { @@ -121,11 +121,21 @@ impl DraughtsBoard { } } - fn undo_move(&mut self, mv: &Move) { + fn undo_move(&mut self, mv: &LoggedMove) { let end_pos = mv.get_end_position(); let p = self.get(end_pos); + //Revert promotion if any + let initial_piece = if mv.pawn_promoted() { + match p.player() { + Some(Player::White) => Piece::SimpleWhitePawn, + Some(Player::Red) => Piece::SimpleRedPawn, + None => Piece::NoPiece, + } + } else { + p + }; self.set(end_pos, Piece::NoPiece); - self.set(mv.start_position(), p); + self.set(mv.start_position(), initial_piece); if mv.is_capture() { let captured_position = (mv.start_position() + mv.get_end_position()) / (2, 2); let captured_piece = match p.player().unwrap() { @@ -148,24 +158,33 @@ impl DraughtsBoard { } } - fn apply_move(&mut self, mv: &Move) { + fn apply_move(&mut self, mv: &LoggedMove) { let start = mv.start_position(); let piece = self.get_piece(&start); + let final_piece = if mv.pawn_promoted() { + match piece.player() { + Some(Player::Red) => Piece::CrownedRedPawn, + Some(Player::White) => Piece::CrownedWhitePawn, + None => Piece::NoPiece, + } + } else { + piece + }; if mv.is_movement() { let end = mv.get_end_position(); self.set(start, Piece::NoPiece); - self.set(end, piece); + self.set(end, final_piece); } else { let end = mv.get_end_position(); let captured_pos = (start + end) / (2, 2); self.set(start, Piece::NoPiece); - self.set(end, piece); + self.set(end, final_piece); self.set(captured_pos, Piece::NoPiece); }; } fn check_and_apply_move(&mut self, mv: &Move) -> Result<(), Error> { - let start = mv.start_position(); + let start = mv.get_start_position(); let piece = self.get_piece(&start); if let Piece::NoPiece = piece { Err(Error::InvalidMove) @@ -383,17 +402,22 @@ impl DraughtsGame { } } - fn undo_move(&mut self, mv: &Move) { + fn undo_move(&mut self, mv: &LoggedMove) { self.board.undo_move(mv); - self.next_turn(); + if !mv.is_capture() || !mv.multi_capture() { + self.next_turn(); + } } - fn redo_move(&mut self, mv: &Move) { + + fn redo_move(&mut self, mv: &LoggedMove) { self.board.apply_move(mv); - self.next_turn(); + if !mv.is_capture() || !mv.multi_capture() { + self.next_turn(); + } } pub fn check_and_apply_move(&mut self, mv: &Move) -> Result<(), Error> { - let start = mv.start_position(); + let start = mv.get_start_position(); let piece = self.board.get_piece(&start); if let Some(player) = piece.player() { if mv.is_movement() { @@ -631,11 +655,7 @@ impl<'a> Iterator for MoveIterator<'a> { } _ => { if piece.player() != piece_at_next_position.player() { - let capture = Move::capture( - self.position, - direction, - piece_at_next_position.is_crowned(), - ); + let capture = Move::capture(self.position, direction); let next_position = capture.get_end_position(); if DraughtsBoard::is_position_valid(next_position) { let piece_at_end_position = self.board.get(next_position); @@ -654,7 +674,7 @@ impl<'a> Iterator for MoveIterator<'a> { pub struct RDraughtApplication { game: DraughtsGame, - moves: CircularBuffer, + moves: CircularBuffer, cursor: usize, } @@ -662,7 +682,7 @@ impl RDraughtApplication { pub fn new(game: DraughtsGame) -> RDraughtApplication { RDraughtApplication { game, - moves: CircularBuffer::::new(), + moves: CircularBuffer::::new(), cursor: 0usize, } } @@ -722,8 +742,40 @@ impl RDraughtApplication { self.moves.pop_back(); self.cursor -= 1; } + let initial_piece = self.game.piece_at(mv.get_start_position()); + let end_position = mv.get_end_position(); + let crowned_capture = if let Move::Capture { + start: _, + direction: _, + } = mv + { + self.game + .piece_at((mv.get_start_position() + end_position) / (2, 2)) + .is_crowned() + } else { + false + }; + let next_player_move = self.game.next_move; self.game.check_and_apply_move(&mv)?; - self.moves.push_evict(mv); + let final_piece = self.game.piece_at(end_position); + let is_pawn_promoted = + initial_piece != final_piece && !initial_piece.is_crowned() && final_piece.is_crowned(); + let logged_move = match mv { + Move::Movement { start, direction } => { + LoggedMove::movement(start, direction, is_pawn_promoted) + } + Move::Capture { start, direction } => { + let multi_capture = self.game.next_move == next_player_move; + LoggedMove::capture( + start, + direction, + is_pawn_promoted, + crowned_capture, + multi_capture, + ) + } + }; + self.moves.push_evict(logged_move); Ok(()) } @@ -750,7 +802,7 @@ mod std { #[cfg(feature = "std")] mod tests { extern crate std; - use crate::{Move, MoveDirection}; + use crate::{LoggedMove, MoveDirection}; use super::{DraughtsBoard, Piece, Position}; use std::collections::HashMap; @@ -774,10 +826,22 @@ mod tests { }); let moves = [ - Move::capture(Position::new(0, 4).unwrap(), MoveDirection::NW, false), - Move::movement(Position::new(0, 4).unwrap(), MoveDirection::NE), - Move::capture(Position::new(5, 5).unwrap(), MoveDirection::SW, false), - Move::movement(Position::new(4, 4).unwrap(), MoveDirection::NW), + LoggedMove::capture( + Position::new(0, 4).unwrap(), + MoveDirection::NW, + false, + false, + false, + ), + LoggedMove::movement(Position::new(0, 4).unwrap(), MoveDirection::NE, false), + LoggedMove::capture( + Position::new(5, 5).unwrap(), + MoveDirection::SW, + false, + false, + false, + ), + LoggedMove::movement(Position::new(4, 4).unwrap(), MoveDirection::NW, false), ]; for mv in moves { let mut board_clone = board.clone(); @@ -875,7 +939,7 @@ mod ai_tests { assert_eq!( Some(Move::movement( Position::new(6, 2).unwrap(), - MoveDirection::SW + MoveDirection::SW, )), best_move ); diff --git a/rdraught/src/lib.rs b/rdraught/src/lib.rs index 07c8ffd..1a7ec35 100644 --- a/rdraught/src/lib.rs +++ b/rdraught/src/lib.rs @@ -9,7 +9,7 @@ mod player; mod position; pub use draughts::{DraughtsBoard, DraughtsGame, Error, RDraughtApplication, RectangularBoard}; -pub use movement::{Move, MoveDirection}; +pub use movement::{LoggedMove, Move, MoveDirection}; pub use piece::Piece; pub use player::Player; pub use position::Position; diff --git a/rdraught/src/movement.rs b/rdraught/src/movement.rs index e21f7f5..95ce3c0 100644 --- a/rdraught/src/movement.rs +++ b/rdraught/src/movement.rs @@ -21,26 +21,75 @@ impl MoveDirection { } } +pub fn compute_end_position( + start_position: Position, + direction: MoveDirection, + is_capture: bool, +) -> Position { + if is_capture { + let direction = direction; + match direction { + MoveDirection::NE => start_position + (2, 2), + MoveDirection::SE => start_position + (-2, 2), + MoveDirection::SW => start_position + (-2, -2), + MoveDirection::NW => start_position + (2, -2), + } + } else { + match direction { + MoveDirection::NE => start_position + (1, 1), + MoveDirection::SE => start_position + (-1, 1), + MoveDirection::SW => start_position + (-1, -1), + MoveDirection::NW => start_position + (1, -1), + } + } +} + #[derive(Debug, PartialEq, Eq)] -pub struct Move { +pub struct LoggedMove { data: u16, } -impl Move { - pub fn movement(start: Position, dir: MoveDirection) -> Move { - let mut result = >::into(start) as u16; - result |= (dir as u16) << 5; - Move { data: result } +impl LoggedMove { + fn is_pawn_promoted( + piece: Piece, + start: Position, + dir: MoveDirection, + is_capture: bool, + ) -> bool { + let end_row = compute_end_position(start, dir, is_capture).row(); + !piece.is_crowned() && (Some(Player::White) == piece.player() && end_row == 7) + || (Some(Player::Red) == piece.player() && end_row == 0) } - pub fn capture(start: Position, dir: MoveDirection, crowned_capture: bool) -> Move { + pub fn movement(start: Position, dir: MoveDirection, is_pawn_promoted: bool) -> LoggedMove { + let mut result = >::into(start) as u16; + result |= (dir as u16) << 5; + if is_pawn_promoted { + result |= 1u16 << 8; + } + LoggedMove { data: result } + } + + pub fn capture( + start: Position, + dir: MoveDirection, + is_pawn_promoted: bool, + crowned_capture: bool, + multi_capture: bool, + ) -> LoggedMove { let mut result = >::into(start) as u16; result |= (dir as u16) << 5; result |= 1u16 << 7; - if crowned_capture { + if is_pawn_promoted { result |= 1u16 << 8; } - Move { data: result } + if crowned_capture { + result |= 1u16 << 9; + } + if multi_capture { + result |= 1u16 << 10; + } + LoggedMove { data: result } } pub fn start_position(&self) -> Position { @@ -59,63 +108,173 @@ impl Move { !self.is_capture() } - pub fn crowned_captured(&self) -> bool { + pub fn pawn_promoted(&self) -> bool { (self.data & 256) != 0 } + + pub fn crowned_captured(&self) -> bool { + (self.data & 512) != 0 + } + + pub fn multi_capture(&self) -> bool { + (self.data & 1024) != 0 + } + pub fn get_end_position(&self) -> Position { - if self.is_capture() { - let start = self.start_position(); - let direction = self.direction(); - match direction { - MoveDirection::NE => start + (2, 2), - MoveDirection::SE => start + (-2, 2), - MoveDirection::SW => start + (-2, -2), - MoveDirection::NW => start + (2, -2), - } - } else { - let start = self.start_position(); - let direction = self.direction(); - match direction { - MoveDirection::NE => start + (1, 1), - MoveDirection::SE => start + (-1, 1), - MoveDirection::SW => start + (-1, -1), - MoveDirection::NW => start + (1, -1), - } + compute_end_position( + self.start_position().clone(), + self.direction(), + self.is_capture(), + ) + } +} + +#[derive(Debug, PartialEq, Eq)] +pub enum Move { + Movement { + start: Position, + direction: MoveDirection, + }, + Capture { + start: Position, + direction: MoveDirection, + }, +} + +impl Move { + pub fn movement(start: Position, direction: MoveDirection) -> Move { + Move::Movement { start, direction } + } + pub fn capture(start: Position, direction: MoveDirection) -> Move { + Move::Capture { start, direction } + } + + pub fn is_movement(&self) -> bool { + match self { + Move::Movement { + start: _, + direction: _, + } => true, + Move::Capture { + start: _, + direction: _, + } => false, + } + } + + pub fn is_capture(&self) -> bool { + !self.is_movement() + } + + pub fn get_start_position(&self) -> Position { + match self { + Move::Movement { + start, + direction: _, + } => start.clone(), + Move::Capture { + start, + direction: _, + } => start.clone(), + } + } + + pub fn get_end_position(&self) -> Position { + compute_end_position( + self.get_start_position(), + self.get_direction(), + self.is_capture(), + ) + } + + pub fn get_direction(&self) -> MoveDirection { + match self { + Move::Movement { + start: _, + direction, + } => direction.clone(), + Move::Capture { + start: _, + direction, + } => direction.clone(), } } } #[cfg(test)] mod tests { - use super::{Move, MoveDirection}; + use super::{LoggedMove, MoveDirection, Piece}; use crate::Position; struct MoveData { + piece: Piece, start: Position, dir: MoveDirection, capture: bool, crowned_capture: bool, + pawn_promoted: bool, + multi_capture: bool, } #[test] fn test_from_into() { - let move_datas = [MoveData { - start: Position::new(0, 0).unwrap(), - dir: super::MoveDirection::NE, - capture: false, - crowned_capture: false, - }]; + let move_datas = [ + MoveData { + piece: Piece::SimpleRedPawn, + start: Position::new(0, 0).unwrap(), + dir: super::MoveDirection::NE, + capture: false, + crowned_capture: false, + pawn_promoted: false, + multi_capture: false, + }, + MoveData { + piece: Piece::SimpleWhitePawn, + start: Position::new(3, 5).unwrap(), + dir: super::MoveDirection::SW, + capture: false, + crowned_capture: false, + pawn_promoted: false, + multi_capture: false, + }, + MoveData { + piece: Piece::SimpleWhitePawn, + start: Position::new(6, 6).unwrap(), + dir: super::MoveDirection::NW, + capture: false, + crowned_capture: false, + pawn_promoted: true, + multi_capture: false, + }, + MoveData { + piece: Piece::SimpleWhitePawn, + start: Position::new(5, 5).unwrap(), + dir: super::MoveDirection::NE, + capture: true, + crowned_capture: true, + pawn_promoted: true, + multi_capture: true, + }, + ]; for md in move_datas { let mv = if md.capture { - Move::capture(md.start, md.dir, md.crowned_capture) + LoggedMove::capture( + md.start, + md.dir, + md.pawn_promoted, + md.crowned_capture, + md.multi_capture, + ) } else { - Move::movement(md.start, md.dir) + LoggedMove::movement(md.start, md.dir, md.pawn_promoted) }; assert_eq!(md.start, mv.start_position()); assert_eq!(md.dir, mv.direction()); assert_eq!(md.capture, mv.is_capture()); assert_eq!(md.crowned_capture, mv.crowned_captured()); + assert_eq!(md.pawn_promoted, mv.pawn_promoted()); + assert_eq!(md.multi_capture, mv.multi_capture()); } } }