fixed undo/redo bugs
renamed rdraught-ui to rdraught-gtk
This commit is contained in:
@@ -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]
|
||||
|
@@ -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"
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
@@ -3,5 +3,5 @@ use rdraught::draughts::DraughtsGame;
|
||||
|
||||
fn main() -> ExitCode {
|
||||
let game = DraughtsGame::default();
|
||||
rdraught_ui::run(game)
|
||||
rdraught_gtk::run(game)
|
||||
}
|
@@ -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<Move, 128>,
|
||||
moves: CircularBuffer<LoggedMove, 128>,
|
||||
cursor: usize,
|
||||
}
|
||||
|
||||
@@ -662,7 +682,7 @@ impl RDraughtApplication {
|
||||
pub fn new(game: DraughtsGame) -> RDraughtApplication {
|
||||
RDraughtApplication {
|
||||
game,
|
||||
moves: CircularBuffer::<Move, 128>::new(),
|
||||
moves: CircularBuffer::<LoggedMove, 128>::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
|
||||
);
|
||||
|
@@ -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;
|
||||
|
@@ -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 = <Position as Into<u8>>::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 = <Position as Into<u8>>::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 = <Position as Into<u8>>::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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user