1use super::{Card, Card64, HandN, Hash, N_FLOP, fmt};
2
3#[cfg(any(test, feature = "benchmark"))]
13#[macro_export]
14macro_rules! flop {
15 ($s:expr) => {
16 $crate::Flop::from(
17 <[$crate::Card; 3]>::try_from($crate::Card::new_vec($s)).unwrap(),
18 )
19 };
20}
21
22#[cfg(any(test, feature = "benchmark"))]
32#[macro_export]
33macro_rules! board {
34 ($s:expr) => {
35 $crate::Board::from(
36 $crate::Card::new_vec($s).as_ref() as &[$crate::Card]
37 )
38 };
39}
40
41pub type Flop = HandN<3>;
42
43#[derive(Copy, Clone, PartialEq, Eq, Hash, Default)]
62pub struct Board {
63 pub flop: Option<Flop>,
64 pub turn: Option<Card>,
65 pub river: Option<Card>,
66}
67
68impl Board {
69 pub fn from_slice(cards: &[Card]) -> Self {
71 let flop = if cards.len() >= N_FLOP {
72 Some(Flop::from_slice(&cards[0..3]))
73 } else {
74 None
75 };
76 let turn = cards.get(3).copied();
77 let river = cards.get(4).copied();
78
79 Self { flop, turn, river }
80 }
81
82 #[must_use]
84 #[inline]
85 pub const fn is_empty(&self) -> bool {
86 self.flop.is_none()
87 }
88
89 pub fn len(&self) -> usize {
91 match self.flop {
92 Some(_) => 3 + self.turn.iter().count() + self.river.iter().count(),
93 None => 0,
94 }
95 }
96
97 pub fn iter(&self) -> impl Iterator<Item = Card> + '_ {
99 let flop_iter = self.flop.iter().flat_map(Flop::iter);
100 flop_iter.chain(self.turn).chain(self.river)
101 }
102
103 pub fn to_vec(&self) -> Vec<Card> {
105 self.iter().collect()
106 }
107
108 pub fn clear(&mut self) {
110 *self = Self::default();
111 }
112
113 pub fn contains_card(&self, card: Card) -> bool {
114 if let Some(flop) = self.flop
115 && flop.as_slice().contains(&card)
116 {
117 return true;
118 }
119
120 Some(card) == self.turn || Some(card) == self.river
121 }
122
123 #[must_use]
124 pub(crate) const fn swap_turn(&self, card: Card) -> Self {
125 Self {
126 flop: self.flop,
127 turn: Some(card),
128 river: self.river,
129 }
130 }
131
132 #[must_use]
133 pub(crate) const fn swap_river(&self, card: Card) -> Self {
134 Self {
135 flop: self.flop,
136 turn: self.turn,
137 river: Some(card),
138 }
139 }
140}
141
142impl From<&[Card]> for Board {
143 fn from(xs: &[Card]) -> Self {
144 Self::from_slice(xs)
145 }
146}
147
148impl fmt::Debug for Board {
149 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
150 write!(f, "Board<")?;
151 for c in self.iter() {
152 write!(f, "{c}")?;
153 }
154 write!(f, ">")
155 }
156}
157
158impl From<Board> for Card64 {
159 fn from(board: Board) -> Self {
160 let mut result = Self::default();
161 if let Some(flop) = board.flop {
162 result |= Self::from(flop.as_slice());
163 }
164 if let Some(turn) = board.turn {
165 result |= Self::from(turn);
166 }
167 if let Some(river) = board.river {
168 result |= Self::from(river);
169 }
170 result
171 }
172}
173
174impl From<(Card, Card, Card, Card, Card)> for Board {
175 fn from(cs: (Card, Card, Card, Card, Card)) -> Self {
176 Self {
177 flop: Some(Flop::from_slice(&[cs.0, cs.1, cs.2])),
178 turn: Some(cs.3),
179 river: Some(cs.4),
180 }
181 }
182}
183
184impl From<Flop> for Board {
185 fn from(flop: Flop) -> Self {
186 Self {
187 flop: Some(flop),
188 ..Default::default()
189 }
190 }
191}
192
193#[cfg(test)]
194mod tests {
195 use super::*;
196 use crate::*;
197
198 impl Arbitrary for Board {
199 fn arbitrary(g: &mut quickcheck::Gen) -> Self {
200 let cards = CardN::<5>::arbitrary(g);
201
202 Self::from_slice(cards.as_ref())
203 }
204 }
205
206 #[quickcheck]
207 fn test_flop_eq(cards: CardN<3>) {
208 let flops: Vec<Flop> = vec![
209 Flop::from_slice(&[cards[0], cards[1], cards[2]]),
210 Flop::from_slice(&[cards[0], cards[2], cards[1]]),
211 Flop::from_slice(&[cards[1], cards[0], cards[2]]),
212 Flop::from_slice(&[cards[1], cards[2], cards[0]]),
213 Flop::from_slice(&[cards[2], cards[1], cards[0]]),
214 Flop::from_slice(&[cards[2], cards[0], cards[1]]),
215 ];
216
217 for i in 0..flops.len() {
218 for j in 0..flops.len() {
219 assert_eq!(flops[i], flops[j]);
220 }
221 }
222 }
223
224 #[quickcheck]
225 fn test_board_eq(cs: CardN<5>) {
226 assert_eq!(
227 Board::from([cs[0], cs[1], cs[2], cs[3], cs[4]].as_slice()),
228 Board::from([cs[2], cs[1], cs[0], cs[3], cs[4]].as_slice())
229 );
230 }
231
232 #[test]
233 fn test_board_creation() {
234 let empty_board = Board::default();
236 assert!(empty_board.is_empty());
237 assert_eq!(empty_board.len(), 0);
238
239 let flop_cards = cards!("QdKhAs");
241 for j in 0..=2 {
242 assert_eq!(Board::from_slice(&flop_cards[0..j]).len(), 0);
243 }
244
245 let flop_board = Board::from_slice(&flop_cards);
247 assert!(!flop_board.is_empty());
248 assert_eq!(flop_board.len(), 3);
249 assert_eq!(flop_board.flop, Some(flop!("QdKhAs")));
250 assert_eq!(flop_board.turn, None);
251 assert_eq!(flop_board.river, None);
252
253 let turn_card = Card::new(Rank::RJ, Suit::C);
255 let mut flop_turn_cards = flop_cards;
256 flop_turn_cards.push(turn_card);
257 let flop_turn_board = Board::from_slice(&flop_turn_cards);
258 assert_eq!(flop_turn_board.len(), 4);
259 assert_eq!(flop_turn_board.turn, Some(turn_card));
260 assert_eq!(flop_turn_board.river, None);
261
262 let river_card = Card::new(Rank::RT, Suit::S);
264 let mut full_cards = flop_turn_cards;
265 full_cards.push(river_card);
266 let full_board = Board::from_slice(&full_cards);
267 assert_eq!(full_board.len(), 5);
268 assert_eq!(full_board.river, Some(river_card));
269 }
270
271 #[test]
272 fn test_board_clear() {
273 let cards = cards!("QdKhAsJcTs");
274 let mut board = Board::from_slice(&cards);
275
276 assert!(!board.is_empty());
277 board.clear();
278 assert!(board.is_empty());
279 }
280
281 #[test]
282 fn test_board_iteration() {
283 let cards = cards!("QdKhAsJcTs");
284 for i in N_FLOP..=5 {
286 let board = Board::from_slice(&cards[0..i]);
287 let collected: Vec<Card> = board.iter().collect();
288 assert_eq!(collected, cards[0..i].to_vec());
289 assert_eq!(board.to_vec(), cards[0..i].to_vec());
290 assert_eq!(board.len(), i);
291 }
292 }
293
294 #[test]
295 fn test_board_card64_conversion() {
296 let cards = cards!("AsKhQdJcTs");
297
298 for i in N_FLOP..=5 {
300 let board = Board::from_slice(&cards[0..i]);
301 let card64 = Card64::from(board);
302
303 for j in 0..i {
305 assert!(card64.contains_card(cards[j]));
306 }
307
308 assert_eq!(card64.count() as usize, i);
309 }
310 }
311
312 #[quickcheck]
313 fn test_board_contains_card(board: Board, card: Card) {
314 assert_eq!(board.to_vec().contains(&card), board.contains_card(card));
315 }
316
317 #[test]
318 fn test_board_debug_format() {
319 let cards = cards!["AsKhQd"];
320
321 let board = Board::from_slice(&cards);
322 let debug_str = format!("{board:?}");
323 assert!(debug_str.contains("Board<QdKhAs>"));
324 }
325}