Ready to implement boats layout check
This commit is contained in:
		@@ -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;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user