From d3d5feda4f94eff1829c392710d61aa59081b5b2 Mon Sep 17 00:00:00 2001 From: Pierre Hubert Date: Thu, 15 Sep 2022 18:04:22 +0200 Subject: [PATCH] Ready to implement boats layout check --- sea_battle_backend/src/bot_client.rs | 26 +++++++++--- sea_battle_backend/src/data/boats_layout.rs | 6 +++ .../src/data/current_game_status.rs | 4 +- sea_battle_backend/src/data/game_rules.rs | 2 +- sea_battle_backend/src/data/play_config.rs | 2 +- sea_battle_backend/src/game.rs | 11 +++++ sea_battle_backend/src/human_player.rs | 7 +++- sea_battle_backend/src/human_player_ws.rs | 6 ++- sea_battle_backend/src/random_bot.rs | 4 ++ sea_battle_backend/src/test/client.rs | 40 ++++++++++++++----- 10 files changed, 85 insertions(+), 23 deletions(-) diff --git a/sea_battle_backend/src/bot_client.rs b/sea_battle_backend/src/bot_client.rs index 9449468..c9e4411 100644 --- a/sea_battle_backend/src/bot_client.rs +++ b/sea_battle_backend/src/bot_client.rs @@ -6,11 +6,21 @@ use tokio_tungstenite::tungstenite::Message; use crate::data::{BoatsLayout, GameRules}; use crate::human_player_ws::{ClientMessage, ServerMessage}; -pub async fn run_client(server: &str, rules: &GameRules) -> Result<(), Box> { +#[derive(Debug, Eq, PartialEq)] +pub enum ClientEndResult { + Finished, + InvalidLayout, +} + +pub async fn run_client( + server: &str, + requested_rules: &GameRules, + layout: BoatsLayout, +) -> Result> { let url = format!( "{}/play/bot?{}", server.replace("http", "ws"), - serde_urlencoded::to_string(rules).unwrap() + serde_urlencoded::to_string(requested_rules).unwrap() ); log::debug!("Connecting to {}...", url); let (mut socket, _) = match tokio_tungstenite::connect_async(url).await { @@ -54,11 +64,13 @@ pub async fn run_client(server: &str, rules: &GameRules) -> Result<(), Box log::debug!("Waiting for other player..."), ServerMessage::QueryBoatsLayout { rules } => { + assert_eq!(&rules, requested_rules); log::debug!("Server requested boats layout"); - let layout = BoatsLayout::gen_random_for_rules(&rules)?; socket .send(Message::Text(serde_json::to_string( - &ClientMessage::BoatsLayout { layout }, + &ClientMessage::BoatsLayout { + layout: layout.clone(), + }, )?)) .await?; } @@ -103,8 +115,12 @@ pub async fn run_client(server: &str, rules: &GameRules) -> Result<(), Box { + log::warn!("Rejected boat layout: {:?}", errors); + return Ok(ClientEndResult::InvalidLayout); + } } } - Ok(()) + Ok(ClientEndResult::Finished) } diff --git a/sea_battle_backend/src/data/boats_layout.rs b/sea_battle_backend/src/data/boats_layout.rs index 550173a..d94030a 100644 --- a/sea_battle_backend/src/data/boats_layout.rs +++ b/sea_battle_backend/src/data/boats_layout.rs @@ -272,6 +272,12 @@ impl BoatsLayout { pub fn number_of_boats(&self) -> usize { self.0.len() } + + /// Check for layout invalid configuration + pub fn layouts_errors(&self, _rules: &GameRules) -> Vec<&'static str> { + //TODO : implement + vec![] + } } #[cfg(test)] diff --git a/sea_battle_backend/src/data/current_game_status.rs b/sea_battle_backend/src/data/current_game_status.rs index 8779852..3d570df 100644 --- a/sea_battle_backend/src/data/current_game_status.rs +++ b/sea_battle_backend/src/data/current_game_status.rs @@ -4,13 +4,13 @@ use rand::RngCore; #[derive(Debug, serde::Serialize, serde::Deserialize)] pub struct CurrentGameStatus { pub rules: GameRules, - // TODO + //TODO } impl CurrentGameStatus { /// Check if user can fire at a given location pub fn can_fire_at_location(&self, _location: Coordinates) -> bool { - // TODO + //TODO true } diff --git a/sea_battle_backend/src/data/game_rules.rs b/sea_battle_backend/src/data/game_rules.rs index 64d9a1e..4edbac9 100644 --- a/sea_battle_backend/src/data/game_rules.rs +++ b/sea_battle_backend/src/data/game_rules.rs @@ -1,7 +1,7 @@ use crate::consts::*; use crate::data::{BotType, PlayConfiguration}; -#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] +#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Eq, PartialEq)] pub struct GameRules { pub map_width: usize, pub map_height: usize, diff --git a/sea_battle_backend/src/data/play_config.rs b/sea_battle_backend/src/data/play_config.rs index 0edd806..cc4ee41 100644 --- a/sea_battle_backend/src/data/play_config.rs +++ b/sea_battle_backend/src/data/play_config.rs @@ -1,7 +1,7 @@ use crate::consts::*; /// Specifies the kind of boat to use -#[derive(serde::Serialize, serde::Deserialize, Debug, Copy, Clone)] +#[derive(serde::Serialize, serde::Deserialize, Debug, Copy, Clone, Eq, PartialEq)] pub enum BotType { Random, } diff --git a/sea_battle_backend/src/game.rs b/sea_battle_backend/src/game.rs index 481e637..b4015ab 100644 --- a/sea_battle_backend/src/game.rs +++ b/sea_battle_backend/src/game.rs @@ -13,6 +13,8 @@ pub trait Player { fn query_boats_layout(&self, rules: &GameRules); + fn rejected_boats_layout(&self, errors: Vec<&'static str>); + fn notify_other_player_ready(&self); fn notify_game_starting(&self); @@ -205,6 +207,15 @@ impl Handler for Game { } let player_index = self.player_id_by_uuid(msg.0); + + let errors = msg.1.layouts_errors(&self.rules); + if !errors.is_empty() { + log::error!("Got invalid boats layou!"); + self.players[player_index].rejected_boats_layout(errors); + self.players[player_index].query_boats_layout(&self.rules); + return; + } + log::debug!("Got boat disposition for player {}", player_index); match player_index { 0 => self.map_0 = Some(GameMap::new(self.rules.clone(), msg.1)), diff --git a/sea_battle_backend/src/human_player.rs b/sea_battle_backend/src/human_player.rs index c17e210..d0f244f 100644 --- a/sea_battle_backend/src/human_player.rs +++ b/sea_battle_backend/src/human_player.rs @@ -27,6 +27,12 @@ impl Player for HumanPlayer { }); } + fn rejected_boats_layout(&self, errors: Vec<&'static str>) { + self.player.do_send(ServerMessage::RejectedBoatsLayout { + errors: errors.iter().map(|s| s.to_string()).collect(), + }); + } + fn notify_other_player_ready(&self) { self.player.do_send(ServerMessage::OtherPlayerReady); } @@ -80,7 +86,6 @@ impl HumanPlayer { // TODO : do something } ClientMessage::BoatsLayout { layout } => { - // TODO : check boat layout validity self.game.do_send(SetBoatsLayout(self.uuid, layout)) } ClientMessage::Fire { location } => self.game.do_send(Fire(self.uuid, location)), diff --git a/sea_battle_backend/src/human_player_ws.rs b/sea_battle_backend/src/human_player_ws.rs index 9a7de2c..70e9250 100644 --- a/sea_battle_backend/src/human_player_ws.rs +++ b/sea_battle_backend/src/human_player_ws.rs @@ -1,8 +1,8 @@ use std::sync::Arc; use std::time::{Duration, Instant}; -use actix::{Actor, Handler, StreamHandler}; use actix::prelude::*; +use actix::{Actor, Handler, StreamHandler}; use actix_web_actors::ws; use actix_web_actors::ws::{CloseCode, CloseReason, Message, ProtocolError, WebsocketContext}; use uuid::Uuid; @@ -44,6 +44,9 @@ pub enum ServerMessage { QueryBoatsLayout { rules: GameRules, }, + RejectedBoatsLayout { + errors: Vec, + }, WaitingForOtherPlayerConfiguration, OtherPlayerReady, GameStarting, @@ -87,7 +90,6 @@ impl Default for HumanPlayerWS { } } - impl HumanPlayerWS { /// helper method that sends ping to client every second. /// diff --git a/sea_battle_backend/src/random_bot.rs b/sea_battle_backend/src/random_bot.rs index 57d7c77..9b000e3 100644 --- a/sea_battle_backend/src/random_bot.rs +++ b/sea_battle_backend/src/random_bot.rs @@ -39,6 +39,10 @@ impl Player for RandomBot { } } + fn rejected_boats_layout(&self, _errors: Vec<&'static str>) { + unreachable!() + } + fn notify_other_player_ready(&self) {} fn notify_game_starting(&self) {} diff --git a/sea_battle_backend/src/test/client.rs b/sea_battle_backend/src/test/client.rs index d12a6c8..0489914 100644 --- a/sea_battle_backend/src/test/client.rs +++ b/sea_battle_backend/src/test/client.rs @@ -2,7 +2,8 @@ use tokio::task; use crate::args::Args; use crate::bot_client; -use crate::data::GameRules; +use crate::bot_client::ClientEndResult; +use crate::data::{BoatsLayout, GameRules}; use crate::server::start_server; use crate::test::network_utils::wait_for_port; use crate::test::TestPort; @@ -11,9 +12,11 @@ use crate::test::TestPort; async fn invalid_port() { let _ = env_logger::builder().is_test(true).try_init(); + let rules = GameRules::random_players_rules(); bot_client::run_client( &TestPort::ClientInvalidPort.as_url(), - &GameRules::random_players_rules(), + &rules, + BoatsLayout::gen_random_for_rules(&rules).unwrap(), ) .await .unwrap_err(); @@ -32,9 +35,13 @@ async fn invalid_rules() { task::spawn_local(start_server(Args::for_test(TestPort::ClientInvalidRules))); wait_for_port(TestPort::ClientInvalidRules.port()).await; - bot_client::run_client(&TestPort::ClientInvalidRules.as_url(), &rules) - .await - .unwrap_err(); + bot_client::run_client( + &TestPort::ClientInvalidRules.as_url(), + &rules, + BoatsLayout::gen_random_for_rules(&GameRules::random_players_rules()).unwrap(), + ) + .await + .unwrap_err(); }) .await; } @@ -50,9 +57,14 @@ async fn full_game() { task::spawn_local(start_server(Args::for_test(TestPort::FullGame))); wait_for_port(TestPort::FullGame.port()).await; - bot_client::run_client(&TestPort::FullGame.as_url(), &rules) - .await - .unwrap(); + let res = bot_client::run_client( + &TestPort::FullGame.as_url(), + &rules, + BoatsLayout::gen_random_for_rules(&rules).unwrap(), + ) + .await + .unwrap(); + assert_eq!(res, ClientEndResult::Finished); }) .await; } @@ -71,9 +83,15 @@ async fn full_game_no_touching_boats() { ))); wait_for_port(TestPort::FullGameTouchingBoats.port()).await; - bot_client::run_client(&TestPort::FullGameTouchingBoats.as_url(), &rules) - .await - .unwrap(); + let res = bot_client::run_client( + &TestPort::FullGameTouchingBoats.as_url(), + &rules, + BoatsLayout::gen_random_for_rules(&rules).unwrap(), + ) + .await + .unwrap(); + + assert_eq!(res, ClientEndResult::Finished); }) .await; }