1use super::{Card, Card64, HandN, Hash, N_FLOP, fmt};
2
3pub type Flop = HandN<3>;
4
5#[derive(Copy, Clone, PartialEq, Eq, Hash, Default)]
7pub struct Board {
8 pub flop: Option<Flop>,
9 pub turn: Option<Card>,
10 pub river: Option<Card>,
11}
12
13impl Board {
14 pub fn from_slice(cards: &[Card]) -> Self {
16 let flop = if cards.len() >= N_FLOP {
17 Some(Flop::from_slice(&cards[0..3]))
18 } else {
19 None
20 };
21 let turn = cards.get(3).copied();
22 let river = cards.get(4).copied();
23
24 Self { flop, turn, river }
25 }
26
27 #[must_use]
29 #[inline]
30 pub const fn is_empty(&self) -> bool {
31 self.flop.is_none()
32 }
33
34 pub fn len(&self) -> usize {
36 match self.flop {
37 Some(_) => 3 + self.turn.iter().count() + self.river.iter().count(),
38 None => 0,
39 }
40 }
41
42 pub fn get(&self, i: usize) -> Option<Card> {
44 match i {
45 0..=2 => self.flop.map(|f| f[i]),
46 3 => self.turn,
47 4 => self.river,
48 _ => None,
49 }
50 }
51
52 pub fn iter(&self) -> impl Iterator<Item = Card> + '_ {
54 let flop_iter = self.flop.iter().flat_map(Flop::iter);
55 flop_iter.chain(self.turn).chain(self.river)
56 }
57
58 pub fn to_card64(&self) -> Card64 {
60 let mut result = Card64::default();
61 if let Some(flop) = self.flop {
62 result |= Card64::from(flop.as_slice());
63 }
64 if let Some(turn) = self.turn {
65 result |= Card64::from(turn);
66 }
67 if let Some(river) = self.river {
68 result |= Card64::from(river);
69 }
70 result
71 }
72
73 pub fn to_vec(&self) -> Vec<Card> {
75 self.iter().collect()
76 }
77
78 pub fn clear(&mut self) {
80 *self = Self::default();
81 }
82
83 pub fn contains_card(&self, card: Card) -> bool {
84 if let Some(flop) = self.flop
85 && flop.as_slice().contains(&card)
86 {
87 return true;
88 }
89
90 Some(card) == self.turn || Some(card) == self.river
91 }
92
93 pub const fn swap_turn(&self, card: Card) -> Self {
94 Self {
95 flop: self.flop,
96 turn: Some(card),
97 river: self.river,
98 }
99 }
100
101 pub const fn swap_river(&self, card: Card) -> Self {
102 Self {
103 flop: self.flop,
104 turn: self.turn,
105 river: Some(card),
106 }
107 }
108}
109
110impl From<&[Card]> for Board {
111 fn from(xs: &[Card]) -> Self {
112 Self::from_slice(xs)
113 }
114}
115
116impl fmt::Debug for Board {
117 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
118 write!(f, "Board<")?;
119 for c in self.iter() {
120 write!(f, "{c}")?;
121 }
122 write!(f, ">")
123 }
124}
125
126impl From<Board> for Card64 {
127 fn from(board: Board) -> Self {
128 board.to_card64()
129 }
130}
131
132impl From<(Card, Card, Card, Card, Card)> for Board {
133 fn from(cs: (Card, Card, Card, Card, Card)) -> Self {
134 Self {
135 flop: Some(Flop::from_slice(&[cs.0, cs.1, cs.2])),
136 turn: Some(cs.3),
137 river: Some(cs.4),
138 }
139 }
140}
141
142impl From<Board> for [Card; 5] {
143 fn from(board: Board) -> Self {
144 if let (Some(flop), Some(turn), Some(river)) =
145 (board.flop, board.turn, board.river)
146 {
147 [flop[0], flop[1], flop[2], turn, river]
148 } else {
149 panic!()
150 }
151 }
152}
153
154impl From<Flop> for Board {
155 fn from(flop: Flop) -> Self {
156 Self {
157 flop: Some(flop),
158 ..Default::default()
159 }
160 }
161}
162
163#[cfg(test)]
164mod tests {
165 use super::*;
166 use crate::*;
167
168 impl Arbitrary for Board {
169 fn arbitrary(g: &mut quickcheck::Gen) -> Self {
170 let cards = CardN::<5>::arbitrary(g);
171
172 Self::from_slice(cards.as_ref())
173 }
174 }
175
176 #[quickcheck]
177 fn test_flop_eq(cards: CardN<3>) {
178 let flops: Vec<Flop> = vec![
179 Flop::from_slice(&[cards[0], cards[1], cards[2]]),
180 Flop::from_slice(&[cards[0], cards[2], cards[1]]),
181 Flop::from_slice(&[cards[1], cards[0], cards[2]]),
182 Flop::from_slice(&[cards[1], cards[2], cards[0]]),
183 Flop::from_slice(&[cards[2], cards[1], cards[0]]),
184 Flop::from_slice(&[cards[2], cards[0], cards[1]]),
185 ];
186
187 for i in 0..flops.len() {
188 for j in 0..flops.len() {
189 assert_eq!(flops[i], flops[j]);
190 }
191 }
192 }
193
194 #[quickcheck]
195 fn test_board_eq(cs: CardN<5>) {
196 assert_eq!(
197 Board::from([cs[0], cs[1], cs[2], cs[3], cs[4]].as_slice()),
198 Board::from([cs[2], cs[1], cs[0], cs[3], cs[4]].as_slice())
199 );
200 }
201
202 #[test]
203 fn test_board_creation() {
204 let empty_board = Board::default();
206 assert!(empty_board.is_empty());
207 assert_eq!(empty_board.len(), 0);
208
209 let flop_cards = cards!("AsKhQd");
211 for j in 0..=2 {
212 assert_eq!(Board::from_slice(&flop_cards[0..j]).len(), 0);
213 }
214
215 let flop_board = Board::from_slice(&flop_cards);
217 assert!(!flop_board.is_empty());
218 assert_eq!(flop_board.len(), 3);
219 assert_eq!(flop_board.get(0), Some(flop_cards[2]));
220 assert_eq!(flop_board.get(1), Some(flop_cards[1]));
221 assert_eq!(flop_board.get(2), Some(flop_cards[0]));
222 assert_eq!(flop_board.get(3), None);
223 assert_eq!(flop_board.get(4), None);
224
225 let turn_card = Card::new(Rank::RJ, Suit::C);
227 let mut flop_turn_cards = flop_cards;
228 flop_turn_cards.push(turn_card);
229 let flop_turn_board = Board::from_slice(&flop_turn_cards);
230 assert_eq!(flop_turn_board.len(), 4);
231 assert_eq!(flop_turn_board.get(3), Some(turn_card));
232 assert_eq!(flop_turn_board.get(4), None);
233
234 let river_card = Card::new(Rank::RT, Suit::S);
236 let mut full_cards = flop_turn_cards;
237 full_cards.push(river_card);
238 let full_board = Board::from_slice(&full_cards);
239 assert_eq!(full_board.len(), 5);
240 assert_eq!(full_board.get(4), Some(river_card));
241 assert_eq!(full_board.get(5), None);
242 }
243
244 #[test]
245 fn test_board_clear() {
246 let cards = cards!("QdKhAsJcTs");
247 let mut board = Board::from_slice(&cards);
248
249 assert!(!board.is_empty());
250 board.clear();
251 assert!(board.is_empty());
252 }
253
254 #[test]
255 fn test_board_iteration() {
256 let cards = cards!("QdKhAsJcTs");
257 for i in N_FLOP..=5 {
259 let board = Board::from_slice(&cards[0..i]);
260 let collected: Vec<Card> = board.iter().collect();
261 assert_eq!(collected, cards[0..i].to_vec());
262 assert_eq!(board.to_vec(), cards[0..i].to_vec());
263 assert_eq!(board.len(), i);
264 }
265 }
266
267 #[test]
268 fn test_board_card64_conversion() {
269 let cards = cards!("AsKhQdJcTs");
270
271 for i in N_FLOP..=5 {
273 let board = Board::from_slice(&cards[0..i]);
274 let card64 = board.to_card64();
275
276 for j in 0..i {
278 assert!(card64.contains_card(cards[j]));
279 }
280
281 assert_eq!(card64.count() as usize, i);
282 }
283 }
284
285 #[test]
286 fn test_board_debug_format() {
287 let cards = cards!["AsKhQd"];
288
289 let board = Board::from_slice(&cards);
290 let debug_str = format!("{board:?}");
291 assert!(debug_str.contains("Board<QdKhAs>"));
292 }
293}