1use crate::errors::{BASE_COIN_TOSS_ERROR_CODE, ErrorCode};
21
22use core::fmt;
23
24use rand::RngExt;
25
26#[repr(C)]
28#[derive(
29 Debug, Copy, Clone, serde::Serialize, serde::Deserialize, Eq, PartialEq, PartialOrd, Ord, Hash,
30)]
31pub enum CoinError {
32 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#[derive(
55 Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord, serde::Serialize, serde::Deserialize,
56)]
57pub enum Coin {
58 Heads,
60 Tails,
62}
63
64impl Coin {
65 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#[derive(
82 Copy, Clone, Debug, serde::Serialize, serde::Deserialize, Ord, PartialOrd, PartialEq, Eq, Hash,
83)]
84pub struct CoinToss {
85 pub guess: Coin,
87 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 pub fn guess(guess: Coin) -> Self {
124 CoinToss {
125 guess,
126 real: Coin::flip(),
127 }
128 }
129 pub fn is_correct(self) -> bool {
131 self.real == self.guess
132 }
133}