1#![cfg_attr(test, allow(clippy::needless_pass_by_value))]
2#![cfg_attr(test, allow(clippy::trivially_copy_pass_by_ref))]
3#![cfg_attr(test, allow(clippy::wildcard_imports))]
4#[cfg(test)]
7#[macro_use(quickcheck)]
8extern crate quickcheck_macros;
9
10#[cfg(test)]
11use std::hash::Hasher;
12use std::{
13 cmp::Ordering,
14 fmt,
15 ops::{
16 Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, FnMut, FnOnce,
17 },
18 str::FromStr,
19};
20
21use derive_more::derive::{AsMut, AsRef, Display, From, Into};
22#[cfg(test)]
23use quickcheck::{Arbitrary, TestResult};
24use tailcall::tailcall;
25
26mod base;
27mod concurrency;
28mod error;
29mod helper;
30mod pql_type;
31mod range_checker;
32mod runner;
33
34pub mod constants;
35pub mod functions;
36pub mod pql_parser;
37pub mod prim;
38pub mod range_parser;
39pub mod vm;
40
41pub use base::*;
42pub use error::*;
43#[allow(unused)]
44pub use helper::*;
45pub use pql_type::*;
46use prim::eval;
47use range_checker::CachedChecker;
48pub use runner::*;
49
50pub type Loc = usize;
51pub type LocInfo = (Loc, Loc);
52
53pub type HandRatingInt = i16;
54
55pub const fn eval_holdem7(c: Card64) -> PQLHiRating {
56 PQLHiRating::new(eval::holdem7::eval(c.to_u64()))
57}
58
59pub const fn eval_shortdeck7(c: Card64) -> PQLHiRating {
60 PQLHiRating::new(eval::shortdeck7::eval(c.to_u64()))
61}
62
63pub const fn eval_omaha9(player: Card64, board: Card64) -> PQLHiRating {
64 PQLHiRating::new(eval::omaha9::eval(player.to_u64(), board.to_u64()))
65}
66
67#[cfg(test)]
68pub use crate::tests::{CardN, *};
69
70#[cfg(test)]
71pub mod tests {
72 use std::fmt::{self};
73
74 use derive_more::derive::{From, Index, Into, IntoIterator};
75 use regex::Regex;
76 use rustc_hash::FxHashSet;
77
78 use super::*;
79
80 #[derive(Clone, Index, From, Into, IntoIterator)]
81 pub struct CardN<const N: usize, const SHORT: bool = false>([Card; N]);
82
83 impl<const N: usize, const S: bool> Arbitrary for CardN<N, S> {
84 fn arbitrary(_g: &mut quickcheck::Gen) -> Self {
85 let mut rng = fastrand::Rng::new();
86
87 let v = if S {
88 rng.choose_multiple(Card::ARR_ALL_SHORT, N)
89 } else {
90 rng.choose_multiple(Card::ARR_ALL, N)
91 };
92
93 <[_; N]>::try_from(v).unwrap().into()
94 }
95 }
96
97 impl<const N: usize, const S: bool> fmt::Debug for CardN<N, S> {
98 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
99 let s = format!("{:?}", self.0);
100
101 let replaced =
102 Regex::new(r"(Card|,|\[|\])").unwrap().replace_all(&s, "");
103
104 f.write_str(&format!("cards!(\"{replaced}\")"))
105 }
106 }
107
108 impl<const N: usize, const S: bool> AsRef<[Card]> for CardN<N, S> {
109 fn as_ref(&self) -> &[Card] {
110 &self.0
111 }
112 }
113
114 impl<const N: usize, const S: bool> From<CardN<N, S>> for Vec<Card> {
115 fn from(cards: CardN<N, S>) -> Self {
116 cards.0.into()
117 }
118 }
119
120 impl<const N: usize, const S: bool> From<CardN<N, S>> for Card64 {
121 fn from(cards: CardN<N, S>) -> Self {
122 cards.as_ref().into()
123 }
124 }
125
126 #[allow(clippy::fallible_impl_from)]
127 impl<const X: usize, const Y: usize, const Z: usize, const S: bool>
128 From<CardN<Z, S>> for (CardN<X, S>, CardN<Y, S>)
129 {
130 fn from(cards: CardN<Z, S>) -> Self {
131 assert!(X + Y <= Z, "Not enough cards {Z} < {X} + {Y} = {}", X + Y);
132
133 let mut x: [_; X] = [Card::default(); X];
134 let mut y: [_; Y] = [Card::default(); Y];
135
136 for i in 0..X {
137 x[i] = cards[i];
138 }
139
140 for j in 0..Y {
141 y[j] = cards[X + j];
142 }
143
144 (x.into(), y.into())
145 }
146 }
147
148 #[derive(Debug, Clone)]
149 pub struct HandBoardGame {
150 pub hand: Vec<Card>,
151 pub another_hand: Vec<Card>,
152 pub board: Board,
153 pub game: PQLGame,
154 pub dead: DeadCards,
155 pub street: PQLStreet,
156 }
157
158 impl Arbitrary for HandBoardGame {
159 fn arbitrary(g: &mut quickcheck::Gen) -> Self {
160 const N: usize = 4 + 4 + 5 + 3;
161
162 let game = PQLGame::arbitrary(g);
163 let n_dead = *g.choose(&[0, 1, 2, 3]).unwrap();
164
165 let (mut cs, n): (Vec<_>, _) = match game {
166 PQLGame::Holdem => (CardN::<N>::arbitrary(g).into(), 2),
167 PQLGame::Omaha => (CardN::<N>::arbitrary(g).into(), 4),
168 PQLGame::ShortDeck => {
169 (CardN::<N, false>::arbitrary(g).into(), 2)
170 }
171 };
172
173 let hand = cs.drain(0..n).collect();
174 let another_hand = cs.drain(0..n).collect();
175 let board: Vec<_> = cs.drain(0..5).collect();
176 let dead: Vec<_> = cs.drain(0..n_dead).collect();
177
178 Self {
179 hand,
180 another_hand,
181 board: (&board as &[_]).into(),
182 game,
183 dead: (&dead as &[_]).into(),
184 street: PQLStreet::arbitrary(g),
185 }
186 }
187 }
188
189 #[quickcheck]
190 fn test_cards_n_destruct(cards: CardN<10>) -> TestResult {
191 let (c4, c5): (CardN<4>, CardN<5>) = cards.clone().into();
192
193 TestResult::from_bool(
194 c4[0] == cards[0]
195 && c4[1] == cards[1]
196 && c4[2] == cards[2]
197 && c4[3] == cards[3]
198 && c5[0] == cards[4]
199 && c5[1] == cards[5]
200 && c5[2] == cards[6]
201 && c5[3] == cards[7]
202 && c5[4] == cards[8],
203 )
204 }
205
206 #[quickcheck]
207 fn test_cards_n_distinct(cards: CardN<10>) -> TestResult {
208 let v: FxHashSet<_> = cards.into_iter().collect();
209
210 TestResult::from_bool(v.len() == 10)
211 }
212}