2022-09-13 17:25:14 +02:00
|
|
|
use crate::data::boats_layout::{BoatsLayout, Coordinates};
|
2022-09-15 20:13:06 +02:00
|
|
|
use crate::data::{BoatPosition, CurrentGameMapStatus, EndGameMap, GameRules};
|
2022-09-12 16:38:14 +02:00
|
|
|
|
2022-09-15 17:37:26 +02:00
|
|
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
|
|
|
|
pub enum FireResult {
|
|
|
|
Missed,
|
|
|
|
Hit,
|
|
|
|
Sunk,
|
|
|
|
Rejected,
|
|
|
|
AlreadyTargetedPosition,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
|
2022-09-13 19:02:26 +02:00
|
|
|
pub enum MapCellContent {
|
2022-09-12 16:38:14 +02:00
|
|
|
Invalid,
|
|
|
|
Nothing,
|
|
|
|
TouchedBoat,
|
2022-09-15 17:37:26 +02:00
|
|
|
SunkBoat,
|
2022-09-12 16:38:14 +02:00
|
|
|
Boat,
|
|
|
|
FailedStrike,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl MapCellContent {
|
2022-09-15 17:37:26 +02:00
|
|
|
pub fn letter(&self) -> &'static str {
|
2022-09-12 16:38:14 +02:00
|
|
|
match self {
|
|
|
|
MapCellContent::Invalid => "!",
|
2022-09-13 17:25:14 +02:00
|
|
|
MapCellContent::Nothing => ".",
|
2022-09-12 16:38:14 +02:00
|
|
|
MapCellContent::TouchedBoat => "T",
|
2022-09-15 17:37:26 +02:00
|
|
|
MapCellContent::SunkBoat => "S",
|
2022-09-12 16:38:14 +02:00
|
|
|
MapCellContent::Boat => "B",
|
2022-09-15 17:37:26 +02:00
|
|
|
MapCellContent::FailedStrike => "x",
|
2022-09-12 16:38:14 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct GameMap {
|
|
|
|
rules: GameRules,
|
|
|
|
boats_config: BoatsLayout,
|
2022-09-15 17:37:26 +02:00
|
|
|
failed_strikes: Vec<Coordinates>,
|
|
|
|
successful_strikes: Vec<Coordinates>,
|
|
|
|
sunk_boats: Vec<BoatPosition>,
|
2022-09-12 16:38:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
impl GameMap {
|
|
|
|
pub fn new(rules: GameRules, boats_config: BoatsLayout) -> Self {
|
|
|
|
Self {
|
|
|
|
rules,
|
|
|
|
boats_config,
|
2022-09-15 17:37:26 +02:00
|
|
|
failed_strikes: vec![],
|
|
|
|
successful_strikes: vec![],
|
|
|
|
sunk_boats: vec![],
|
2022-09-12 16:38:14 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-13 17:25:14 +02:00
|
|
|
pub fn get_cell_content(&self, c: Coordinates) -> MapCellContent {
|
2022-09-15 17:37:26 +02:00
|
|
|
if !c.is_valid(&self.rules) {
|
|
|
|
return MapCellContent::Invalid;
|
|
|
|
}
|
|
|
|
|
|
|
|
if self.failed_strikes.contains(&c) {
|
|
|
|
return MapCellContent::FailedStrike;
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(b) = self.boats_config.find_boat_at_position(c) {
|
|
|
|
if !self.successful_strikes.contains(&c) {
|
|
|
|
return MapCellContent::Boat;
|
|
|
|
}
|
2022-09-13 17:25:14 +02:00
|
|
|
|
2022-09-15 17:37:26 +02:00
|
|
|
if self.sunk_boats.contains(b) {
|
|
|
|
return MapCellContent::SunkBoat;
|
|
|
|
}
|
|
|
|
|
|
|
|
return MapCellContent::TouchedBoat;
|
2022-09-13 17:25:14 +02:00
|
|
|
}
|
|
|
|
|
2022-09-13 19:02:26 +02:00
|
|
|
MapCellContent::Nothing
|
2022-09-12 16:38:14 +02:00
|
|
|
}
|
|
|
|
|
2022-09-15 17:37:26 +02:00
|
|
|
pub fn fire(&mut self, c: Coordinates) -> FireResult {
|
|
|
|
if !c.is_valid(&self.rules) {
|
|
|
|
return FireResult::Rejected;
|
|
|
|
}
|
|
|
|
|
|
|
|
if self.failed_strikes.contains(&c) || self.successful_strikes.contains(&c) {
|
|
|
|
return FireResult::AlreadyTargetedPosition;
|
|
|
|
}
|
|
|
|
|
|
|
|
match self.boats_config.find_boat_at_position(c) {
|
|
|
|
None => {
|
|
|
|
self.failed_strikes.push(c);
|
|
|
|
FireResult::Missed
|
|
|
|
}
|
|
|
|
Some(b) => {
|
|
|
|
self.successful_strikes.push(c);
|
|
|
|
|
|
|
|
if !b
|
|
|
|
.all_coordinates()
|
|
|
|
.iter()
|
|
|
|
.all(|c| self.successful_strikes.contains(c))
|
|
|
|
{
|
|
|
|
return FireResult::Hit;
|
|
|
|
}
|
|
|
|
|
|
|
|
self.sunk_boats.push(*b);
|
|
|
|
|
|
|
|
if !self.rules.boats_can_touch {
|
|
|
|
for c in b.neighbor_coordinates(&self.rules) {
|
|
|
|
if !self.failed_strikes.contains(&c) {
|
|
|
|
self.failed_strikes.push(c);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
FireResult::Sunk
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn are_all_boat_sunk(&self) -> bool {
|
|
|
|
self.sunk_boats.len() == self.boats_config.number_of_boats()
|
|
|
|
}
|
|
|
|
|
2022-09-12 16:38:14 +02:00
|
|
|
pub fn print_map(&self) {
|
|
|
|
for y in 0..self.rules.map_height {
|
|
|
|
for x in 0..self.rules.map_width {
|
2022-09-13 17:25:14 +02:00
|
|
|
print!(
|
|
|
|
"{} ",
|
|
|
|
self.get_cell_content(Coordinates::new(x as i32, y as i32))
|
|
|
|
.letter()
|
|
|
|
);
|
2022-09-12 16:38:14 +02:00
|
|
|
}
|
|
|
|
println!();
|
|
|
|
}
|
|
|
|
}
|
2022-09-15 17:37:26 +02:00
|
|
|
|
2022-09-15 20:13:06 +02:00
|
|
|
pub fn current_map_status(&self, for_opponent: bool) -> CurrentGameMapStatus {
|
|
|
|
CurrentGameMapStatus {
|
|
|
|
boats: match for_opponent {
|
|
|
|
true => BoatsLayout::new_invalid(),
|
|
|
|
false => self.boats_config.clone(),
|
|
|
|
},
|
|
|
|
successful_strikes: self.successful_strikes.clone(),
|
|
|
|
failed_strikes: self.failed_strikes.clone(),
|
|
|
|
sunk_boats: self.sunk_boats.clone(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-15 17:37:26 +02:00
|
|
|
pub fn final_map(&self) -> EndGameMap {
|
|
|
|
EndGameMap {
|
|
|
|
boats: self.boats_config.clone(),
|
|
|
|
grid: (0..self.rules.map_height)
|
|
|
|
.map(|y| {
|
|
|
|
(0..self.rules.map_width)
|
|
|
|
.map(|x| self.get_cell_content(Coordinates::new(x as i32, y as i32)))
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
})
|
|
|
|
.collect::<Vec<_>>(),
|
|
|
|
}
|
|
|
|
}
|
2022-09-12 16:38:14 +02:00
|
|
|
}
|