1use rbp_core::*;
2
3#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq, Ord, PartialOrd)]
11pub struct Odds(Chips, Chips);
12
13impl Odds {
14 pub const fn new(n: Chips, d: Chips) -> Self {
16 Self(n, d)
17 }
18 pub fn numer(&self) -> Chips {
20 self.0
21 }
22 pub fn denom(&self) -> Chips {
24 self.1
25 }
26 fn gcd(a: Chips, b: Chips) -> (Chips, Chips) {
28 let (mut x, mut y) = (a, b);
29 while y != 0 {
30 (x, y) = (y, x % y);
31 }
32 (a / x, b / x)
33 }
34 pub fn ratio(&self) -> String {
36 format!("{}:{}", self.0, self.1)
37 }
38 pub const GRID: [Self; 10] = [
40 Self(1, 4), Self(1, 3), Self(1, 2), Self(2, 3), Self(3, 4), Self(1, 1), Self(5, 4), Self(3, 2), Self(2, 1), Self(3, 1), ];
51}
52
53impl From<Odds> for Probability {
54 fn from(odds: Odds) -> Self {
55 odds.0 as Probability / odds.1 as Probability
56 }
57}
58
59impl From<(Chips, Chips)> for Odds {
60 fn from((a, b): (Chips, Chips)) -> Self {
61 let (a, b) = Self::gcd(a, b);
62 Self(a, b)
63 }
64}
65
66impl TryFrom<&str> for Odds {
69 type Error = anyhow::Error;
70 fn try_from(s: &str) -> Result<Self, Self::Error> {
71 match (s.strip_prefix('+'), s.strip_prefix('-')) {
72 (Some(x), _) => Ok(Self::new(1, x.parse()?)),
73 (_, Some(x)) => Ok(Self::new(x.parse()?, 1)),
74 _ => Err(anyhow::anyhow!("odds string missing + or -")),
75 }
76 }
77}
78
79impl std::fmt::Display for Odds {
80 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
81 let p = Probability::from(*self);
82 if p > 1.0 {
83 write!(f, "-{}", p.round() as Chips)
84 } else {
85 write!(f, "+{}", (1.0 / p).round() as Chips)
86 }
87 }
88}
89
90impl Arbitrary for Odds {
91 fn random() -> Self {
92 use rand::prelude::IndexedRandom;
93 let ref mut rng = rand::rng();
94 Self::GRID.choose(rng).copied().expect("GRID is empty")
95 }
96}