add interactive solver #4

Merged
cscherrNT merged 29 commits from feat/interactive-solver into devel 2024-07-25 15:19:54 +02:00
2 changed files with 89 additions and 8 deletions
Showing only changes of commit 0c4adba682 - Show all commits

View File

@ -29,7 +29,6 @@ where
step: usize, step: usize,
solution: Option<WordData>, solution: Option<WordData>,
wordlist: &'wl WL, wordlist: &'wl WL,
finished: bool,
responses: Vec<GuessResponse>, responses: Vec<GuessResponse>,
// TODO: keep track of the letters the user has tried // TODO: keep track of the letters the user has tried
} }
@ -50,8 +49,8 @@ impl<'wl, WL: WordList> Game<'wl, WL> {
/// ///
/// # Errors /// # Errors
/// ///
/// This function will return an error if . /// No Errors
pub(crate) fn build( pub fn build(
length: usize, length: usize,
precompute: bool, precompute: bool,
max_steps: usize, max_steps: usize,
@ -70,13 +69,17 @@ impl<'wl, WL: WordList> Game<'wl, WL> {
None None
}, },
wordlist: wlist, wordlist: wlist,
finished: false,
responses: Vec::new(), responses: Vec::new(),
}; };
Ok(game) Ok(game)
} }
/// set a solution, can be used for testing
pub fn set_solution(&mut self, sol: Option<WordData>) {
self.solution = sol;
}
/// Make a new guess /// Make a new guess
/// ///
/// The word will be evaluated against the [solution](Game::solution) of the [Game]. /// The word will be evaluated against the [solution](Game::solution) of the [Game].
@ -93,7 +96,7 @@ impl<'wl, WL: WordList> Game<'wl, WL> {
if guess.len() != self.length { if guess.len() != self.length {
return Err(GameError::GuessHasWrongLength(guess.len())); return Err(GameError::GuessHasWrongLength(guess.len()));
} }
if self.finished || self.step > self.max_steps { if self.finished() || self.step > self.max_steps {
return Err(GameError::TryingToPlayAFinishedGame); return Err(GameError::TryingToPlayAFinishedGame);
} }
if self.wordlist.get_word(&guess).is_none() { if self.wordlist.get_word(&guess).is_none() {
@ -110,7 +113,6 @@ impl<'wl, WL: WordList> Game<'wl, WL> {
panic!("there is neither an evaluation nor a predefined solution for this guess"); panic!("there is neither an evaluation nor a predefined solution for this guess");
} }
self.responses.push(response.clone()); self.responses.push(response.clone());
self.finished = response.finished();
Ok(response) Ok(response)
} }
@ -145,7 +147,17 @@ impl<'wl, WL: WordList> Game<'wl, WL> {
} }
pub fn finished(&self) -> bool { pub fn finished(&self) -> bool {
self.finished if self.responses().is_empty() {
return false;
}
self.responses().last().unwrap().finished()
}
pub fn won(&self) -> bool {
if self.responses().is_empty() {
return false;
}
self.responses().last().unwrap().won()
} }
pub fn max_steps(&self) -> usize { pub fn max_steps(&self) -> usize {
@ -182,7 +194,7 @@ impl<'wl, WL: WordList> Game<'wl, WL> {
/// # use anyhow::Result; /// # use anyhow::Result;
/// # fn main() -> Result<()> { /// # fn main() -> Result<()> {
/// let wl = BuiltinWList::default(); /// let wl = BuiltinWList::default();
/// let game: Game<_> = GameBuilder::new(&wl) /// let game: Game<_> = GameBuilder::new(&wl, true)
/// .build()?; /// .build()?;
/// # Ok(()) /// # Ok(())
/// # } /// # }

69
tests/solver.rs Normal file
View File

@ -0,0 +1,69 @@
use wordle_analyzer::game::Game;
use wordle_analyzer::solve::{AnyBuiltinSolver, NaiveSolver, Solver, StupidSolver};
use wordle_analyzer::wlist::builtin::BuiltinWList;
use wordle_analyzer::wlist::word::Word;
use wordle_analyzer::wlist::WordList;
fn wordlist() -> impl WordList {
BuiltinWList::default()
}
#[test]
fn test_build_builtin_solvers() {
let wl = wordlist();
let stupid_solver =
AnyBuiltinSolver::Stupid(StupidSolver::build(&wl).expect("could not build naive solver"));
let naive_solver =
AnyBuiltinSolver::Naive(NaiveSolver::build(&wl).expect("could not build naive solver"));
}
#[test]
fn test_naive_play_predetermined_game() -> anyhow::Result<()> {
let wl = wordlist();
let sl =
AnyBuiltinSolver::Naive(NaiveSolver::build(&wl).expect("could not build naive solver"));
let mut game = Game::build(5, false, 6, &wl, false)?;
game.set_solution(Some(("nines".into(), 0.002))); // The accuracy is made up but shouldn't
// matter
sl.make_a_move(&mut game)?;
assert_eq!(
game.responses().last().unwrap().guess(),
&Word::from("which")
);
sl.make_a_move(&mut game)?;
assert_eq!(
game.responses().last().unwrap().guess(),
&Word::from("their")
);
sl.make_a_move(&mut game)?;
assert_eq!(
game.responses().last().unwrap().guess(),
&Word::from("being")
);
sl.make_a_move(&mut game)?;
assert_eq!(
game.responses().last().unwrap().guess(),
&Word::from("since")
);
sl.make_a_move(&mut game)?;
assert_eq!(
game.responses().last().unwrap().guess(),
&Word::from("lines")
);
sl.make_a_move(&mut game)?;
assert_eq!(
game.responses().last().unwrap().guess(),
&Word::from("mines")
);
sl.make_a_move(&mut game)?;
assert_eq!(
game.responses().last().unwrap().guess(),
&Word::from("wines")
);
// naive is at the moment too bad to solve "nines"
assert!(game.finished());
assert!(!game.won());
Ok(())
}