codebreaker_solver/
hint.rs

1use std::cmp::min;
2
3use crate::{
4    code::{Code, NUMBER_OF_PEGS_IN_CODE},
5    peg::{Peg, POSSIBLE_COLORS},
6};
7
8/// Hints are generated in response to guesses, to give the codebreaker a glue how many pegs fit
9/// both, in color and position, and how many pegs only match in color.
10#[derive(Debug, PartialEq, Eq, Clone, Copy)]
11pub struct Hint {
12    /// Number of pegs in the guess, which both match color and position of the code.
13    pub correct: u8,
14    /// Number of pegs in the guess, which only match the color, but not the position. Each peg is
15    /// only counted once. E.g. if a guess is red-red-red-blue and the code is red-yellow-green-red
16    /// the number of displaced pegs is one.
17    pub displaced: u8,
18}
19
20impl Hint {
21    pub fn new(guess: Code, code: Code) -> Self {
22        let (correct, displaced) =
23            POSSIBLE_COLORS
24                .iter()
25                .fold((0, 0), |(total_correct, total_displaced), &color| {
26                    let (color_correct, color_displaced) = color_hint(color, guess, code);
27                    (
28                        total_correct + color_correct,
29                        total_displaced + color_displaced,
30                    )
31                });
32
33        Hint { correct, displaced }
34    }
35
36    pub fn is_solution(&self) -> bool {
37        self.correct == NUMBER_OF_PEGS_IN_CODE as u8
38    }
39}
40
41/// Returns for a single color how many pegs are `(correct, displaced)`
42fn color_hint(color: Peg, guess: Code, code: Code) -> (u8, u8) {
43    let mut color_guess = 0;
44    let mut color_code = 0;
45    let mut correct = 0;
46    for (&guess, code) in guess.0.iter().zip(code.0) {
47        if guess == color {
48            color_guess += 1;
49        }
50        if code == color {
51            color_code += 1;
52        }
53        if guess == color && code == color {
54            correct += 1;
55        }
56    }
57    let displaced = min(color_code, color_guess) - correct;
58    (correct, displaced)
59}
60
61#[cfg(test)]
62mod tests {
63    use super::Hint;
64
65    #[test]
66    fn hint() {
67        let hint = Hint::new("6335".parse().unwrap(), "3311".parse().unwrap());
68        assert_eq!(
69            hint,
70            Hint {
71                correct: 1,
72                displaced: 1
73            }
74        )
75    }
76}