1use std::{io::Write, rc::Rc};
4
5use either::Either;
6use rand::random_range;
7
8pub enum WagerData {
10 PlayerWager,
11 BankWager,
12}
13
14pub trait Wager {
16 fn wager(&self, data: WagerData) -> u32;
17}
18
19#[derive(Eq, Hash, PartialEq, Clone, Debug)]
20pub struct Player {
21 name: String,
22}
23
24impl Player {
26 #[must_use]
28 pub const fn new(name: String) -> Self {
29 Self { name }
30 }
31}
32
33#[derive(Clone)]
35pub struct PlayerData {
36 points: u32,
37 wager: Rc<dyn Wager>,
38}
39
40impl PlayerData {
41 pub const fn new(wager: Rc<dyn Wager>, points: u32) -> Self {
43 Self { points, wager }
44 }
45}
46
47impl std::fmt::Debug for PlayerData {
48 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
49 f.debug_struct("PlayerData")
50 .field("points", &self.points)
51 .field(
52 "wager",
53 &"Wager is `Rc<dyn Wager>` and therefore not `Debug`able",
54 )
55 .finish()
56 }
57}
58
59pub type Bet = u32;
60#[must_use]
63pub const fn fade_bet(bank_bet: u32, player_bet: u32) -> (u32, u32) {
64 let leftovers = bank_bet.saturating_sub(player_bet);
65
66 (
67 leftovers,
68 if leftovers == 0 { bank_bet } else { player_bet },
69 )
70}
71
72#[must_use]
74pub fn fade_bets(players: Vec<(Player, PlayerData)>) -> Vec<(Player, PlayerData, Bet)> {
75 let (bank, players) = {
77 let mut players = players;
78 (players.remove(0), players)
79 };
80 let original_bank_wager = bank.1.wager.wager(WagerData::BankWager);
81 let mut bank_wager = original_bank_wager;
82 let players: Vec<(Player, PlayerData, Bet)> = players
83 .into_iter()
84 .map(|(player, player_data)| {
85 let faded_wager = fade_bet(bank_wager, player_data.wager.wager(WagerData::PlayerWager));
86 bank_wager = faded_wager.0;
87 (player, player_data, faded_wager.1)
88 })
89 .collect();
90 let mut new_players = players;
91 let (bank, bank_data) = bank;
92 new_players.insert(0, (bank, bank_data, original_bank_wager));
93 new_players
94}
95
96#[must_use]
98pub fn roll_dice() -> (u8, u8, u8) {
99 (
100 random_range(1..6 + 1),
101 random_range(1..6 + 1),
102 random_range(1..6 + 1),
103 )
104}
105
106#[derive(PartialEq, Debug, Eq, Clone)]
113pub enum Rolls {
114 InstantWin { multiplier: u8 },
115 Point { point: u8, other: u8 }, Loss,
117}
118
119#[derive(Debug)]
121pub struct Reroll;
122
123impl TryFrom<(u8, u8, u8)> for Rolls {
131 type Error = Reroll;
132
133 fn try_from(value: (u8, u8, u8)) -> Result<Self, Self::Error> {
134 match (value.0 == value.1, value.1 == value.2) {
135 (true, true) => Ok(Self::InstantWin {
136 multiplier: if value.0 == 1 { 5 } else { 3 },
137 }),
138 (true, false) => {
139 if value.2 == 6 {
140 Ok(Self::InstantWin { multiplier: 1 })
141 } else if value.2 == 1 {
142 Ok(Self::Loss)
143 } else {
144 Ok(Self::Point {
145 point: value.2,
146 other: value.0,
147 })
148 }
149 }
150 (false, true) => {
151 if value.0 == 6 {
152 Ok(Self::InstantWin { multiplier: 1 })
153 } else if value.0 == 1 {
154 Ok(Self::Loss)
155 } else {
156 Ok(Self::Point {
157 point: value.0,
158 other: value.1,
159 })
160 }
161 }
162 (false, false) => {
163 if value.0 == value.2 {
164 if value.1 == 6 {
165 Ok(Self::InstantWin { multiplier: 1 })
166 } else if value.1 == 1 {
167 Ok(Self::Loss)
168 } else {
169 Ok(Self::Point {
170 point: value.1,
171 other: value.0,
172 })
173 }
174 } else {
175 let p = [value.0, value.1, value.2];
176 if p.contains(&4) && p.contains(&5) && p.contains(&6) {
177 Ok(Self::InstantWin { multiplier: 2 })
178 } else if p.contains(&1) && p.contains(&2) && p.contains(&3) {
179 Ok(Self::Loss)
180 } else {
181 Err(Reroll)
182 }
183 }
184 }
185 }
186 }
187}
188
189#[must_use]
191pub fn always_roll() -> ((u8, u8, u8), Rolls) {
192 let mut raw = roll_dice();
193 loop {
194 if let Ok(res) = Rolls::try_from(raw) {
195 return (raw, res);
196 }
197 raw = roll_dice();
198 }
199}
200
201#[must_use]
203pub fn everyone_roll(
204 players: Vec<(Player, PlayerData, Bet)>,
205) -> Vec<(Player, PlayerData, Bet, (u8, u8, u8), Rolls)> {
206 let mut rolls = vec![];
207 for (player, player_data, wager) in players {
208 let (roll_raw, roll) = always_roll();
209 rolls.push((player, player_data, wager, roll_raw, roll));
210 }
211 rolls
212}
213
214#[derive(Clone)]
216pub enum WinOrLose {
217 InstWin(u8),
218 Win,
219 Tie,
220 Lose(u8),
221}
222
223#[must_use]
226pub fn wins_and_losses(
227 players: Vec<(Player, PlayerData, Bet, (u8, u8, u8), Rolls)>,
228) -> Vec<(
229 Player,
230 PlayerData,
231 Bet,
232 (u8, u8, u8),
233 Rolls,
234 Option<WinOrLose>,
235)> {
236 let (bank, players) = {
237 let mut players = players;
238 (players.remove(0), players)
239 };
240
241 match bank.4 {
242 Rolls::InstantWin { multiplier } => vec![players
244 .into_iter()
245 .map(|(player, pd, b, rr, r)| (player, pd, b, rr, r, Some(WinOrLose::Lose(multiplier))))
246 .collect::<Vec<(
247 Player,
248 PlayerData,
249 Bet,
250 (u8, u8, u8),
251 Rolls,
252 Option<WinOrLose>,
253 )>>()]
254 .into_iter()
255 .fold(std::iter::empty().collect(), |_, x| {
256 let mut unpushed = x;
257 let (bpl, bpd, bbet, brr, br) = &bank;
258
259 unpushed.insert(0, (bpl.clone(), bpd.clone(), *bbet, *brr, br.clone(), None));
260 unpushed
261 }),
262 Rolls::Point {
263 point: bank_point,
264 other: _,
265 } => {
266 let mut win_and_losses = Vec::new();
267 for (player, pd, b, rr, player_roll) in players {
268 match player_roll {
269 Rolls::InstantWin { multiplier } => {
270 win_and_losses.push((
271 player,
272 pd,
273 b,
274 rr,
275 player_roll,
276 Some(WinOrLose::InstWin(multiplier)),
277 ));
278 }
279 Rolls::Point {
280 point: player_point,
281 other: _,
282 } => match player_point.cmp(&bank_point) {
283 std::cmp::Ordering::Less => {
284 win_and_losses.push((
285 player,
286 pd,
287 b,
288 rr,
289 player_roll,
290 Some(WinOrLose::Lose(1)),
291 ));
292 }
293 std::cmp::Ordering::Equal => {
294 win_and_losses.push((
295 player,
296 pd,
297 b,
298 rr,
299 player_roll,
300 Some(WinOrLose::Tie),
301 ));
302 }
303 std::cmp::Ordering::Greater => {
304 win_and_losses.push((
305 player,
306 pd,
307 b,
308 rr,
309 player_roll,
310 Some(WinOrLose::Win),
311 ));
312 }
313 },
314 Rolls::Loss => {
315 win_and_losses.push((
316 player,
317 pd,
318 b,
319 rr,
320 player_roll,
321 Some(WinOrLose::Lose(1)),
322 ));
323 }
324 }
325 }
326 let (pl, pd, bet, rr, rolls) = bank;
327 let new_bank = (pl, pd, bet, rr, rolls, None);
328 win_and_losses.insert(0, new_bank);
329 win_and_losses
330 }
331 Rolls::Loss => {
333 let mut handled: Vec<(
334 Player,
335 PlayerData,
336 u32,
337 (u8, u8, u8),
338 Rolls,
339 Option<WinOrLose>,
340 )> = players
341 .into_iter()
342 .map(|(player, pd, b, rr, r)| (player, pd, b, rr, r, Some(WinOrLose::InstWin(1))))
343 .collect();
344 let (pl, pd, bet, rr, rolls) = bank;
345 let new_bank = (pl, pd, bet, rr, rolls, Some(WinOrLose::Lose(1)));
346 handled.insert(0, new_bank);
347 handled
348 }
349 }
350}
351
352impl WinOrLose {
353 #[must_use]
355 pub const fn is_win(&self) -> bool {
356 match self {
357 Self::InstWin(_) | Self::Win | Self::Tie => true,
358 Self::Lose(_) => false,
359 }
360 }
361}
362
363#[must_use]
365pub fn wins_and_losses_no_bank(
366 players: Vec<(
367 Player,
368 PlayerData,
369 u32,
370 (u8, u8, u8),
371 Rolls,
372 Option<WinOrLose>,
373 )>,
374) -> (
375 (
376 Player,
377 PlayerData,
378 u32,
379 (u8, u8, u8),
380 Rolls,
381 Option<WinOrLose>,
382 ),
383 Vec<(Player, PlayerData, u32, (u8, u8, u8), Rolls, WinOrLose)>,
384) {
385 let mut the_players = players;
386 let bank = the_players.remove(0);
387 let some_players = the_players
388 .into_iter()
389 .filter_map(|(pl, pd, bet, rr, roll, won_or_lost)| {
390 won_or_lost.map(|wol| (pl, pd, bet, rr, roll, wol))
391 })
392 .collect();
393 (bank, some_players)
394}
395
396#[must_use]
398pub fn payout(
399 players: Vec<(
400 Player,
401 PlayerData,
402 Bet,
403 (u8, u8, u8),
404 Rolls,
405 Option<WinOrLose>,
406 )>,
407) -> Vec<(Player, PlayerData, Bet, (u8, u8, u8), Rolls)> {
408 let (mut bank, the_players) = wins_and_losses_no_bank(players);
409 let losers_then_winners = {
410 let (mut losers, mut winners): (
411 Vec<(Player, PlayerData, u32, (u8, u8, u8), Rolls, WinOrLose)>,
412 _,
413 ) = the_players
414 .into_iter()
415 .partition(|player| player.5.is_win());
416
417 losers.append(&mut winners);
418 losers
419 };
420 let mut upd_players = vec![];
421 let mut winners = 0usize;
422 let mut instant_won = None;
423 for (player, pd, bet, rr, roll, win_or_lose) in losers_then_winners {
424 match win_or_lose {
425 WinOrLose::InstWin(mult) => {
426 let (bank_leftover, player_left_over) =
427 fade_bet(bank.1.points, bet.saturating_mul(mult.into()));
428 bank.1.points = bank_leftover;
429 let new_player = (
430 player.clone(),
431 PlayerData {
432 points: pd.points.saturating_add(player_left_over),
433 wager: pd.wager,
434 },
435 bet,
436 rr,
437 roll,
438 );
439 instant_won = Some(new_player.clone());
440 upd_players.push(new_player);
441 }
442 WinOrLose::Win => {
443 winners = winners.saturating_add(1);
444 let (bank_leftover, player_leftover) = fade_bet(bank.1.points, bet);
445 bank.1.points = bank_leftover;
446 upd_players.push((
447 player,
448 PlayerData {
449 points: pd.points.saturating_add(player_leftover),
450 wager: pd.wager,
451 },
452 bet,
453 rr,
454 roll,
455 ));
456 }
457 WinOrLose::Tie => upd_players.push((player, pd, bet, rr, roll)),
458 WinOrLose::Lose(mult) => {
459 let (player_leftover, player_loss) =
460 fade_bet(pd.points, bet.saturating_mul(mult.into()));
461 bank.1.points = bank.1.points.saturating_add(player_loss);
462
463 upd_players.push((
464 player,
465 PlayerData {
466 points: player_leftover,
467 wager: pd.wager,
468 },
469 bet,
470 rr,
471 roll,
472 ));
473 }
474 }
475 }
476 let new_bank = {
477 let (bank_player, bank_player_data, bbet, brr, brolls, ..) = bank;
478 (bank_player, bank_player_data, bbet, brr, brolls)
479 };
480
481 if let Some(next_bank) = instant_won {
482 upd_players.push(new_bank);
483 loop {
485 let front = upd_players.remove(0);
486 if front.0 == next_bank.0 {
488 upd_players.insert(0, front);
489 break;
490 }
491 upd_players.push(front);
492 }
493 } else if winners == upd_players.len().saturating_add(1) || bank.5.is_some() {
494 upd_players.push(new_bank);
495 } else {
496 upd_players.insert(0, new_bank);
497 }
498 upd_players
499}
500
501#[must_use]
503pub fn winner(
504 players: Vec<(Player, PlayerData, Bet, (u8, u8, u8), Rolls)>,
505) -> Either<
506 (Player, PlayerData, Bet, (u8, u8, u8), Rolls),
507 Vec<(Player, PlayerData, Bet, (u8, u8, u8), Rolls)>,
508> {
509 match players {
510 mut lone if lone.len() == 1 => Either::Left(lone.remove(0)),
511 multiple => either::Right(multiple),
512 }
513}
514
515pub type History = Vec<Vec<(Player, PlayerData, Bet, (u8, u8, u8), Rolls)>>;
517
518#[must_use]
520pub fn play_until_winner(
521 mut players: Vec<(Player, PlayerData)>,
522) -> ((Player, PlayerData, Bet, (u8, u8, u8), Rolls), History) {
523 let mut history = vec![];
524 loop {
525 let payed = payout(wins_and_losses(everyone_roll(fade_bets(players.clone()))));
526 history.push(payed.clone());
527 let maybe_winner = winner(payed);
531 let mut the_winner: Option<(Player, PlayerData, Bet, (u8, u8, u8), Rolls)> = None;
532 match maybe_winner {
533 Either::Left(winner) => the_winner = Some(winner),
534 Either::Right(not_winners) => {
535 players = not_winners
537 .into_iter()
538 .map(|(player, pd, ..)| (player, pd))
539 .collect();
540 players.retain(|player| player.1.points != 0);
541 }
542 }
543 if let Some(winner) = the_winner {
544 return (winner, history);
545 }
546 }
547}
548
549pub struct WagerN(
551 pub u32,
553);
554
555impl Wager for WagerN {
556 fn wager(&self, _: WagerData) -> u32 {
558 self.0
559 }
560}
561
562pub struct WagerConsole;
564impl Wager for WagerConsole {
565 fn wager(&self, _data: WagerData) -> u32 {
567 loop {
568 let mut buffer = String::new();
569 print!("Wager Here => ");
570 let _ = std::io::stdout().flush();
571 if std::io::stdin().read_line(&mut buffer).is_ok() {
572 if let Ok(i) = buffer.trim().parse::<u32>() {
573 return i;
574 }
575 println!("Failed to parse");
576 }
577 }
578 }
579}
580
581impl<T: Fn(WagerData) -> u32> Wager for T {
592 fn wager(&self, data: WagerData) -> u32 {
594 self(data)
595 }
596}
597
598#[must_use]
599pub const fn add(left: u64, right: u64) -> u64 {
600 left.saturating_add(right)
601}
602
603#[cfg(test)]
604mod tests {
605 use super::*;
606
607 #[test]
608 fn it_works() {
609 let result = add(2, 2);
610 assert_eq!(result, 4);
611 }
612}