open_pql/vm/
mod.rs

1#![allow(clippy::wildcard_imports)]
2
3use std::{cmp::Ordering, collections::VecDeque, ops::*};
4
5use derive_more::derive::{Display, From, TryInto};
6
7use crate::{
8    Board, Card, Flop, FlopHandCategory, Hand, HandType, InternalError,
9    LocInfo, PQLBoardRange, PQLBoolean, PQLCard, PQLCardCount, PQLDouble,
10    PQLGame, PQLHiRating, PQLInteger, PQLLong, PQLPlayer, PQLRange, PQLRank,
11    PQLRankSet, PQLStreet, PQLString, PQLType, TypeError,
12    error::PQLError,
13    functions::PQLFn,
14    pql_parser::ast::{self, Selector},
15    *,
16};
17
18mod bin_op;
19mod buffer;
20pub mod instruction;
21mod push_expr;
22mod push_fncall;
23mod push_ident;
24mod push_num;
25mod push_selector;
26mod push_str;
27mod rng;
28mod stack;
29mod stack_value;
30mod stack_value_num;
31mod store;
32mod store_var_idx;
33mod value;
34
35use bin_op::*;
36pub(crate) use buffer::*;
37pub use instruction::VmInstruction;
38use push_expr::push_expr;
39use push_selector::push_selector;
40pub use rng::*;
41pub use stack::VmStack;
42pub use stack_value::*;
43pub use stack_value_num::VmStackValueNum;
44pub use store::VmStore;
45pub use store_var_idx::*;
46pub use value::*;
47
48pub type VmInstructions = Vec<VmInstruction>;
49
50#[derive(Debug, Clone, Default)]
51pub struct Vm {
52    pub(crate) board_range: PQLRange,
53    pub(crate) player_ranges: Vec<PQLRange>,
54    pub(crate) instructions: VmInstructions,
55    pub(crate) store: VmStore,
56    pub(crate) buffer: VmBuffer,
57    pub(crate) stack: VmStack,
58    pub(crate) rng: Rng,
59    pub n_trials: usize,
60    pub n_failed: usize,
61}
62
63impl Vm {
64    fn sample_next_frame(&mut self) -> Option<()> {
65        self.rng.reset();
66
67        for (i, range) in self.player_ranges.iter_mut().enumerate() {
68            self.rng.deal(range)?;
69
70            self.buffer.player_hands[i].clone_from(&self.rng.mem);
71        }
72
73        self.rng.deal(&mut self.board_range)?;
74
75        let cards = &self.rng.mem;
76        self.buffer.board = Board::from_slice(&cards[..5]);
77
78        Some(())
79    }
80
81    fn compute(&mut self) -> Result<(), PQLError> {
82        let stack = &mut self.stack;
83
84        for ins in &self.instructions {
85            ins.execute(&mut self.buffer, &mut self.store, stack)?;
86        }
87
88        Ok(())
89    }
90
91    pub fn try_run(mut self) -> Result<Self, PQLError> {
92        let mut n = self.n_trials;
93
94        while n > 0 && self.n_failed < self.n_trials {
95            if self.sample_next_frame() == Some(()) {
96                self.compute()?;
97
98                n -= 1;
99            } else {
100                self.n_failed += 1;
101            }
102        }
103
104        Ok(self)
105    }
106}
107
108#[derive(Debug, Clone, Default)]
109struct InitDeps<'init, 'input> {
110    pub game: PQLGame,
111    pub player_names: &'init [&'input str],
112}
113
114#[cfg(test)]
115mod tests {
116    use super::*;
117    use crate::*;
118
119    impl Vm {
120        pub fn new_test_vm() -> Self {
121            let mut buffer = VmBuffer::default();
122            buffer.player_hands.push(Vec::new());
123
124            Self {
125                board_range: PQLBoardRange::default().into(),
126                player_ranges: [PQLRange::default()].into(),
127                instructions: [].into(),
128                store: VmStore::default(),
129                buffer,
130                stack: VmStack::default(),
131                rng: Rng::default(),
132                n_trials: 1,
133                n_failed: 0,
134            }
135        }
136    }
137
138    fn create_vm_shortdeck_cards() -> Vm {
139        let mut vm = Vm::new_test_vm();
140
141        vm.rng = Rng::new(Card::ARR_ALL_SHORT.into());
142
143        vm
144    }
145
146    #[test]
147    fn test_sample_next_frame() {
148        fastrand::seed(0);
149
150        let mut vm = create_vm_shortdeck_cards();
151
152        let g = (&vm.buffer).into();
153
154        vm.sample_next_frame().unwrap();
155        assert_eq!(Some(flop!("6d6c8d")), vm.buffer.board.flop);
156
157        vm.board_range = PQLBoardRange::from_src("222").unwrap().into();
158        assert!(vm.sample_next_frame().is_none());
159
160        vm.player_ranges[0] = PQLRange::from_src("22", g).unwrap();
161        assert!(vm.sample_next_frame().is_none());
162    }
163
164    #[test]
165    fn test_sample_try_run() {
166        fastrand::seed(0);
167
168        let mut vm = create_vm_shortdeck_cards();
169
170        vm.instructions =
171            vec![VmInstruction::Call(&(functions::turn_card as fn(_) -> _))];
172
173        let mut vm = vm.try_run().unwrap();
174
175        assert_eq!(card!("8h"), vm.stack.downcast_pop::<PQLCard>().unwrap());
176    }
177
178    #[test]
179    fn test_sample_try_run_error() {
180        let mut vm = create_vm_shortdeck_cards();
181        let g = (&vm.buffer).into();
182
183        vm.player_ranges[0] = PQLRange::from_src("22", g).unwrap();
184
185        let vm = vm.try_run().unwrap();
186        assert_eq!(vm.n_failed, vm.n_trials);
187
188        let mut vm = create_vm_shortdeck_cards();
189        vm.store.try_push(PQLString::from("").into()).unwrap();
190        vm.instructions = vec![
191            VmStackValue::from(VmStoreVarIdx::from(0)).into(),
192            VmInstruction::Call(&(functions::rate_hi_hand as fn(&_, _) -> _)),
193        ];
194        assert!(vm.try_run().is_err());
195    }
196}