diff --git a/board-network/src/protocol.rs b/board-network/src/protocol.rs index 47c88d7..b97eb3e 100644 --- a/board-network/src/protocol.rs +++ b/board-network/src/protocol.rs @@ -51,6 +51,8 @@ pub enum ServerMessage { PlayerTurn(usize), /// Update the current hand of the player SyncHand(Vec), + /// Update the score of the n-th player. + SyncScore(usize, u32), /// Informs that a tile has been placed TilePlaced(Position2dRef, TileRef), /// Informs that a tile has been removed diff --git a/board-server/src/room.rs b/board-server/src/room.rs index 8d2f8ba..b43228d 100644 --- a/board-server/src/room.rs +++ b/board-server/src/room.rs @@ -6,6 +6,7 @@ use board_shared::deck::RngDeck; use board_shared::expr::is_valid_guess; use board_shared::game::Hand; use board_shared::position::Position2d; +use board_shared::score::calc_score; use board_shared::tile::Tile; use futures::channel::mpsc::{UnboundedReceiver, UnboundedSender}; use futures::StreamExt; @@ -189,6 +190,7 @@ impl Room { .hand .complete(&mut self.deck) .ok(); + self.on_validated_move(self.active_player, &diff); self.next_player(); } else { self.send( @@ -198,6 +200,23 @@ impl Room { } } + fn on_validated_move(&mut self, player_id: usize, diff: &[Position2d]) { + let tiles_placed = diff + .iter() + .map(|&pos| { + self.board + .get(pos.x, pos.y) + .expect("A placed tile should not be empty.") + }) + .collect::>(); + self.players[player_id].score += calc_score(&tiles_placed); + self.broadcast(ServerMessage::SyncScore( + player_id, + self.players[player_id].score, + )); + self.validated_board = self.board.clone(); + } + fn reset_player_moves(&mut self) { let diff = self.board.difference(&self.validated_board); for pos in diff { diff --git a/board-shared/src/lib.rs b/board-shared/src/lib.rs index 41efeea..603693e 100644 --- a/board-shared/src/lib.rs +++ b/board-shared/src/lib.rs @@ -7,4 +7,5 @@ pub mod game; mod lexer; mod parser; pub mod position; +pub mod score; pub mod tile; diff --git a/board-shared/src/score.rs b/board-shared/src/score.rs new file mode 100644 index 0000000..9a4313b --- /dev/null +++ b/board-shared/src/score.rs @@ -0,0 +1,79 @@ +use crate::tile::{Operator, Tile}; + +/// Calculate the score for a given list of tiles. +pub fn calc_score(tiles: &[Tile]) -> u32 { + tiles + .split(|tile| matches!(tile, Tile::Equals)) + .map(calc_expression_score) + .sum() +} + +fn calc_expression_score(tiles: &[Tile]) -> u32 { + let mut digit_score = 0u32; + let mut multiplier = 1u32; + for token in tiles { + match token { + Tile::Digit(_) => digit_score += 1, + Tile::Operator(op) => { + match op { + Operator::Add | Operator::Subtract => multiplier = 2, + Operator::Multiply => multiplier = 3, + Operator::Divide => digit_score += 10, + }; + } + _ => unreachable!(), + } + } + digit_score * multiplier +} + +#[cfg(test)] +mod tests { + use crate::tile::Digit; + + use super::*; + + #[test] + fn only_digits() { + assert_eq!( + calc_score(&[ + Tile::Digit(Digit::new(7)), + Tile::Digit(Digit::new(8)), + Tile::Equals, + Tile::Digit(Digit::new(9)), + Tile::Digit(Digit::new(4)), + ]), + 4 + ); + } + + #[test] + fn calc_individually() { + assert_eq!( + calc_score(&[ + Tile::Digit(Digit::new(4)), + Tile::Operator(Operator::Add), + Tile::Digit(Digit::new(5)), + Tile::Equals, + Tile::Digit(Digit::new(3)), + Tile::Operator(Operator::Multiply), + Tile::Digit(Digit::new(3)), + ]), + 10 + ); + } + + #[test] + fn diviser_add_to_original_sum() { + assert_eq!( + calc_score(&[ + Tile::Digit(Digit::new(8)), + Tile::Operator(Operator::Divide), + Tile::Digit(Digit::new(4)), + Tile::Equals, + Tile::Digit(Digit::new(2)), + ]), + 13 + ); + } +}