open_pql/
lib.rs

1#![allow(clippy::wildcard_imports)]
2#![cfg_attr(coverage_nightly, feature(coverage_attribute))]
3#![cfg_attr(test, allow(clippy::needless_pass_by_value))]
4
5#[cfg(test)]
6#[macro_use(quickcheck)]
7extern crate quickcheck_macros;
8
9use std::{
10    cmp::Ordering,
11    fmt,
12    hash::{Hash, Hasher},
13    mem,
14    ops::{Add, AddAssign, *},
15    str::FromStr,
16};
17
18use derive_more::{
19    derive::{AsMut, AsRef, Display, From, Into},
20    BitAnd, BitAndAssign, BitOr, BitOrAssign, Not,
21};
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#[derive(
68    Debug, Clone, Copy, Default, PartialEq, Eq, From, Into, AsRef, AsMut,
69)]
70pub struct DeadCards(Card64);
71
72impl From<&[Card]> for DeadCards {
73    fn from(cs: &[Card]) -> Self {
74        Card64::from(cs).into()
75    }
76}
77
78#[cfg(test)]
79pub use crate::tests::{CardN, *};
80
81#[cfg(test)]
82pub mod tests {
83    use std::fmt::{self};
84
85    use derive_more::derive::{From, Index, Into, IntoIterator};
86    use regex::Regex;
87    use rustc_hash::FxHashSet;
88
89    use super::*;
90
91    #[derive(Clone, Index, From, Into, IntoIterator)]
92    pub struct CardN<const N: usize, const SHORT: bool = false>([Card; N]);
93
94    impl<const N: usize, const S: bool> Arbitrary for CardN<N, S> {
95        fn arbitrary(_g: &mut quickcheck::Gen) -> Self {
96            let mut rng = fastrand::Rng::new();
97
98            let v = if S {
99                rng.choose_multiple(Card::ARR_ALL_SHORT.into_iter(), N)
100            } else {
101                rng.choose_multiple(Card::ARR_ALL.into_iter(), N)
102            };
103
104            <[_; N]>::try_from(v).unwrap().into()
105        }
106    }
107
108    impl<const N: usize, const S: bool> fmt::Debug for CardN<N, S> {
109        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
110            let s = format!("{:?}", self.0);
111
112            let replaced =
113                Regex::new(r"(Card|,|\[|\])").unwrap().replace_all(&s, "");
114
115            f.write_str(&format!("cards!(\"{replaced}\")"))
116        }
117    }
118
119    impl<const N: usize, const S: bool> AsRef<[Card]> for CardN<N, S> {
120        fn as_ref(&self) -> &[Card] {
121            &self.0
122        }
123    }
124
125    impl<const N: usize, const S: bool> From<CardN<N, S>> for Vec<Card> {
126        fn from(cards: CardN<N, S>) -> Self {
127            cards.0.into()
128        }
129    }
130
131    impl<const N: usize, const S: bool> From<CardN<N, S>> for Card64 {
132        fn from(cards: CardN<N, S>) -> Self {
133            cards.as_ref().into()
134        }
135    }
136
137    #[allow(clippy::fallible_impl_from)]
138    impl<const X: usize, const Y: usize, const Z: usize, const S: bool>
139        From<CardN<Z, S>> for (CardN<X, S>, CardN<Y, S>)
140    {
141        fn from(cards: CardN<Z, S>) -> Self {
142            assert!(X + Y <= Z, "Not enough cards {Z} < {X} + {Y} = {}", X + Y);
143
144            let mut x: [_; X] = [Card::default(); X];
145            let mut y: [_; Y] = [Card::default(); Y];
146
147            for i in 0..X {
148                x[i] = cards[i];
149            }
150
151            for j in 0..Y {
152                y[j] = cards[X + j];
153            }
154
155            (x.into(), y.into())
156        }
157    }
158
159    #[derive(Debug, Clone)]
160    pub struct HandBoardGame {
161        pub hand: Vec<Card>,
162        pub another_hand: Vec<Card>,
163        pub board: Board,
164        pub game: PQLGame,
165        pub dead: DeadCards,
166        pub street: PQLStreet,
167    }
168
169    impl Arbitrary for HandBoardGame {
170        fn arbitrary(g: &mut quickcheck::Gen) -> Self {
171            const N: usize = 4 + 4 + 5 + 3;
172
173            let game = PQLGame::arbitrary(g);
174            let n_dead = *g.choose(&[0, 1, 2, 3]).unwrap();
175
176            let (mut cs, n): (Vec<_>, _) = match game {
177                PQLGame::Holdem => (CardN::<N>::arbitrary(g).into(), 2),
178                PQLGame::Omaha => (CardN::<N>::arbitrary(g).into(), 4),
179                PQLGame::ShortDeck => {
180                    (CardN::<N, false>::arbitrary(g).into(), 2)
181                }
182            };
183
184            let hand = cs.drain(0..n).collect();
185            let another_hand = cs.drain(0..n).collect();
186            let board: Vec<_> = cs.drain(0..5).collect();
187            let dead: Vec<_> = cs.drain(0..n_dead).collect();
188
189            Self {
190                hand,
191                another_hand,
192                board: (&board as &[_]).into(),
193                game,
194                dead: (&dead as &[_]).into(),
195                street: PQLStreet::arbitrary(g),
196            }
197        }
198    }
199
200    #[quickcheck]
201    fn test_cards_n_destruct(cards: CardN<10>) -> TestResult {
202        let (c4, c5): (CardN<4>, CardN<5>) = cards.clone().into();
203
204        TestResult::from_bool(
205            c4[0] == cards[0]
206                && c4[1] == cards[1]
207                && c4[2] == cards[2]
208                && c4[3] == cards[3]
209                && c5[0] == cards[4]
210                && c5[1] == cards[5]
211                && c5[2] == cards[6]
212                && c5[3] == cards[7]
213                && c5[4] == cards[8],
214        )
215    }
216
217    #[quickcheck]
218    fn test_cards_n_distinct(cards: CardN<10>) -> TestResult {
219        let v: FxHashSet<_> = cards.into_iter().collect();
220
221        TestResult::from_bool(v.len() == 10)
222    }
223}