diff --git a/src/bot_client.rs b/src/bot_client.rs index 23e0c06..a5442ab 100644 --- a/src/bot_client.rs +++ b/src/bot_client.rs @@ -67,6 +67,16 @@ pub async fn run_client(server: &str, rules: &GameRules) -> Result<(), Box log::debug!("Other player is ready!"), ServerMessage::GameStarting => log::debug!("The game is starting..."), + ServerMessage::OtherPlayerMustFire { .. } => log::debug!("Other player must fire!"), + ServerMessage::RequestFire { status } => { + let location = status.find_valid_random_fire_location(); + log::debug!("Will fire at {:?}", location); + socket + .send(Message::Text(serde_json::to_string( + &ClientMessage::Fire { location }, + )?)) + .await?; + } } } diff --git a/src/data/current_game_status.rs b/src/data/current_game_status.rs new file mode 100644 index 0000000..8779852 --- /dev/null +++ b/src/data/current_game_status.rs @@ -0,0 +1,30 @@ +use crate::data::{Coordinates, GameRules}; +use rand::RngCore; + +#[derive(Debug, serde::Serialize, serde::Deserialize)] +pub struct CurrentGameStatus { + pub rules: GameRules, + // TODO +} + +impl CurrentGameStatus { + /// Check if user can fire at a given location + pub fn can_fire_at_location(&self, _location: Coordinates) -> bool { + // TODO + true + } + + /// Find valid random fire location. Loop until one is found + pub fn find_valid_random_fire_location(&self) -> Coordinates { + let mut rng = rand::thread_rng(); + loop { + let coordinates = Coordinates::new( + (rng.next_u32() % self.rules.map_width as u32) as i32, + (rng.next_u32() % self.rules.map_height as u32) as i32, + ); + if coordinates.is_valid(&self.rules) && self.can_fire_at_location(coordinates) { + return coordinates; + } + } + } +} diff --git a/src/data/mod.rs b/src/data/mod.rs index 02adb93..9d84c56 100644 --- a/src/data/mod.rs +++ b/src/data/mod.rs @@ -1,9 +1,11 @@ pub use boats_layout::*; +pub use current_game_status::*; pub use game_map::*; pub use game_rules::*; pub use play_config::*; mod boats_layout; +mod current_game_status; mod game_map; mod game_rules; mod play_config; diff --git a/src/game.rs b/src/game.rs index 1b219b6..11ce9e5 100644 --- a/src/game.rs +++ b/src/game.rs @@ -4,7 +4,7 @@ use actix::prelude::*; use actix::{Actor, Context, Handler}; use uuid::Uuid; -use crate::data::{BoatsLayout, GameMap, GameRules}; +use crate::data::*; pub trait Player { fn get_name(&self) -> &str; @@ -16,6 +16,10 @@ pub trait Player { fn notify_other_player_ready(&self); fn notify_game_starting(&self); + + fn request_fire(&self, status: CurrentGameStatus); + + fn other_player_must_fire(&self, status: CurrentGameStatus); } fn opponent(index: usize) -> usize { @@ -40,6 +44,7 @@ pub struct Game { status: GameStatus, map_0: Option, map_1: Option, + turn: usize, } impl Game { @@ -50,6 +55,7 @@ impl Game { status: GameStatus::Created, map_0: None, map_1: None, + turn: 0, } } @@ -75,7 +81,22 @@ impl Game { /// Start fires exchange fn start_fire_exchanges(&mut self) { self.status = GameStatus::Started; - log::debug!("Start fire exchanges"); + log::debug!( + "Start fire exchanges. Player {}#{} goes first", + self.players[self.turn].get_name(), + self.turn + ); + + self.players[self.turn].request_fire(self.get_game_status_for_player(self.turn)); + self.players[opponent(self.turn)] + .request_fire(self.get_game_status_for_player(opponent(self.turn))); + } + + /// Get current game status for a specific player + fn get_game_status_for_player(&self, _id: usize) -> CurrentGameStatus { + CurrentGameStatus { + rules: self.rules.clone(), + } } } @@ -113,7 +134,11 @@ impl Handler for Game { /// Receive game configuration of a player fn handle(&mut self, msg: SetBoatsLayout, _ctx: &mut Self::Context) -> Self::Result { - assert_eq!(self.status, GameStatus::WaitingForBoatsDisposition); + if self.status != GameStatus::WaitingForBoatsDisposition { + log::error!("Player attempted to set boat configuration on invalid step!"); + return; + } + let player_index = self.player_id_by_uuid(msg.0); log::debug!("Got boat disposition for player {}", player_index); match player_index { @@ -130,3 +155,15 @@ impl Handler for Game { } } } + +#[derive(Message, Debug)] +#[rtype(result = "()")] +pub struct Fire(pub Uuid, pub Coordinates); + +impl Handler for Game { + type Result = (); + + fn handle(&mut self, msg: Fire, _ctx: &mut Self::Context) -> Self::Result { + log::debug!("FIRE ===> {:?}", msg); + } +} diff --git a/src/human_player.rs b/src/human_player.rs index fab42a0..f6a7619 100644 --- a/src/human_player.rs +++ b/src/human_player.rs @@ -1,8 +1,8 @@ use actix::Addr; use uuid::Uuid; -use crate::data::GameRules; -use crate::game::{Game, Player, SetBoatsLayout}; +use crate::data::{CurrentGameStatus, GameRules}; +use crate::game::{Fire, Game, Player, SetBoatsLayout}; use crate::human_player_ws::{ClientMessage, HumanPlayerWS, ServerMessage}; pub struct HumanPlayer { @@ -34,6 +34,15 @@ impl Player for HumanPlayer { fn notify_game_starting(&self) { self.player.do_send(ServerMessage::GameStarting); } + + fn request_fire(&self, status: CurrentGameStatus) { + self.player.do_send(ServerMessage::RequestFire { status }); + } + + fn other_player_must_fire(&self, status: CurrentGameStatus) { + self.player + .do_send(ServerMessage::OtherPlayerMustFire { status }); + } } impl HumanPlayer { @@ -45,6 +54,7 @@ impl HumanPlayer { ClientMessage::BoatsLayout { layout } => { self.game.do_send(SetBoatsLayout(self.uuid, layout)) } + ClientMessage::Fire { location } => self.game.do_send(Fire(self.uuid, location)), } } } diff --git a/src/human_player_ws.rs b/src/human_player_ws.rs index aeeb8eb..8a4400a 100644 --- a/src/human_player_ws.rs +++ b/src/human_player_ws.rs @@ -6,7 +6,7 @@ use actix_web_actors::ws; use actix_web_actors::ws::{CloseCode, CloseReason, Message, ProtocolError, WebsocketContext}; use uuid::Uuid; -use crate::data::{BoatsLayout, BotType, GameRules}; +use crate::data::{BoatsLayout, BotType, Coordinates, CurrentGameStatus, GameRules}; use crate::game::{AddPlayer, Game}; use crate::human_player::HumanPlayer; use crate::random_bot::RandomBot; @@ -24,6 +24,7 @@ pub enum StartMode { pub enum ClientMessage { StopGame, BoatsLayout { layout: BoatsLayout }, + Fire { location: Coordinates }, } #[derive(Message)] @@ -36,6 +37,8 @@ pub enum ServerMessage { WaitingForOtherPlayerConfiguration, OtherPlayerReady, GameStarting, + OtherPlayerMustFire { status: CurrentGameStatus }, + RequestFire { status: CurrentGameStatus }, } #[derive(Default)] diff --git a/src/random_bot.rs b/src/random_bot.rs index 02e8a8d..9f1897a 100644 --- a/src/random_bot.rs +++ b/src/random_bot.rs @@ -1,8 +1,8 @@ use actix::Addr; use uuid::Uuid; -use crate::data::{BoatsLayout, GameRules}; -use crate::game::{Game, Player, SetBoatsLayout}; +use crate::data::{BoatsLayout, CurrentGameStatus, GameRules}; +use crate::game::{Fire, Game, Player, SetBoatsLayout}; #[derive(Clone)] pub struct RandomBot { @@ -42,4 +42,11 @@ impl Player for RandomBot { fn notify_other_player_ready(&self) {} fn notify_game_starting(&self) {} + + fn request_fire(&self, status: CurrentGameStatus) { + self.game + .do_send(Fire(self.uuid, status.find_valid_random_fire_location())); + } + + fn other_player_must_fire(&self, _status: CurrentGameStatus) {} }