codebreaker_solver/
code.rs

1use std::{fmt::Display, str::FromStr};
2
3use rand::{distributions::Standard, prelude::Distribution, Rng};
4use thiserror::Error;
5
6use crate::peg::{Peg, NUM_DIFFERENT_PEGS};
7
8/// Number of Pegs in code
9pub const NUMBER_OF_PEGS_IN_CODE: usize = 4;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub struct Code(pub [Peg; NUMBER_OF_PEGS_IN_CODE]);
13
14impl Distribution<Code> for Standard {
15    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Code {
16        let inner = rng.gen();
17        Code(inner)
18    }
19}
20
21impl Display for Code {
22    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
23        for i in 0..NUMBER_OF_PEGS_IN_CODE {
24            write!(f, "{}", self.0[i])?;
25        }
26        Ok(())
27    }
28}
29
30impl FromStr for Code {
31    type Err = CodeParsingError;
32
33    fn from_str(s: &str) -> Result<Self, Self::Err> {
34        let mut pegs = [Peg::new(0); NUMBER_OF_PEGS_IN_CODE];
35        let mut it_c = s.chars();
36        for peg in &mut pegs {
37            let Some(c) = it_c.next() else {
38                return Err(CodeParsingError::TooFewPegs);
39            };
40            *peg = Peg::from_char(c).ok_or(CodeParsingError::InvalidPeg(c))?;
41        }
42        if it_c.next().is_some() {
43            return Err(CodeParsingError::TooManyPegs);
44        }
45        Ok(Code(pegs))
46    }
47}
48
49/// A gererator for all possible codes
50pub fn all_possible_codes() -> impl Iterator<Item = Code> {
51    let num_possible_codes = (NUM_DIFFERENT_PEGS as u32).pow(NUMBER_OF_PEGS_IN_CODE as u32);
52    (0..num_possible_codes).map(|n| {
53        let mut code = [Peg::new(0); NUMBER_OF_PEGS_IN_CODE];
54        let mut n = n;
55        for peg in &mut code {
56            *peg = Peg::new((n % NUM_DIFFERENT_PEGS as u32) as u8);
57            n /= NUM_DIFFERENT_PEGS as u32;
58        }
59        Code(code)
60    })
61}
62
63#[derive(Error, Debug)]
64pub enum CodeParsingError {
65    #[error("Too many pegs in code")]
66    TooManyPegs,
67    #[error("Too few pegs in code")]
68    TooFewPegs,
69    #[error("Invalid peg '{0}'")]
70    InvalidPeg(char),
71}