use rand::RngCore; use crate::data::{ BoatPosition, BoatsLayout, Coordinates, GameRules, MapCellContent, PrintableMap, }; #[derive(Debug, serde::Serialize, serde::Deserialize, Clone)] pub struct CurrentGameMapStatus { pub boats: BoatsLayout, pub successful_strikes: Vec, pub failed_strikes: Vec, pub sunk_boats: Vec, } impl CurrentGameMapStatus { pub fn did_fire_at_location(&self, c: Coordinates) -> bool { self.successful_strikes.contains(&c) || self.failed_strikes.contains(&c) } } struct PrintableCurrentGameMapStatus(GameRules, CurrentGameMapStatus); impl PrintableMap for PrintableCurrentGameMapStatus { fn map_cell_content(&self, c: Coordinates) -> MapCellContent { if !c.is_valid(&self.0) { return MapCellContent::Invalid; } if self.1.failed_strikes.contains(&c) { return MapCellContent::FailedStrike; } if self .1 .sunk_boats .iter() .any(|b| b.all_coordinates().contains(&c)) { return MapCellContent::SunkBoat; } if self.1.successful_strikes.contains(&c) { return MapCellContent::TouchedBoat; } if self.1.boats.find_boat_at_position(c).is_some() { return MapCellContent::Boat; } MapCellContent::Nothing } } #[derive(Debug, serde::Serialize, serde::Deserialize, Clone)] pub struct CurrentGameStatus { pub rules: GameRules, pub your_map: CurrentGameMapStatus, pub opponent_map: CurrentGameMapStatus, } impl CurrentGameStatus { /// Check if opponent can fire at a given location pub fn can_fire_at_location(&self, location: Coordinates) -> bool { !self.opponent_map.successful_strikes.contains(&location) && !self.opponent_map.failed_strikes.contains(&location) } /// Find valid random fire location. Loop until one is found pub fn find_valid_random_fire_location(&self) -> Coordinates { let mut rng = rand::thread_rng(); loop { let coordinates = Coordinates::new( (rng.next_u32() % self.rules.map_width as u32) as i32, (rng.next_u32() % self.rules.map_height as u32) as i32, ); if coordinates.is_valid(&self.rules) && self.can_fire_at_location(coordinates) { return coordinates; } } } /// Find valid linear fire location. Loop until one is found pub fn find_first_valid_fire_location(&self) -> Coordinates { for y in 0..self.rules.map_height { for x in 0..self.rules.map_width { let coordinates = Coordinates::new(x as i32, y as i32); if self.can_fire_at_location(coordinates) { return coordinates; } } } panic!("Could not find fire location!") } pub fn print_your_map(&self) { PrintableCurrentGameMapStatus(self.rules.clone(), self.your_map.clone()).print_map() } pub fn print_opponent_map(&self) { PrintableCurrentGameMapStatus(self.rules.clone(), self.opponent_map.clone()).print_map() } }