generated from PlexSheep/rs-base
add interactive solver #4
|
@ -8,12 +8,13 @@ use libpt::cli::{repl::Repl, strum};
|
|||
use libpt::log::*;
|
||||
use strum::EnumIter;
|
||||
|
||||
use wordle_analyzer::error::Error;
|
||||
use wordle_analyzer::game::evaluation::Evaluation;
|
||||
use wordle_analyzer::game::response::GuessResponse;
|
||||
|
||||
use wordle_analyzer::solve::{BuiltinSolverNames, Solver};
|
||||
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::{self, game};
|
||||
|
||||
|
@ -33,12 +34,22 @@ struct Cli {
|
|||
#[command(flatten)]
|
||||
verbose: libpt::cli::args::VerbosityLevel,
|
||||
/// which solver to use
|
||||
#[arg(short, long, default_value_t = BuiltinSolverNames::default())]
|
||||
#[arg(long, default_value_t = BuiltinSolverNames::default())]
|
||||
solver: BuiltinSolverNames,
|
||||
|
||||
/// set if the solver should play a full native game without interaction
|
||||
#[arg(short, long)]
|
||||
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)]
|
||||
|
@ -184,10 +195,22 @@ fn wlcommand_handler(_cli: &Cli, cmd: &WlCommand, wl: &impl WordList) -> anyhow:
|
|||
|
||||
fn play_native_non_interactive(cli: Cli) -> anyhow::Result<()> {
|
||||
let wl = BuiltinWList::default();
|
||||
let builder = game::Game::builder(&wl)
|
||||
let mut builder = game::Game::builder(&wl)
|
||||
.length(cli.length)
|
||||
.max_steps(cli.max_steps)
|
||||
.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 mut game = builder.build()?;
|
||||
|
||||
|
|
|
@ -49,7 +49,7 @@ pub enum GameError {
|
|||
GuessHasWrongLength(usize),
|
||||
#[error("The game is finished but a guess is being made")]
|
||||
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),
|
||||
}
|
||||
|
||||
|
|
|
@ -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> {
|
||||
length: usize,
|
||||
precompute: bool,
|
||||
max_steps: usize,
|
||||
wordlist: &'wl WL,
|
||||
generate_solution: bool,
|
||||
solution: Option<WordData>,
|
||||
}
|
||||
|
||||
impl<'wl, WL: WordList> GameBuilder<'wl, WL> {
|
||||
|
@ -234,19 +235,23 @@ impl<'wl, WL: WordList> GameBuilder<'wl, WL> {
|
|||
max_steps: super::DEFAULT_MAX_STEPS,
|
||||
wordlist: wl,
|
||||
generate_solution,
|
||||
solution: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// build a [`Game`] with the stored configuration
|
||||
pub fn build(&'wl self) -> GameResult<Game<'wl, WL>> {
|
||||
trace!("{:#?}", self);
|
||||
let game: Game<WL> = Game::build(
|
||||
let mut game: Game<WL> = Game::build(
|
||||
self.length,
|
||||
self.precompute,
|
||||
self.max_steps,
|
||||
self.wordlist,
|
||||
self.generate_solution,
|
||||
)?;
|
||||
if self.solution.is_some() {
|
||||
game.set_solution(self.solution.clone())
|
||||
}
|
||||
Ok(game)
|
||||
}
|
||||
|
||||
|
@ -284,6 +289,22 @@ impl<'wl, WL: WordList> GameBuilder<'wl, WL> {
|
|||
self.wordlist = wl;
|
||||
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> {
|
||||
|
|
|
@ -21,6 +21,10 @@ impl<'wl, WL: WordList> Solver<'wl, WL> for NaiveSolver<'wl, WL> {
|
|||
let mut pattern: String = String::from(".....");
|
||||
let mut other_chars: Vec<char> = Vec::new();
|
||||
let response = game.last_response();
|
||||
trace!(
|
||||
"guessing best guess for last response: {response:#?}\n{:#?}",
|
||||
response.map(|a| a.evaluation())
|
||||
);
|
||||
if response.is_some() {
|
||||
for (idx, p) in response
|
||||
.unwrap()
|
||||
|
@ -37,10 +41,11 @@ impl<'wl, WL: WordList> Solver<'wl, WL> for NaiveSolver<'wl, WL> {
|
|||
}
|
||||
}
|
||||
trace!("other chars: {:?}", other_chars);
|
||||
let matches: Vec<WordData> = game
|
||||
.wordlist()
|
||||
.get_words_matching(pattern)
|
||||
.expect("the solution does not exist in the wordlist")
|
||||
let mut matches: Vec<WordData> = game.wordlist().get_words_matching(pattern)?;
|
||||
if matches.is_empty() {
|
||||
return Err(SolverError::NoMatches.into());
|
||||
}
|
||||
matches = matches
|
||||
.iter()
|
||||
// only words that have not been guessed yet
|
||||
.filter(|p| !game.made_guesses().contains(&&p.0))
|
||||
|
|
Loading…
Reference in New Issue