cribbage_core/
board.rs

1use std::ops::{Index, IndexMut};
2
3use crate::CribbageCoreError;
4
5#[derive(Clone, Copy, Debug, Eq, PartialEq)]
6pub enum TwoPlayers {
7    PlayerOne,
8    PlayerTwo,
9}
10
11pub struct TwoPlayerScore<T> {
12    scores: [T; 2],
13}
14
15impl<T> Index<TwoPlayers> for TwoPlayerScore<T> {
16    type Output = T;
17
18    fn index(&self, index: TwoPlayers) -> &Self::Output {
19        match index {
20            TwoPlayers::PlayerOne => &self.scores[0],
21            TwoPlayers::PlayerTwo => &self.scores[1],
22        }
23    }
24}
25
26impl<T> IndexMut<TwoPlayers> for TwoPlayerScore<T> {
27    fn index_mut(&mut self, index: TwoPlayers) -> &mut T {
28        match index {
29            TwoPlayers::PlayerOne => &mut self.scores[0],
30            TwoPlayers::PlayerTwo => &mut self.scores[1],
31        }
32    }
33}
34
35#[derive(Clone, Copy, Debug, Eq, PartialEq)]
36pub enum ThreePlayers {
37    PlayerOne,
38    PlayerTwo,
39    PlayerThree,
40}
41
42pub struct ThreePlayerScore<T> {
43    scores: [T; 3],
44}
45
46impl<T> Index<ThreePlayers> for ThreePlayerScore<T> {
47    type Output = T;
48
49    fn index(&self, index: ThreePlayers) -> &Self::Output {
50        match index {
51            ThreePlayers::PlayerOne => &self.scores[0],
52            ThreePlayers::PlayerTwo => &self.scores[1],
53            ThreePlayers::PlayerThree => &self.scores[2],
54        }
55    }
56}
57
58impl<T> IndexMut<ThreePlayers> for ThreePlayerScore<T> {
59    fn index_mut(&mut self, index: ThreePlayers) -> &mut T {
60        match index {
61            ThreePlayers::PlayerOne => &mut self.scores[0],
62            ThreePlayers::PlayerTwo => &mut self.scores[1],
63            ThreePlayers::PlayerThree => &mut self.scores[2],
64        }
65    }
66}
67
68#[derive(Clone, Copy, Debug, Eq, PartialEq)]
69pub enum FourPlayers {
70    PlayerOne,
71    PlayerTwo,
72    PlayerThree,
73    PlayerFour,
74}
75
76pub struct FourPlayerScore<T> {
77    scores: [T; 4],
78}
79
80impl<T> Index<FourPlayers> for FourPlayerScore<T> {
81    type Output = T;
82
83    fn index(&self, index: FourPlayers) -> &Self::Output {
84        match index {
85            FourPlayers::PlayerOne => &self.scores[0],
86            FourPlayers::PlayerTwo => &self.scores[1],
87            FourPlayers::PlayerThree => &self.scores[2],
88            FourPlayers::PlayerFour => &self.scores[3],
89        }
90    }
91}
92
93impl<T> IndexMut<FourPlayers> for FourPlayerScore<T> {
94    fn index_mut(&mut self, index: FourPlayers) -> &mut T {
95        match index {
96            FourPlayers::PlayerOne => &mut self.scores[0],
97            FourPlayers::PlayerTwo => &mut self.scores[1],
98            FourPlayers::PlayerThree => &mut self.scores[2],
99            FourPlayers::PlayerFour => &mut self.scores[3],
100        }
101    }
102}
103
104pub trait Score: Copy + PartialEq + PartialOrd + Sized {
105    fn initial() -> Self;
106
107    fn saturating_add(self, other: Self) -> Self;
108
109    fn saturating_sub(self, other: Self) -> Self;
110}
111
112impl Score for u8 {
113    fn initial() -> Self {
114        0
115    }
116
117    fn saturating_add(self, other: Self) -> Self {
118        self.saturating_add(other)
119    }
120
121    fn saturating_sub(self, other: Self) -> Self {
122        self.saturating_sub(other)
123    }
124}
125
126pub struct Board<P, S>
127where
128    S: Index<P>,
129    S::Output: Sized,
130{
131    scores: S,
132    target: S::Output,
133    winner: Option<P>,
134}
135
136impl<P, S> Board<P, S>
137where
138    P: Copy,
139    S: Index<P> + IndexMut<P>,
140    S::Output: Score,
141{
142    pub fn add_points(&mut self, id: P, points: S::Output) -> Result<S::Output, CribbageCoreError> {
143        if self.winner.is_some() {
144            return Err(CribbageCoreError::WinnerExists);
145        }
146
147        self.scores[id] = self.scores[id].saturating_add(points);
148        if self.scores[id] > self.target {
149            self.scores[id] = self.target;
150        }
151
152        if self.scores[id] == self.target {
153            self.winner = Some(id);
154        }
155
156        Ok(self.scores[id])
157    }
158
159    pub fn subtract_points(
160        &mut self,
161        id: P,
162        points: S::Output,
163    ) -> Result<S::Output, CribbageCoreError> {
164        if self.winner.is_some() {
165            return Err(CribbageCoreError::WinnerExists);
166        }
167
168        self.scores[id] = self.scores[id].saturating_sub(points);
169        Ok(self.scores[id])
170    }
171
172    pub fn score(&self, id: P) -> S::Output {
173        self.scores[id]
174    }
175
176    pub fn winner(&self) -> Option<P> {
177        self.winner
178    }
179}
180
181pub fn custom_board<P, S>(scores: S, target: S::Output) -> Board<P, S>
182where
183    S: Index<P>,
184    S::Output: Sized,
185{
186    Board {
187        scores,
188        target,
189        winner: None,
190    }
191}
192
193pub fn standard_two_player_board() -> Board<TwoPlayers, TwoPlayerScore<u8>> {
194    custom_board(TwoPlayerScore { scores: [0; 2] }, 121)
195}
196
197pub fn standard_three_player_board() -> Board<ThreePlayers, ThreePlayerScore<u8>> {
198    custom_board(ThreePlayerScore { scores: [0; 3] }, 121)
199}
200
201pub fn standard_four_player_board() -> Board<FourPlayers, FourPlayerScore<u8>> {
202    custom_board(FourPlayerScore { scores: [0; 4] }, 121)
203}
204
205#[cfg(test)]
206mod tests {
207    use crate::board::{
208        standard_four_player_board, standard_three_player_board, standard_two_player_board,
209        FourPlayers, ThreePlayers, TwoPlayers,
210    };
211    use crate::CribbageCoreError;
212
213    #[test]
214    pub fn test_two_player_board() {
215        let mut board = standard_two_player_board();
216        assert_eq!(board.score(TwoPlayers::PlayerOne), 0);
217        assert_eq!(board.score(TwoPlayers::PlayerTwo), 0);
218
219        assert!(board.add_points(TwoPlayers::PlayerOne, 1).is_ok());
220        assert_eq!(board.score(TwoPlayers::PlayerOne), 1);
221        assert_eq!(board.score(TwoPlayers::PlayerTwo), 0);
222
223        assert!(board.add_points(TwoPlayers::PlayerOne, 119).is_ok());
224        assert_eq!(board.score(TwoPlayers::PlayerOne), 120);
225        assert_eq!(board.winner().is_some(), false);
226        assert_eq!(board.winner(), None);
227
228        assert!(board.add_points(TwoPlayers::PlayerOne, 2).is_ok());
229        assert_eq!(board.score(TwoPlayers::PlayerOne), 121);
230        assert_eq!(board.winner().is_some(), true);
231        assert_eq!(board.winner(), Some(TwoPlayers::PlayerOne));
232
233        assert_eq!(
234            board.add_points(TwoPlayers::PlayerOne, 1),
235            Err(CribbageCoreError::WinnerExists)
236        );
237
238        let mut board = standard_two_player_board();
239        assert!(board.add_points(TwoPlayers::PlayerTwo, 1).is_ok());
240        assert_eq!(board.score(TwoPlayers::PlayerOne), 0);
241        assert_eq!(board.score(TwoPlayers::PlayerTwo), 1);
242
243        assert!(board.add_points(TwoPlayers::PlayerTwo, 119).is_ok());
244        assert_eq!(board.score(TwoPlayers::PlayerTwo), 120);
245        assert_eq!(board.winner().is_some(), false);
246        assert_eq!(board.winner(), None);
247
248        assert!(board.add_points(TwoPlayers::PlayerTwo, 2).is_ok());
249        assert_eq!(board.score(TwoPlayers::PlayerTwo), 121);
250        assert_eq!(board.winner().is_some(), true);
251        assert_eq!(board.winner(), Some(TwoPlayers::PlayerTwo));
252
253        assert_eq!(
254            board.add_points(TwoPlayers::PlayerTwo, 1),
255            Err(CribbageCoreError::WinnerExists)
256        );
257    }
258
259    #[test]
260    pub fn test_three_player_board() {
261        let mut board = standard_three_player_board();
262        assert_eq!(board.score(ThreePlayers::PlayerOne), 0);
263        assert_eq!(board.score(ThreePlayers::PlayerTwo), 0);
264        assert_eq!(board.score(ThreePlayers::PlayerThree), 0);
265
266        assert!(board.add_points(ThreePlayers::PlayerOne, 1).is_ok());
267        assert_eq!(board.score(ThreePlayers::PlayerOne), 1);
268        assert_eq!(board.score(ThreePlayers::PlayerTwo), 0);
269        assert_eq!(board.score(ThreePlayers::PlayerThree), 0);
270
271        assert!(board.add_points(ThreePlayers::PlayerOne, 119).is_ok());
272        assert_eq!(board.score(ThreePlayers::PlayerOne), 120);
273        assert_eq!(board.winner().is_some(), false);
274        assert_eq!(board.winner(), None);
275
276        assert!(board.add_points(ThreePlayers::PlayerOne, 2).is_ok());
277        assert_eq!(board.score(ThreePlayers::PlayerOne), 121);
278        assert_eq!(board.winner().is_some(), true);
279        assert_eq!(board.winner(), Some(ThreePlayers::PlayerOne));
280
281        assert_eq!(
282            board.add_points(ThreePlayers::PlayerOne, 1),
283            Err(CribbageCoreError::WinnerExists)
284        );
285
286        let mut board = standard_three_player_board();
287        assert!(board.add_points(ThreePlayers::PlayerTwo, 1).is_ok());
288        assert_eq!(board.score(ThreePlayers::PlayerOne), 0);
289        assert_eq!(board.score(ThreePlayers::PlayerTwo), 1);
290        assert_eq!(board.score(ThreePlayers::PlayerThree), 0);
291
292        assert!(board.add_points(ThreePlayers::PlayerTwo, 119).is_ok());
293        assert_eq!(board.score(ThreePlayers::PlayerTwo), 120);
294        assert_eq!(board.winner().is_some(), false);
295        assert_eq!(board.winner(), None);
296
297        assert!(board.add_points(ThreePlayers::PlayerTwo, 2).is_ok());
298        assert_eq!(board.score(ThreePlayers::PlayerTwo), 121);
299        assert_eq!(board.winner().is_some(), true);
300        assert_eq!(board.winner(), Some(ThreePlayers::PlayerTwo));
301
302        assert_eq!(
303            board.add_points(ThreePlayers::PlayerTwo, 1),
304            Err(CribbageCoreError::WinnerExists)
305        );
306
307        let mut board = standard_three_player_board();
308        assert!(board.add_points(ThreePlayers::PlayerThree, 1).is_ok());
309        assert_eq!(board.score(ThreePlayers::PlayerOne), 0);
310        assert_eq!(board.score(ThreePlayers::PlayerTwo), 0);
311        assert_eq!(board.score(ThreePlayers::PlayerThree), 1);
312
313        assert!(board.add_points(ThreePlayers::PlayerThree, 119).is_ok());
314        assert_eq!(board.score(ThreePlayers::PlayerThree), 120);
315        assert_eq!(board.winner().is_some(), false);
316        assert_eq!(board.winner(), None);
317
318        assert!(board.add_points(ThreePlayers::PlayerThree, 2).is_ok());
319        assert_eq!(board.score(ThreePlayers::PlayerThree), 121);
320        assert_eq!(board.winner().is_some(), true);
321        assert_eq!(board.winner(), Some(ThreePlayers::PlayerThree));
322
323        assert_eq!(
324            board.add_points(ThreePlayers::PlayerThree, 1),
325            Err(CribbageCoreError::WinnerExists)
326        );
327    }
328
329    #[test]
330    pub fn test_four_player_board() {
331        let mut board = standard_four_player_board();
332        assert_eq!(board.score(FourPlayers::PlayerOne), 0);
333        assert_eq!(board.score(FourPlayers::PlayerTwo), 0);
334        assert_eq!(board.score(FourPlayers::PlayerThree), 0);
335        assert_eq!(board.score(FourPlayers::PlayerFour), 0);
336
337        assert!(board.add_points(FourPlayers::PlayerOne, 1).is_ok());
338        assert_eq!(board.score(FourPlayers::PlayerOne), 1);
339        assert_eq!(board.score(FourPlayers::PlayerTwo), 0);
340        assert_eq!(board.score(FourPlayers::PlayerThree), 0);
341        assert_eq!(board.score(FourPlayers::PlayerFour), 0);
342
343        assert!(board.add_points(FourPlayers::PlayerOne, 119).is_ok());
344        assert_eq!(board.score(FourPlayers::PlayerOne), 120);
345        assert_eq!(board.winner().is_some(), false);
346        assert_eq!(board.winner(), None);
347
348        assert!(board.add_points(FourPlayers::PlayerOne, 2).is_ok());
349        assert_eq!(board.score(FourPlayers::PlayerOne), 121);
350        assert_eq!(board.winner().is_some(), true);
351        assert_eq!(board.winner(), Some(FourPlayers::PlayerOne));
352
353        assert_eq!(
354            board.add_points(FourPlayers::PlayerOne, 1),
355            Err(CribbageCoreError::WinnerExists)
356        );
357
358        let mut board = standard_four_player_board();
359        assert!(board.add_points(FourPlayers::PlayerTwo, 1).is_ok());
360        assert_eq!(board.score(FourPlayers::PlayerOne), 0);
361        assert_eq!(board.score(FourPlayers::PlayerTwo), 1);
362        assert_eq!(board.score(FourPlayers::PlayerThree), 0);
363        assert_eq!(board.score(FourPlayers::PlayerFour), 0);
364
365        assert!(board.add_points(FourPlayers::PlayerTwo, 119).is_ok());
366        assert_eq!(board.score(FourPlayers::PlayerTwo), 120);
367        assert_eq!(board.winner().is_some(), false);
368        assert_eq!(board.winner(), None);
369
370        assert!(board.add_points(FourPlayers::PlayerTwo, 2).is_ok());
371        assert_eq!(board.score(FourPlayers::PlayerTwo), 121);
372        assert_eq!(board.winner().is_some(), true);
373        assert_eq!(board.winner(), Some(FourPlayers::PlayerTwo));
374
375        assert_eq!(
376            board.add_points(FourPlayers::PlayerTwo, 1),
377            Err(CribbageCoreError::WinnerExists)
378        );
379
380        let mut board = standard_four_player_board();
381        assert!(board.add_points(FourPlayers::PlayerThree, 1).is_ok());
382        assert_eq!(board.score(FourPlayers::PlayerOne), 0);
383        assert_eq!(board.score(FourPlayers::PlayerTwo), 0);
384        assert_eq!(board.score(FourPlayers::PlayerThree), 1);
385        assert_eq!(board.score(FourPlayers::PlayerFour), 0);
386
387        assert!(board.add_points(FourPlayers::PlayerThree, 119).is_ok());
388        assert_eq!(board.score(FourPlayers::PlayerThree), 120);
389        assert_eq!(board.winner().is_some(), false);
390        assert_eq!(board.winner(), None);
391
392        assert!(board.add_points(FourPlayers::PlayerThree, 2).is_ok());
393        assert_eq!(board.score(FourPlayers::PlayerThree), 121);
394        assert_eq!(board.winner().is_some(), true);
395        assert_eq!(board.winner(), Some(FourPlayers::PlayerThree));
396
397        assert_eq!(
398            board.add_points(FourPlayers::PlayerThree, 1),
399            Err(CribbageCoreError::WinnerExists)
400        );
401
402        let mut board = standard_four_player_board();
403        assert!(board.add_points(FourPlayers::PlayerFour, 1).is_ok());
404        assert_eq!(board.score(FourPlayers::PlayerOne), 0);
405        assert_eq!(board.score(FourPlayers::PlayerTwo), 0);
406        assert_eq!(board.score(FourPlayers::PlayerThree), 0);
407        assert_eq!(board.score(FourPlayers::PlayerFour), 1);
408
409        assert!(board.add_points(FourPlayers::PlayerFour, 119).is_ok());
410        assert_eq!(board.score(FourPlayers::PlayerFour), 120);
411        assert_eq!(board.winner().is_some(), false);
412        assert_eq!(board.winner(), None);
413
414        assert!(board.add_points(FourPlayers::PlayerFour, 2).is_ok());
415        assert_eq!(board.score(FourPlayers::PlayerFour), 121);
416        assert_eq!(board.winner().is_some(), true);
417        assert_eq!(board.winner(), Some(FourPlayers::PlayerFour));
418
419        assert_eq!(
420            board.add_points(FourPlayers::PlayerFour, 1),
421            Err(CribbageCoreError::WinnerExists)
422        );
423    }
424}