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