Skip to main content

backgammon/
rules.rs

1/*
2 * BSD 2-Clause License
3 *
4 * Copyright (c) 2020-2026, Carlo Strub <cs@carlostrub.ch>
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright notice, this
10 *    list of conditions and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright notice,
13 *    this list of conditions and the following disclaimer in the documentation
14 *    and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28use crate::Error;
29use std::fmt;
30
31pub use board::{Board, BoardDisplay, Move, Position};
32pub use cube::Cube;
33pub use dice::{Dice, Roll};
34pub use player::Player;
35
36/// Implements the board.
37mod board;
38/// Implements the double dice or cube.
39mod cube;
40/// Implements the pair of dice.
41mod dice;
42/// Implements the players.
43mod player;
44
45/// Provides commonly used types and structures for ease of use in external modules. It is
46/// recommended to always use the prelude module for convenience.
47pub mod prelude {
48    pub use crate::rules::{Board, BoardDisplay, Dice, Move, Player, Position, Roll};
49}
50
51/// Holds all the rule settings.
52#[derive(Debug, Clone, Copy, Eq, Ord, PartialEq, PartialOrd, Hash)]
53pub struct Rules {
54    /// The amount points to reach for declaring a winner of the match, default is 7.
55    points: u64,
56    /// Use the double dice or cube, default is true.
57    cube_play: bool,
58    /// When offered the cube, allow to re-double but keep it, default is false.
59    beaver: bool,
60    /// If a player plays “beaver”, the other may double again, letting the opponent keep the cube.
61    /// Default is false.
62    raccoon: bool,
63    /// If both players roll the same opening number, the dice is doubled, remaining in the middle
64    /// of the board. Default is false.
65    murphy: bool,
66    /// How often to apply the automatic doubling rule in a Murphy game. 0 means always on.
67    /// Default is 0.
68    murphy_limit: u8,
69    /// Gammon and Backgammon only count for double or triple values if the cube has already been
70    /// offered. Default is false.
71    jacobi: bool,
72    /// When a player first reaches a score of points - 1, no doubling is allowed for the following
73    /// game. Default is true.
74    crawford: bool,
75    /// Permits to double after the Crawford game only if both players have rolled at least twice.
76    /// Default is false.
77    holland: bool,
78}
79
80impl Default for Rules {
81    /// Sets the default rules:
82    /// - Points: 7,
83    /// - Beaver: false,
84    /// - Raccoon: false,
85    /// - Murphy: false,
86    /// - Jacobi: false,
87    /// - Crawford: true,
88    /// - Holland: false.
89    fn default() -> Self {
90        Rules {
91            points: 7,
92            cube_play: true,
93            beaver: false,
94            raccoon: false,
95            murphy: false,
96            murphy_limit: 0,
97            jacobi: false,
98            crawford: true,
99            holland: false,
100        }
101    }
102}
103
104// implement Display trait
105impl fmt::Display for Rules {
106    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
107        write!(
108            f,
109            "Points: {}, Cube: {}, Beaver: {}, Raccoon: {}, Murphy: {}, Murphy Limit: {}, Jacobi: {}, \
110            Crawford: {}, Holland: {}",
111            self.points,
112            self.cube_play,
113            self.beaver,
114            self.raccoon,
115            self.murphy,
116            self.murphy_limit,
117            self.jacobi,
118            self.crawford,
119            self.holland
120        )
121    }
122}
123
124/// This trait provides methods to modify the rules of a match.
125pub trait MatchRules {
126    /// Set the number of points required to win the match.
127    fn set_points(&mut self, points: u64) -> Result<(), Error>;
128
129    /// Get the number of points required to win the match.
130    fn get_points(&self) -> Result<u64, Error>;
131
132    /// Set the cube status.
133    fn set_cube_play(&mut self, cube: bool) -> Result<(), Error>;
134
135    /// Get the cube status.
136    fn get_cube_play(&self) -> Result<bool, Error>;
137
138    /// Apply the Crawford rule: if the first player reaches a score of points - 1, no doubling is
139    /// allowed for the following game.
140    fn set_crawford(&mut self, state: bool) -> Result<(), Error>;
141
142    /// Get the Crawford rule status.
143    fn get_crawford(&self) -> Result<bool, Error>;
144
145    /// Apply the Beaver rule: if offered the cube, allow to re-double but keep it.
146    fn set_beaver(&mut self, state: bool) -> Result<(), Error>;
147
148    /// Get the Beaver rule status.
149    fn get_beaver(&self) -> Result<bool, Error>;
150
151    /// Apply the Raccoon rule: if a player beavered the offered cube, the other may double again,
152    /// letting the player keep the cube. This requires the Crawford rule to be enabled.
153    fn set_raccoon(&mut self, state: bool) -> Result<(), Error>;
154
155    /// Get the Raccoon rule status.
156    fn get_raccoon(&self) -> Result<bool, Error>;
157
158    /// Apply the Murphy rule: if both players roll the same opening number, the cube is doubled,
159    /// remaining in the middle of the board. The limit parameter defines how often to apply the
160    /// automatic doubling rule in a Murphy game. 0 means always on.
161    fn set_murphy(&mut self, state: bool, limit: u8) -> Result<(), Error>;
162
163    /// Get the Murphy rule status and the limit setting. A limit of 0 means that the Murphy rule is
164    /// always on, given the rule is enabled.
165    fn get_murphy(&self) -> Result<(bool, u8), Error>;
166
167    /// Apply the Jacobi rule: Gammon and Backgammon only count for double or triple values if the
168    /// cube has already been offered.
169    fn set_jacobi(&mut self, state: bool) -> Result<(), Error>;
170
171    /// Get the Jacobi rule status.
172    fn get_jacobi(&self) -> Result<bool, Error>;
173
174    /// Apply the Holland rule: Permits to double after the Crawford game only if both players have
175    /// rolled at least twice. Requires the Crawford rule to be enabled.
176    fn set_holland(&mut self, state: bool) -> Result<(), Error>;
177
178    /// Get the Holland rule status.
179    fn get_holland(&self) -> Result<bool, Error>;
180}
181
182impl MatchRules for Rules {
183    fn set_points(&mut self, points: u64) -> Result<(), Error> {
184        if points < 1 {
185            return Err(Error::PointsInvalid);
186        }
187        self.points = points;
188        Ok(())
189    }
190
191    /// Returns the number of points required to win the match.
192    fn get_points(&self) -> Result<u64, Error> {
193        Ok(self.points)
194    }
195
196    fn set_cube_play(&mut self, cube: bool) -> Result<(), Error> {
197        self.cube_play = cube;
198        Ok(())
199    }
200
201    fn get_cube_play(&self) -> Result<bool, Error> {
202        Ok(self.cube_play)
203    }
204
205    fn set_crawford(&mut self, state: bool) -> Result<(), Error> {
206        self.crawford = state;
207        Ok(())
208    }
209
210    /// Returns whether the Crawford rule is enabled.
211    fn get_crawford(&self) -> Result<bool, Error> {
212        Ok(self.crawford)
213    }
214
215    fn set_beaver(&mut self, state: bool) -> Result<(), Error> {
216        self.beaver = state;
217        Ok(())
218    }
219
220    /// Returns whether the beaver rule is enabled.
221    fn get_beaver(&self) -> Result<bool, Error> {
222        Ok(self.beaver)
223    }
224
225    fn set_raccoon(&mut self, state: bool) -> Result<(), Error> {
226        self.raccoon = state;
227        Ok(())
228    }
229
230    /// Returns whether the raccoon rule is enabled.
231    fn get_raccoon(&self) -> Result<bool, Error> {
232        Ok(self.raccoon)
233    }
234
235    fn set_murphy(&mut self, state: bool, limit: u8) -> Result<(), Error> {
236        self.murphy = state;
237        self.murphy_limit = limit;
238        Ok(())
239    }
240
241    /// Returns the Murphy limit if murphy rule is enabled.
242    fn get_murphy(&self) -> Result<(bool, u8), Error> {
243        if !self.murphy {
244            return Ok((false, 0));
245        }
246        Ok((true, self.murphy_limit))
247    }
248
249    fn set_jacobi(&mut self, state: bool) -> Result<(), Error> {
250        self.jacobi = state;
251        Ok(())
252    }
253
254    /// Returns whether the Jacobi rule is enabled.
255    fn get_jacobi(&self) -> Result<bool, Error> {
256        Ok(self.jacobi)
257    }
258
259    fn set_holland(&mut self, state: bool) -> Result<(), Error> {
260        self.crawford = state;
261        self.holland = state;
262        Ok(())
263    }
264
265    /// Returns whether the Holland rule is enabled.
266    fn get_holland(&self) -> Result<bool, Error> {
267        Ok(self.holland)
268    }
269}
270
271/// Test if the default rule is created correctly and if the rules can be modified.
272#[cfg(test)]
273mod tests {
274    use super::*;
275
276    #[test]
277    fn test_default_rules() {
278        let rules = Rules::default();
279        assert_eq!(rules.points, 7);
280        assert!(!rules.beaver);
281        assert!(!rules.raccoon);
282        assert!(!rules.murphy);
283        assert_eq!(rules.murphy_limit, 0);
284        assert!(!rules.jacobi);
285        assert!(rules.crawford);
286        assert!(!rules.holland);
287    }
288
289    #[test]
290    fn test_display() {
291        let rules = Rules::default();
292        assert_eq!(
293            format!("{}", rules),
294            "Points: 7, Cube: true, Beaver: false, Raccoon: false, Murphy: false, Murphy Limit: \
295            0, Jacobi: false, Crawford: true, Holland: false"
296        );
297    }
298
299    #[test]
300    fn test_set_points_valid() -> Result<(), Error> {
301        let mut rules = Rules::default();
302        rules.set_points(10)?;
303
304        assert_eq!(rules.get_points(), Ok(10));
305        Ok(())
306    }
307
308    #[test]
309    fn test_set_points_invalid() {
310        let mut rules = Rules::default();
311        let result = rules.set_points(0);
312        assert!(result.is_err());
313        assert_eq!(result.unwrap_err(), Error::PointsInvalid);
314    }
315
316    #[test]
317    fn test_set_crawford() -> Result<(), Error> {
318        let mut rules = Rules::default();
319        rules.set_crawford(true)?;
320
321        assert_eq!(rules.get_crawford(), Ok(true));
322        Ok(())
323    }
324
325    #[test]
326    fn test_set_beaver() -> Result<(), Error> {
327        let mut rules = Rules::default();
328        rules.set_beaver(true)?;
329
330        assert_eq!(rules.get_beaver(), Ok(true));
331        Ok(())
332    }
333
334    #[test]
335    fn test_set_raccoon() -> Result<(), Error> {
336        let mut rules = Rules::default();
337        rules.set_raccoon(true)?;
338
339        assert_eq!(rules.get_raccoon(), Ok(true));
340        Ok(())
341    }
342
343    #[test]
344    fn test_set_murphy() -> Result<(), Error> {
345        let mut rules = Rules::default();
346        rules.set_murphy(true, 8)?;
347
348        assert_eq!(rules.get_murphy(), Ok((true, 8)));
349        Ok(())
350    }
351
352    #[test]
353    fn test_set_murphy_false() -> Result<(), Error> {
354        let rules = Rules::default();
355
356        assert_eq!(rules.get_murphy(), Ok((false, 0)));
357        Ok(())
358    }
359
360    #[test]
361    fn test_set_jacobi() -> Result<(), Error> {
362        let mut rules = Rules::default();
363        rules.set_jacobi(true)?;
364
365        assert_eq!(rules.get_jacobi(), Ok(true));
366        Ok(())
367    }
368
369    #[test]
370    fn test_set_holland() -> Result<(), Error> {
371        let mut rules = Rules::default();
372        rules.set_crawford(false)?;
373        rules.set_holland(true)?;
374
375        assert_eq!(rules.get_holland(), Ok(true));
376        assert_eq!(rules.get_crawford(), Ok(true));
377        Ok(())
378    }
379}