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}