Skip to main content

games/
coin_toss.rs

1//! Coin Toss, make a prediction on which side the coin will land
2//! ```rust
3//! use games::coin_toss::*;
4//! fn main() -> Result<(), CoinError> {
5//!     // Method A
6//!     if CoinToss::guess(Coin::Heads).is_correct() {
7//!         println!("You guessed correctly for A");
8//!     }
9//!     // Method B
10//!     if CoinToss::guess("Heads".parse()?).is_correct() {
11//!         println!("You guessed correctly for B");
12//!     }
13//!     // Method C
14//!     if CoinToss::guess(Coin::flip()).is_correct() {
15//!         println!("Fate loves you");
16//!     }
17//!     Ok(())
18//! }
19//! ```
20use crate::errors::{BASE_COIN_TOSS_ERROR_CODE, ErrorCode};
21
22use core::fmt;
23
24use rand::RngExt;
25
26/// Error raised by Coin Toss
27#[repr(C)]
28#[derive(
29    Debug, Copy, Clone, serde::Serialize, serde::Deserialize, Eq, PartialEq, PartialOrd, Ord, Hash,
30)]
31pub enum CoinError {
32    /// Failed to parse coin side (5001)
33    InvalidSideError,
34}
35
36impl fmt::Display for CoinError {
37    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
38        match *self {
39            CoinError::InvalidSideError => f.write_str("Not a valid side"),
40        }
41    }
42}
43
44impl ErrorCode for CoinError {
45    fn error_code(&self) -> i32 {
46        BASE_COIN_TOSS_ERROR_CODE
47            + match *self {
48                CoinError::InvalidSideError => 1,
49            }
50    }
51}
52
53/// Enum representing a coin
54#[derive(
55    Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord, serde::Serialize, serde::Deserialize,
56)]
57pub enum Coin {
58    /// Head side of the coin
59    Heads,
60    /// Tails side of the coin
61    Tails,
62}
63
64impl Coin {
65    /// Flips a coin, returning a Coin with its current Side up
66    pub fn flip() -> Coin {
67        Coin::from(crate::get_rng().random_bool(0.5))
68    }
69}
70
71impl fmt::Display for Coin {
72    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
73        match self {
74            Coin::Heads => f.write_str("Heads"),
75            Coin::Tails => f.write_str("Tails"),
76        }
77    }
78}
79
80/// Coin toss struct
81#[derive(
82    Copy, Clone, Debug, serde::Serialize, serde::Deserialize, Ord, PartialOrd, PartialEq, Eq, Hash,
83)]
84pub struct CoinToss {
85    /// The coin's state that the player guessed
86    pub guess: Coin,
87    /// The coin's state that happened after the toss
88    pub real: Coin,
89}
90
91impl From<CoinToss> for bool {
92    fn from(val: CoinToss) -> Self {
93        val.guess == val.real
94    }
95}
96
97impl core::str::FromStr for Coin {
98    type Err = CoinError;
99    fn from_str(s: &str) -> Result<Self, Self::Err> {
100        let guess = s.to_ascii_lowercase();
101        if ["heads", "h", "t", "tails"].contains(&guess.as_str()) {
102            Ok(Coin::from(guess.starts_with('h')))
103        } else {
104            Err(CoinError::InvalidSideError)
105        }
106    }
107}
108
109impl From<bool> for Coin {
110    fn from(b: bool) -> Coin {
111        if b { Coin::Heads } else { Coin::Tails }
112    }
113}
114
115impl fmt::Display for CoinToss {
116    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
117        write!(f, "Guess: {}\nResult: {}", self.guess, self.real)
118    }
119}
120
121impl CoinToss {
122    /// Make a guess on what side the coin will land on
123    pub fn guess(guess: Coin) -> Self {
124        CoinToss {
125            guess,
126            real: Coin::flip(),
127        }
128    }
129    /// Returns if the guess was correct
130    pub fn is_correct(self) -> bool {
131        self.real == self.guess
132    }
133}