improve naive solver #13

Open
cscherrNT wants to merge 12 commits from feat/naive into devel
4 changed files with 103 additions and 48 deletions
Showing only changes of commit af1a2ee36c - Show all commits

View File

@ -1,3 +1,7 @@
use std::fmt::Display;
use std::io::Write;
use colored::Colorize;
use libpt::cli::console::{style, StyledObject}; use libpt::cli::console::{style, StyledObject};
use crate::wlist::word::Word; use crate::wlist::word::Word;
@ -15,21 +19,6 @@ pub struct Evaluation {
} }
impl Evaluation { impl Evaluation {
/// Display the evaluation color coded
pub fn colorized_display(&self) -> Vec<StyledObject<String>> {
let mut buf = Vec::new();
for e in self.inner.iter() {
let mut c = style(e.0.to_string());
if e.1 == Status::Matched {
c = c.green();
} else if e.1 == Status::Exists {
c = c.yellow();
}
buf.push(c);
}
buf
}
/// The first string is the word the evaluation is for, The second string defines how the /// The first string is the word the evaluation is for, The second string defines how the
/// characters of the first string match the solution. /// characters of the first string match the solution.
/// ///
@ -108,3 +97,20 @@ impl From<&Evaluation> for Word {
Word::from(value.inner.iter().map(|v| v.0).collect::<String>()) Word::from(value.inner.iter().map(|v| v.0).collect::<String>())
} }
} }
impl Display for Evaluation {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for s in &self.inner {
write!(
f,
"{}",
match s.1 {
Status::None => s.0.to_string().into(),
Status::Exists => s.0.to_string().yellow(),
Status::Matched => s.0.to_string().green(),
}
)?;
}
std::fmt::Result::Ok(())
}
}

View File

@ -29,7 +29,6 @@ where
solution: Option<WordData>, solution: Option<WordData>,
wordlist: &'wl WL, wordlist: &'wl WL,
responses: Vec<GuessResponse>, responses: Vec<GuessResponse>,
// TODO: keep track of the letters the user has tried
} }
impl<'wl, WL: WordList> Game<'wl, WL> { impl<'wl, WL: WordList> Game<'wl, WL> {
@ -126,15 +125,29 @@ impl<'wl, WL: WordList> Game<'wl, WL> {
} }
/// Generates an [Evaluation] for a given solution and guess. /// Generates an [Evaluation] for a given solution and guess.
pub(crate) fn evaluate(mut solution: WordData, guess: &Word) -> Evaluation { pub(crate) fn evaluate(solution: WordData, guess: &Word) -> Evaluation {
let mut evaluation = Vec::new(); let mut evaluation = Vec::new();
let mut status: Status; let mut status: Status;
let mut buf = solution.0.clone(); let mut buf = solution.0.clone();
for ((idx, c_guess), c_sol) in guess.chars().enumerate().zip(solution.0.chars()) { for ((idx, c_guess), c_sol) in guess.chars().enumerate().zip(solution.0.chars()) {
if c_guess == c_sol { if c_guess == c_sol {
status = Status::Matched; status = Status::Matched;
buf.replace_range(idx..idx + 1, "_"); buf.replace_range(idx..idx + 1, "_");
} else if buf.contains(c_guess) { } else if buf.contains(c_guess)
&& buf
.char_indices()
.filter(|c| c.1 == c_guess)
.filter(|c| {
guess
.chars()
.nth(c.0)
.expect("the evaluations are somehow different lengths")
== c.1
})
.count()
== 0
{
status = Status::Exists; status = Status::Exists;
buf = buf.replacen(c_guess, "_", 1); buf = buf.replacen(c_guess, "_", 1);
} else { } else {
@ -359,16 +372,8 @@ impl<'wl, WL: WordList> Display for Game<'wl, WL> {
self.step(), self.step(),
self.solution(), self.solution(),
)?; )?;
for s in self for s in self.responses() {
.responses() write!(f, "\"{s}\",")?;
.iter()
.map(|v| v.evaluation().to_owned().colorized_display())
{
write!(f, "\"")?;
for si in s {
write!(f, "{si}")?;
}
write!(f, "\", ")?;
} }
Ok(()) Ok(())
} }

View File

@ -92,17 +92,6 @@ impl GuessResponse {
impl Display for GuessResponse { impl Display for GuessResponse {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for s in self.evaluation.clone().into_iter() { write!(f, "{}", self.evaluation())
write!(
f,
"{}",
match s.1 {
Status::None => s.0.to_string().into(),
Status::Exists => s.0.to_string().yellow(),
Status::Matched => s.0.to_string().green(),
}
)?;
}
std::fmt::Result::Ok(())
} }
} }

View File

@ -1,6 +1,7 @@
use wordle_analyzer::error::GameError; use test_log::test; // set the log level with an envvar: `RUST_LOG=trace cargo test`
use libpt::log::info;
use wordle_analyzer::game::evaluation::Evaluation; use wordle_analyzer::game::evaluation::Evaluation;
use wordle_analyzer::game::response::GuessResponse;
use wordle_analyzer::wlist::builtin::BuiltinWList; use wordle_analyzer::wlist::builtin::BuiltinWList;
use wordle_analyzer::wlist::WordList; use wordle_analyzer::wlist::WordList;
@ -23,10 +24,25 @@ fn test_eval_simple() -> anyhow::Result<()> {
let mut game = builder.build()?; let mut game = builder.build()?;
let guess = Word::from("slate"); let guess = Word::from("slate");
game.guess(&guess, None)?; game.guess(&guess, None)?;
assert_eq!( let correct = Evaluation::build(&guess, "xxccc")?;
info!(
"{} =? {}",
*game.last_response().unwrap().evaluation(), *game.last_response().unwrap().evaluation(),
Evaluation::build(&guess, "xxccc")? correct
); );
assert_eq!(*game.last_response().unwrap().evaluation(), correct);
let mut game = builder.build()?;
let guess = Word::from("about");
game.guess(&guess, None)?;
let correct = Evaluation::build(&guess, "fxxxf")?;
info!(
"{} =? {}",
*game.last_response().unwrap().evaluation(),
correct
);
assert_eq!(*game.last_response().unwrap().evaluation(), correct);
Ok(()) Ok(())
} }
@ -40,18 +56,57 @@ fn test_eval_reoccuring_char() -> anyhow::Result<()> {
let mut game = builder.build()?; let mut game = builder.build()?;
let guess = Word::from("pines"); let guess = Word::from("pines");
game.guess(&guess, None)?; game.guess(&guess, None)?;
assert_eq!( let correct = Evaluation::build(&guess, "xcccc")?;
info!(
"{} =? {}",
*game.last_response().unwrap().evaluation(), *game.last_response().unwrap().evaluation(),
Evaluation::build(&guess, "xcccc")? correct
); );
assert_eq!(*game.last_response().unwrap().evaluation(), correct);
let mut game = builder.build()?; let mut game = builder.build()?;
let guess = Word::from("sides"); let guess = Word::from("sides");
game.guess(&guess, None)?; game.guess(&guess, None)?;
assert_eq!( let correct = Evaluation::build(&guess, "xcxcc")?;
info!(
"{} =? {}",
*game.last_response().unwrap().evaluation(), *game.last_response().unwrap().evaluation(),
Evaluation::build(&guess, "xcxcc")? correct
); );
assert_eq!(*game.last_response().unwrap().evaluation(), correct);
let mut game = builder.build()?;
let guess = Word::from("ninja");
game.guess(&guess, None)?;
let correct = Evaluation::build(&guess, "cccxx")?;
info!(
"{} =? {}",
*game.last_response().unwrap().evaluation(),
correct
);
assert_eq!(*game.last_response().unwrap().evaluation(), correct);
let mut game = builder.build()?;
let guess = Word::from("which");
game.guess(&guess, None)?;
let correct = Evaluation::build(&guess, "xxfxx")?;
info!(
"{} =? {}",
*game.last_response().unwrap().evaluation(),
correct
);
assert_eq!(*game.last_response().unwrap().evaluation(), correct);
let mut game = builder.build()?;
let guess = Word::from("indie");
game.guess(&guess, None)?;
let correct = Evaluation::build(&guess, "ffxxf")?;
info!(
"{} =? {}",
*game.last_response().unwrap().evaluation(),
correct
);
assert_eq!(*game.last_response().unwrap().evaluation(), correct);
Ok(()) Ok(())
} }