From f511e158cd55c584e71b54527b0e6706a4f4b411 Mon Sep 17 00:00:00 2001 From: "Christoph J. Scherr" Date: Fri, 2 Aug 2024 15:33:41 +0200 Subject: [PATCH] feat(naive): state building from responses works --- src/solve/naive/mod.rs | 38 +++++++++++++++++++++++++++----------- src/solve/naive/states.rs | 25 ++++++++++++++++++++++--- 2 files changed, 49 insertions(+), 14 deletions(-) diff --git a/src/solve/naive/mod.rs b/src/solve/naive/mod.rs index 13203b0..c4410ac 100644 --- a/src/solve/naive/mod.rs +++ b/src/solve/naive/mod.rs @@ -32,6 +32,11 @@ 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(); @@ -43,24 +48,33 @@ impl<'wl, WL: WordList> Solver<'wl, WL> for NaiveSolver<'wl, WL> { Status::Matched => { pattern.replace_range(idx..idx + 1, &p.0.to_string()); - state.char_map_mut().entry(p.0).or_default().found_at(idx); + state + .char_map_mut() + .entry(p.0) + .or_insert(CharInfo::new(game.length())) + .found_at(idx); } - Status::Exists => state - .char_map_mut() - .entry(p.0) - .or_default() - .at_least_n_occurences( + Status::Exists => { + let cinfo = state + .char_map_mut() + .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(), - ), + ) + } Status::None => state .char_map_mut() .entry(p.0) - .or_default() + .or_insert(CharInfo::new(game.length())) .not_in_solution(), } } } + debug!("built state from responses: {state:#?}"); + // get all words that have the correct chars on the same positions let mut matches: Vec = game.wordlist().get_words_matching(&pattern)?; if matches.is_empty() { @@ -72,11 +86,13 @@ impl<'wl, WL: WordList> Solver<'wl, WL> for NaiveSolver<'wl, WL> { .filter(|p| !game.made_guesses().contains(&&p.0)) .filter(|solution_candidate| { for (idx, c) in solution_candidate.0.char_indices() { - let cinfo = state.char_map_mut().entry(c).or_default(); - // bad word if it uses a char thats not in the solution + let cinfo = state + .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) + || !cinfo.occurences_of_char_possible(&solution_candidate.0, c) { return false; } diff --git a/src/solve/naive/states.rs b/src/solve/naive/states.rs index a69b535..0d512e6 100644 --- a/src/solve/naive/states.rs +++ b/src/solve/naive/states.rs @@ -1,11 +1,12 @@ use std::collections::{HashMap, HashSet}; +use std::fmt::Debug; use std::ops::{Range, RangeBounds}; use crate::error::WResult; pub(crate) type CharMap = HashMap; -#[derive(Debug, Clone, PartialEq, Eq, Default)] +#[derive(Clone, PartialEq, Eq)] pub(crate) struct CharInfo { confirmed_indexes: HashSet, tried_indexes: HashSet, @@ -34,8 +35,12 @@ impl SolverState { } impl CharInfo { - pub fn new() -> Self { - Self::default() + pub fn new(word_length: usize) -> Self { + Self { + confirmed_indexes: HashSet::new(), + tried_indexes: HashSet::new(), + occurences_amount: 0..word_length, + } } pub fn found_at(&mut self, idx: usize) { @@ -108,3 +113,17 @@ impl CharInfo { ) } } + +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("amnt_occ", &self.occurences_amount) + .field("tried", &self.tried_indexes) + .finish() + } else { + write!(f, "(not in solution)") + } + } +}