codebreaker_solver/
peg.rs

1use std::fmt::{self, Display, Formatter};
2
3use rand::{distributions::Standard, prelude::Distribution, Rng};
4pub const NUM_DIFFERENT_PEGS: u8 = 6;
5
6/// Array of all different peg variants
7pub const POSSIBLE_COLORS: [Peg; NUM_DIFFERENT_PEGS as usize] = possible_pegs();
8
9/// A peg represents one of the places in the code which has to be guessed. Usuall represented as
10/// colors or numbers. The default version of codebreaker uses 6 different kind of pegs.
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub struct Peg(u8);
13
14impl Peg {
15    /// Construct a peg from a number between 0 and 5.
16    pub const fn new(n: u8) -> Self {
17        assert!(n < NUM_DIFFERENT_PEGS);
18        Peg(n)
19    }
20
21    pub fn from_char(c: char) -> Option<Self> {
22        match c {
23            '1' => Some(Peg(0)),
24            '2' => Some(Peg(1)),
25            '3' => Some(Peg(2)),
26            '4' => Some(Peg(3)),
27            '5' => Some(Peg(4)),
28            '6' => Some(Peg(5)),
29            _ => None,
30        }
31    }
32
33    pub fn to_char(self) -> char {
34        match self {
35            Peg(0) => '1',
36            Peg(1) => '2',
37            Peg(2) => '3',
38            Peg(3) => '4',
39            Peg(4) => '5',
40            Peg(5) => '6',
41            _ => unreachable!(),
42        }
43    }
44}
45
46impl Display for Peg {
47    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
48        write!(f, "{}", self.to_char())
49    }
50}
51
52impl Distribution<Peg> for Standard {
53    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Peg {
54        // Avoid bias, by ensuring all numbers are equally likely.
55        let byte = loop {
56            let candidate: u8 = rng.gen();
57            if candidate <= u8::MAX - ((u8::MAX - NUM_DIFFERENT_PEGS + 1) % NUM_DIFFERENT_PEGS) {
58                break candidate;
59            }
60        };
61        Peg(byte % NUM_DIFFERENT_PEGS)
62    }
63}
64
65const fn possible_pegs() -> [Peg; NUM_DIFFERENT_PEGS as usize] {
66    let mut pegs = [Peg(0); NUM_DIFFERENT_PEGS as usize];
67    let mut index = 0;
68    loop {
69        if index == NUM_DIFFERENT_PEGS {
70            break;
71        }
72        pegs[index as usize] = Peg::new(index);
73        index += 1;
74    }
75    pegs
76}