generated from PlexSheep/rs-base
match and get by frequency
This commit is contained in:
parent
74ef508841
commit
9beb38a5bb
|
@ -28,6 +28,7 @@ clap = { version = "4.5.3", features = ["derive"], optional = true }
|
|||
colored = { version = "2.1.0", optional = false }
|
||||
libpt = "0.4.2"
|
||||
rand = "0.8.5"
|
||||
regex = "1.10.3"
|
||||
serde = { version = "1.0.197", optional = true, features = ["serde_derive"] }
|
||||
serde_json = { version = "1.0.114", optional = true }
|
||||
thiserror = "1.0.58"
|
||||
|
|
|
@ -54,8 +54,8 @@ fn main() -> anyhow::Result<()> {
|
|||
response = match game.guess(guess) {
|
||||
Ok(r) => r,
|
||||
Err(err) => match err {
|
||||
GameError::GuessHasWrongLength => {
|
||||
println!("word length: must be {} long", game.length());
|
||||
GameError::GuessHasWrongLength(len) => {
|
||||
println!("word length: must be {} long but is {}", game.length(), len);
|
||||
continue;
|
||||
}
|
||||
_ => {
|
||||
|
|
13
src/error.rs
13
src/error.rs
|
@ -1,5 +1,7 @@
|
|||
use thiserror::Error;
|
||||
|
||||
use crate::wlist::word::Word;
|
||||
|
||||
pub type WResult<T> = std::result::Result<T, Error>;
|
||||
pub type GameResult<T> = std::result::Result<T, GameError>;
|
||||
|
||||
|
@ -18,12 +20,19 @@ pub enum Error {
|
|||
// for `FromStr` of `BuiltinSolver`
|
||||
#[error("Unknown builtin solver")]
|
||||
UnknownBuiltinSolver,
|
||||
#[error("pattern matching error")]
|
||||
Regex{
|
||||
#[from]
|
||||
source: regex::Error
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Error)]
|
||||
pub enum GameError {
|
||||
#[error("The guess has the wrong length")]
|
||||
GuessHasWrongLength,
|
||||
#[error("The guess has the wrong length ({0})")]
|
||||
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})")]
|
||||
WordNotInWordlist(Word),
|
||||
}
|
||||
|
|
|
@ -78,11 +78,14 @@ impl<'wl, WL: WordList> Game<'wl, WL> {
|
|||
/// if the game is finished.
|
||||
pub fn guess(&mut self, guess: Word) -> GameResult<GuessResponse> {
|
||||
if guess.len() != self.length {
|
||||
return Err(GameError::GuessHasWrongLength);
|
||||
return Err(GameError::GuessHasWrongLength(guess.len()));
|
||||
}
|
||||
if self.finished || self.step > self.max_steps {
|
||||
return Err(GameError::TryingToPlayAFinishedGame);
|
||||
}
|
||||
if self.wordlist.get_word(&guess).is_none() {
|
||||
return Err(GameError::WordNotInWordlist(guess));
|
||||
}
|
||||
self.step += 1;
|
||||
|
||||
let mut compare_solution = self.solution.0.clone();
|
||||
|
@ -101,7 +104,8 @@ impl<'wl, WL: WordList> Game<'wl, WL> {
|
|||
evaluation.push((c, status));
|
||||
}
|
||||
|
||||
let mut response = GuessResponse::new(guess, evaluation, &self);
|
||||
let response = GuessResponse::new(guess, evaluation, self);
|
||||
self.responses.push(response.clone());
|
||||
self.finished = response.finished();
|
||||
Ok(response)
|
||||
}
|
||||
|
@ -128,6 +132,13 @@ impl<'wl, WL: WordList> Game<'wl, WL> {
|
|||
pub fn responses(&self) -> &Vec<GuessResponse> {
|
||||
&self.responses
|
||||
}
|
||||
pub fn last_response(&self) -> Option<&GuessResponse> {
|
||||
self.responses().last()
|
||||
}
|
||||
|
||||
pub fn wordlist(&self) -> &WL {
|
||||
self.wordlist
|
||||
}
|
||||
}
|
||||
|
||||
/// Build and Configure a [`Game`]
|
||||
|
|
|
@ -62,6 +62,14 @@ impl GuessResponse {
|
|||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn evaluation(&self) -> &[(char, Status)] {
|
||||
&self.evaluation
|
||||
}
|
||||
|
||||
pub fn guess(&self) -> &str {
|
||||
&self.guess
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for GuessResponse {
|
||||
|
|
|
@ -3,7 +3,7 @@ use libpt::log::info;
|
|||
use crate::wlist::word::Word;
|
||||
use crate::wlist::WordList;
|
||||
|
||||
use super::{AnyBuiltinSolver, Solver};
|
||||
use super::{AnyBuiltinSolver, Solver, Status};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct NaiveSolver<'wl, WL> {
|
||||
|
@ -16,7 +16,21 @@ impl<'wl, WL: WordList> Solver<'wl, WL> for NaiveSolver<'wl, WL> {
|
|||
Ok(Self { wl: wordlist })
|
||||
}
|
||||
fn guess_for(&self, game: &crate::game::Game<WL>) -> Word {
|
||||
self.wl.rand_word().0
|
||||
// HACK: hardcoded length
|
||||
let mut buf: Word = Word::from(".....");
|
||||
let response = game.last_response();
|
||||
if response.is_some() {
|
||||
for (idx, p) in response.unwrap().evaluation().iter().enumerate() {
|
||||
if p.1 == Status::Matched {
|
||||
buf.replace_range(idx..idx + 1, &p.0.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
game.wordlist()
|
||||
.get_words_matching(buf)
|
||||
.expect("the solution does not exist in the wordlist")[0]
|
||||
.0
|
||||
.clone()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ impl super::WordList for BuiltinWList {
|
|||
&self.words
|
||||
}
|
||||
fn get_word(&self, word: &Word) -> Option<super::WordData> {
|
||||
self.words.get(&word)
|
||||
self.words.get(word)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
use libpt::log::debug;
|
||||
use rand::seq::IteratorRandom;
|
||||
|
||||
use regex::Regex;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::ops::RangeBounds;
|
||||
|
||||
|
@ -14,13 +17,16 @@ pub type AnyWordlist = Box<dyn WordList>;
|
|||
|
||||
pub trait WordList: Clone + std::fmt::Debug + Default {
|
||||
fn solutions(&self) -> ManyWordDatas {
|
||||
let wmap = self.wordmap();
|
||||
let wmap = self.wordmap().clone();
|
||||
let threshold = wmap.threshold();
|
||||
wmap.iter().filter(|i| *i.1 > threshold).collect()
|
||||
wmap.iter()
|
||||
.filter(|i| *i.1 > threshold)
|
||||
.map(|p| (p.0.clone(), *p.1))
|
||||
.collect()
|
||||
}
|
||||
fn rand_solution(&self) -> WordData {
|
||||
let mut rng = rand::thread_rng();
|
||||
let sol = *self.solutions().iter().choose(&mut rng).unwrap();
|
||||
let sol = self.solutions().iter().choose(&mut rng).unwrap().clone();
|
||||
(sol.0.to_owned(), sol.1.to_owned())
|
||||
}
|
||||
fn rand_word(&self) -> WordData {
|
||||
|
@ -62,4 +68,28 @@ pub trait WordList: Clone + std::fmt::Debug + Default {
|
|||
let n: f64 = cmap.keys().len() as f64;
|
||||
cmap.into_iter().map(|p| (p.0, p.1 as f64 / n)).collect()
|
||||
}
|
||||
fn raw_wordlist(&self) -> String {
|
||||
let mut buf = String::new();
|
||||
for w in self.wordmap().keys() {
|
||||
buf += &w;
|
||||
buf += "\n";
|
||||
}
|
||||
buf
|
||||
}
|
||||
fn get_words_matching(&self, pattern: String) -> WResult<ManyWordDatas> {
|
||||
let pattern = Regex::new(&pattern)?;
|
||||
let hay = self.raw_wordlist();
|
||||
let keys = pattern.captures_iter(&hay);
|
||||
let mut buf = ManyWordDatas::new();
|
||||
for k in keys {
|
||||
debug!("match: {k:?}");
|
||||
let w: WordData = self.wordmap().get(&k[0]).unwrap();
|
||||
buf.push(w)
|
||||
}
|
||||
// sort by frequency
|
||||
buf.sort_by(|a, b| {
|
||||
a.1.partial_cmp(&b.1).unwrap()
|
||||
});
|
||||
Ok(buf)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use std::collections::HashMap;
|
||||
use std::fmt::write;
|
||||
use std::hash::Hash;
|
||||
|
||||
use libpt::log::debug;
|
||||
#[cfg(feature = "serde")]
|
||||
|
@ -11,7 +12,7 @@ pub type Frequency = f64;
|
|||
pub type Word = String;
|
||||
pub type WordData = (Word, Frequency);
|
||||
pub type ManyWords<'a> = Vec<&'a Word>;
|
||||
pub type ManyWordDatas<'a> = Vec<(&'a Word, &'a Frequency)>;
|
||||
pub type ManyWordDatas = Vec<(Word, Frequency)>;
|
||||
|
||||
#[derive(Clone)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
|
@ -66,9 +67,9 @@ impl WordMap {
|
|||
pub fn inner(&self) -> &HashMap<Word, Frequency> {
|
||||
&self.inner
|
||||
}
|
||||
pub fn get(&self, word: &Word) -> Option<WordData> {
|
||||
match self.inner.get(word) {
|
||||
Some(f) => Some((word.clone(), *f)),
|
||||
pub fn get<I: std::fmt::Display>(&self, word: I) -> Option<WordData> {
|
||||
match self.inner.get(&word.to_string()) {
|
||||
Some(f) => Some((word.to_string(), *f)),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
@ -104,7 +105,7 @@ impl From<HashMap<Word, Frequency>> for WordMap {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<WordMap > for HashMap<Word, Frequency>{
|
||||
impl From<WordMap> for HashMap<Word, Frequency> {
|
||||
fn from(value: WordMap) -> Self {
|
||||
value.inner
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue