From cc1afda25bfa53eea577c29ce870f3cbfe97c6fd Mon Sep 17 00:00:00 2001 From: PlexSheep Date: Sat, 3 Aug 2024 20:05:46 +0200 Subject: [PATCH] refactor(evaluation): fix yet another evaluation bug by refactoring --- src/game/mod.rs | 64 +++++++++++++++++++++++++++++++------------------ tests/game.rs | 25 ++++++++++++++++++- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/game/mod.rs b/src/game/mod.rs index 1841997..c3f2861 100644 --- a/src/game/mod.rs +++ b/src/game/mod.rs @@ -126,35 +126,53 @@ impl<'wl, WL: WordList> Game<'wl, WL> { /// Generates an [Evaluation] for a given solution and guess. 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 buf = solution.0.clone(); + let mut buf: Vec = solution.chars().collect(); - for ((idx, c_guess), c_sol) in guess.chars().enumerate().zip(solution.0.chars()) { + #[cfg(debug_assertions)] + let buflen = solution.len(); + assert_eq!(buflen, buf.len()); + assert_eq!(buflen, evaluation.len()); + debug!("buf: {buf:?}"); + + // first the correct solutions + for ((idx, c_guess), c_sol) in guess.chars().enumerate().zip(solution.chars()) { if c_guess == c_sol { status = Status::Matched; - buf.replace_range(idx..idx + 1, "_"); - } 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; - buf = buf.replacen(c_guess, "_", 1); - } else { - status = Status::None + buf[idx] = '!'; + evaluation[idx] = (c_guess, status); } - evaluation.push((c_guess, status)); } + + 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); + } + + assert_eq!(buflen, buf.len()); + assert_eq!(buflen, evaluation.len()); evaluation.into() } diff --git a/tests/game.rs b/tests/game.rs index 31eef13..bb8ca0e 100644 --- a/tests/game.rs +++ b/tests/game.rs @@ -47,11 +47,12 @@ fn test_eval_simple() -> anyhow::Result<()> { } #[test] -fn test_eval_reoccuring_char() -> anyhow::Result<()> { +fn test_eval_reoccuring_char0() -> anyhow::Result<()> { let wl = wordlist(); let builder = game::Game::builder(&wl) .solution(Some(wl.get_word(&Word::from("nines")).unwrap())) .precompute(false); + info!("solution=nines"); let mut game = builder.build()?; let guess = Word::from("pines"); @@ -110,3 +111,25 @@ fn test_eval_reoccuring_char() -> anyhow::Result<()> { 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(()) +}