Ready to implement boats layout check

This commit is contained in:
Pierre HUBERT 2022-09-15 18:04:22 +02:00
parent 007fe3b773
commit d3d5feda4f
10 changed files with 85 additions and 23 deletions

View File

@ -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<dyn Error>> {
#[derive(Debug, Eq, PartialEq)]
pub enum ClientEndResult {
Finished,
InvalidLayout,
}
pub async fn run_client(
server: &str,
requested_rules: &GameRules,
layout: BoatsLayout,
) -> Result<ClientEndResult, Box<dyn Error>> {
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<dyn E
match message {
ServerMessage::WaitingForAnotherPlayer => 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<dyn E
log::debug!("Our game:\n{}\n", your_map.get_map());
break;
}
ServerMessage::RejectedBoatsLayout { errors } => {
log::warn!("Rejected boat layout: {:?}", errors);
return Ok(ClientEndResult::InvalidLayout);
}
}
}
Ok(())
Ok(ClientEndResult::Finished)
}

View File

@ -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)]

View File

@ -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
}

View File

@ -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,

View File

@ -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,
}

View File

@ -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<SetBoatsLayout> 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)),

View File

@ -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)),

View File

@ -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<String>,
},
WaitingForOtherPlayerConfiguration,
OtherPlayerReady,
GameStarting,
@ -87,7 +90,6 @@ impl Default for HumanPlayerWS {
}
}
impl HumanPlayerWS {
/// helper method that sends ping to client every second.
///

View File

@ -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) {}

View File

@ -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,7 +35,11 @@ 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)
bot_client::run_client(
&TestPort::ClientInvalidRules.as_url(),
&rules,
BoatsLayout::gen_random_for_rules(&GameRules::random_players_rules()).unwrap(),
)
.await
.unwrap_err();
})
@ -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)
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)
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;
}