diff --git a/sea_battle_backend/src/data/current_game_status.rs b/sea_battle_backend/src/data/current_game_status.rs index 9d64d05..2e2aab6 100644 --- a/sea_battle_backend/src/data/current_game_status.rs +++ b/sea_battle_backend/src/data/current_game_status.rs @@ -241,6 +241,28 @@ impl CurrentGameStatus { >= smallest_boat } + /// Get the locations on the grid where the player could fire + pub fn get_relevant_grid_locations(&self) -> Vec { + let min_boat_size = self.get_size_of_smallest_un_sunk_boat().unwrap_or_default(); + let mut coordinates = vec![]; + + let mut y = 0; + while y < self.rules.map_height { + let mut x = (min_boat_size - 1 + y) % min_boat_size; + while x < self.rules.map_width { + let c = Coordinates::new(x as i32, y as i32); + if self.should_fire_at_location(c) { + coordinates.push(c); + } + + x += min_boat_size + } + y += 1; + } + + coordinates + } + pub fn get_your_map(&self) -> String { PrintableCurrentGameMapStatus(self.rules.clone(), self.your_map.clone()).get_map() } @@ -260,6 +282,8 @@ impl CurrentGameStatus { #[cfg(test)] mod test { + use std::fmt::Write as _; + use crate::data::*; #[test] @@ -443,4 +467,96 @@ mod test { assert!(status.can_fire_at_location(Coordinates::new(0, 0))); assert!(!status.should_fire_at_location(Coordinates::new(0, 0))); } + + fn print_coordinates_grid(status: &CurrentGameStatus, locs: &[Coordinates]) -> String { + let mut s = String::with_capacity(200); + s.push('\n'); + for y in 0..status.rules.map_height { + for x in 0..status.rules.map_width { + let c = Coordinates::new(x as i32, y as i32); + write!( + s, + "{}", + match locs.contains(&c) { + true => "X", + false => ".", + } + ) + .unwrap(); + } + + s.push('\n'); + } + s + } + + #[test] + fn relevant_grid_locations_new_game_min_boat_of_size_2() { + let _ = env_logger::builder().is_test(true).try_init(); + + let status = CurrentGameStatus::default(); + let locs = status.get_relevant_grid_locations(); + + log::debug!("{}", print_coordinates_grid(&status, &locs)); + + for y in 0..status.rules.map_height { + for x in 0..status.rules.map_width { + let c = Coordinates::new(x as i32, y as i32); + if (x + y) % 2 == 1 { + assert!(locs.contains(&c), "Missing {}", c.human_print()); + } else { + assert!( + !locs.contains(&c), + "Unwanted presence of {}", + c.human_print() + ); + } + } + } + } + + #[test] + fn relevant_grid_locations_new_game_min_boat_of_size_3() { + let _ = env_logger::builder().is_test(true).try_init(); + + let mut status = CurrentGameStatus::default(); + status.rules.set_boats_list(&vec![3, 4, 5]); + let locs = status.get_relevant_grid_locations(); + + log::debug!("{}", print_coordinates_grid(&status, &locs)); + + assert!(!locs.contains(&Coordinates::new(0, 0))); + assert!(!locs.contains(&Coordinates::new(1, 0))); + assert!(locs.contains(&Coordinates::new(2, 0))); + assert!(locs.contains(&Coordinates::new(0, 1))); + assert!(!locs.contains(&Coordinates::new(1, 1))); + assert!(!locs.contains(&Coordinates::new(2, 1))); + assert!(locs.contains(&Coordinates::new(3, 1))); + } + + #[test] + fn relevant_grid_locations_partial_play() { + let _ = env_logger::builder().is_test(true).try_init(); + + let mut status = CurrentGameStatus::default(); + status.rules.set_boats_list(&vec![3, 4, 5]); + status + .opponent_map + .failed_strikes + .push(Coordinates::new(0, 0)); + status + .opponent_map + .failed_strikes + .push(Coordinates::new(3, 0)); + status + .opponent_map + .failed_strikes + .push(Coordinates::new(2, 1)); + let locs = status.get_relevant_grid_locations(); + + log::debug!("{}", print_coordinates_grid(&status, &locs)); + + assert!(!locs.contains(&Coordinates::new(1, 0))); + assert!(!locs.contains(&Coordinates::new(2, 0))); + } } diff --git a/sea_battle_backend/src/data/game_rules.rs b/sea_battle_backend/src/data/game_rules.rs index bf42d65..9198f9a 100644 --- a/sea_battle_backend/src/data/game_rules.rs +++ b/sea_battle_backend/src/data/game_rules.rs @@ -61,7 +61,7 @@ impl GameRules { } /// Remove last boat - pub fn pop_boat(&mut self) -> usize { + pub fn remove_last_boat(&mut self) -> usize { let list = self.boats_list(); self.set_boats_list(&list[0..list.len() - 1]); *list.last().unwrap() diff --git a/sea_battle_backend/src/test/bot_client_bot_random_play.rs b/sea_battle_backend/src/test/bot_client_bot_random_play.rs index 991da2c..ffb9214 100644 --- a/sea_battle_backend/src/test/bot_client_bot_random_play.rs +++ b/sea_battle_backend/src/test/bot_client_bot_random_play.rs @@ -106,7 +106,7 @@ async fn invalid_boats_layout_number_of_boats() { wait_for_port(TestPort::RandomBotInvalidBoatsLayoutNumberOfBoats.port()).await; let mut rules_modified = rules.clone(); - rules_modified.pop_boat(); + rules_modified.remove_last_boat(); let layout = BoatsLayout::gen_random_for_rules(&rules_modified).unwrap(); let res = bot_client::BotClient::new( @@ -138,7 +138,7 @@ async fn invalid_boats_layout_len_of_a_boat() { wait_for_port(TestPort::RandomBotInvalidBoatsLayoutLenOfABoat.port()).await; let mut rules_modified = rules.clone(); - let previous = rules_modified.pop_boat(); + let previous = rules_modified.remove_last_boat(); rules_modified.add_boat(previous - 1); let layout = BoatsLayout::gen_random_for_rules(&rules_modified).unwrap();