refactor(evaluation): fix yet another evaluation bug by refactoring
cargo devel CI / cargo CI (push) Failing after 1m49s Details

This commit is contained in:
Christoph J. Scherr 2024-08-03 20:05:46 +02:00
parent cd48322e89
commit 5f8c92de47
2 changed files with 73 additions and 24 deletions

View File

@ -126,34 +126,60 @@ 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(solution: WordData, guess: &Word) -> Evaluation { pub(crate) fn evaluate(solution: WordData, guess: &Word) -> Evaluation {
let mut evaluation = Vec::new(); let solution = solution.0;
let mut evaluation: Vec<(char, Status)> = vec![('!', Status::None); solution.len()];
let mut status: Status; let mut status: Status;
let mut buf = solution.0.clone(); let mut buf: Vec<char> = solution.chars().collect();
for ((idx, c_guess), c_sol) in guess.chars().enumerate().zip(solution.0.chars()) { #[cfg(debug_assertions)]
let buflen = solution.len();
#[cfg(debug_assertions)]
{
assert_eq!(buflen, buf.len());
assert_eq!(buflen, evaluation.len());
}
// first the correct solutions
for ((idx, c_guess), c_sol) in guess.chars().enumerate().zip(solution.chars()) {
if c_guess == c_sol { if c_guess == c_sol {
status = Status::Matched; status = Status::Matched;
buf.replace_range(idx..idx + 1, "_"); buf[idx] = '!';
} else if buf.contains(c_guess) evaluation[idx] = (c_guess, status);
&& 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;
buf = buf.replacen(c_guess, "_", 1);
} else {
status = Status::None
} }
evaluation.push((c_guess, status)); }
#[cfg(debug_assertions)]
{
assert_eq!(buflen, buf.len());
assert_eq!(buflen, evaluation.len());
}
// then check if the char exists, but was not guessed to be at the correct position
//
// We split this up, because finding the "exists" chars at the same time as the "correct"
// chars causes bugs
for ((idx, c_guess), c_sol) in guess.chars().enumerate().zip(solution.chars()) {
if c_guess == c_sol {
continue;
} else if buf.contains(&c_guess) {
status = Status::Exists;
// replace that char in the buffer to signal that is has been paired with the
// current char
let idx_of_a_match = buf
.iter()
.position(|c| *c == c_guess)
.expect("did not find a character in a string even though we know it exists");
buf[idx_of_a_match] = '!';
} else {
status = Status::None;
}
evaluation[idx] = (c_guess, status);
}
#[cfg(debug_assertions)]
{
assert_eq!(buflen, buf.len());
assert_eq!(buflen, evaluation.len());
} }
evaluation.into() evaluation.into()
} }

View File

@ -47,11 +47,12 @@ fn test_eval_simple() -> anyhow::Result<()> {
} }
#[test] #[test]
fn test_eval_reoccuring_char() -> anyhow::Result<()> { fn test_eval_reoccuring_char0() -> anyhow::Result<()> {
let wl = wordlist(); let wl = wordlist();
let builder = game::Game::builder(&wl) let builder = game::Game::builder(&wl)
.solution(Some(wl.get_word(&Word::from("nines")).unwrap())) .solution(Some(wl.get_word(&Word::from("nines")).unwrap()))
.precompute(false); .precompute(false);
info!("solution=nines");
let mut game = builder.build()?; let mut game = builder.build()?;
let guess = Word::from("pines"); let guess = Word::from("pines");
@ -110,3 +111,25 @@ fn test_eval_reoccuring_char() -> anyhow::Result<()> {
Ok(()) Ok(())
} }
#[test]
fn test_eval_reoccuring_char1() -> anyhow::Result<()> {
let wl = wordlist();
let builder = game::Game::builder(&wl)
.solution(Some(wl.get_word(&Word::from("fatty")).unwrap()))
.precompute(false);
info!("solution=fatty");
let mut game = builder.build()?;
let guess = Word::from("state");
game.guess(&guess, None)?;
let correct = Evaluation::build(&guess, "xffcx")?;
info!(
"{} =? {}",
*game.last_response().unwrap().evaluation(),
correct
);
assert_eq!(*game.last_response().unwrap().evaluation(), correct);
Ok(())
}