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()],
};
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);

View File

@ -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) {

View File

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

View File

@ -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<Arc<dyn Player>>,
status: GameStatus,
map_0: Option<GameMap>,
map_1: Option<GameMap>,
}
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<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(),
});
}
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 {

View File

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

View File

@ -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) {}
}