From 001d4341def31e089ee7cd56ed61adeae37e6369 Mon Sep 17 00:00:00 2001 From: Pierre Hubert Date: Tue, 13 Sep 2022 19:02:26 +0200 Subject: [PATCH] Boat automatically send its boats configuration --- src/data/boats_layout.rs | 21 ++++++++++---- src/data/game_map.rs | 4 +-- src/data/mod.rs | 2 ++ src/game.rs | 61 +++++++++++++++++++++++++++++++++++++++- src/human_player.rs | 8 ++++++ src/human_player_ws.rs | 1 + src/random_bot.rs | 17 +++++++++-- 7 files changed, 102 insertions(+), 12 deletions(-) diff --git a/src/data/boats_layout.rs b/src/data/boats_layout.rs index 75e8eb1..e4d7239 100644 --- a/src/data/boats_layout.rs +++ b/src/data/boats_layout.rs @@ -159,7 +159,7 @@ impl BoatsLayout { direction: directions[rng.gen::() % directions.len()], }; - if boats.is_acceptable_boat_position(&position, &rules) { + if boats.is_acceptable_boat_position(&position, rules) { boats.0.push(position); break; } @@ -194,7 +194,7 @@ impl BoatsLayout { // Check if the boat touch another boat in a configuration where is it forbidden if !rules.boats_can_touch && pos - .neighbor_coordinates(&rules) + .neighbor_coordinates(rules) .iter() .any(|c| self.find_boat_at_position(*c).is_some()) { @@ -225,7 +225,7 @@ impl BoatsLayout { for boat_i in 0..self.0.len() { // Check boat coordinates let boat_i_coordinates = self.0[boat_i].all_coordinates(); - if boat_i_coordinates.iter().any(|c| !c.is_valid(&rules)) { + if boat_i_coordinates.iter().any(|c| !c.is_valid(rules)) { errors.push("A boat goes outside the game map!"); } @@ -241,7 +241,7 @@ impl BoatsLayout { if !rules.boats_can_touch && self.0[boat_i] - .neighbor_coordinates(&rules) + .neighbor_coordinates(rules) .iter() .any(|c| boat_j_coords.contains(c)) { @@ -263,7 +263,6 @@ mod test { use crate::data::boats_layout::{BoatDirection, BoatPosition, BoatsLayout, Coordinates}; use crate::data::game_map::GameMap; use crate::data::{BotType, GameRules, PlayConfiguration}; - use crate::game::Game; #[test] fn get_boat_coordinates() { @@ -317,13 +316,23 @@ mod test { game.print_map(); } + #[test] + fn generate_random_boats_layout_without_touching_boats() { + let mut rules = GameRules::random_players_rules(); + rules.boats_can_touch = false; + let boats = BoatsLayout::gen_random_for_rules(&rules).unwrap(); + assert!(boats.errors(&rules).is_empty()); + let game = GameMap::new(rules, boats); + game.print_map(); + } + #[test] fn impossible_map() { let mut rules = GameRules::random_players_rules(); rules.map_height = PlayConfiguration::default().min_map_height; rules.map_width = PlayConfiguration::default().min_map_width; let mut boats = Vec::new(); - for i in 0..PlayConfiguration::default().max_boats_number { + for _i in 0..PlayConfiguration::default().max_boats_number { boats.push(PlayConfiguration::default().max_boat_len); } rules.set_boats_list(&boats); diff --git a/src/data/game_map.rs b/src/data/game_map.rs index e8a9231..6fcb0d2 100644 --- a/src/data/game_map.rs +++ b/src/data/game_map.rs @@ -1,7 +1,7 @@ use crate::data::boats_layout::{BoatsLayout, Coordinates}; use crate::data::GameRules; -enum MapCellContent { +pub enum MapCellContent { Invalid, Nothing, TouchedBoat, @@ -41,7 +41,7 @@ impl GameMap { return MapCellContent::Boat; } - return MapCellContent::Nothing; + MapCellContent::Nothing } pub fn print_map(&self) { diff --git a/src/data/mod.rs b/src/data/mod.rs index 827074f..02adb93 100644 --- a/src/data/mod.rs +++ b/src/data/mod.rs @@ -1,3 +1,5 @@ +pub use boats_layout::*; +pub use game_map::*; pub use game_rules::*; pub use play_config::*; diff --git a/src/game.rs b/src/game.rs index 7585b23..b190bcc 100644 --- a/src/game.rs +++ b/src/game.rs @@ -1,16 +1,29 @@ use std::sync::Arc; -use crate::data::GameRules; use actix::prelude::*; use actix::{Actor, Context, Handler}; use uuid::Uuid; +use crate::data::{BoatsLayout, GameMap, GameRules}; + pub trait Player { fn get_name(&self) -> &str; fn get_uid(&self) -> Uuid; fn query_boats_layout(&self, rules: &GameRules); + + fn notify_other_player_ready(&self); + + fn notify_game_starting(&self); +} + +fn opponent(index: usize) -> usize { + match index { + 0 => 1, + 1 => 0, + _ => unreachable!(), + } } #[derive(Default, Eq, PartialEq, Debug, Copy, Clone)] @@ -18,12 +31,15 @@ enum GameStatus { #[default] Created, WaitingForBoatsDisposition, + Started, } pub struct Game { rules: GameRules, players: Vec>, status: GameStatus, + map_0: Option, + map_1: Option, } impl Game { @@ -32,9 +48,21 @@ impl Game { rules, players: vec![], status: GameStatus::Created, + map_0: None, + map_1: None, } } + /// Find the ID of a player from its UUID + fn player_id_by_uuid(&self, uuid: Uuid) -> usize { + self.players + .iter() + .enumerate() + .find(|p| p.1.get_uid() == uuid) + .expect("Player is not member of this game!") + .0 + } + /// Once the two player has been registered, the game may start fn query_boats_disposition(&mut self) { assert_eq!(self.status, GameStatus::Created); @@ -42,6 +70,11 @@ impl Game { self.players[0].query_boats_layout(&self.rules); self.players[1].query_boats_layout(&self.rules); } + + /// Start fires exchange + fn start_fire_exchanges(&mut self) { + self.status = GameStatus::Started; + } } impl Actor for Game { @@ -68,3 +101,29 @@ where } } } + +#[derive(Message)] +#[rtype(result = "()")] +pub struct SetBoatsLayout(pub Uuid, pub BoatsLayout); + +impl Handler for Game { + type Result = (); + + /// Receive game configuration of a player + fn handle(&mut self, msg: SetBoatsLayout, _ctx: &mut Self::Context) -> Self::Result { + assert_eq!(self.status, GameStatus::WaitingForBoatsDisposition); + let player_index = self.player_id_by_uuid(msg.0); + match player_index { + 0 => self.map_0 = Some(GameMap::new(self.rules.clone(), msg.1)), + 1 => self.map_1 = Some(GameMap::new(self.rules.clone(), msg.1)), + _ => unreachable!(), + } + + self.players[opponent(player_index)].notify_other_player_ready(); + + if self.map_0.is_some() && self.map_1.is_some() { + self.players.iter().for_each(|p| p.notify_game_starting()); + self.start_fire_exchanges(); + } + } +} diff --git a/src/human_player.rs b/src/human_player.rs index 1478cdd..6efadcf 100644 --- a/src/human_player.rs +++ b/src/human_player.rs @@ -26,6 +26,14 @@ impl Player for HumanPlayer { rules: rules.clone(), }); } + + fn notify_other_player_ready(&self) { + self.player.do_send(ServerMessage::OtherPlayerReady); + } + + fn notify_game_starting(&self) { + self.player.do_send(ServerMessage::GameStarting); + } } impl HumanPlayer { diff --git a/src/human_player_ws.rs b/src/human_player_ws.rs index 9f61369..179c852 100644 --- a/src/human_player_ws.rs +++ b/src/human_player_ws.rs @@ -34,6 +34,7 @@ pub enum ServerMessage { QueryBoatsLayout { rules: GameRules }, WaitingForOtherPlayerConfiguration, OtherPlayerReady, + GameStarting, } #[derive(Default)] diff --git a/src/random_bot.rs b/src/random_bot.rs index d2f97e6..02e8a8d 100644 --- a/src/random_bot.rs +++ b/src/random_bot.rs @@ -1,8 +1,8 @@ -use crate::data::GameRules; use actix::Addr; use uuid::Uuid; -use crate::game::{Game, Player}; +use crate::data::{BoatsLayout, GameRules}; +use crate::game::{Game, Player, SetBoatsLayout}; #[derive(Clone)] pub struct RandomBot { @@ -29,6 +29,17 @@ impl Player for RandomBot { } fn query_boats_layout(&self, rules: &GameRules) { - log::info!("Player requested boats configuration!"); + match BoatsLayout::gen_random_for_rules(rules) { + Ok(layout) => self.game.do_send(SetBoatsLayout(self.uuid, layout)), + + Err(e) => log::error!( + "Failed to use game rules to construct boats layout: {:?}", + e + ), + } } + + fn notify_other_player_ready(&self) {} + + fn notify_game_starting(&self) {} }