openpql_prelude/game/
street.rs

1use super::{Board, Card64, CardCount, Display, FromStr, ParseError};
2
3#[derive(
4    Debug, Clone, PartialEq, Eq, Copy, PartialOrd, Ord, Default, Display,
5)]
6pub enum Street {
7    #[default]
8    Preflop = 0,
9    Flop,
10    Turn,
11    River,
12}
13
14impl Street {
15    // won't truncate since n ≤ 5
16    #[allow(clippy::cast_possible_truncation)]
17    pub const fn board_card_count(self) -> CardCount {
18        (match self {
19            Self::Preflop => Board::N_PREFLOP,
20            Self::Flop => Board::N_FLOP,
21            Self::Turn => Board::N_TURN,
22            Self::River => Board::N_RIVER,
23        }) as CardCount
24    }
25}
26
27impl FromStr for Street {
28    type Err = ParseError;
29
30    fn from_str(s: &str) -> Result<Self, Self::Err> {
31        match s.to_ascii_lowercase().trim() {
32            "preflop" => Ok(Self::Preflop),
33            "flop" => Ok(Self::Flop),
34            "turn" => Ok(Self::Turn),
35            "river" => Ok(Self::River),
36
37            _ => Err(ParseError::InvalidStreet(s.into())),
38        }
39    }
40}
41
42impl From<(Board, Street)> for Card64 {
43    fn from((board, street): (Board, Street)) -> Self {
44        match street {
45            Street::Preflop => Self::EMPTY,
46            Street::Flop => board.to_c64_flop(),
47            Street::Turn => board.to_c64_flop() | board.to_c64_turn(),
48            Street::River => {
49                board.to_c64_flop() | board.to_c64_turn() | board.to_c64_river()
50            }
51        }
52    }
53}
54
55#[cfg(any(test, feature = "quickcheck"))]
56impl quickcheck::Arbitrary for Street {
57    #[cfg_attr(coverage_nightly, coverage(off))]
58    fn arbitrary(g: &mut quickcheck::Gen) -> Self {
59        #[allow(unused)]
60        const fn completeness_check(e: Street) {
61            match e {
62                Street::Preflop
63                | Street::Flop
64                | Street::Turn
65                | Street::River => (),
66            }
67        }
68        *g.choose(&[Self::Preflop, Self::Flop, Self::Turn, Self::River])
69            .unwrap()
70    }
71}
72
73#[cfg(test)]
74#[cfg_attr(coverage_nightly, coverage(off))]
75pub mod tests {
76    use super::*;
77    use crate::*;
78
79    #[test]
80    fn test_to_card64_with_board() {
81        assert_eq!(
82            Card64::from((board!("As Kh Qd Jc Ts"), Street::Preflop)),
83            c64!("")
84        );
85
86        assert_eq!(
87            Card64::from((board!("As Kh Qd Jc Ts"), Street::Flop)),
88            c64!("As Kh Qd")
89        );
90
91        assert_eq!(
92            Card64::from((board!("As Kh Qd Jc Ts"), Street::Turn)),
93            c64!("As Kh Qd Jc")
94        );
95
96        assert_eq!(
97            Card64::from((board!("As Kh Qd Jc Ts"), Street::River)),
98            c64!("As Kh Qd Jc Ts")
99        );
100
101        assert_eq!(
102            Card64::from((board!("As Kh Qd"), Street::River)),
103            c64!("As Kh Qd")
104        );
105
106        assert!(Card64::from((board!(""), Street::River)).is_empty());
107    }
108
109    #[quickcheck]
110    fn test_n_board(street: Street) {
111        let n = match street {
112            Street::Preflop => 0,
113            Street::Flop => 3,
114            Street::Turn => 4,
115            Street::River => 5,
116        };
117
118        assert_eq!(street.board_card_count(), n);
119    }
120
121    #[test]
122    fn test_from_str() {
123        assert_eq!(Ok(Street::Preflop), "preFlop".parse());
124        assert_eq!(Ok(Street::Flop), "Flop".parse());
125        assert_eq!(Ok(Street::Turn), "tUrn".parse());
126        assert_eq!(Ok(Street::River), "riVer".parse());
127
128        assert_eq!(Ok(Street::Flop), " flop ".parse(), "should trim");
129
130        assert!("invalid".parse::<Street>().is_err());
131    }
132}