Boat automatically send its boats configuration
This commit is contained in:
parent
b36f2d4f20
commit
001d4341de
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -1,3 +1,5 @@
|
||||
pub use boats_layout::*;
|
||||
pub use game_map::*;
|
||||
pub use game_rules::*;
|
||||
pub use play_config::*;
|
||||
|
||||
|
61
src/game.rs
61
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<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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -34,6 +34,7 @@ pub enum ServerMessage {
|
||||
QueryBoatsLayout { rules: GameRules },
|
||||
WaitingForOtherPlayerConfiguration,
|
||||
OtherPlayerReady,
|
||||
GameStarting,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
|
@ -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) {}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user