2022-09-11 14:48:20 +00:00
|
|
|
use std::sync::Arc;
|
|
|
|
|
|
|
|
use actix::prelude::*;
|
|
|
|
use actix::{Actor, Context, Handler};
|
|
|
|
use uuid::Uuid;
|
|
|
|
|
2022-09-13 17:02:26 +00:00
|
|
|
use crate::data::{BoatsLayout, GameMap, GameRules};
|
|
|
|
|
2022-09-11 14:48:20 +00:00
|
|
|
pub trait Player {
|
|
|
|
fn get_name(&self) -> &str;
|
|
|
|
|
|
|
|
fn get_uid(&self) -> Uuid;
|
|
|
|
|
|
|
|
fn query_boats_layout(&self, rules: &GameRules);
|
2022-09-13 17:02:26 +00:00
|
|
|
|
|
|
|
fn notify_other_player_ready(&self);
|
|
|
|
|
|
|
|
fn notify_game_starting(&self);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn opponent(index: usize) -> usize {
|
|
|
|
match index {
|
|
|
|
0 => 1,
|
|
|
|
1 => 0,
|
|
|
|
_ => unreachable!(),
|
|
|
|
}
|
2022-09-11 14:48:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Default, Eq, PartialEq, Debug, Copy, Clone)]
|
|
|
|
enum GameStatus {
|
|
|
|
#[default]
|
|
|
|
Created,
|
|
|
|
WaitingForBoatsDisposition,
|
2022-09-13 17:02:26 +00:00
|
|
|
Started,
|
2022-09-11 14:48:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub struct Game {
|
|
|
|
rules: GameRules,
|
|
|
|
players: Vec<Arc<dyn Player>>,
|
|
|
|
status: GameStatus,
|
2022-09-13 17:02:26 +00:00
|
|
|
map_0: Option<GameMap>,
|
|
|
|
map_1: Option<GameMap>,
|
2022-09-11 14:48:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Game {
|
|
|
|
pub fn new(rules: GameRules) -> Self {
|
|
|
|
Self {
|
|
|
|
rules,
|
|
|
|
players: vec![],
|
|
|
|
status: GameStatus::Created,
|
2022-09-13 17:02:26 +00:00
|
|
|
map_0: None,
|
|
|
|
map_1: None,
|
2022-09-11 14:48:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-13 17:02:26 +00:00
|
|
|
/// 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
|
|
|
|
}
|
|
|
|
|
2022-09-11 14:48:20 +00:00
|
|
|
/// Once the two player has been registered, the game may start
|
2022-09-11 14:51:14 +00:00
|
|
|
fn query_boats_disposition(&mut self) {
|
2022-09-13 17:12:16 +00:00
|
|
|
log::debug!("Query boats disposition");
|
2022-09-11 14:48:20 +00:00
|
|
|
assert_eq!(self.status, GameStatus::Created);
|
|
|
|
self.status = GameStatus::WaitingForBoatsDisposition;
|
|
|
|
self.players[0].query_boats_layout(&self.rules);
|
|
|
|
self.players[1].query_boats_layout(&self.rules);
|
|
|
|
}
|
2022-09-13 17:02:26 +00:00
|
|
|
|
|
|
|
/// Start fires exchange
|
|
|
|
fn start_fire_exchanges(&mut self) {
|
|
|
|
self.status = GameStatus::Started;
|
2022-09-13 17:12:16 +00:00
|
|
|
log::debug!("Start fire exchanges");
|
2022-09-13 17:02:26 +00:00
|
|
|
}
|
2022-09-11 14:48:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Actor for Game {
|
|
|
|
type Context = Context<Self>;
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Message)]
|
|
|
|
#[rtype(result = "()")]
|
2022-09-11 14:58:40 +00:00
|
|
|
pub struct AddPlayer<E>(pub E);
|
2022-09-11 14:48:20 +00:00
|
|
|
|
2022-09-11 14:58:40 +00:00
|
|
|
impl<E> Handler<AddPlayer<Arc<E>>> for Game
|
|
|
|
where
|
|
|
|
E: Player + 'static,
|
|
|
|
{
|
2022-09-11 14:48:20 +00:00
|
|
|
type Result = ();
|
|
|
|
|
|
|
|
/// Add a new player to the game
|
2022-09-11 14:58:40 +00:00
|
|
|
fn handle(&mut self, msg: AddPlayer<Arc<E>>, _ctx: &mut Self::Context) -> Self::Result {
|
2022-09-11 14:48:20 +00:00
|
|
|
assert!(self.players.len() < 2);
|
2022-09-11 14:58:40 +00:00
|
|
|
self.players.push(msg.0);
|
2022-09-11 14:48:20 +00:00
|
|
|
|
|
|
|
if self.players.len() == 2 {
|
2022-09-11 14:51:14 +00:00
|
|
|
self.query_boats_disposition();
|
2022-09-11 14:48:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-09-13 17:02:26 +00:00
|
|
|
|
|
|
|
#[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);
|
2022-09-13 17:12:16 +00:00
|
|
|
log::debug!("Got boat disposition for player {}", player_index);
|
2022-09-13 17:02:26 +00:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|