open_pql/base/
suit4.rs

1use super::*;
2
3#[cfg(any(test, feature = "benchmark"))]
4#[macro_export]
5macro_rules! s4 {
6    ($s:expr) => {
7        $crate::Suit4::from(
8            $s.chars()
9                .filter(|c| !c.is_whitespace())
10                .map(|c| $crate::Suit::try_from(c).unwrap())
11                .collect::<Vec<_>>()
12                .as_ref(),
13        )
14    };
15}
16
17/// Suit Masks
18/// # Memory Layout:
19/// ```text
20/// [8, 0]:   xxxxCDHS // x: unused
21/// ```
22#[derive(Copy, Clone, PartialEq, Eq, BitAnd, BitOr)]
23pub struct Suit4(u8);
24
25impl Suit4 {
26    /// Constructs [Suit4] from [u8]
27    ///
28    /// # Examples
29    ///
30    /// ```
31    /// use open_pql::{Suit, Suit4};
32    ///
33    /// let i: u8 = 0b0011;
34    /// let suits: Suit4 = Suit4::from_u8(i);
35    ///
36    /// assert_eq!(suits, Suit4::from([Suit::S, Suit::H].as_ref()));
37    /// ```
38    #[must_use]
39    #[inline]
40    pub const fn from_u8(v: u8) -> Self {
41        Self(v)
42    }
43
44    /// Returns the inner [u8]
45    ///
46    /// # Examples
47    ///
48    /// ```
49    /// use open_pql::Suit4;
50    ///
51    /// let i: u8 = 0b0011;
52    /// let suits: Suit4 = Suit4::from_u8(i);
53    ///
54    /// assert_eq!(i, suits.to_u8());
55    /// ```
56    #[must_use]
57    #[inline]
58    pub const fn to_u8(self) -> u8 {
59        self.0
60    }
61
62    #[inline]
63    const fn from_suit(s: Suit) -> Self {
64        Self(1 << s as u8)
65    }
66
67    /// Constructs an empty [Suit4]
68    ///
69    /// # Examples
70    ///
71    /// ```
72    /// use open_pql::Suit4;
73    ///
74    /// let suits: Suit4 = Suit4::empty();
75    ///
76    /// assert_eq!(suits, Suit4::from([].as_ref()));
77    /// ```
78    #[must_use]
79    #[inline]
80    pub const fn empty() -> Self {
81        Self(0)
82    }
83
84    /// Checks whether all suit masks are unset
85    ///
86    /// # Examples
87    ///
88    /// ```
89    /// use open_pql::{Suit, Suit4};
90    ///
91    /// let empty: Suit4 = Suit4::empty();
92    /// let not_empty: Suit4 = Suit4::from(Suit::D);
93    ///
94    /// assert!(empty.is_empty());
95    /// assert!(!not_empty.is_empty());
96    /// ```
97    pub const fn is_empty(self) -> bool {
98        self.0 == 0
99    }
100
101    /// Mark a [Suit]
102    ///
103    /// # Examples
104    ///
105    /// ```
106    /// use open_pql::{Suit, Suit4};
107    ///
108    /// let mut suits: Suit4 = Suit4::empty();
109    /// suits.set(Suit::D);
110    ///
111    /// assert_eq!(suits, Suit4::from(Suit::D));
112    /// ```
113    #[inline]
114    pub fn set(&mut self, s: Suit) {
115        self.0 |= 1 << s as u8;
116    }
117
118    /// Unmark a [Suit]
119    ///
120    /// # Examples
121    ///
122    /// ```
123    /// use open_pql::{Suit, Suit4};
124    ///
125    /// let mut suits: Suit4 = Suit4::from(Suit::D);
126    /// suits.unset(Suit::D);
127    ///
128    /// assert_eq!(suits, Suit4::empty());
129    /// ```
130    #[inline]
131    pub fn unset(&mut self, s: Suit) {
132        self.0 &= !(1 << s as u8);
133    }
134
135    /// Checks whether a [Suit] is marked
136    ///
137    /// # Examples
138    ///
139    /// ```
140    /// use open_pql::{Suit, Suit4};
141    ///
142    /// let mut suits: Suit4 = Suit4::from(Suit::D);
143    ///
144    /// assert!(suits.contains_suit(Suit::D));
145    /// assert!(!suits.contains_suit(Suit::H));
146    /// ```
147    #[must_use]
148    #[inline]
149    pub const fn contains_suit(self, s: Suit) -> bool {
150        let v = 1u8 << (s as u8);
151        v == v & self.0
152    }
153
154    /// Returns the number of marked suits
155    ///
156    /// # Examples
157    ///
158    /// ```
159    /// use open_pql::{Suit, Suit4};
160    ///
161    /// let mut suits: Suit4 = Suit4::from(Suit::D);
162    ///
163    /// assert_eq!(suits.count(), 1);
164    /// ```
165    #[must_use]
166    #[inline]
167    pub const fn count(&self) -> PQLCardCount {
168        self.0.count_ones().to_le_bytes()[0]
169    }
170}
171
172impl From<Suit> for Suit4 {
173    fn from(s: Suit) -> Self {
174        Self::from_suit(s)
175    }
176}
177
178impl From<&[Suit]> for Suit4 {
179    fn from(ss: &[Suit]) -> Self {
180        let mut res = Self::empty();
181
182        for s in ss {
183            res.set(*s);
184        }
185
186        res
187    }
188}
189
190pub fn u8_to_suit_str(v: u8) -> String {
191    let to_c = |i: u8| {
192        if v & 1 << i == 0 {
193            '\0'
194        } else {
195            SUIT_NAMES[i as usize]
196        }
197    };
198
199    (0..N_SUITS)
200        .map(to_c)
201        .filter(|c| c.is_alphanumeric())
202        .collect::<String>()
203}
204
205impl fmt::Debug for Suit4 {
206    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
207        f.debug_tuple("Suit4")
208            .field(&format_args!("{}", u8_to_suit_str(self.0)))
209            .finish()
210    }
211}
212
213impl From<Card64> for Suit4 {
214    fn from(c: Card64) -> Self {
215        // TODO: refactor
216
217        let [s, h, d, c] = Suit::ARR_ALL.map(|s| c.count_by_suit(s) > 0);
218
219        Self(
220            u8::from(s)
221                | u8::from(h) << 1
222                | u8::from(d) << 2
223                | u8::from(c) << 3,
224        )
225    }
226}
227
228#[cfg(test)]
229mod tests {
230    use super::*;
231
232    #[test]
233    fn test_empty() {
234        assert_eq!(Suit4::empty(), Suit4(0));
235        assert!(Suit4::empty().is_empty());
236        assert!(!Suit4(1).is_empty());
237    }
238
239    #[quickcheck]
240    fn test_set_and_contains(s: Suit) {
241        let mut suits = Suit4::empty();
242
243        suits.set(s);
244
245        assert!(!suits.is_empty());
246        assert!(suits.contains_suit(s));
247
248        suits.unset(s);
249
250        assert!(suits.is_empty());
251        assert!(!suits.contains_suit(s));
252    }
253
254    #[quickcheck]
255    fn test_u8(i: u8) -> TestResult {
256        if i > 0b1111 {
257            return TestResult::discard();
258        }
259
260        assert_eq!(Suit4(i), Suit4::from_u8(i));
261        assert_eq!(i, Suit4(i).to_u8());
262
263        TestResult::passed()
264    }
265
266    #[quickcheck]
267    fn test_from_suit(s1: Suit, s2: Suit) {
268        let suits = Suit4::from(s1);
269
270        assert!(suits.contains_suit(s1));
271
272        let suits = Suit4::from([s1, s2].as_ref());
273
274        assert!(suits.contains_suit(s1));
275        assert!(suits.contains_suit(s2));
276    }
277
278    #[quickcheck]
279    fn test_bit_and(s1: Suit, s2: Suit) {
280        let a = Suit4::from(s1);
281        let b = Suit4::from(s2);
282
283        assert_eq!((a & b).is_empty(), s1 != s2);
284    }
285
286    #[quickcheck]
287    fn test_bit_or(s1: Suit, s2: Suit) {
288        let a = Suit4::from(s1);
289        let b = Suit4::from(s2);
290
291        assert!((a | b).contains_suit(s1));
292        assert!((a | b).contains_suit(s2));
293    }
294
295    #[quickcheck]
296    fn test_count(s1: Suit, s2: Suit) {
297        let suits = Suit4::from([s1, s2].as_ref());
298
299        let count = if s1 == s2 { 1 } else { 2 };
300
301        assert_eq!(count, suits.count());
302    }
303
304    #[quickcheck]
305    fn test_from_card64(cards: Vec<Card>) -> TestResult {
306        let mut suits = Suit4::empty();
307
308        for i in 0..cards.len() {
309            suits.set(cards[i].s);
310
311            let c64: Card64 = cards[0..=i].into();
312
313            assert_eq!(Suit4::from(c64), suits);
314        }
315
316        TestResult::passed()
317    }
318
319    #[test]
320    fn test_debug() {
321        let s = format!("{:?}", s4!("s") | s4!("d"));
322        assert_eq!(s, "Suit4(sd)");
323    }
324}