generated from PlexSheep/rs-base
refactor: improve error handling
cargo devel CI / cargo CI (push) Failing after 1m41s
Details
cargo devel CI / cargo CI (push) Failing after 1m41s
Details
This commit is contained in:
parent
500e99d8ee
commit
713a661cc5
|
@ -8,12 +8,13 @@ use libpt::cli::{repl::Repl, strum};
|
||||||
use libpt::log::*;
|
use libpt::log::*;
|
||||||
use strum::EnumIter;
|
use strum::EnumIter;
|
||||||
|
|
||||||
|
use wordle_analyzer::error::Error;
|
||||||
use wordle_analyzer::game::evaluation::Evaluation;
|
use wordle_analyzer::game::evaluation::Evaluation;
|
||||||
use wordle_analyzer::game::response::GuessResponse;
|
use wordle_analyzer::game::response::GuessResponse;
|
||||||
|
|
||||||
use wordle_analyzer::solve::{BuiltinSolverNames, Solver};
|
use wordle_analyzer::solve::{BuiltinSolverNames, Solver};
|
||||||
use wordle_analyzer::wlist::builtin::BuiltinWList;
|
use wordle_analyzer::wlist::builtin::BuiltinWList;
|
||||||
use wordle_analyzer::wlist::word::Word;
|
use wordle_analyzer::wlist::word::{Word, WordData};
|
||||||
use wordle_analyzer::wlist::WordList;
|
use wordle_analyzer::wlist::WordList;
|
||||||
use wordle_analyzer::{self, game};
|
use wordle_analyzer::{self, game};
|
||||||
|
|
||||||
|
@ -33,12 +34,22 @@ struct Cli {
|
||||||
#[command(flatten)]
|
#[command(flatten)]
|
||||||
verbose: libpt::cli::args::VerbosityLevel,
|
verbose: libpt::cli::args::VerbosityLevel,
|
||||||
/// which solver to use
|
/// which solver to use
|
||||||
#[arg(short, long, default_value_t = BuiltinSolverNames::default())]
|
#[arg(long, default_value_t = BuiltinSolverNames::default())]
|
||||||
solver: BuiltinSolverNames,
|
solver: BuiltinSolverNames,
|
||||||
|
|
||||||
/// set if the solver should play a full native game without interaction
|
/// set if the solver should play a full native game without interaction
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
non_interactive: bool,
|
non_interactive: bool,
|
||||||
|
|
||||||
|
// FIXME: line breaks don't work correctly in the cli help
|
||||||
|
//
|
||||||
|
/// Solution for the game
|
||||||
|
///
|
||||||
|
/// This will only be used when non-interactive is used. You can use this option to see how the
|
||||||
|
/// selected solver behaves when trying to guess a specific solution, which can help reproduce
|
||||||
|
/// behavior.
|
||||||
|
#[arg(short, long)]
|
||||||
|
solution: Option<Word>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Subcommand, Debug, EnumIter, Clone)]
|
#[derive(Subcommand, Debug, EnumIter, Clone)]
|
||||||
|
@ -184,10 +195,22 @@ fn wlcommand_handler(_cli: &Cli, cmd: &WlCommand, wl: &impl WordList) -> anyhow:
|
||||||
|
|
||||||
fn play_native_non_interactive(cli: Cli) -> anyhow::Result<()> {
|
fn play_native_non_interactive(cli: Cli) -> anyhow::Result<()> {
|
||||||
let wl = BuiltinWList::default();
|
let wl = BuiltinWList::default();
|
||||||
let builder = game::Game::builder(&wl)
|
let mut builder = game::Game::builder(&wl)
|
||||||
.length(cli.length)
|
.length(cli.length)
|
||||||
.max_steps(cli.max_steps)
|
.max_steps(cli.max_steps)
|
||||||
.precompute(cli.precompute);
|
.precompute(cli.precompute);
|
||||||
|
if cli.solution.is_some() {
|
||||||
|
let solw: Word = cli.solution.unwrap();
|
||||||
|
let sol = wl.get_word(&solw);
|
||||||
|
if sol.is_none() {
|
||||||
|
eprintln!("the requested solution \"{solw}\" is not in the wordlist");
|
||||||
|
return Err(Error::GameError {
|
||||||
|
source: wordle_analyzer::error::GameError::WordNotInWordlist(solw),
|
||||||
|
}
|
||||||
|
.into());
|
||||||
|
}
|
||||||
|
builder = builder.solution(sol);
|
||||||
|
}
|
||||||
let solver = cli.solver.to_solver(&wl);
|
let solver = cli.solver.to_solver(&wl);
|
||||||
let mut game = builder.build()?;
|
let mut game = builder.build()?;
|
||||||
|
|
||||||
|
|
|
@ -49,7 +49,7 @@ pub enum GameError {
|
||||||
GuessHasWrongLength(usize),
|
GuessHasWrongLength(usize),
|
||||||
#[error("The game is finished but a guess is being made")]
|
#[error("The game is finished but a guess is being made")]
|
||||||
TryingToPlayAFinishedGame,
|
TryingToPlayAFinishedGame,
|
||||||
#[error("Tried to guess a word that is not in the wordlist ({0})")]
|
#[error("Tried to guess or use a word that is not in the wordlist ({0})")]
|
||||||
WordNotInWordlist(Word),
|
WordNotInWordlist(Word),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -214,13 +214,14 @@ impl<'wl, WL: WordList> Game<'wl, WL> {
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct GameBuilder<'wl, WL: WordList> {
|
pub struct GameBuilder<'wl, WL: WordList> {
|
||||||
length: usize,
|
length: usize,
|
||||||
precompute: bool,
|
precompute: bool,
|
||||||
max_steps: usize,
|
max_steps: usize,
|
||||||
wordlist: &'wl WL,
|
wordlist: &'wl WL,
|
||||||
generate_solution: bool,
|
generate_solution: bool,
|
||||||
|
solution: Option<WordData>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'wl, WL: WordList> GameBuilder<'wl, WL> {
|
impl<'wl, WL: WordList> GameBuilder<'wl, WL> {
|
||||||
|
@ -234,19 +235,23 @@ impl<'wl, WL: WordList> GameBuilder<'wl, WL> {
|
||||||
max_steps: super::DEFAULT_MAX_STEPS,
|
max_steps: super::DEFAULT_MAX_STEPS,
|
||||||
wordlist: wl,
|
wordlist: wl,
|
||||||
generate_solution,
|
generate_solution,
|
||||||
|
solution: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// build a [`Game`] with the stored configuration
|
/// build a [`Game`] with the stored configuration
|
||||||
pub fn build(&'wl self) -> GameResult<Game<'wl, WL>> {
|
pub fn build(&'wl self) -> GameResult<Game<'wl, WL>> {
|
||||||
trace!("{:#?}", self);
|
trace!("{:#?}", self);
|
||||||
let game: Game<WL> = Game::build(
|
let mut game: Game<WL> = Game::build(
|
||||||
self.length,
|
self.length,
|
||||||
self.precompute,
|
self.precompute,
|
||||||
self.max_steps,
|
self.max_steps,
|
||||||
self.wordlist,
|
self.wordlist,
|
||||||
self.generate_solution,
|
self.generate_solution,
|
||||||
)?;
|
)?;
|
||||||
|
if self.solution.is_some() {
|
||||||
|
game.set_solution(self.solution.clone())
|
||||||
|
}
|
||||||
Ok(game)
|
Ok(game)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -284,6 +289,22 @@ impl<'wl, WL: WordList> GameBuilder<'wl, WL> {
|
||||||
self.wordlist = wl;
|
self.wordlist = wl;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the solution for the games built by the builder
|
||||||
|
///
|
||||||
|
/// If this is [Some], then the solution generated by
|
||||||
|
/// [generate_solution](Self::generate_solution) will be overwritten (if it
|
||||||
|
/// is true).
|
||||||
|
///
|
||||||
|
/// If [generate_solution](Self::generate_solution) is false and this method is not used, the
|
||||||
|
/// game will not have a predetermined solution and will not be able to generate evaluations
|
||||||
|
/// for guesses, so these will need to be added manually by the user. The intention is that
|
||||||
|
/// this can be used for use cases where the user plays wordle not within wordle-analyzer but
|
||||||
|
/// in another program (like their browser). It can also be used to test solvers.
|
||||||
|
pub fn solution(mut self, solution: Option<WordData>) -> Self {
|
||||||
|
self.solution = solution;
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'wl, WL: WordList> Display for Game<'wl, WL> {
|
impl<'wl, WL: WordList> Display for Game<'wl, WL> {
|
||||||
|
|
|
@ -21,6 +21,10 @@ impl<'wl, WL: WordList> Solver<'wl, WL> for NaiveSolver<'wl, WL> {
|
||||||
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();
|
||||||
let response = game.last_response();
|
let response = game.last_response();
|
||||||
|
trace!(
|
||||||
|
"guessing best guess for last response: {response:#?}\n{:#?}",
|
||||||
|
response.map(|a| a.evaluation())
|
||||||
|
);
|
||||||
if response.is_some() {
|
if response.is_some() {
|
||||||
for (idx, p) in response
|
for (idx, p) in response
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
@ -37,10 +41,11 @@ impl<'wl, WL: WordList> Solver<'wl, WL> for NaiveSolver<'wl, WL> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
trace!("other chars: {:?}", other_chars);
|
trace!("other chars: {:?}", other_chars);
|
||||||
let matches: Vec<WordData> = game
|
let mut matches: Vec<WordData> = game.wordlist().get_words_matching(pattern)?;
|
||||||
.wordlist()
|
if matches.is_empty() {
|
||||||
.get_words_matching(pattern)
|
return Err(SolverError::NoMatches.into());
|
||||||
.expect("the solution does not exist in the wordlist")
|
}
|
||||||
|
matches = matches
|
||||||
.iter()
|
.iter()
|
||||||
// only words that have not been guessed yet
|
// only words that have not been guessed yet
|
||||||
.filter(|p| !game.made_guesses().contains(&&p.0))
|
.filter(|p| !game.made_guesses().contains(&&p.0))
|
||||||
|
|
Loading…
Reference in New Issue