Can play full game

This commit is contained in:
2022-09-15 17:37:26 +02:00
parent 5b7a56d060
commit 2723724589
13 changed files with 339 additions and 22 deletions

View File

@@ -2,9 +2,10 @@ use std::io::ErrorKind;
use rand::{Rng, RngCore};
use crate::consts::ALPHABET;
use crate::data::GameRules;
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Copy)]
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Copy, Eq, PartialEq)]
pub enum BoatDirection {
Left,
Right,
@@ -95,9 +96,20 @@ impl Coordinates {
&& self.x < rules.map_width as i32
&& self.y < rules.map_height as i32
}
pub fn human_print(&self) -> String {
format!(
"{}:{}",
match self.y < 0 || self.y >= ALPHABET.len() as i32 {
true => self.y.to_string(),
false => ALPHABET.chars().nth(self.y as usize).unwrap().to_string(),
},
self.x
)
}
}
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Copy)]
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Copy, Eq, PartialEq)]
pub struct BoatPosition {
start: Coordinates,
len: usize,
@@ -256,6 +268,10 @@ impl BoatsLayout {
pub fn find_boat_at_position(&self, pos: Coordinates) -> Option<&BoatPosition> {
self.0.iter().find(|f| f.all_coordinates().contains(&pos))
}
pub fn number_of_boats(&self) -> usize {
self.0.len()
}
}
#[cfg(test)]

View File

@@ -0,0 +1,20 @@
use crate::data::{BoatsLayout, MapCellContent};
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct EndGameMap {
pub boats: BoatsLayout,
pub grid: Vec<Vec<MapCellContent>>,
}
impl EndGameMap {
pub fn get_map(&self) -> String {
let mut s = String::new();
for row in &self.grid {
for col in row {
s.push_str(&format!("{} ", col.letter()));
}
s.push('\n');
}
s
}
}

View File

@@ -1,22 +1,34 @@
use crate::data::boats_layout::{BoatsLayout, Coordinates};
use crate::data::GameRules;
use crate::data::{BoatPosition, EndGameMap, GameRules};
#[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)]
pub enum MapCellContent {
Invalid,
Nothing,
TouchedBoat,
SunkBoat,
Boat,
FailedStrike,
}
impl MapCellContent {
fn letter(&self) -> &'static str {
pub fn letter(&self) -> &'static str {
match self {
MapCellContent::Invalid => "!",
MapCellContent::Nothing => ".",
MapCellContent::TouchedBoat => "T",
MapCellContent::SunkBoat => "S",
MapCellContent::Boat => "B",
MapCellContent::FailedStrike => "X",
MapCellContent::FailedStrike => "x",
}
}
}
@@ -24,6 +36,9 @@ impl MapCellContent {
pub struct GameMap {
rules: GameRules,
boats_config: BoatsLayout,
failed_strikes: Vec<Coordinates>,
successful_strikes: Vec<Coordinates>,
sunk_boats: Vec<BoatPosition>,
}
impl GameMap {
@@ -31,19 +46,80 @@ impl GameMap {
Self {
rules,
boats_config,
failed_strikes: vec![],
successful_strikes: vec![],
sunk_boats: vec![],
}
}
pub fn get_cell_content(&self, c: Coordinates) -> MapCellContent {
//TODO : improve this
if !c.is_valid(&self.rules) {
return MapCellContent::Invalid;
}
if self.boats_config.find_boat_at_position(c).is_some() {
return MapCellContent::Boat;
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;
}
if self.sunk_boats.contains(b) {
return MapCellContent::SunkBoat;
}
return MapCellContent::TouchedBoat;
}
MapCellContent::Nothing
}
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()
}
pub fn print_map(&self) {
for y in 0..self.rules.map_height {
for x in 0..self.rules.map_width {
@@ -56,4 +132,17 @@ impl GameMap {
println!();
}
}
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<_>>(),
}
}
}

View File

@@ -1,11 +1,13 @@
pub use boats_layout::*;
pub use current_game_status::*;
pub use end_game_map::*;
pub use game_map::*;
pub use game_rules::*;
pub use play_config::*;
mod boats_layout;
mod current_game_status;
mod end_game_map;
mod game_map;
mod game_rules;
mod play_config;

View File

@@ -23,6 +23,7 @@ pub struct PlayConfiguration {
pub min_boats_number: usize,
pub max_boats_number: usize,
pub bot_types: Vec<BotDescription>,
pub ordinate_alphabet: &'static str,
}
impl Default for PlayConfiguration {
@@ -40,6 +41,7 @@ impl Default for PlayConfiguration {
r#type: BotType::Random,
description: "Random strike. All the time.".to_string(),
}],
ordinate_alphabet: ALPHABET,
}
}
}