2022-09-14 18:30:33 +02:00
|
|
|
use rand::RngCore;
|
|
|
|
|
2022-09-17 12:02:13 +02:00
|
|
|
use crate::data::{
|
|
|
|
BoatPosition, BoatsLayout, Coordinates, GameRules, MapCellContent, PrintableMap,
|
|
|
|
};
|
2022-09-15 20:13:06 +02:00
|
|
|
|
2022-09-17 12:02:13 +02:00
|
|
|
#[derive(Debug, serde::Serialize, serde::Deserialize, Clone)]
|
2022-09-15 20:13:06 +02:00
|
|
|
pub struct CurrentGameMapStatus {
|
|
|
|
pub boats: BoatsLayout,
|
|
|
|
pub successful_strikes: Vec<Coordinates>,
|
|
|
|
pub failed_strikes: Vec<Coordinates>,
|
|
|
|
pub sunk_boats: Vec<BoatPosition>,
|
|
|
|
}
|
|
|
|
|
2022-09-17 12:02:13 +02:00
|
|
|
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)]
|
2022-09-14 18:30:33 +02:00
|
|
|
pub struct CurrentGameStatus {
|
|
|
|
pub rules: GameRules,
|
2022-09-15 20:13:06 +02:00
|
|
|
pub your_map: CurrentGameMapStatus,
|
|
|
|
pub opponent_map: CurrentGameMapStatus,
|
2022-09-14 18:30:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
impl CurrentGameStatus {
|
2022-09-15 20:13:06 +02:00
|
|
|
/// 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)
|
2022-09-14 18:30:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/// 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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-09-17 12:02:13 +02:00
|
|
|
|
|
|
|
/// 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()
|
|
|
|
}
|
2022-09-14 18:30:33 +02:00
|
|
|
}
|