1use crate::{Category, Dice};
8
9fn has_run(dice: Dice, run: u8) -> bool {
11 let mut streak = 0;
12 (1..=6).any(|face| {
13 streak = if dice.count(face) > 0 { streak + 1 } else { 0 };
14 streak >= run
15 })
16}
17
18impl Category {
19 #[must_use]
25 pub fn score(self, dice: Dice) -> u8 {
26 let of_a_kind = |n| (1..=6).any(|face| dice.count(face) >= n);
27 match self {
28 Self::Aces | Self::Twos | Self::Threes | Self::Fours | Self::Fives | Self::Sixes => {
29 let face = self.upper_face().expect("an upper category");
30 dice.count(face) * face
31 }
32 Self::ThreeOfAKind => {
33 if of_a_kind(3) {
34 dice.sum()
35 } else {
36 0
37 }
38 }
39 Self::FourOfAKind => {
40 if of_a_kind(4) {
41 dice.sum()
42 } else {
43 0
44 }
45 }
46 Self::FullHouse => {
47 let counts = dice.counts();
48 if counts.contains(&3) && counts.contains(&2) {
49 25
50 } else {
51 0
52 }
53 }
54 Self::SmallStraight => {
55 if has_run(dice, 4) {
56 30
57 } else {
58 0
59 }
60 }
61 Self::LargeStraight => {
62 if has_run(dice, 5) {
63 40
64 } else {
65 0
66 }
67 }
68 Self::Yahtzee => {
69 if dice.yahtzee_face().is_some() {
70 50
71 } else {
72 0
73 }
74 }
75 Self::Chance => dice.sum(),
76 }
77 }
78}
79
80#[cfg(test)]
81mod tests {
82 use super::*;
83
84 fn dice(s: &str) -> Dice {
85 s.parse().expect("a valid roll")
86 }
87
88 fn scores(s: &str) -> [u8; 13] {
89 Category::ALL.map(|c| c.score(dice(s)))
90 }
91
92 #[test]
93 fn upper_section_counts_one_face() {
94 assert_eq!(scores("13446")[..6], [1, 0, 3, 8, 0, 6]);
95 assert_eq!(scores("55555")[..6], [0, 0, 0, 0, 25, 0]);
96 }
97
98 #[test]
99 fn n_of_a_kind_pays_the_whole_roll() {
100 assert_eq!(Category::ThreeOfAKind.score(dice("33345")), 18);
101 assert_eq!(Category::ThreeOfAKind.score(dice("33445")), 0);
102 assert_eq!(Category::FourOfAKind.score(dice("33334")), 16);
103 assert_eq!(Category::FourOfAKind.score(dice("33345")), 0);
104 assert_eq!(Category::ThreeOfAKind.score(dice("66666")), 30);
106 assert_eq!(Category::FourOfAKind.score(dice("66666")), 30);
107 }
108
109 #[test]
110 fn full_house_is_exactly_three_and_two() {
111 assert_eq!(Category::FullHouse.score(dice("22333")), 25);
112 assert_eq!(Category::FullHouse.score(dice("22334")), 0);
113 assert_eq!(Category::FullHouse.score(dice("22223")), 0);
114 assert_eq!(Category::FullHouse.score(dice("33333")), 0);
116 }
117
118 #[test]
119 fn straights() {
120 assert_eq!(Category::SmallStraight.score(dice("12346")), 30);
121 assert_eq!(Category::SmallStraight.score(dice("23345")), 30);
122 assert_eq!(Category::SmallStraight.score(dice("12356")), 0);
123 assert_eq!(Category::LargeStraight.score(dice("12345")), 40);
124 assert_eq!(Category::LargeStraight.score(dice("23456")), 40);
125 assert_eq!(Category::LargeStraight.score(dice("12346")), 0);
126 assert_eq!(Category::SmallStraight.score(dice("23456")), 30);
128 }
129
130 #[test]
131 fn yahtzee_and_chance() {
132 assert_eq!(Category::Yahtzee.score(dice("44444")), 50);
133 assert_eq!(Category::Yahtzee.score(dice("44443")), 0);
134 assert_eq!(Category::Chance.score(dice("13446")), 18);
135 assert_eq!(Category::Chance.score(dice("66666")), 30);
136 }
137}