generated from PlexSheep/rs-base
reuse wordlist
This commit is contained in:
parent
df15d117ef
commit
02d67cdd0c
|
@ -13,10 +13,10 @@ keywords = ["wordle", "benchmark"]
|
|||
default-run = "wordlec"
|
||||
|
||||
[features]
|
||||
default = ["game", "bench", "tui", "solvers", "builtin_wlist", "serde"]
|
||||
default = ["game", "bench", "tui", "solve", "builtin_wlist", "serde"]
|
||||
builtin_wlist = ["dep:serde_json", "serde"]
|
||||
game = ["builtin_wlist"]
|
||||
solvers = []
|
||||
solve = []
|
||||
tui = ["cli"]
|
||||
cli = ["dep:clap"]
|
||||
bench = []
|
||||
|
@ -41,3 +41,8 @@ required-features = ["game", "cli"]
|
|||
name = "wordlet"
|
||||
path = "src/bin/game/tui.rs"
|
||||
required-features = ["tui", "game"]
|
||||
|
||||
[[bin]]
|
||||
name = "wordlesolve"
|
||||
path = "src/bin/solve/simple.rs"
|
||||
required-features = ["game", "solve", "cli"]
|
||||
|
|
|
@ -39,11 +39,13 @@ fn main() -> anyhow::Result<()> {
|
|||
}
|
||||
debug!("dumping CLI: {:#?}", cli);
|
||||
|
||||
let mut game = game::Game::<BuiltinWList>::builder()
|
||||
let wl = BuiltinWList::default();
|
||||
let builder = game::Game::builder()
|
||||
.length(cli.length)
|
||||
.max_steps(cli.max_steps)
|
||||
.precompute(cli.precompute)
|
||||
.build()?;
|
||||
.wordlist(wl);
|
||||
let mut game = builder.build()?;
|
||||
|
||||
debug!("{game:#?}");
|
||||
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
fn main() {
|
||||
unimplemented!();
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
use thiserror::Error;
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
pub type WResult<T> = std::result::Result<T, Error>;
|
||||
pub type GameResult<T> = std::result::Result<T, GameError>;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
use crate::error::*;
|
||||
use crate::wlist::word::{Solution, Word};
|
||||
use crate::wlist::word::{WordData, Word};
|
||||
use crate::wlist::WordList;
|
||||
|
||||
pub mod response;
|
||||
use libpt::log::debug;
|
||||
|
||||
pub mod response;
|
||||
use response::GuessResponse;
|
||||
|
||||
use self::response::Status;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Game<WL>
|
||||
pub struct Game<'wl, WL>
|
||||
where
|
||||
WL: WordList,
|
||||
{
|
||||
|
@ -17,13 +18,13 @@ where
|
|||
precompute: bool,
|
||||
max_steps: usize,
|
||||
step: usize,
|
||||
solution: Solution,
|
||||
wordlist: WL,
|
||||
solution: WordData,
|
||||
wordlist: &'wl WL,
|
||||
finished: bool,
|
||||
// TODO: keep track of the letters the user has tried
|
||||
}
|
||||
|
||||
impl<WL: WordList> Game<WL> {
|
||||
impl<'wl, WL: WordList> Game<'wl, WL> {
|
||||
/// get a new [`GameBuilder`]
|
||||
pub fn builder() -> GameBuilder<WL> {
|
||||
GameBuilder::default()
|
||||
|
@ -44,30 +45,33 @@ impl<WL: WordList> Game<WL> {
|
|||
length: usize,
|
||||
precompute: bool,
|
||||
max_steps: usize,
|
||||
wlist: WL,
|
||||
wlist: &'wl WL,
|
||||
) -> GameResult<Self> {
|
||||
// TODO: check if the length is in the range bounds of the wordlist
|
||||
let solution = wlist.rand_solution();
|
||||
let game = Game {
|
||||
let game: Game<'wl, WL> = Game {
|
||||
length,
|
||||
precompute,
|
||||
max_steps,
|
||||
step: 1,
|
||||
solution,
|
||||
wordlist: wlist,
|
||||
wordlist: &wlist,
|
||||
finished: false,
|
||||
};
|
||||
|
||||
Ok(game)
|
||||
}
|
||||
|
||||
pub fn reset(mut self) -> Self {
|
||||
self.solution = self.wordlist.rand_solution();
|
||||
self.step = 1;
|
||||
self.finished = false;
|
||||
self
|
||||
}
|
||||
|
||||
/// Make a new guess
|
||||
///
|
||||
/// The word will be evaluated against the [solution](Game::solution) of the [Game].
|
||||
/// A [GuessResponse] will be formulated, showing us which letters are correctly placed, in the
|
||||
/// solution, or just wrong.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if the length of the [Word] is wrong It will also error
|
||||
/// if the game is finished.
|
||||
pub fn guess(&mut self, guess: Word) -> GameResult<GuessResponse> {
|
||||
if guess.len() != self.length {
|
||||
return Err(GameError::GuessHasWrongLength);
|
||||
|
@ -102,7 +106,7 @@ impl<WL: WordList> Game<WL> {
|
|||
self.length
|
||||
}
|
||||
|
||||
pub fn solution(&self) -> &Solution {
|
||||
pub fn solution(&self) -> &WordData {
|
||||
&self.solution
|
||||
}
|
||||
|
||||
|
@ -153,10 +157,10 @@ pub struct GameBuilder<WL: WordList> {
|
|||
|
||||
impl<WL: WordList> GameBuilder<WL> {
|
||||
/// build a [`Game`] with the stored configuration
|
||||
pub fn build(self) -> GameResult<Game<WL>> {
|
||||
pub fn build(&self) -> GameResult<Game<WL>> {
|
||||
debug!("{:#?}", self);
|
||||
let game: Game<WL> =
|
||||
Game::build(self.length, self.precompute, self.max_steps, WL::default())?;
|
||||
Game::build(self.length, self.precompute, self.max_steps, &self.wordlist)?;
|
||||
Ok(game)
|
||||
}
|
||||
|
||||
|
@ -185,6 +189,15 @@ impl<WL: WordList> GameBuilder<WL> {
|
|||
debug!("max steps: {:#?}", self.max_steps);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the wordlist for the builder
|
||||
///
|
||||
/// The builder can be used multiple times. Each [`Game`] will have a immutable reference to
|
||||
/// `wl`.
|
||||
pub fn wordlist(mut self, wl: WL) -> Self {
|
||||
self.wordlist = wl;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<WL: WordList> Default for GameBuilder<WL> {
|
||||
|
|
|
@ -4,10 +4,12 @@ use colored::{ColoredString, Colorize};
|
|||
use libpt::log::debug;
|
||||
use std::fmt::Display;
|
||||
|
||||
pub type Evaluation = Vec<(char, Status)>;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct GuessResponse {
|
||||
guess: Word,
|
||||
evaluation: Vec<(char, Status)>,
|
||||
evaluation: Evaluation,
|
||||
step: usize,
|
||||
finish: bool,
|
||||
win: bool,
|
||||
|
|
|
@ -1 +1,12 @@
|
|||
use crate::{
|
||||
error::WResult,
|
||||
game::{response::*, Game},
|
||||
wlist::{word::WordData, WordList},
|
||||
};
|
||||
|
||||
pub trait Solver<'wl, WL: WordList>: Clone + Default {
|
||||
fn build(wordlist: WL) -> WResult<Self>;
|
||||
fn build_game(&self) -> Game<'wl, WL>;
|
||||
fn play(game: &mut Game<'wl, WL>) -> Game<'wl, WL>;
|
||||
fn solve(game: &mut Game<'wl, WL>) -> WResult<WordData>;
|
||||
}
|
||||
|
|
|
@ -18,6 +18,9 @@ impl super::WordList for BuiltinWList {
|
|||
fn wordmap(&self) -> &super::WordMap {
|
||||
&self.words
|
||||
}
|
||||
fn get_word(&self, word: &Word) -> Option<super::WordData> {
|
||||
self.words.get(&word)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for BuiltinWList {
|
||||
|
|
|
@ -8,15 +8,17 @@ pub mod builtin;
|
|||
pub mod word;
|
||||
use word::*;
|
||||
|
||||
use crate::error::WResult;
|
||||
|
||||
pub type AnyWordlist = Box<dyn WordList>;
|
||||
|
||||
pub trait WordList: Clone + std::fmt::Debug + Default {
|
||||
fn solutions(&self) -> ManySolutions {
|
||||
fn solutions(&self) -> ManyWordDatas {
|
||||
let wmap = self.wordmap();
|
||||
let threshold = wmap.threshold();
|
||||
wmap.iter().filter(|i| *i.1 > threshold).collect()
|
||||
}
|
||||
fn rand_solution(&self) -> Solution {
|
||||
fn rand_solution(&self) -> WordData {
|
||||
let mut rng = rand::thread_rng();
|
||||
let sol = *self.solutions().iter().choose(&mut rng).unwrap();
|
||||
(sol.0.to_owned(), sol.1.to_owned())
|
||||
|
@ -39,4 +41,5 @@ pub trait WordList: Clone + std::fmt::Debug + Default {
|
|||
}
|
||||
WordMap::new(hm)
|
||||
}
|
||||
fn get_word(&self, word: &Word) -> Option<WordData>;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
use std::collections::HashMap;
|
||||
use std::fmt::{write, Display};
|
||||
use std::iter::Sum;
|
||||
use std::ops::RangeFull;
|
||||
use std::fmt::write;
|
||||
|
||||
use libpt::log::debug;
|
||||
#[cfg(feature = "serde")]
|
||||
|
@ -11,8 +9,9 @@ pub type Frequency = f64;
|
|||
|
||||
// PERF: Hash for String is probably a bottleneck
|
||||
pub type Word = String;
|
||||
pub type ManySolutions<'a> = Vec<(&'a Word, &'a Frequency)>;
|
||||
pub type Solution = (Word, Frequency);
|
||||
pub type WordData = (Word, Frequency);
|
||||
pub type ManyWords<'a> = Vec<&'a Word>;
|
||||
pub type ManyWordDatas<'a> = Vec<(&'a Word, &'a Frequency)>;
|
||||
|
||||
#[derive(Clone)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
|
@ -35,6 +34,7 @@ impl WordMap {
|
|||
self.inner.iter()
|
||||
}
|
||||
pub fn freq_range(&self) -> std::ops::Range<Frequency> {
|
||||
// TODO: calculate this instead of estimating like this
|
||||
return 0.1e-10..1e-6;
|
||||
let lowest: Frequency = todo!();
|
||||
let highest: Frequency = todo!();
|
||||
|
@ -64,6 +64,12 @@ 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)),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for WordMap {
|
||||
|
|
Loading…
Reference in New Issue