add interactive solver #4

Merged
cscherrNT merged 29 commits from feat/interactive-solver into devel 2024-07-25 15:19:54 +02:00
6 changed files with 32 additions and 18 deletions
Showing only changes of commit 610b404ab7 - Show all commits

View File

@ -128,7 +128,7 @@ fn help_guess_interactive(cli: Cli) -> anyhow::Result<()> {
println!("{}", game); println!("{}", game);
} }
ReplCommand::Solve => { ReplCommand::Solve => {
let best_guess = solver.guess_for(&game); let best_guess = solver.guess_for(&game)?;
println!("best guess: {best_guess}"); println!("best guess: {best_guess}");
} }
ReplCommand::Guess { ReplCommand::Guess {

View File

@ -13,6 +13,11 @@ pub enum Error {
#[from] #[from]
source: GameError, source: GameError,
}, },
#[error("Solver Error")]
SolverError {
#[from]
source: SolverError,
},
#[error("Benchmark Error")] #[error("Benchmark Error")]
BenchError { BenchError {
#[from] #[from]
@ -53,3 +58,9 @@ pub enum BenchError {
#[error("Trying to modify a finished report")] #[error("Trying to modify a finished report")]
ModifyFinishedReport, ModifyFinishedReport,
} }
#[derive(Debug, Clone, Error)]
pub enum SolverError {
#[error("Wordlist has no matches for the gamestate")]
NoMatches,
}

View File

@ -21,7 +21,6 @@ pub struct AtomicEvaluation {
pub struct GuessResponse { pub struct GuessResponse {
guess: Word, guess: Word,
evaluation: Evaluation, evaluation: Evaluation,
finish: bool,
solution: Option<WordData>, solution: Option<WordData>,
step: usize, step: usize,
max_steps: usize, max_steps: usize,
@ -50,10 +49,9 @@ impl From<char> for Status {
impl GuessResponse { impl GuessResponse {
pub(crate) fn new<WL: WordList>(guess: &Word, status: Evaluation, game: &Game<WL>) -> Self { pub(crate) fn new<WL: WordList>(guess: &Word, status: Evaluation, game: &Game<WL>) -> Self {
let new = Self { let mut new = Self {
guess: guess.to_owned(), guess: guess.to_owned(),
evaluation: status, evaluation: status,
finish: game.step() > game.max_steps(),
solution: game.solution().cloned(), solution: game.solution().cloned(),
step: game.step(), step: game.step(),
max_steps: game.max_steps(), max_steps: game.max_steps(),
@ -62,7 +60,7 @@ impl GuessResponse {
} }
pub fn finished(&self) -> bool { pub fn finished(&self) -> bool {
self.finish self.step() > self.max_steps() || self.won()
} }
pub fn won(&self) -> bool { pub fn won(&self) -> bool {

View File

@ -11,6 +11,7 @@ use crate::{
#[cfg(feature = "builtin")] #[cfg(feature = "builtin")]
pub mod naive; pub mod naive;
use libpt::log::debug;
#[cfg(feature = "builtin")] #[cfg(feature = "builtin")]
pub use naive::NaiveSolver; pub use naive::NaiveSolver;
#[cfg(feature = "builtin")] #[cfg(feature = "builtin")]
@ -44,14 +45,14 @@ pub trait Solver<'wl, WL: WordList>: Clone + std::fmt::Debug + Sized + Sync {
/// ///
/// Each [Solver] needs to implement this method themselves, many other methods rely on this to /// Each [Solver] needs to implement this method themselves, many other methods rely on this to
/// play the [Game], such as [play](Solver::play) or [solve](Solver::solve). /// play the [Game], such as [play](Solver::play) or [solve](Solver::solve).
fn guess_for(&self, game: &Game<'wl, WL>) -> Word; fn guess_for(&self, game: &Game<'wl, WL>) -> WResult<Word>;
/// Make a singular step for a [Game] /// Make a singular step for a [Game]
/// ///
/// # Errors /// # Errors
/// ///
/// This function will return an error if [guess_for](Solver::guess_for) fails. /// This function will return an error if [guess_for](Solver::guess_for) fails.
fn make_a_move(&self, game: &mut Game<'wl, WL>) -> WResult<GuessResponse> { fn make_a_move(&self, game: &mut Game<'wl, WL>) -> WResult<GuessResponse> {
Ok(game.guess(self.guess_for(game), None)?) Ok(game.guess(self.guess_for(game)?, None)?)
} }
/// Play a [Game] and return the last [GuessResponse]. /// Play a [Game] and return the last [GuessResponse].
/// ///
@ -146,10 +147,11 @@ impl<'wl, WL: WordList> Solver<'wl, WL> for AnyBuiltinSolver<'wl, WL> {
fn build(wordlist: &'wl WL) -> WResult<Self> { fn build(wordlist: &'wl WL) -> WResult<Self> {
Ok(Self::Naive(NaiveSolver::build(wordlist)?)) Ok(Self::Naive(NaiveSolver::build(wordlist)?))
} }
fn guess_for(&self, game: &Game<'wl, WL>) -> Word { fn guess_for(&self, game: &Game<'wl, WL>) -> WResult<Word> {
match self { debug!("solver: {self:?}");
Self::Naive(solver) => solver.guess_for(game), Ok(match self {
Self::Stupid(solver) => solver.guess_for(game), Self::Naive(solver) => solver.guess_for(game)?,
} Self::Stupid(solver) => solver.guess_for(game)?,
})
} }
} }

View File

@ -1,5 +1,6 @@
use libpt::log::{info, trace}; use libpt::log::{info, trace};
use crate::error::{Error, SolverError, WResult};
use crate::wlist::word::{ManyWordDatas, Word}; use crate::wlist::word::{ManyWordDatas, Word};
use crate::wlist::WordList; use crate::wlist::WordList;
@ -15,7 +16,7 @@ impl<'wl, WL: WordList> Solver<'wl, WL> for NaiveSolver<'wl, WL> {
info!("using naive solver"); info!("using naive solver");
Ok(Self { wl: wordlist }) Ok(Self { wl: wordlist })
} }
fn guess_for(&self, game: &crate::game::Game<WL>) -> Word { fn guess_for(&self, game: &crate::game::Game<WL>) -> WResult<Word> {
// HACK: hardcoded length // HACK: hardcoded length
let mut pattern: String = String::from("....."); let mut pattern: String = String::from(".....");
let mut other_chars: Vec<char> = Vec::new(); let mut other_chars: Vec<char> = Vec::new();
@ -54,9 +55,10 @@ impl<'wl, WL: WordList> Solver<'wl, WL> for NaiveSolver<'wl, WL> {
}) })
.map(|v| v.to_owned()) .map(|v| v.to_owned())
.collect(); .collect();
matches[0].0.to_owned() // FIXME: panicks in interactive solve, when I insert bullshit and if matches.is_empty() {
// pretend that there is a solution #5 It also crashes in return Err(SolverError::NoMatches.into());
// non-interactive mode }
Ok(matches[0].0.to_owned())
} }
} }

View File

@ -1,5 +1,6 @@
use libpt::log::info; use libpt::log::info;
use crate::error::WResult;
use crate::wlist::word::Word; use crate::wlist::word::Word;
use crate::wlist::WordList; use crate::wlist::WordList;
@ -15,8 +16,8 @@ impl<'wl, WL: WordList> Solver<'wl, WL> for StupidSolver<'wl, WL> {
info!("using stupid solver"); info!("using stupid solver");
Ok(Self { wl: wordlist }) Ok(Self { wl: wordlist })
} }
fn guess_for(&self, game: &crate::game::Game<WL>) -> Word { fn guess_for(&self, game: &crate::game::Game<WL>) -> WResult<Word> {
self.wl.rand_word().0 Ok(self.wl.rand_word().0)
} }
} }