1mod card;
11mod rng;
12mod wasm;
13
14use crate::card::Card;
15use crate::rng::ProvablyFairRNG;
16use crate::rng::ProvablyFairRNGFloat;
17
18use std::cmp::Ordering;
19use std::fmt;
20use BaccaratCardRecipient::*;
21
22#[derive(Debug, PartialEq, Eq)]
23enum Outcome {
24 Banker,
25 Player,
26 Tie,
27}
28
29impl fmt::Display for Outcome {
30 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31 let s = match self {
32 Outcome::Banker => "Banker won",
33 Outcome::Player => "Player won",
34 Outcome::Tie => "It's a tie",
35 };
36 write!(f, "{}", s)
37 }
38}
39
40#[derive(Debug)]
41struct SimulationResultTotals {
42 player: u32,
43 banker: u32,
44}
45
46#[derive(Debug)]
47struct Step(BaccaratCardRecipient, Card);
48
49#[derive(Debug)]
50pub struct SimulationResult {
51 outcome: Outcome,
52 totals: SimulationResultTotals,
53 steps: Vec<Step>,
54}
55
56impl SimulationResult {
57 fn from_steps(steps: Vec<Step>) -> SimulationResult {
58 let totals = SimulationResultTotals {
59 player: sum_cards_player(&steps),
60 banker: sum_cards_banker(&steps),
61 };
62 let outcome = match totals.player.cmp(&totals.banker) {
63 Ordering::Less => Outcome::Banker,
64 Ordering::Greater => Outcome::Player,
65 Ordering::Equal => Outcome::Tie,
66 };
67 SimulationResult {
68 outcome,
69 totals,
70 steps,
71 }
72 }
73}
74impl fmt::Display for SimulationResult {
75 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
76 fn pretty_print_steps(recipient: &BaccaratCardRecipient, steps: &Vec<Step>) -> String {
77 let step_str = steps
78 .iter()
79 .filter_map(|Step(r, c)| {
80 if r == recipient {
81 Some(c.to_string())
82 } else {
83 None
84 }
85 })
86 .collect::<Vec<String>>()
87 .join(" - ");
88 let total = sum_cards(recipient, steps);
89 format!("{} ({}): {}", recipient, total, step_str)
90 }
91 let banker = pretty_print_steps(&BANKER, &self.steps);
92 let player = pretty_print_steps(&PLAYER, &self.steps);
93
94 write!(f, "{}\n\n{}\n{}", self.outcome, player, banker)
95 }
96}
97
98#[derive(Debug, PartialEq, Eq)]
99enum BaccaratCardRecipient {
100 BANKER,
101 PLAYER,
102}
103impl fmt::Display for BaccaratCardRecipient {
104 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
105 match self {
106 BANKER => f.write_str("Banker"),
107 PLAYER => f.write_str("Player"),
108 }
109 }
110}
111
112fn baccarat_add(left: u32, right: u32) -> u32 {
113 (left + right) % 10
114}
115
116fn sum_cards(for_recipient: &BaccaratCardRecipient, steps: &Vec<Step>) -> u32 {
117 steps
118 .iter()
119 .filter(|Step(recipient, _)| recipient == for_recipient)
120 .fold(0, |acc, Step(_, card)| {
121 baccarat_add(acc, card.to_baccarat_value() as u32)
122 })
123}
124
125fn sum_cards_player(steps: &Vec<Step>) -> u32 {
126 sum_cards(&PLAYER, &steps)
127}
128fn sum_cards_banker(steps: &Vec<Step>) -> u32 {
129 sum_cards(&BANKER, &steps)
130}
131
132pub fn simulate(client_seed: &str, server_seed: &str, nonce: u64) -> SimulationResult {
150 let mut rng = ProvablyFairRNGFloat::new(ProvablyFairRNG::new(client_seed, server_seed, nonce));
151
152 let mut steps: Vec<Step> = vec![];
154
155 steps.push(Step(PLAYER, Card::random(&mut rng)));
156 steps.push(Step(PLAYER, Card::random(&mut rng)));
157 steps.push(Step(BANKER, Card::random(&mut rng)));
158 steps.push(Step(BANKER, Card::random(&mut rng)));
159
160 if sum_cards_banker(&steps) >= 8 || sum_cards_player(&steps) >= 8 {
164 return SimulationResult::from_steps(steps);
166 }
167
168 if sum_cards_player(&steps) > 5 {
173 if sum_cards_banker(&steps) <= 5 {
176 steps.push(Step(BANKER, Card::random(&mut rng)));
177 }
178 return SimulationResult::from_steps(steps);
179 }
180
181 let player_third_card = Card::random(&mut rng);
183 steps.push(Step(PLAYER, player_third_card));
184
185 fn banker_should_draw_third_card(banker_total: u32, player_third_card: Card) -> bool {
186 let rank = player_third_card.to_baccarat_value();
187
188 match banker_total {
189 0 | 1 | 2 => true,
190 3 => rank != 8,
191 4 => match rank {
192 2 | 3 | 4 | 5 | 6 | 7 => true,
193 _ => false,
194 },
195 5 => match rank {
196 4 | 5 | 6 | 7 => true,
197 _ => false,
198 },
199 6 => match rank {
200 6 | 7 => true,
201 _ => false,
202 },
203 7 => false,
204 _ => {
205 panic!(
206 "got an impossible value \"{}\" for banker total (>7), something with library!",
207 banker_total
208 );
209 }
210 }
211 }
212
213 if banker_should_draw_third_card(sum_cards_banker(&steps), player_third_card) {
214 steps.push(Step(BANKER, Card::random(&mut rng)));
215 }
216
217 SimulationResult::from_steps(steps)
218}
219
220#[cfg(test)]
221mod test {
222 use super::*;
223
224 fn pretty_print_steps(steps: &Vec<Step>) -> Vec<String> {
225 steps
226 .iter()
227 .map(|Step(recipient, card)| format!("{}: {}", recipient, card))
228 .collect::<Vec<String>>()
229 }
230
231 #[test]
232 fn simulate_five_cards_drawn() {
233 let client_seed = "some client seed";
234 let server_seed = "some server seed";
235 let nonce = 2;
236 let result = simulate(client_seed, server_seed, nonce);
237 assert_eq!(result.outcome, Outcome::Banker);
239
240 assert_eq!(
241 pretty_print_steps(&result.steps),
242 vec![
243 "Player: ♥Q",
244 "Player: ♣Q",
245 "Banker: ♥4",
246 "Banker: ♥3",
247 "Player: ♣10"
248 ]
249 );
250 }
251
252 #[test]
253 fn simulate_four_cards_drawn() {
254 let client_seed = "some client seed";
255 let server_seed = "some server seed";
256 let nonce = 1;
257 let result = simulate(client_seed, server_seed, nonce);
258 assert_eq!(result.totals.player, 9);
260 assert_eq!(result.totals.banker, 9);
261 assert_eq!(result.outcome, Outcome::Tie);
262
263 assert_eq!(
264 pretty_print_steps(&result.steps),
265 vec!["Player: ♠9", "Player: ♠Q", "Banker: ♦4", "Banker: ♠5"]
266 );
267 }
268}