Skip to main content

server/domain/scoreboard/
position.rs

1use serde::{Deserialize, Serialize};
2
3use super::Points;
4
5/// Represents a player's position in the game.
6///
7/// Tracks points in the "front" and "back" peg positions.
8#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
9pub struct Position {
10    back: Points,
11    front: Points,
12}
13
14impl Position {
15    #[cfg(test)]
16    pub(crate) fn new(points: usize) -> Self {
17        Self {
18            back: Points::from(0),
19            front: Points::from(points),
20        }
21    }
22
23    /// Returns the points in the back position.
24    #[must_use]
25    pub const fn back(&self) -> Points {
26        self.back
27    }
28
29    /// Returns the points in the front position.
30    #[must_use]
31    pub const fn front(&self) -> Points {
32        self.front
33    }
34
35    /// Returns the total points for the position (same as `front`).
36    #[must_use]
37    pub fn points(&self) -> Points {
38        self.front
39    }
40}
41
42impl std::ops::Add<Points> for Position {
43    type Output = Self;
44
45    fn add(mut self, rhs: Points) -> Self::Output {
46        self += rhs;
47        self
48    }
49}
50
51impl std::ops::AddAssign<Points> for Position {
52    fn add_assign(&mut self, rhs: Points) {
53        self.back = self.front;
54        self.front += rhs;
55    }
56}
57
58impl std::fmt::Display for Position {
59    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
60        let Self {
61            back: back_peg,
62            front: front_peg,
63        } = self;
64        write!(f, "{back_peg:>3} -> {front_peg:>3}")
65    }
66}
67
68#[cfg(test)]
69mod test {
70    use super::*;
71
72    #[test]
73    fn scoring_will_jump_back_peg_over_front() {
74        let mut score = Position {
75            back: Points::from(21),
76            front: Points::from(42),
77        };
78        let points = Points::from(15);
79
80        score += points;
81
82        assert_eq!(score.back(), Points::from(42));
83        assert_eq!(score.front(), Points::from(57));
84        assert_eq!(score.points(), Points::from(57));
85    }
86
87    #[test]
88    fn score_can_be_displayed() {
89        let score = Position {
90            back: Points::from(21),
91            front: Points::from(42),
92        };
93
94        insta::assert_snapshot!(score.to_string(), @" 21 ->  42");
95    }
96}