1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
/// This module contains all the rules for the game of Backgammon
pub mod player;

use std::fmt;

/// Holds the rules of the match
#[derive(Debug, Clone, Copy, Eq, Ord, PartialEq, PartialOrd, Hash)]
pub struct Rules {
    /// The amount of points to reach for declaring a winner, default is 7.
    pub points: u32,
    /// When offered the cube, allow to re-double but keep it, default is false.
    pub beaver: bool,
    /// If a player plays "beaver", the other may double again, letting the opponent keep the cube.
    /// Default is false
    pub raccoon: bool,
    /// If both players roll the same opening number, the dice is doubled, remaining in the middle
    /// of the board. Default is false.
    pub murphy: bool,
    /// How often to apply automatic doubling rule. 0 means always on. Default is 0.
    pub murphy_limit: u8,
    /// Gammon and Backgammon only count for double or triple values if the cube has already been
    /// offered. Default is false.
    pub jacoby: bool,
    /// When a player first reaches a score of points - 1, no doubling is allowed for the following
    /// game. Default is true.
    pub crawford: bool,
    /// Permits to double after Crawford game only if both players have rolled at least twice.
    /// Default is false.
    pub holland: bool,
}

impl Default for Rules {
    fn default() -> Self {
        Rules {
            points: 7,
            beaver: false,
            raccoon: false,
            murphy: false,
            murphy_limit: 0,
            jacoby: false,
            crawford: true,
            holland: false,
        }
    }
}

// implement Display trait
impl fmt::Display for Rules {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "Points: {}, Beaver: {}, Raccoon: {}, Murphy: {}, Murphy Limit: {}, Jacoby: {}, Crawford: {}, Holland: {}",
            self.points, self.beaver, self.raccoon, self.murphy, self.murphy_limit, self.jacoby, self.crawford, self.holland
        )
    }
}

/// SetRules allows to modify the rules
pub trait SetRules {
    /// Set the amount of points to reach for declaring a winner
    fn with_points(self, points: u32) -> Self;
    /// When offered the cube, allow to re-double but keep it
    fn with_beaver(self) -> Self;
    /// If a player plays "beaver", the other may double again, letting the opponent keep the cube
    fn with_raccoon(self) -> Self;
    /// If both players roll the same opening number, the dice is doubled, remaining in the middle
    /// of the board
    fn with_murphy(self, limit: u8) -> Self;
    /// Gammon and Backgammon only count for double or triple values if the cube has already been
    /// offered
    fn with_jacoby(self) -> Self;
    /// When a player first reaches a score of points - 1, no doubling is allowed for the following
    /// game
    fn with_crawford(self) -> Self;
    /// Permits to double after Crawford game only if both players have rolled at least twice
    fn with_holland(self) -> Self;
}

/// Implements SetRules for Rules
impl SetRules for Rules {
    fn with_points(mut self, points: u32) -> Self {
        self.points = points;
        self
    }

    fn with_beaver(mut self) -> Self {
        self.beaver = true;
        self
    }

    fn with_raccoon(mut self) -> Self {
        self.raccoon = true;
        self
    }

    fn with_murphy(mut self, limit: u8) -> Self {
        self.murphy = true;
        self.murphy_limit = limit;
        self
    }

    fn with_jacoby(mut self) -> Self {
        self.jacoby = true;
        self
    }

    fn with_crawford(mut self) -> Self {
        self.crawford = true;
        self
    }

    fn with_holland(mut self) -> Self {
        self.holland = true;
        self
    }
}

/// Test if default rule is created correctly and if the rules can be modified
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_default_rules() {
        let rules = Rules::default();
        assert_eq!(rules.points, 7);
        assert!(!rules.beaver);
        assert!(!rules.raccoon);
        assert!(!rules.murphy);
        assert_eq!(rules.murphy_limit, 0);
        assert!(!rules.jacoby);
        assert!(rules.crawford);
        assert!(!rules.holland);
    }

    #[test]
    fn test_set_rules() {
        let rules = Rules::default()
            .with_points(5)
            .with_beaver()
            .with_raccoon()
            .with_murphy(3)
            .with_jacoby()
            .with_crawford()
            .with_holland();
        assert_eq!(rules.points, 5);
        assert!(rules.beaver);
        assert!(rules.raccoon);
        assert!(rules.murphy);
        assert_eq!(rules.murphy_limit, 3);
        assert!(rules.jacoby);
        assert!(rules.crawford);
        assert!(rules.holland);
    }

    #[test]
    fn test_with_holland() {
        let rules = Rules::default().with_holland();
        assert!(rules.crawford);
    }

    #[test]
    fn test_with_raccoon() {
        let rules = Rules::default().with_raccoon();
        assert!(rules.raccoon);
    }
}