rustoku_lib/core/techniques/
flags.rs1use bitflags::bitflags;
2use serde::{Deserialize, Serialize};
3
4bitflags! {
5 #[repr(transparent)]
15 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
16 pub struct TechniqueFlags: u32 {
17 const NAKED_SINGLES = 1 << 0;
19 const HIDDEN_SINGLES = 1 << 1;
21
22 const NAKED_PAIRS = 1 << 8;
24 const HIDDEN_PAIRS = 1 << 9;
26 const LOCKED_CANDIDATES = 1 << 10;
28 const NAKED_TRIPLES = 1 << 11;
30 const HIDDEN_TRIPLES = 1 << 12;
32
33 const X_WING = 1 << 16;
35 const NAKED_QUADS = 1 << 17;
37 const HIDDEN_QUADS = 1 << 18;
39 const SWORDFISH = 1 << 19;
41 const JELLYFISH = 1 << 20;
43 const SKYSCRAPER = 1 << 21;
45
46 const W_WING = 1 << 24;
48 const XY_WING = 1 << 25;
50 const XYZ_WING = 1 << 26;
52 const ALTERNATING_INFERENCE_CHAIN = 1 << 27;
54
55 #[deprecated(note = "use X_WING instead")]
57 const XWING = Self::X_WING.bits();
58
59 const EASY = 0x0000_00FF;
61 const MEDIUM = 0x0000_FF00;
63 const HARD = 0x00FF_0000;
65 const EXPERT = 0xFF00_0000;
67 }
68}
69
70#[derive(
71 Debug,
72 Clone,
73 Copy,
74 PartialEq,
75 Eq,
76 PartialOrd,
77 Ord,
78 Hash,
79 clap::ValueEnum,
80 Serialize,
81 Deserialize,
82)]
83pub enum Difficulty {
84 Easy,
85 Medium,
86 Hard,
87 Expert,
88}
89
90impl std::fmt::Display for Difficulty {
91 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
92 write!(f, "{}", self.difficulty_name())
93 }
94}
95
96impl From<TechniqueFlags> for Difficulty {
97 fn from(flags: TechniqueFlags) -> Self {
98 if !(flags & TechniqueFlags::EXPERT).is_empty() {
99 Difficulty::Expert
100 } else if !(flags & TechniqueFlags::HARD).is_empty() {
101 Difficulty::Hard
102 } else if !(flags & TechniqueFlags::MEDIUM).is_empty() {
103 Difficulty::Medium
104 } else {
105 Difficulty::Easy
106 }
107 }
108}
109
110impl TechniqueFlags {
111 pub fn difficulty(&self) -> Difficulty {
113 Difficulty::from(*self)
114 }
115
116 pub fn difficulty_name(&self) -> &'static str {
118 self.difficulty().difficulty_name()
119 }
120}
121
122impl Difficulty {
123 pub fn difficulty_name(&self) -> &'static str {
124 match self {
125 Difficulty::Easy => "Easy",
126 Difficulty::Medium => "Medium",
127 Difficulty::Hard => "Hard",
128 Difficulty::Expert => "Expert",
129 }
130 }
131}
132
133impl std::str::FromStr for Difficulty {
134 type Err = ();
135
136 fn from_str(s: &str) -> Result<Self, Self::Err> {
137 match s.to_lowercase().as_str() {
138 "easy" => Ok(Difficulty::Easy),
139 "medium" => Ok(Difficulty::Medium),
140 "hard" => Ok(Difficulty::Hard),
141 "expert" => Ok(Difficulty::Expert),
142 _ => Err(()),
143 }
144 }
145}
146
147#[cfg(test)]
148mod tests {
149 use super::*;
150
151 #[test]
152 fn test_difficulty_display() {
153 assert_eq!(format!("{}", Difficulty::Easy), "Easy");
154 assert_eq!(format!("{}", Difficulty::Medium), "Medium");
155 assert_eq!(format!("{}", Difficulty::Hard), "Hard");
156 assert_eq!(format!("{}", Difficulty::Expert), "Expert");
157 }
158
159 #[test]
160 fn test_difficulty_name() {
161 assert_eq!(Difficulty::Easy.difficulty_name(), "Easy");
162 assert_eq!(Difficulty::Medium.difficulty_name(), "Medium");
163 assert_eq!(Difficulty::Hard.difficulty_name(), "Hard");
164 assert_eq!(Difficulty::Expert.difficulty_name(), "Expert");
165 }
166
167 #[test]
168 fn test_technique_flags_difficulty() {
169 assert_eq!(TechniqueFlags::NAKED_SINGLES.difficulty(), Difficulty::Easy);
171 assert_eq!(TechniqueFlags::NAKED_PAIRS.difficulty(), Difficulty::Medium);
172 assert_eq!(TechniqueFlags::X_WING.difficulty(), Difficulty::Hard);
173 assert_eq!(TechniqueFlags::W_WING.difficulty(), Difficulty::Expert);
174
175 let combined = TechniqueFlags::NAKED_SINGLES | TechniqueFlags::X_WING;
177 assert_eq!(combined.difficulty(), Difficulty::Hard);
178
179 let all = TechniqueFlags::all();
180 assert_eq!(all.difficulty(), Difficulty::Expert);
181 }
182
183 #[test]
184 fn test_from_technique_flags_for_difficulty() {
185 assert_eq!(
186 Difficulty::from(TechniqueFlags::HIDDEN_SINGLES),
187 Difficulty::Easy
188 );
189 assert_eq!(
190 Difficulty::from(TechniqueFlags::LOCKED_CANDIDATES),
191 Difficulty::Medium
192 );
193 assert_eq!(
194 Difficulty::from(TechniqueFlags::JELLYFISH),
195 Difficulty::Hard
196 );
197 assert_eq!(
198 Difficulty::from(TechniqueFlags::XYZ_WING),
199 Difficulty::Expert
200 );
201 }
202}