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
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
/// Implements the board
mod board;
pub use board::{Board, BoardDisplay, Move};
/// Implements the double dice or cube
mod cube;
pub use cube::Cube;
/// Implements the players
mod player;
pub use player::Player;
/// Implements the pair of dices
mod dices;
pub use dices::{Dices, Roll};

use serde::{Deserialize, Serialize};
use std::fmt;

/// Holds all the rule settings
#[derive(Debug, Clone, Copy, Eq, Ord, PartialEq, PartialOrd, Serialize, Deserialize, Hash)]
pub struct Rules {
    /// The amount of points to reach for declaring a winner of the match, 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
        )
    }
}

/// Allows to modify the rules
pub trait MatchRules {
    /// Set the amount of points to reach for declaring a winner
    fn with_points(self, points: u32) -> Self;
    /// When a player first reaches a score of points - 1, no doubling is allowed for the following
    /// game
    fn with_crawford(self) -> Self;
}

/// Allows to modify the rules
pub trait GameRules {
    /// 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;
    /// 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 MatchRules for Rules {
    fn with_points(mut self, points: u32) -> Self {
        self.points = points;
        self
    }

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

/// Implements SetRules for Rules
impl GameRules for Rules {
    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_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);
    }
}