feat(solve): add wl commands for show and top words

This commit is contained in:
Christoph J. Scherr 2024-07-23 11:22:12 +02:00
parent 70711ad334
commit d9d84f8a11
4 changed files with 53 additions and 2 deletions

View File

@ -14,6 +14,7 @@ use wordle_analyzer::game::response::GuessResponse;
use wordle_analyzer::solve::{BuiltinSolverNames, Solver}; use wordle_analyzer::solve::{BuiltinSolverNames, Solver};
use wordle_analyzer::wlist::builtin::BuiltinWList; use wordle_analyzer::wlist::builtin::BuiltinWList;
use wordle_analyzer::wlist::word::Word; use wordle_analyzer::wlist::word::Word;
use wordle_analyzer::wlist::WordList;
use wordle_analyzer::{self, game}; use wordle_analyzer::{self, game};
#[derive(Parser, Clone, Debug)] #[derive(Parser, Clone, Debug)]
@ -69,10 +70,24 @@ enum ReplCommand {
Solve, Solve,
/// Show the current state of the game /// Show the current state of the game
Show, Show,
/// Display data about the wordlist
Wl {
#[command(subcommand)]
cmd: WlCommand,
},
/// Leave the Repl /// Leave the Repl
Exit, Exit,
} }
#[derive(Subcommand, Debug, EnumIter, Clone, Default)]
enum WlCommand {
#[default]
Stats,
Top {
amount: usize,
},
}
fn main() -> anyhow::Result<()> { fn main() -> anyhow::Result<()> {
let cli = Cli::parse(); let cli = Cli::parse();
Logger::builder() Logger::builder()
@ -124,6 +139,7 @@ fn help_guess_interactive(cli: Cli) -> anyhow::Result<()> {
// only None if the repl has not stepped yet // only None if the repl has not stepped yet
match repl.command().to_owned().unwrap() { match repl.command().to_owned().unwrap() {
ReplCommand::Exit => break, ReplCommand::Exit => break,
ReplCommand::Wl { cmd } => wlcommand_handler(&cli, &cmd, &wl)?,
ReplCommand::Show => { ReplCommand::Show => {
println!("{}", game); println!("{}", game);
} }
@ -151,6 +167,21 @@ fn help_guess_interactive(cli: Cli) -> anyhow::Result<()> {
Ok(()) Ok(())
} }
fn wlcommand_handler(cli: &Cli, cmd: &WlCommand, wl: &impl WordList) -> anyhow::Result<()> {
match cmd {
WlCommand::Stats => {
println!("{wl}")
}
WlCommand::Top { amount } => {
println!();
for s in wl.n_most_likely(*amount).iter() {
println!("\t\"{}\":\t{:.08}%", s.0, s.1 * 100.0);
}
}
}
Ok(())
}
fn play_native_non_interactive(cli: Cli) -> anyhow::Result<()> { fn play_native_non_interactive(cli: Cli) -> anyhow::Result<()> {
let wl = BuiltinWList::default(); let wl = BuiltinWList::default();
let builder = game::Game::builder(&wl) let builder = game::Game::builder(&wl)

View File

@ -1,4 +1,4 @@
use std::fmt::{write, Debug}; use std::fmt::{write, Debug, Display};
use serde_json; use serde_json;
@ -54,3 +54,9 @@ impl Debug for BuiltinWList {
) )
} }
} }
impl Display for BuiltinWList {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{self:#?}")
}
}

View File

@ -3,6 +3,7 @@ use rand::seq::IteratorRandom;
use regex::Regex; use regex::Regex;
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt::Display;
use std::ops::RangeBounds; use std::ops::RangeBounds;
#[cfg(feature = "builtin")] #[cfg(feature = "builtin")]
@ -14,7 +15,7 @@ use crate::error::WResult;
pub type AnyWordlist = Box<dyn WordList>; pub type AnyWordlist = Box<dyn WordList>;
pub trait WordList: Clone + std::fmt::Debug + Default + Sync { pub trait WordList: Clone + std::fmt::Debug + Default + Sync + Display {
fn solutions(&self) -> ManyWordDatas { fn solutions(&self) -> ManyWordDatas {
let wmap = self.wordmap().clone(); let wmap = self.wordmap().clone();
let threshold = wmap.threshold(); let threshold = wmap.threshold();
@ -41,6 +42,18 @@ pub trait WordList: Clone + std::fmt::Debug + Default + Sync {
fn total_freq(&self) -> Frequency { fn total_freq(&self) -> Frequency {
self.wordmap().values().map(|a| a.to_owned()).sum() self.wordmap().values().map(|a| a.to_owned()).sum()
} }
fn sort_likelihood(&self) -> Vec<WordData> {
let wmap = self.wordmap();
let mut wpairs: Vec<(_, _)> = wmap.iter().collect();
wpairs.sort_by(|a, b| a.1.partial_cmp(b.1).unwrap().reverse());
wpairs
.iter()
.map(|v| (v.0.to_owned(), v.1.to_owned()))
.collect()
}
fn n_most_likely(&self, n: usize) -> Vec<WordData> {
self.sort_likelihood().into_iter().take(n).collect()
}
fn over_threashold(&self) -> WordMap { fn over_threashold(&self) -> WordMap {
let wmap = self.wordmap(); let wmap = self.wordmap();
let threshold = wmap.threshold(); let threshold = wmap.threshold();

View File

@ -10,6 +10,7 @@ pub type Frequency = f64;
// PERF: Hash for String is probably a bottleneck // PERF: Hash for String is probably a bottleneck
pub type Word = String; pub type Word = String;
pub type WordData = (Word, Frequency); pub type WordData = (Word, Frequency);
pub type WordDataRef<'wl> = (&'wl Word, &'wl Frequency);
pub type ManyWordsRef<'a> = Vec<&'a Word>; pub type ManyWordsRef<'a> = Vec<&'a Word>;
pub type ManyWordDatas = Vec<(Word, Frequency)>; pub type ManyWordDatas = Vec<(Word, Frequency)>;