1use std::{cmp::Ordering, fmt};
2
3#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq)]
5pub enum Score {
6 #[default]
8 EMPTY,
9 PINS(usize),
11 SPARE,
13 STRIKE
15}
16
17impl Score {
18 pub fn value(&self) -> usize {
20 match self {
21 Score::EMPTY => 0,
22 Score::PINS(value) => *value.min(&10),
23 Score::SPARE | Score::STRIKE => 10
24 }
25 }
26
27 pub fn is_strike(&self) -> bool {
29 if *self == Score::SPARE {
30 return false;
31 }
32
33 if let Score::PINS(value) = self {
34 if value >= &10 {
35 return true;
36 }
37 }
38
39 *self == Score::STRIKE
40 }
41}
42
43impl fmt::Display for Score {
44 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
45 let mut value = match self {
46 Score::EMPTY => String::from("<empty>"),
47 Score::PINS(v) => v.to_string(),
48 Score::SPARE => String::from("/"),
49 Score::STRIKE => String::from("X")
50 };
51
52 if value == "0" {
53 value = String::from("-");
54 }
55
56 write!(formatter, "{}", value)
57 }
58}
59
60impl<V> From<V> for Score where V: Into<usize> {
61 fn from(value: V) -> Self {
62 let v = value.into();
63
64 if v >= 10 {
65 return Self::STRIKE;
66 }
67
68 Self::PINS(v)
69 }
70}
71
72impl PartialOrd for Score {
73 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
74 Some(match self {
75 Score::EMPTY => match other {
76 Score::EMPTY => Ordering::Equal,
77 Score::PINS(_) => Ordering::Less,
78 Score::SPARE => Ordering::Less,
79 Score::STRIKE => Ordering::Less
80 },
81 Score::PINS(a) => match other {
82 Score::EMPTY => a.cmp(&0),
83 Score::PINS(b) => a.cmp(b),
84 Score::SPARE => Ordering::Less,
85 Score::STRIKE => Ordering::Less
86 },
87 Score::SPARE => match other {
88 Score::EMPTY => Ordering::Greater,
89 Score::PINS(b) => self.value().cmp(b),
90 Score::SPARE => Ordering::Equal,
91 Score::STRIKE => Ordering::Less
92 },
93 Score::STRIKE => match other {
94 Score::EMPTY => Ordering::Greater,
95 Score::PINS(_) => Ordering::Greater,
96 Score::SPARE => Ordering::Greater,
97 Score::STRIKE => Ordering::Equal
98 }
99 })
100 }
101}