wordle-analyzer/src/solve/naive/mod.rs
Christoph J. Scherr 18a5125028
All checks were successful
cargo devel CI / cargo CI (push) Successful in 1m58s
fix(evaluation): evaluation did not get built from string correctly #9
2024-07-25 14:48:16 +02:00

83 lines
2.9 KiB
Rust

use libpt::log::{info, trace};
use crate::error::{SolverError, WResult};
use crate::wlist::word::{Word, WordData};
use crate::wlist::WordList;
use super::{AnyBuiltinSolver, Solver, Status};
#[derive(Debug, Clone)]
pub struct NaiveSolver<'wl, WL> {
wl: &'wl WL,
}
impl<'wl, WL: WordList> Solver<'wl, WL> for NaiveSolver<'wl, WL> {
fn build(wordlist: &'wl WL) -> crate::error::WResult<Self> {
info!("using naive solver");
Ok(Self { wl: wordlist })
}
/// Guess a word from the wordlist for the given game
///
/// ## Algorithm
///
/// * Look at the evaluation for the last response and keep the correct letters
/// * Get all words that have these letters at the right position
/// * Discard words that have already been tried
/// * 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<WL>) -> WResult<Word> {
// HACK: hardcoded length
let mut pattern: String = String::from(".....");
let mut other_chars: Vec<char> = Vec::new();
let response = game.last_response();
trace!(
"guessing best guess for last response: {response:#?}\n{:#?}",
response.map(|a| a.evaluation())
);
if response.is_some() {
for (idx, p) in response
.unwrap()
.evaluation()
.clone()
.into_iter()
.enumerate()
{
if p.1 == Status::Matched {
pattern.replace_range(idx..idx + 1, &p.0.to_string());
} else if p.1 == Status::Exists {
other_chars.push(p.0)
}
}
}
trace!("other chars: {:?}", other_chars);
let mut matches: Vec<WordData> = game.wordlist().get_words_matching(pattern)?;
if matches.is_empty() {
return Err(SolverError::NoMatches.into());
}
matches = matches
.iter()
// only words that have not been guessed yet
.filter(|p| !game.made_guesses().contains(&&p.0))
// only words that contain the letters we found earlier (that were not matched)
.filter(|p| {
// TODO: don't repeat unmatched contained chars on the same position twice #2
let mut fits = true;
for c in other_chars.iter() {
fits &= p.0.contains(*c);
}
fits
})
.map(|v| v.to_owned())
.collect();
if matches.is_empty() {
return Err(SolverError::NoMatches.into());
}
Ok(matches[0].0.to_owned())
}
}
impl<'wl, WL: WordList> From<NaiveSolver<'wl, WL>> for AnyBuiltinSolver<'wl, WL> {
fn from(value: NaiveSolver<'wl, WL>) -> Self {
Self::Naive(value)
}
}