diff --git a/src/logic/board.rs b/src/logic/board.rs index 0a43866..a83e77d 100644 --- a/src/logic/board.rs +++ b/src/logic/board.rs @@ -121,7 +121,7 @@ impl Board { pos.is_in_range(self.size) && self[pos].pawn.is_none() } - fn is_movable(&self, pos: Position, player: &Player) -> bool { + fn is_pawn_movable(&self, pos: Position, player: &Player) -> bool { self.get_available_moves(pos, player) .is_some_and(|moves| !moves.is_empty()) } @@ -221,6 +221,7 @@ impl Board { fn find_captures( &self, + start_pos: Position, current_pos: Position, current_path: &mut Vec, captured_enemies: &Vec, @@ -252,6 +253,7 @@ impl Board { new_enemies.push(target); self.find_captures( + start_pos, path_after_capture, &mut new_path, &new_enemies, @@ -264,14 +266,20 @@ impl Board { } } - if !found_any_capture && !current_path.is_empty() { + if !found_any_capture && !current_path.is_empty() && !captured_enemies.is_empty() { all_paths.push(CapturePath { + from: start_pos, steps: current_path.to_vec(), + captured_enemies: captured_enemies.to_vec(), }); } } - fn get_available_captures(&self, from: Position, player: &Player) -> Option> { + pub fn get_available_captures( + &self, + from: Position, + player: &Player, + ) -> Option> { let Some(capturing_pawn) = &self[from].pawn else { return None; }; @@ -303,6 +311,7 @@ impl Board { }; self.find_captures( + from, from, &mut vec![], &vec![], @@ -315,6 +324,20 @@ impl Board { Some(available_captures) } + pub fn capture(&mut self, path: &CapturePath, capturing_player: &Player) { + /* + debug!("Capturing: {}", path); + */ + + let mut from = path.from; + + for (next_pos, enemy_path) in path.iter() { + self[*enemy_path].pawn.take(); + self.move_pawn_anywhere(capturing_player, from, *next_pos); + from = *next_pos; + } + } + pub fn get_player_pawns_positions(&self, player: &Player) -> Vec { self.board .iter() @@ -352,13 +375,55 @@ impl Board { }; // we can safely unwrap, because if there's not pawn function returns false earlier - let mut mowing_pawn: Pawn = self[from].pawn.take().unwrap(); + let mut moving_pawn: Pawn = self[from].pawn.take().unwrap(); if to.row == end_row { - mowing_pawn.state = super::pawn::PawnState::Dame; + moving_pawn.state = super::pawn::PawnState::Dame; } - self[to.row][to.column].pawn = Some(mowing_pawn); + self[to.row][to.column].pawn = Some(moving_pawn); + + true + } + + fn move_pawn_anywhere(&mut self, player: &Player, from: Position, to: Position) -> bool { + let Some(pawn) = &self[from].pawn else { + warn!( + "Tried moving pawn from: {}, but there is no pawn there!", + from + ); + return false; + }; + + if self[to].pawn.is_some() { + warn!( + "Tried moving pawn from {} to {}, but there was a pawn at the destination!", + from, to + ); + return false; + } + + if !self.is_player_owner_of_the_pawn(player, &pawn) { + warn!("Player {} tried moving a pawn which he doesn own!", player); + return false; + } + + let Some(end_row) = player.end_row else { + warn!( + "Tried moving pawn of player {}, who wasn't initialized by the board!", + player.id + ); + return false; + }; + + let mut moving_pawn: Pawn = self[from].pawn.take().unwrap(); + + if to.row == end_row { + moving_pawn.state = super::pawn::PawnState::Dame; + } + + debug!("Placing pawn from {} to {}", from, to); + self[to].pawn = Some(moving_pawn); true } @@ -560,15 +625,15 @@ mod tests { let mut p2: Player = Player::new("Milo".to_string()); let test_board: Board = Board::new(&mut p1, &mut p2, None); - assert!(test_board.is_movable(Position { row: 2, column: 1 }, &p1)); - assert!(!test_board.is_movable( + assert!(test_board.is_pawn_movable(Position { row: 2, column: 1 }, &p1)); + assert!(!test_board.is_pawn_movable( Position { row: 10, column: 12 }, &p1 )); - assert!(!test_board.is_movable(Position { row: 0, column: 0 }, &p1)); + assert!(!test_board.is_pawn_movable(Position { row: 0, column: 0 }, &p1)); } #[test] @@ -577,8 +642,8 @@ mod tests { let mut p1 = Player::new("Morbius".to_string()); let mut p2 = Player::new("Milo".to_string()); - // - let mut board = Board::new_empty(&mut p1, &mut p2, Some(8)); + + let mut board = Board::new_empty(&mut p1, &mut p2, None); let start_pos = Position { row: 2, column: 2 }; @@ -602,4 +667,35 @@ mod tests { assert_eq!(paths[0].steps, expected_path,); } + + #[test] + fn test_capturing() { + init_logger(); + + let mut p1 = Player::new("Morbius".to_string()); + let mut p2 = Player::new("Milo".to_string()); + + let mut board = Board::new_empty(&mut p1, &mut p2, None); + + let start_pos = Position { row: 2, column: 2 }; + + board.place_pawn(start_pos, &p1); + + board.place_pawn(Position { row: 3, column: 3 }, &p2); + board.place_pawn(Position { row: 5, column: 3 }, &p2); + + let captures_result = board.get_available_captures(start_pos, &p1); + let path = &captures_result.unwrap()[0]; + + board.capture(path, &p1); + + assert!( + board[6][2] + .pawn + .as_ref() + .is_some_and(|pawn| pawn.owner == p1.id) + ); + assert!(board[3][3].pawn.is_none()); + assert!(board[5][3].pawn.is_none()); + } } diff --git a/src/logic/pawn.rs b/src/logic/pawn.rs index e09d98d..9693334 100644 --- a/src/logic/pawn.rs +++ b/src/logic/pawn.rs @@ -1,3 +1,5 @@ +use core::fmt; + use uuid::Uuid; use crate::logic::{math::position::Position, player::Player}; @@ -26,5 +28,15 @@ impl Pawn { } pub struct CapturePath { + pub from: Position, pub steps: Vec, + pub captured_enemies: Vec, +} + +impl CapturePath { + pub fn iter( + &self, + ) -> std::iter::Zip, std::slice::Iter<'_, Position>> { + self.steps.iter().zip(self.captured_enemies.iter()) + } }