From 3c44760a2cc9843f8dd0bb6c3d34bed4ce54de4e Mon Sep 17 00:00:00 2001 From: "Christoph J. Scherr" Date: Fri, 2 Aug 2024 16:13:41 +0200 Subject: [PATCH] feat(naive): make it almost work --- src/solve/naive/mod.rs | 26 ++++++++--------------- src/solve/naive/states.rs | 43 ++++++++++++++++++--------------------- 2 files changed, 28 insertions(+), 41 deletions(-) diff --git a/src/solve/naive/mod.rs b/src/solve/naive/mod.rs index c4410ac..ea34d20 100644 --- a/src/solve/naive/mod.rs +++ b/src/solve/naive/mod.rs @@ -32,16 +32,12 @@ impl<'wl, WL: WordList> Solver<'wl, WL> for NaiveSolver<'wl, WL> { /// * Discard all words that don't have the chars that we know from the last guess are in the /// word, but don't know the position of. fn guess_for(&self, game: &crate::game::Game) -> WResult { - if game.responses().is_empty() { - // TODO: come up with a proper way to make the first guess - return Ok(Word::from("which")); - } - let mut pattern: String = ".".repeat(game.length()); // indexes we tried for that char and the number of occurences let mut state: SolverState = SolverState::new(); let responses = game.responses().iter().enumerate(); for (_idx, response) in responses { + let mut already_found_amounts: HashMap = HashMap::new(); let evaluation: &Evaluation = response.evaluation(); for (idx, p) in evaluation.clone().into_iter().enumerate() { match p.1 { @@ -53,6 +49,7 @@ impl<'wl, WL: WordList> Solver<'wl, WL> for NaiveSolver<'wl, WL> { .entry(p.0) .or_insert(CharInfo::new(game.length())) .found_at(idx); + *already_found_amounts.entry(p.0).or_default() += 1; } Status::Exists => { let cinfo = state @@ -60,16 +57,16 @@ impl<'wl, WL: WordList> Solver<'wl, WL> for NaiveSolver<'wl, WL> { .entry(p.0) .or_insert(CharInfo::new(game.length())); cinfo.tried_but_failed(idx); - cinfo.at_least_n_occurences( - response.guess().chars().filter(|c| *c == p.0).count(), - ) + *already_found_amounts.entry(p.0).or_default() += 1; + cinfo.min_occurences(already_found_amounts[&p.0]); } Status::None => state .char_map_mut() .entry(p.0) .or_insert(CharInfo::new(game.length())) - .not_in_solution(), + .max_occurences(*already_found_amounts.entry(p.0).or_default()), } + trace!("absolute frequencies: {already_found_amounts:?}"); } } @@ -90,9 +87,8 @@ impl<'wl, WL: WordList> Solver<'wl, WL> for NaiveSolver<'wl, WL> { .char_map_mut() .entry(c) .or_insert(CharInfo::new(game.length())); - if !cinfo.part_of_solution() - || cinfo.has_been_tried(idx) - || !cinfo.occurences_of_char_possible(&solution_candidate.0, c) + if !cinfo.part_of_solution() || cinfo.has_been_tried(idx) + // || !cinfo.occurences_of_char_possible(&solution_candidate.0, c) { return false; } @@ -108,12 +104,6 @@ impl<'wl, WL: WordList> Solver<'wl, WL> for NaiveSolver<'wl, WL> { } } -impl<'wl, WL: WordList> NaiveSolver<'wl, WL> { - fn get_highest_possible_abs_freq(indexes: &[usize], game: &crate::game::Game) -> usize { - game.length() - indexes.len() - } -} - impl<'wl, WL: WordList> From> for AnyBuiltinSolver<'wl, WL> { fn from(value: NaiveSolver<'wl, WL>) -> Self { Self::Naive(value) diff --git a/src/solve/naive/states.rs b/src/solve/naive/states.rs index 0d512e6..76dac46 100644 --- a/src/solve/naive/states.rs +++ b/src/solve/naive/states.rs @@ -9,7 +9,7 @@ pub(crate) type CharMap = HashMap; #[derive(Clone, PartialEq, Eq)] pub(crate) struct CharInfo { confirmed_indexes: HashSet, - tried_indexes: HashSet, + bad_indexes: HashSet, occurences_amount: Range, } @@ -38,13 +38,12 @@ impl CharInfo { pub fn new(word_length: usize) -> Self { Self { confirmed_indexes: HashSet::new(), - tried_indexes: HashSet::new(), + bad_indexes: HashSet::new(), occurences_amount: 0..word_length, } } pub fn found_at(&mut self, idx: usize) { - self.tried_indexes.insert(idx); self.confirmed_indexes.insert(idx); if self.occurences_amount.start < 1 { @@ -54,17 +53,11 @@ impl CharInfo { /// tried to guess a char we know exists at this position, but it was incorrect pub fn tried_but_failed(&mut self, idx: usize) { - self.tried_indexes.insert(idx); - } - - /// char is not in the solution - pub fn not_in_solution(&mut self) { - self.occurences_amount.start = 0; - self.occurences_amount.end = 0; + self.bad_indexes.insert(idx); } pub fn has_been_tried(&self, idx: usize) -> bool { - self.tried_indexes.contains(&idx) + self.bad_indexes.contains(&idx) } #[must_use] @@ -72,8 +65,12 @@ impl CharInfo { self.occurences_amount.end > 0 } - pub fn at_least_n_occurences(&mut self, n: usize) { - self.occurences_amount.start = n; + pub fn min_occurences(&mut self, min: usize) { + self.occurences_amount.start = min; + } + + pub(crate) fn max_occurences(&mut self, max: usize) { + self.occurences_amount.end = max } pub(crate) fn confirmed_indexes(&self) -> &HashSet { @@ -85,11 +82,11 @@ impl CharInfo { } pub(crate) fn tried_indexes(&self) -> &HashSet { - &self.tried_indexes + &self.bad_indexes } pub(crate) fn tried_indexes_mut(&mut self) -> &mut HashSet { - &mut self.tried_indexes + &mut self.bad_indexes } pub(crate) fn occurences_amount(&self) -> &Range { @@ -105,12 +102,12 @@ impl CharInfo { solution_candidate: &str, character: char, ) -> bool { - self.occurences_amount.contains( - &solution_candidate - .chars() - .filter(|c| *c == character) - .count(), - ) + let occ = solution_candidate + .chars() + .filter(|c| *c == character) + .count(); + + self.occurences_amount.start <= occ && occ <= self.occurences_amount.end } } @@ -118,9 +115,9 @@ impl Debug for CharInfo { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { if self.part_of_solution() { f.debug_struct("CharInfo") - .field("indexes", &self.confirmed_indexes) + .field("correct_idxs", &self.confirmed_indexes) .field("amnt_occ", &self.occurences_amount) - .field("tried", &self.tried_indexes) + .field("bad_idxs", &self.bad_indexes) .finish() } else { write!(f, "(not in solution)")