Boat automatically send its boats configuration

This commit is contained in:
Pierre HUBERT 2022-09-13 19:02:26 +02:00
parent b36f2d4f20
commit 001d4341de
7 changed files with 102 additions and 12 deletions

View File

@ -159,7 +159,7 @@ impl BoatsLayout {
direction: directions[rng.gen::<usize>() % directions.len()], direction: directions[rng.gen::<usize>() % directions.len()],
}; };
if boats.is_acceptable_boat_position(&position, &rules) { if boats.is_acceptable_boat_position(&position, rules) {
boats.0.push(position); boats.0.push(position);
break; break;
} }
@ -194,7 +194,7 @@ impl BoatsLayout {
// Check if the boat touch another boat in a configuration where is it forbidden // Check if the boat touch another boat in a configuration where is it forbidden
if !rules.boats_can_touch if !rules.boats_can_touch
&& pos && pos
.neighbor_coordinates(&rules) .neighbor_coordinates(rules)
.iter() .iter()
.any(|c| self.find_boat_at_position(*c).is_some()) .any(|c| self.find_boat_at_position(*c).is_some())
{ {
@ -225,7 +225,7 @@ impl BoatsLayout {
for boat_i in 0..self.0.len() { for boat_i in 0..self.0.len() {
// Check boat coordinates // Check boat coordinates
let boat_i_coordinates = self.0[boat_i].all_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!"); errors.push("A boat goes outside the game map!");
} }
@ -241,7 +241,7 @@ impl BoatsLayout {
if !rules.boats_can_touch if !rules.boats_can_touch
&& self.0[boat_i] && self.0[boat_i]
.neighbor_coordinates(&rules) .neighbor_coordinates(rules)
.iter() .iter()
.any(|c| boat_j_coords.contains(c)) .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::boats_layout::{BoatDirection, BoatPosition, BoatsLayout, Coordinates};
use crate::data::game_map::GameMap; use crate::data::game_map::GameMap;
use crate::data::{BotType, GameRules, PlayConfiguration}; use crate::data::{BotType, GameRules, PlayConfiguration};
use crate::game::Game;
#[test] #[test]
fn get_boat_coordinates() { fn get_boat_coordinates() {
@ -317,13 +316,23 @@ mod test {
game.print_map(); 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] #[test]
fn impossible_map() { fn impossible_map() {
let mut rules = GameRules::random_players_rules(); let mut rules = GameRules::random_players_rules();
rules.map_height = PlayConfiguration::default().min_map_height; rules.map_height = PlayConfiguration::default().min_map_height;
rules.map_width = PlayConfiguration::default().min_map_width; rules.map_width = PlayConfiguration::default().min_map_width;
let mut boats = Vec::new(); 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); boats.push(PlayConfiguration::default().max_boat_len);
} }
rules.set_boats_list(&boats); rules.set_boats_list(&boats);

View File

@ -1,7 +1,7 @@
use crate::data::boats_layout::{BoatsLayout, Coordinates}; use crate::data::boats_layout::{BoatsLayout, Coordinates};
use crate::data::GameRules; use crate::data::GameRules;
enum MapCellContent { pub enum MapCellContent {
Invalid, Invalid,
Nothing, Nothing,
TouchedBoat, TouchedBoat,
@ -41,7 +41,7 @@ impl GameMap {
return MapCellContent::Boat; return MapCellContent::Boat;
} }
return MapCellContent::Nothing; MapCellContent::Nothing
} }
pub fn print_map(&self) { pub fn print_map(&self) {

View File

@ -1,3 +1,5 @@
pub use boats_layout::*;
pub use game_map::*;
pub use game_rules::*; pub use game_rules::*;
pub use play_config::*; pub use play_config::*;

View File

@ -1,16 +1,29 @@
use std::sync::Arc; use std::sync::Arc;
use crate::data::GameRules;
use actix::prelude::*; use actix::prelude::*;
use actix::{Actor, Context, Handler}; use actix::{Actor, Context, Handler};
use uuid::Uuid; use uuid::Uuid;
use crate::data::{BoatsLayout, GameMap, GameRules};
pub trait Player { pub trait Player {
fn get_name(&self) -> &str; fn get_name(&self) -> &str;
fn get_uid(&self) -> Uuid; fn get_uid(&self) -> Uuid;
fn query_boats_layout(&self, rules: &GameRules); 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)] #[derive(Default, Eq, PartialEq, Debug, Copy, Clone)]
@ -18,12 +31,15 @@ enum GameStatus {
#[default] #[default]
Created, Created,
WaitingForBoatsDisposition, WaitingForBoatsDisposition,
Started,
} }
pub struct Game { pub struct Game {
rules: GameRules, rules: GameRules,
players: Vec<Arc<dyn Player>>, players: Vec<Arc<dyn Player>>,
status: GameStatus, status: GameStatus,
map_0: Option<GameMap>,
map_1: Option<GameMap>,
} }
impl Game { impl Game {
@ -32,9 +48,21 @@ impl Game {
rules, rules,
players: vec![], players: vec![],
status: GameStatus::Created, 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 /// Once the two player has been registered, the game may start
fn query_boats_disposition(&mut self) { fn query_boats_disposition(&mut self) {
assert_eq!(self.status, GameStatus::Created); assert_eq!(self.status, GameStatus::Created);
@ -42,6 +70,11 @@ impl Game {
self.players[0].query_boats_layout(&self.rules); self.players[0].query_boats_layout(&self.rules);
self.players[1].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 { impl Actor for Game {
@ -68,3 +101,29 @@ where
} }
} }
} }
#[derive(Message)]
#[rtype(result = "()")]
pub struct SetBoatsLayout(pub Uuid, pub BoatsLayout);
impl Handler<SetBoatsLayout> 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();
}
}
}

View File

@ -26,6 +26,14 @@ impl Player for HumanPlayer {
rules: rules.clone(), 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 { impl HumanPlayer {

View File

@ -34,6 +34,7 @@ pub enum ServerMessage {
QueryBoatsLayout { rules: GameRules }, QueryBoatsLayout { rules: GameRules },
WaitingForOtherPlayerConfiguration, WaitingForOtherPlayerConfiguration,
OtherPlayerReady, OtherPlayerReady,
GameStarting,
} }
#[derive(Default)] #[derive(Default)]

View File

@ -1,8 +1,8 @@
use crate::data::GameRules;
use actix::Addr; use actix::Addr;
use uuid::Uuid; use uuid::Uuid;
use crate::game::{Game, Player}; use crate::data::{BoatsLayout, GameRules};
use crate::game::{Game, Player, SetBoatsLayout};
#[derive(Clone)] #[derive(Clone)]
pub struct RandomBot { pub struct RandomBot {
@ -29,6 +29,17 @@ impl Player for RandomBot {
} }
fn query_boats_layout(&self, rules: &GameRules) { 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) {}
} }