Ready to implement boats layout check
This commit is contained in:
parent
007fe3b773
commit
d3d5feda4f
@ -6,11 +6,21 @@ use tokio_tungstenite::tungstenite::Message;
|
|||||||
use crate::data::{BoatsLayout, GameRules};
|
use crate::data::{BoatsLayout, GameRules};
|
||||||
use crate::human_player_ws::{ClientMessage, ServerMessage};
|
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!(
|
let url = format!(
|
||||||
"{}/play/bot?{}",
|
"{}/play/bot?{}",
|
||||||
server.replace("http", "ws"),
|
server.replace("http", "ws"),
|
||||||
serde_urlencoded::to_string(rules).unwrap()
|
serde_urlencoded::to_string(requested_rules).unwrap()
|
||||||
);
|
);
|
||||||
log::debug!("Connecting to {}...", url);
|
log::debug!("Connecting to {}...", url);
|
||||||
let (mut socket, _) = match tokio_tungstenite::connect_async(url).await {
|
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 {
|
match message {
|
||||||
ServerMessage::WaitingForAnotherPlayer => log::debug!("Waiting for other player..."),
|
ServerMessage::WaitingForAnotherPlayer => log::debug!("Waiting for other player..."),
|
||||||
ServerMessage::QueryBoatsLayout { rules } => {
|
ServerMessage::QueryBoatsLayout { rules } => {
|
||||||
|
assert_eq!(&rules, requested_rules);
|
||||||
log::debug!("Server requested boats layout");
|
log::debug!("Server requested boats layout");
|
||||||
let layout = BoatsLayout::gen_random_for_rules(&rules)?;
|
|
||||||
socket
|
socket
|
||||||
.send(Message::Text(serde_json::to_string(
|
.send(Message::Text(serde_json::to_string(
|
||||||
&ClientMessage::BoatsLayout { layout },
|
&ClientMessage::BoatsLayout {
|
||||||
|
layout: layout.clone(),
|
||||||
|
},
|
||||||
)?))
|
)?))
|
||||||
.await?;
|
.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());
|
log::debug!("Our game:\n{}\n", your_map.get_map());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
ServerMessage::RejectedBoatsLayout { errors } => {
|
||||||
|
log::warn!("Rejected boat layout: {:?}", errors);
|
||||||
|
return Ok(ClientEndResult::InvalidLayout);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(ClientEndResult::Finished)
|
||||||
}
|
}
|
||||||
|
@ -272,6 +272,12 @@ impl BoatsLayout {
|
|||||||
pub fn number_of_boats(&self) -> usize {
|
pub fn number_of_boats(&self) -> usize {
|
||||||
self.0.len()
|
self.0.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Check for layout invalid configuration
|
||||||
|
pub fn layouts_errors(&self, _rules: &GameRules) -> Vec<&'static str> {
|
||||||
|
//TODO : implement
|
||||||
|
vec![]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use crate::consts::*;
|
use crate::consts::*;
|
||||||
use crate::data::{BotType, PlayConfiguration};
|
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 struct GameRules {
|
||||||
pub map_width: usize,
|
pub map_width: usize,
|
||||||
pub map_height: usize,
|
pub map_height: usize,
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use crate::consts::*;
|
use crate::consts::*;
|
||||||
|
|
||||||
/// Specifies the kind of boat to use
|
/// 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 {
|
pub enum BotType {
|
||||||
Random,
|
Random,
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,8 @@ pub trait Player {
|
|||||||
|
|
||||||
fn query_boats_layout(&self, rules: &GameRules);
|
fn query_boats_layout(&self, rules: &GameRules);
|
||||||
|
|
||||||
|
fn rejected_boats_layout(&self, errors: Vec<&'static str>);
|
||||||
|
|
||||||
fn notify_other_player_ready(&self);
|
fn notify_other_player_ready(&self);
|
||||||
|
|
||||||
fn notify_game_starting(&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 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);
|
log::debug!("Got boat disposition for player {}", player_index);
|
||||||
match player_index {
|
match player_index {
|
||||||
0 => self.map_0 = Some(GameMap::new(self.rules.clone(), msg.1)),
|
0 => self.map_0 = Some(GameMap::new(self.rules.clone(), msg.1)),
|
||||||
|
@ -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) {
|
fn notify_other_player_ready(&self) {
|
||||||
self.player.do_send(ServerMessage::OtherPlayerReady);
|
self.player.do_send(ServerMessage::OtherPlayerReady);
|
||||||
}
|
}
|
||||||
@ -80,7 +86,6 @@ impl HumanPlayer {
|
|||||||
// TODO : do something
|
// TODO : do something
|
||||||
}
|
}
|
||||||
ClientMessage::BoatsLayout { layout } => {
|
ClientMessage::BoatsLayout { layout } => {
|
||||||
// TODO : check boat layout validity
|
|
||||||
self.game.do_send(SetBoatsLayout(self.uuid, layout))
|
self.game.do_send(SetBoatsLayout(self.uuid, layout))
|
||||||
}
|
}
|
||||||
ClientMessage::Fire { location } => self.game.do_send(Fire(self.uuid, location)),
|
ClientMessage::Fire { location } => self.game.do_send(Fire(self.uuid, location)),
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
use actix::{Actor, Handler, StreamHandler};
|
|
||||||
use actix::prelude::*;
|
use actix::prelude::*;
|
||||||
|
use actix::{Actor, Handler, StreamHandler};
|
||||||
use actix_web_actors::ws;
|
use actix_web_actors::ws;
|
||||||
use actix_web_actors::ws::{CloseCode, CloseReason, Message, ProtocolError, WebsocketContext};
|
use actix_web_actors::ws::{CloseCode, CloseReason, Message, ProtocolError, WebsocketContext};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
@ -44,6 +44,9 @@ pub enum ServerMessage {
|
|||||||
QueryBoatsLayout {
|
QueryBoatsLayout {
|
||||||
rules: GameRules,
|
rules: GameRules,
|
||||||
},
|
},
|
||||||
|
RejectedBoatsLayout {
|
||||||
|
errors: Vec<String>,
|
||||||
|
},
|
||||||
WaitingForOtherPlayerConfiguration,
|
WaitingForOtherPlayerConfiguration,
|
||||||
OtherPlayerReady,
|
OtherPlayerReady,
|
||||||
GameStarting,
|
GameStarting,
|
||||||
@ -87,7 +90,6 @@ impl Default for HumanPlayerWS {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl HumanPlayerWS {
|
impl HumanPlayerWS {
|
||||||
/// helper method that sends ping to client every second.
|
/// helper method that sends ping to client every second.
|
||||||
///
|
///
|
||||||
|
@ -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_other_player_ready(&self) {}
|
||||||
|
|
||||||
fn notify_game_starting(&self) {}
|
fn notify_game_starting(&self) {}
|
||||||
|
@ -2,7 +2,8 @@ use tokio::task;
|
|||||||
|
|
||||||
use crate::args::Args;
|
use crate::args::Args;
|
||||||
use crate::bot_client;
|
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::server::start_server;
|
||||||
use crate::test::network_utils::wait_for_port;
|
use crate::test::network_utils::wait_for_port;
|
||||||
use crate::test::TestPort;
|
use crate::test::TestPort;
|
||||||
@ -11,9 +12,11 @@ use crate::test::TestPort;
|
|||||||
async fn invalid_port() {
|
async fn invalid_port() {
|
||||||
let _ = env_logger::builder().is_test(true).try_init();
|
let _ = env_logger::builder().is_test(true).try_init();
|
||||||
|
|
||||||
|
let rules = GameRules::random_players_rules();
|
||||||
bot_client::run_client(
|
bot_client::run_client(
|
||||||
&TestPort::ClientInvalidPort.as_url(),
|
&TestPort::ClientInvalidPort.as_url(),
|
||||||
&GameRules::random_players_rules(),
|
&rules,
|
||||||
|
BoatsLayout::gen_random_for_rules(&rules).unwrap(),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap_err();
|
.unwrap_err();
|
||||||
@ -32,7 +35,11 @@ async fn invalid_rules() {
|
|||||||
task::spawn_local(start_server(Args::for_test(TestPort::ClientInvalidRules)));
|
task::spawn_local(start_server(Args::for_test(TestPort::ClientInvalidRules)));
|
||||||
wait_for_port(TestPort::ClientInvalidRules.port()).await;
|
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
|
.await
|
||||||
.unwrap_err();
|
.unwrap_err();
|
||||||
})
|
})
|
||||||
@ -50,9 +57,14 @@ async fn full_game() {
|
|||||||
task::spawn_local(start_server(Args::for_test(TestPort::FullGame)));
|
task::spawn_local(start_server(Args::for_test(TestPort::FullGame)));
|
||||||
wait_for_port(TestPort::FullGame.port()).await;
|
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
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
assert_eq!(res, ClientEndResult::Finished);
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
@ -71,9 +83,15 @@ async fn full_game_no_touching_boats() {
|
|||||||
)));
|
)));
|
||||||
wait_for_port(TestPort::FullGameTouchingBoats.port()).await;
|
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
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(res, ClientEndResult::Finished);
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user