generated from PlexSheep/rs-base
This commit is contained in:
parent
1b354299d4
commit
6f72fda4ff
|
@ -172,7 +172,7 @@ fn help_guess_interactive(cli: Cli) -> anyhow::Result<()> {
|
|||
eprintln!("{}", style(best_guess.unwrap_err()).red().bold());
|
||||
continue;
|
||||
}
|
||||
debug!("game state: {game:?}");
|
||||
trace!("game state: {game:?}");
|
||||
println!("best guess: {}", best_guess.unwrap());
|
||||
}
|
||||
ReplCommand::Guess {
|
||||
|
@ -188,7 +188,7 @@ fn help_guess_interactive(cli: Cli) -> anyhow::Result<()> {
|
|||
continue;
|
||||
}
|
||||
println!("{}", guess.unwrap());
|
||||
debug!("game state: {game:#?}");
|
||||
trace!("game state: {game:#?}");
|
||||
}
|
||||
ReplCommand::New => game = builder.build()?,
|
||||
ReplCommand::Undo { n } => game.undo(n)?,
|
||||
|
@ -218,7 +218,7 @@ fn play_native_non_interactive(cli: Cli) -> anyhow::Result<()> {
|
|||
"eng" => BuiltinWList::english(cli.length),
|
||||
_ => BuiltinWList::load(&cli.wordlist, cli.length)?,
|
||||
};
|
||||
debug!("wordlist: {wl}");
|
||||
trace!("wordlist: {wl}");
|
||||
let mut builder = game::Game::builder(&wl)
|
||||
.length(cli.length)
|
||||
.max_steps(cli.max_steps)
|
||||
|
@ -238,13 +238,13 @@ fn play_native_non_interactive(cli: Cli) -> anyhow::Result<()> {
|
|||
let solver = cli.solver.to_solver(&wl);
|
||||
let mut game = builder.build()?;
|
||||
|
||||
debug!("{game:#?}");
|
||||
trace!("{game:#?}");
|
||||
|
||||
let mut response: GuessResponse;
|
||||
let mut _guess: Word;
|
||||
loop {
|
||||
response = solver.make_a_move(&mut game)?;
|
||||
debug!("game state: {game:#?}");
|
||||
trace!("game state: {game:#?}");
|
||||
println!("{}. guess: {response}", game.step() - 1);
|
||||
|
||||
if response.finished() {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use thiserror::Error;
|
||||
|
||||
use crate::bench::report::Report;
|
||||
use crate::wlist::word::Word;
|
||||
use crate::wlist::word::{Word, WordData};
|
||||
|
||||
pub type WResult<T> = std::result::Result<T, Error>;
|
||||
pub type GameResult<T> = std::result::Result<T, GameError>;
|
||||
|
@ -62,8 +62,8 @@ pub enum BenchError {
|
|||
|
||||
#[derive(Debug, Clone, Error)]
|
||||
pub enum SolverError {
|
||||
#[error("Wordlist has no matches for the gamestate")]
|
||||
NoMatches,
|
||||
#[error("Wordlist has no matches for the gamestate (solution: {0:?})")]
|
||||
NoMatches(Option<WordData>),
|
||||
#[error("Unknown builtin solver")]
|
||||
UnknownBuiltinSolver,
|
||||
}
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
use libpt::log::{info, trace};
|
||||
use core::panic;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use libpt::log::{debug, info, trace};
|
||||
|
||||
use crate::error::{SolverError, WResult};
|
||||
use crate::game::response;
|
||||
use crate::wlist::word::{Word, WordData};
|
||||
use crate::wlist::WordList;
|
||||
|
||||
|
@ -26,51 +30,67 @@ impl<'wl, WL: WordList> Solver<'wl, WL> for NaiveSolver<'wl, WL> {
|
|||
/// * Discard all words that don't have the chars that we know from the last guess are in the
|
||||
/// word, but don't know the position of.
|
||||
fn guess_for(&self, game: &crate::game::Game<WL>) -> WResult<Word> {
|
||||
// HACK: hardcoded length
|
||||
let mut pattern: String = ".".repeat(game.length());
|
||||
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()
|
||||
.evaluation()
|
||||
.clone()
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
{
|
||||
if p.1 == Status::Matched {
|
||||
pattern.replace_range(idx..idx + 1, &p.0.to_string());
|
||||
} else if p.1 == Status::Exists {
|
||||
other_chars.push(p.0)
|
||||
// a hash map telling how many of the characters may be in a correct word (+1)
|
||||
// if the value for a char is 2 => it may be in the solution 1 time
|
||||
// if the value for a char is 1 => it may not be in the solution
|
||||
let mut wrong_chars: Vec<char> = Vec::new();
|
||||
let responses = game.responses().iter().enumerate();
|
||||
for (_idx, response) in responses {
|
||||
for (idx, p) in response.evaluation().clone().into_iter().enumerate() {
|
||||
match p.1 {
|
||||
Status::Matched => {
|
||||
pattern.replace_range(idx..idx + 1, &p.0.to_string());
|
||||
}
|
||||
Status::Exists => other_chars.push(p.0),
|
||||
Status::None => wrong_chars.push(p.0),
|
||||
}
|
||||
}
|
||||
}
|
||||
trace!("other chars: {:?}", other_chars);
|
||||
let mut matches: Vec<WordData> = game.wordlist().get_words_matching(pattern)?;
|
||||
debug!("other chars: {:?}", other_chars);
|
||||
debug!("wrong chars: {:?}", wrong_chars);
|
||||
let mut matches: Vec<WordData> = game.wordlist().get_words_matching(&pattern)?;
|
||||
if matches.is_empty() {
|
||||
return Err(SolverError::NoMatches.into());
|
||||
return Err(SolverError::NoMatches(game.solution().cloned()).into());
|
||||
}
|
||||
matches = matches
|
||||
.iter()
|
||||
// only words that have not been guessed yet
|
||||
.filter(|p| !game.made_guesses().contains(&&p.0))
|
||||
// only words that contain the letters we found earlier (that were not matched)
|
||||
// only words that do contain the chars we know exist
|
||||
.filter(|p| {
|
||||
// TODO: don't repeat unmatched contained chars on the same position twice #2
|
||||
let mut fits = true;
|
||||
for c in other_chars.iter() {
|
||||
fits &= p.0.contains(*c);
|
||||
for other in &other_chars {
|
||||
if p.0.contains(*other) {
|
||||
// TODO: account for chars that occur multiple times
|
||||
continue;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
fits
|
||||
true
|
||||
})
|
||||
// only words that do not contain the letters we know are wrong
|
||||
.filter(|p| {
|
||||
for wrong in &wrong_chars {
|
||||
if p.0.contains(*wrong) {
|
||||
let mut tmp = 0;
|
||||
let in_other = other_chars.iter().filter(|v| **v == *wrong).count()
|
||||
+ pattern.chars().filter(|v| *v == *wrong).count();
|
||||
// TODO: account for chars that occur multiple times
|
||||
if in_other > tmp {
|
||||
tmp += 1;
|
||||
continue;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
})
|
||||
.map(|v| v.to_owned())
|
||||
.collect();
|
||||
if matches.is_empty() {
|
||||
return Err(SolverError::NoMatches.into());
|
||||
return Err(SolverError::NoMatches(game.solution().cloned()).into());
|
||||
}
|
||||
Ok(matches[0].0.to_owned())
|
||||
}
|
||||
|
|
|
@ -93,7 +93,7 @@ pub trait WordList: Clone + std::fmt::Debug + Default + Sync + Display {
|
|||
}
|
||||
buf
|
||||
}
|
||||
fn get_words_matching(&self, pattern: String) -> WResult<Vec<WordData>> {
|
||||
fn get_words_matching(&self, pattern: &str) -> WResult<Vec<WordData>> {
|
||||
let pattern = Regex::new(&pattern).map_err(WordlistError::from)?;
|
||||
let hay = self.raw_wordlist();
|
||||
let keys = pattern.captures_iter(&hay);
|
||||
|
|
Loading…
Reference in New Issue