server/domain/scoreboard/
board.rs1use constants::*;
2use serde::{Deserialize, Serialize};
3
4use crate::{
5 display::format_vec,
6 domain::{PLAYER0, PLAYER1, Pegging, Player, Points, Position, Positions},
7};
8
9#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
13pub struct Scoreboard {
14 positions: Positions,
15 history: Vec<Pegging>,
16}
17
18pub trait HasScoreboard {
20 fn scoreboard(&self) -> &Scoreboard;
22
23 fn scoreboard_mut(&mut self) -> &mut Scoreboard;
25}
26
27impl Scoreboard {
28 pub fn new(positions: Positions) -> Self {
30 Self {
31 positions,
32 history: Vec::default(),
33 }
34 }
35
36 #[must_use]
38 pub fn position(&self, player: Player) -> Position {
39 self.positions[player]
40 }
41
42 #[must_use]
47 pub fn peg(&mut self, pegging: &Pegging) -> Option<Player> {
48 let player = pegging.recipient();
49 let sheet = pegging.score_sheet();
50
51 let points = sheet.points();
52 if points > Points::from(0) {
53 self.positions[player] += points;
54 self.history.push(pegging.clone());
55 }
56
57 (self.positions[player].points() >= Points::from(WINNING_SCORE)).then_some(*player)
58 }
59
60 #[must_use]
65 pub fn latest_pegging(&self) -> Option<&Pegging> {
66 self.history.last()
67 }
68
69 #[must_use]
71 pub fn winner(&self) -> Option<Player> {
72 if self.position(PLAYER0).points() >= Points::from(WINNING_SCORE) {
73 Some(PLAYER0)
74 } else if self.position(PLAYER1).points() >= Points::from(WINNING_SCORE) {
75 Some(PLAYER1)
76 } else {
77 None
78 }
79 }
80}
81
82impl std::fmt::Display for Scoreboard {
83 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
84 let Self { positions, .. } = self;
85 let positions = format_vec(positions);
86 write!(f, "Scoreboard({positions})")
87 }
88}