Can play full game
This commit is contained in:
@@ -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)]
|
||||
|
||||
20
sea_battle_backend/src/data/end_game_map.rs
Normal file
20
sea_battle_backend/src/data/end_game_map.rs
Normal 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
|
||||
}
|
||||
}
|
||||
@@ -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<_>>(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user