|
|
|
@ -1,5 +1,10 @@
|
|
|
|
|
use crate::player::Player;
|
|
|
|
|
use board_network::protocol::{ClientMessage, ServerMessage};
|
|
|
|
|
use board_shared::board::Board;
|
|
|
|
|
use board_shared::deck::RngDeck;
|
|
|
|
|
use board_shared::expr::is_valid_guess;
|
|
|
|
|
use board_shared::game::Hand;
|
|
|
|
|
use board_shared::position::Position2d;
|
|
|
|
|
use futures::channel::mpsc::{UnboundedReceiver, UnboundedSender};
|
|
|
|
|
use futures::StreamExt;
|
|
|
|
|
use rand::distributions::{Alphanumeric, DistString};
|
|
|
|
@ -16,6 +21,9 @@ pub struct Room {
|
|
|
|
|
pub connections: HashMap<SocketAddr, usize>,
|
|
|
|
|
pub players: Vec<Player>,
|
|
|
|
|
pub active_player: usize,
|
|
|
|
|
pub board: Board,
|
|
|
|
|
pub validated_board: Board,
|
|
|
|
|
pub deck: RngDeck,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Room {
|
|
|
|
@ -43,9 +51,13 @@ impl Room {
|
|
|
|
|
self.broadcast(ServerMessage::PlayerConnected(player_name.clone()));
|
|
|
|
|
player_index = Some(self.players.len());
|
|
|
|
|
|
|
|
|
|
let mut hand = Hand::default();
|
|
|
|
|
hand.complete(&mut self.deck)?;
|
|
|
|
|
|
|
|
|
|
self.players.push(Player {
|
|
|
|
|
name: player_name,
|
|
|
|
|
score: 0,
|
|
|
|
|
hand,
|
|
|
|
|
ws: Some(tx.clone()),
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
@ -67,17 +79,91 @@ impl Room {
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn next_player(&mut self) {
|
|
|
|
|
if self.connections.is_empty() {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
loop {
|
|
|
|
|
self.active_player = (self.active_player + 1) % self.players.len();
|
|
|
|
|
if self.players[self.active_player].ws.is_some() {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
self.broadcast(ServerMessage::PlayerTurn(self.active_player));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn on_message(&mut self, addr: SocketAddr, msg: ClientMessage) -> bool {
|
|
|
|
|
match msg {
|
|
|
|
|
ClientMessage::Disconnected => self.on_client_disconnected(addr),
|
|
|
|
|
ClientMessage::CreateRoom(_) | ClientMessage::JoinRoom(_, _) => {
|
|
|
|
|
eprintln!("[{}] Illegal client message {:?}", self.name, msg);
|
|
|
|
|
}
|
|
|
|
|
ClientMessage::TileUse(pos, tile_idx) => {
|
|
|
|
|
if let Some(p) = self.connections.get(&addr) {
|
|
|
|
|
if *p == self.active_player {
|
|
|
|
|
self.on_tile_use(pos, tile_idx);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
ClientMessage::Validate => {
|
|
|
|
|
if let Some(p) = self.connections.get(&addr) {
|
|
|
|
|
if *p == self.active_player {
|
|
|
|
|
self.on_validate();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
_ => todo!(),
|
|
|
|
|
}
|
|
|
|
|
!self.connections.is_empty()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn on_tile_use(&mut self, pos: Position2d, tile_idx: usize) {
|
|
|
|
|
let hand = &mut self.players[self.active_player].hand;
|
|
|
|
|
if let Some(tile) = hand.remove(tile_idx) {
|
|
|
|
|
self.board.set(pos.x, pos.y, tile);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn on_validate(&mut self) -> anyhow::Result<()> {
|
|
|
|
|
let diff = self.board.difference(&self.validated_board);
|
|
|
|
|
if !Board::has_alignment(&diff) {
|
|
|
|
|
self.reset_player_moves();
|
|
|
|
|
self.send(
|
|
|
|
|
self.active_player,
|
|
|
|
|
ServerMessage::TurnRejected("Move is not aligned".to_string()),
|
|
|
|
|
);
|
|
|
|
|
return Ok(());
|
|
|
|
|
}
|
|
|
|
|
let is_valid = self
|
|
|
|
|
.board
|
|
|
|
|
.find_chains(&diff)
|
|
|
|
|
.iter()
|
|
|
|
|
.all(|chain| is_valid_guess(&self.board, chain) == Ok(true));
|
|
|
|
|
|
|
|
|
|
if is_valid {
|
|
|
|
|
self.players[self.active_player]
|
|
|
|
|
.hand
|
|
|
|
|
.complete(&mut self.deck)?;
|
|
|
|
|
self.next_player();
|
|
|
|
|
} else {
|
|
|
|
|
self.send(
|
|
|
|
|
self.active_player,
|
|
|
|
|
ServerMessage::TurnRejected("Invalid expressions found".to_string()),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn reset_player_moves(&mut self) {
|
|
|
|
|
let diff = self.board.difference(&self.validated_board);
|
|
|
|
|
for pos in diff {
|
|
|
|
|
self.broadcast(ServerMessage::TileRemoved(pos));
|
|
|
|
|
}
|
|
|
|
|
self.board = self.validated_board.clone();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn on_client_disconnected(&mut self, addr: SocketAddr) {
|
|
|
|
|
if let Some(p) = self.connections.remove(&addr) {
|
|
|
|
|
self.players[p].ws = None;
|
|
|
|
@ -97,6 +183,19 @@ impl Room {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn send(&self, i: usize, s: ServerMessage) {
|
|
|
|
|
if let Some(p) = self.players[i].ws.as_ref() {
|
|
|
|
|
if let Err(e) = p.unbounded_send(s) {
|
|
|
|
|
eprintln!(
|
|
|
|
|
"[{}] Failed to send message to {}: {}",
|
|
|
|
|
self.name, self.players[i].name, e
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
eprintln!("[{}] Tried sending message to inactive player", self.name);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type RoomPtr = Arc<Mutex<Room>>;
|
|
|
|
|