1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
use rand::{distributions::Standard, prelude::Distribution, Rng};
use serde::{Deserialize, Serialize};

#[derive(Hash, Eq, PartialEq, Copy, Clone, Debug, Serialize, Deserialize, Ord, PartialOrd)]
#[repr(transparent)]
pub struct Die(pub u8);

#[derive(Hash, Eq, PartialEq, Copy, Clone, Debug, Serialize, Deserialize)]
pub struct Dice(pub Die, pub Die);

impl Dice {
    pub fn iter_all_possible() -> impl Iterator<Item = Dice> {
        (1..=6).flat_map(|a| (1..=6).map(move |b| Dice(Die(a), Die(b))))
    }

    #[must_use]
    #[inline]
    pub fn dice(&self) -> Vec<Die> {
        if self.0 == self.1 {
            return vec![self.0; 4];
        }

        vec![self.0, self.1]
    }

    #[inline]
    pub fn roll(rng: &mut impl Rng) -> Dice {
        rng.gen()
    }

    #[inline]
    pub fn as_u8(&self) -> u8 {
        self.0 .0 << 3 | self.1 .0
    }

    #[inline]
    pub fn from_u8(u: u8) -> Dice {
        Dice(Die(u >> 3), Die(u & 0b111))
    }

    #[inline]
    pub fn as_alnum(&self) -> char {
        let index = (self.0 .0 - 1) * 6 + (self.1 .0 - 1);
        match index {
            0..=9 => ('0' as u8 + index) as char,
            10..=35 => ('A' as u8 + index - 10) as char,
            _ => panic!("Invalid dice rolls"),
        }
    }

    #[inline]
    pub fn from_alnum(c: char) -> Self {
        let index = match c {
            '0'..='9' => c as u8 - '0' as u8,
            'A'..='Z' => 10 + (c as u8 - 'A' as u8),
            _ => panic!("Invalid character"),
        };
        let first = index / 6 + 1;
        let second = index % 6 + 1;
        Dice(Die(first), Die(second))
    }

    #[inline]
    pub fn is_double(&self) -> bool {
        self.0 == self.1
    }
}

impl Distribution<Die> for Standard {
    fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> Die {
        Die(rng.gen_range(1..=6))
    }
}

impl Distribution<Dice> for Standard {
    fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> Dice {
        Dice(rng.gen(), rng.gen())
    }
}