open_pql/
lib.rs

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