generated from PlexSheep/rs-base
too much generics, idk
This commit is contained in:
parent
4367e7fc58
commit
ce9686f7bf
|
@ -16,7 +16,7 @@ default-run = "wordlec"
|
||||||
default = ["game", "bench", "tui", "solve", "builtin_wlist", "serde"]
|
default = ["game", "bench", "tui", "solve", "builtin_wlist", "serde"]
|
||||||
builtin_wlist = ["dep:serde_json", "serde"]
|
builtin_wlist = ["dep:serde_json", "serde"]
|
||||||
game = ["builtin_wlist"]
|
game = ["builtin_wlist"]
|
||||||
solve = []
|
solve = ["game"]
|
||||||
tui = ["cli"]
|
tui = ["cli"]
|
||||||
cli = ["dep:clap"]
|
cli = ["dep:clap"]
|
||||||
bench = []
|
bench = []
|
||||||
|
@ -45,4 +45,4 @@ required-features = ["tui", "game"]
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "wordlesolve"
|
name = "wordlesolve"
|
||||||
path = "src/bin/solve/simple.rs"
|
path = "src/bin/solve/simple.rs"
|
||||||
required-features = ["game", "solve", "cli"]
|
required-features = ["solve", "cli"]
|
||||||
|
|
|
@ -39,11 +39,10 @@ fn main() -> anyhow::Result<()> {
|
||||||
debug!("dumping CLI: {:#?}", cli);
|
debug!("dumping CLI: {:#?}", cli);
|
||||||
|
|
||||||
let wl = BuiltinWList::default();
|
let wl = BuiltinWList::default();
|
||||||
let builder = game::Game::builder()
|
let builder = game::Game::builder(&wl)
|
||||||
.length(cli.length)
|
.length(cli.length)
|
||||||
.max_steps(cli.max_steps)
|
.max_steps(cli.max_steps)
|
||||||
.precompute(cli.precompute)
|
.precompute(cli.precompute);
|
||||||
.wordlist(wl);
|
|
||||||
let mut game = builder.build()?;
|
let mut game = builder.build()?;
|
||||||
|
|
||||||
debug!("{game:#?}");
|
debug!("{game:#?}");
|
||||||
|
|
|
@ -1,3 +1,78 @@
|
||||||
fn main() {
|
#![warn(clippy::all)]
|
||||||
unimplemented!();
|
// #![warn(missing_docs)]
|
||||||
|
#![warn(missing_debug_implementations)]
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
|
use clap::Parser;
|
||||||
|
use libpt::log::*;
|
||||||
|
use wordle_analyzer::error::GameError;
|
||||||
|
use wordle_analyzer::game::response::GuessResponse;
|
||||||
|
|
||||||
|
use wordle_analyzer::game::Game;
|
||||||
|
use wordle_analyzer::solve::{stupid, BuiltinSolvers, Solver};
|
||||||
|
use wordle_analyzer::wlist::builtin::BuiltinWList;
|
||||||
|
use wordle_analyzer::wlist::word::Word;
|
||||||
|
use wordle_analyzer::{self, game};
|
||||||
|
|
||||||
|
#[derive(Parser, Clone, Debug)]
|
||||||
|
#[command(version, about, long_about, author)]
|
||||||
|
struct Cli {
|
||||||
|
/// precompute all possibilities for better performance at runtime
|
||||||
|
#[arg(short, long)]
|
||||||
|
precompute: bool,
|
||||||
|
/// how long should the word be?
|
||||||
|
#[arg(short, long, default_value_t = wordle_analyzer::DEFAULT_WORD_LENGTH)]
|
||||||
|
length: usize,
|
||||||
|
/// how many times can we guess?
|
||||||
|
#[arg(short, long, default_value_t = wordle_analyzer::DEFAULT_MAX_STEPS)]
|
||||||
|
max_steps: usize,
|
||||||
|
/// more verbose logs
|
||||||
|
#[arg(short, long)]
|
||||||
|
verbose: bool,
|
||||||
|
/// which solver to use
|
||||||
|
#[arg(short, long, default_value_t = BuiltinSolvers::default())]
|
||||||
|
solver: BuiltinSolvers,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> anyhow::Result<()> {
|
||||||
|
let cli = Cli::parse();
|
||||||
|
if cli.verbose {
|
||||||
|
Logger::build_mini(Some(Level::TRACE))?;
|
||||||
|
} else {
|
||||||
|
Logger::build_mini(Some(Level::INFO))?;
|
||||||
|
}
|
||||||
|
debug!("dumping CLI: {:#?}", cli);
|
||||||
|
|
||||||
|
let wl = BuiltinWList::default();
|
||||||
|
let builder = game::Game::builder(&wl)
|
||||||
|
.length(cli.length)
|
||||||
|
.max_steps(cli.max_steps)
|
||||||
|
.precompute(cli.precompute);
|
||||||
|
let solver = match cli.solver {
|
||||||
|
BuiltinSolvers::Naive => {
|
||||||
|
stupid::StupidSolver::build(&wl)?
|
||||||
|
},
|
||||||
|
_ => todo!()
|
||||||
|
};
|
||||||
|
let mut game = builder.build()?;
|
||||||
|
|
||||||
|
debug!("{game:#?}");
|
||||||
|
|
||||||
|
let mut response: GuessResponse;
|
||||||
|
let mut guess: Word;
|
||||||
|
loop {
|
||||||
|
response = solver.play(&mut game)?;
|
||||||
|
println!("{response}");
|
||||||
|
|
||||||
|
if response.finished() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if response.won() {
|
||||||
|
println!("You win! You took {} guesses.", game.step() - 1);
|
||||||
|
} else {
|
||||||
|
println!("You lose! The solution was {:?}.", game.solution());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,9 @@ pub enum Error {
|
||||||
#[from]
|
#[from]
|
||||||
source: anyhow::Error,
|
source: anyhow::Error,
|
||||||
},
|
},
|
||||||
|
// for `FromStr` of `BuiltinSolver`
|
||||||
|
#[error("Unknown builtin solver")]
|
||||||
|
UnknownBuiltinSolver
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Error)]
|
#[derive(Debug, Clone, Error)]
|
||||||
|
|
|
@ -7,6 +7,8 @@ use libpt::log::debug;
|
||||||
pub mod response;
|
pub mod response;
|
||||||
use response::GuessResponse;
|
use response::GuessResponse;
|
||||||
|
|
||||||
|
pub mod summary;
|
||||||
|
|
||||||
use self::response::Status;
|
use self::response::Status;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
@ -26,8 +28,8 @@ where
|
||||||
|
|
||||||
impl<'wl, WL: WordList> Game<'wl, WL> {
|
impl<'wl, WL: WordList> Game<'wl, WL> {
|
||||||
/// get a new [`GameBuilder`]
|
/// get a new [`GameBuilder`]
|
||||||
pub fn builder() -> GameBuilder<WL> {
|
pub fn builder(wl: &'wl WL) -> GameBuilder<'wl, WL> {
|
||||||
GameBuilder::default()
|
GameBuilder::new(wl)
|
||||||
}
|
}
|
||||||
/// Create a [Game] of wordle
|
/// Create a [Game] of wordle
|
||||||
///
|
///
|
||||||
|
@ -97,7 +99,7 @@ impl<'wl, WL: WordList> Game<'wl, WL> {
|
||||||
evaluation.push((c, status));
|
evaluation.push((c, status));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut response = GuessResponse::new(guess, evaluation, self.step, self.max_steps);
|
let mut response = GuessResponse::new(guess, evaluation, &self);
|
||||||
self.finished = response.finished();
|
self.finished = response.finished();
|
||||||
Ok(response)
|
Ok(response)
|
||||||
}
|
}
|
||||||
|
@ -113,6 +115,14 @@ impl<'wl, WL: WordList> Game<'wl, WL> {
|
||||||
pub fn step(&self) -> usize {
|
pub fn step(&self) -> usize {
|
||||||
self.step
|
self.step
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn finished(&self) -> bool {
|
||||||
|
self.finished
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn max_steps(&self) -> usize {
|
||||||
|
self.max_steps
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Build and Configure a [`Game`]
|
/// Build and Configure a [`Game`]
|
||||||
|
@ -126,9 +136,11 @@ impl<'wl, WL: WordList> Game<'wl, WL> {
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # use wordle_analyzer::game::*;
|
/// # use wordle_analyzer::game::*;
|
||||||
|
/// # use wordle_analyzer::wlist::builtin::BuiltinWList;
|
||||||
/// # use anyhow::Result;
|
/// # use anyhow::Result;
|
||||||
/// # fn main() -> Result<()> {
|
/// # fn main() -> Result<()> {
|
||||||
/// let game: Game = GameBuilder::default()
|
/// let wl = BuiltinWList::default();
|
||||||
|
/// let game: Game<_> = GameBuilder::new(&wl)
|
||||||
/// .build()?;
|
/// .build()?;
|
||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
|
@ -136,9 +148,11 @@ impl<'wl, WL: WordList> Game<'wl, WL> {
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # use wordle_analyzer::game::*;
|
/// # use wordle_analyzer::game::*;
|
||||||
|
/// # use wordle_analyzer::wlist::builtin::BuiltinWList;
|
||||||
/// # use anyhow::Result;
|
/// # use anyhow::Result;
|
||||||
/// # fn main() -> Result<()> {
|
/// # fn main() -> Result<()> {
|
||||||
/// let game: Game = Game::builder()
|
/// let wl = BuiltinWList::default();
|
||||||
|
/// let game: Game<_> = Game::builder(&wl)
|
||||||
/// .length(5)
|
/// .length(5)
|
||||||
/// .precompute(false)
|
/// .precompute(false)
|
||||||
/// .max_steps(6)
|
/// .max_steps(6)
|
||||||
|
@ -148,19 +162,31 @@ impl<'wl, WL: WordList> Game<'wl, WL> {
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
pub struct GameBuilder<WL: WordList> {
|
pub struct GameBuilder<'wl, WL: WordList> {
|
||||||
length: usize,
|
length: usize,
|
||||||
precompute: bool,
|
precompute: bool,
|
||||||
max_steps: usize,
|
max_steps: usize,
|
||||||
wordlist: WL,
|
wordlist: &'wl WL,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<WL: WordList> GameBuilder<WL> {
|
impl<'wl, WL: WordList> GameBuilder<'wl, WL> {
|
||||||
|
/// make a new [GameBuilder]
|
||||||
|
///
|
||||||
|
/// We need a [WordList], so provide one here.
|
||||||
|
pub fn new(wl: &'wl WL) -> Self {
|
||||||
|
Self {
|
||||||
|
length: super::DEFAULT_WORD_LENGTH,
|
||||||
|
precompute: false,
|
||||||
|
max_steps: super::DEFAULT_MAX_STEPS,
|
||||||
|
wordlist: wl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// build a [`Game`] with the stored configuration
|
/// build a [`Game`] with the stored configuration
|
||||||
pub fn build(&self) -> GameResult<Game<WL>> {
|
pub fn build(&self) -> GameResult<Game<'wl, WL>> {
|
||||||
debug!("{:#?}", self);
|
debug!("{:#?}", self);
|
||||||
let game: Game<WL> =
|
let game: Game<WL> =
|
||||||
Game::build(self.length, self.precompute, self.max_steps, &self.wordlist)?;
|
Game::build(self.length, self.precompute, self.max_steps, self.wordlist)?;
|
||||||
Ok(game)
|
Ok(game)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -194,19 +220,8 @@ impl<WL: WordList> GameBuilder<WL> {
|
||||||
///
|
///
|
||||||
/// The builder can be used multiple times. Each [`Game`] will have a immutable reference to
|
/// The builder can be used multiple times. Each [`Game`] will have a immutable reference to
|
||||||
/// `wl`.
|
/// `wl`.
|
||||||
pub fn wordlist(mut self, wl: WL) -> Self {
|
pub fn wordlist(mut self, wl: &'wl WL) -> Self {
|
||||||
self.wordlist = wl;
|
self.wordlist = wl;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<WL: WordList> Default for GameBuilder<WL> {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
length: super::DEFAULT_WORD_LENGTH,
|
|
||||||
precompute: false,
|
|
||||||
max_steps: super::DEFAULT_MAX_STEPS,
|
|
||||||
wordlist: WL::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,18 +1,20 @@
|
||||||
use crate::wlist::word::Word;
|
use crate::wlist::word::{Word, WordData};
|
||||||
|
use crate::wlist::WordList;
|
||||||
use anyhow::Ok;
|
use anyhow::Ok;
|
||||||
use colored::{ColoredString, Colorize};
|
use colored::{ColoredString, Colorize};
|
||||||
use libpt::log::debug;
|
use libpt::log::debug;
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
use super::Game;
|
||||||
|
|
||||||
pub type Evaluation = Vec<(char, Status)>;
|
pub type Evaluation = Vec<(char, Status)>;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct GuessResponse {
|
pub struct GuessResponse {
|
||||||
guess: Word,
|
guess: Word,
|
||||||
evaluation: Evaluation,
|
evaluation: Evaluation,
|
||||||
step: usize,
|
|
||||||
finish: bool,
|
finish: bool,
|
||||||
win: bool,
|
solution: WordData,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
@ -23,29 +25,21 @@ pub enum Status {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GuessResponse {
|
impl GuessResponse {
|
||||||
pub(crate) fn new(
|
pub(crate) fn new<WL: WordList>(guess: Word, status: Vec<(char, Status)>, game: &Game<WL>) -> Self {
|
||||||
guess: Word,
|
let finish: bool = if game.step() > game.max_steps() {
|
||||||
status: Vec<(char, Status)>,
|
|
||||||
step: usize,
|
|
||||||
max_step: usize,
|
|
||||||
) -> Self {
|
|
||||||
let mut win = false;
|
|
||||||
let mut finish: bool = if step > max_step {
|
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
let mut matched = true;
|
let mut matched = true;
|
||||||
for p in &status {
|
for p in &status {
|
||||||
matched &= p.1 == Status::Matched;
|
matched &= p.1 == Status::Matched;
|
||||||
}
|
}
|
||||||
win = matched;
|
matched
|
||||||
win
|
|
||||||
};
|
};
|
||||||
Self {
|
Self {
|
||||||
guess,
|
guess,
|
||||||
evaluation: status,
|
evaluation: status,
|
||||||
step,
|
|
||||||
finish,
|
finish,
|
||||||
win,
|
solution: game.solution().clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,7 +48,15 @@ impl GuessResponse {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn won(&self) -> bool {
|
pub fn won(&self) -> bool {
|
||||||
self.win
|
self.guess == self.solution.0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn solution(&self) -> Option<WordData> {
|
||||||
|
if self.won() {
|
||||||
|
Some(self.solution.clone())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
use crate::wlist::WordList;
|
||||||
|
|
||||||
|
use super::Game;
|
||||||
|
|
||||||
|
pub struct Summary<'wl, WL: WordList> {
|
||||||
|
data: &'wl Vec<Game<'wl, WL>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'wl, WL: WordList> From<&'wl Vec<Game<'wl, WL>>> for Summary<'wl, WL> {
|
||||||
|
fn from(value: &'wl Vec<Game<'wl, WL>>) -> Self {
|
||||||
|
Summary { data: value }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'wl, WL: WordList> From<&'wl mut Vec<Game<'wl, WL>>> for Summary<'wl, WL> {
|
||||||
|
fn from(value: &'wl mut Vec<Game<'wl, WL>>) -> Self {
|
||||||
|
Summary { data: value }
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,6 +12,6 @@ pub mod bench;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
#[cfg(feature = "game")]
|
#[cfg(feature = "game")]
|
||||||
pub mod game;
|
pub mod game;
|
||||||
#[cfg(feature = "solvers")]
|
#[cfg(feature = "solve")]
|
||||||
pub mod solvers;
|
pub mod solve;
|
||||||
pub mod wlist;
|
pub mod wlist;
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
use std::{fmt::Display, str::FromStr};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
error::{Error, WResult},
|
||||||
|
game::{response::*, summary::Summary, Game},
|
||||||
|
wlist::{word::WordData, WordList},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub mod naive;
|
||||||
|
pub mod stupid;
|
||||||
|
|
||||||
|
pub trait Solver<'wl, WL: WordList>: Clone + std::fmt::Debug {
|
||||||
|
fn build(wordlist: &'wl WL) -> WResult<Self>;
|
||||||
|
fn play(&self, game: &mut Game<'wl, WL>) -> WResult<GuessResponse>;
|
||||||
|
fn solve(&self, game: &mut Game<'wl, WL>) -> WResult<Option<WordData>> {
|
||||||
|
let mut resp: GuessResponse;
|
||||||
|
loop {
|
||||||
|
resp = self.play(game)?;
|
||||||
|
if game.finished() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(resp.solution())
|
||||||
|
}
|
||||||
|
fn play_n(&self, games: &'wl mut Vec<Game<'wl, WL>>) -> WResult<Summary<'wl, WL>> {
|
||||||
|
for game in games.iter_mut() {
|
||||||
|
self.play(game)?;
|
||||||
|
}
|
||||||
|
Ok(Summary::from(games))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[non_exhaustive]
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||||
|
pub enum BuiltinSolvers {
|
||||||
|
#[default]
|
||||||
|
Naive,
|
||||||
|
Stupid,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for BuiltinSolvers {
|
||||||
|
type Err = Error;
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
match s.to_lowercase().as_str() {
|
||||||
|
"naive" => Ok(Self::Naive),
|
||||||
|
"stupid" => Ok(Self::Stupid),
|
||||||
|
_ => Err(Self::Err::UnknownBuiltinSolver),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for BuiltinSolvers {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{:?}", self)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
use crate::wlist::WordList;
|
||||||
|
|
||||||
|
use super::Solver;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct StupidSolver<'wl, WL> {
|
||||||
|
wl: &'wl WL,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'wl, WL: WordList> Solver<'wl, WL> for StupidSolver<'wl, WL> {
|
||||||
|
fn build(wordlist: &'wl WL) -> crate::error::WResult<Self> {
|
||||||
|
Ok(Self { wl: wordlist })
|
||||||
|
}
|
||||||
|
fn play(
|
||||||
|
&self,
|
||||||
|
game: &mut crate::game::Game<'wl, WL>,
|
||||||
|
) -> crate::error::WResult<super::GuessResponse> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,12 +0,0 @@
|
||||||
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>;
|
|
||||||
}
|
|
Loading…
Reference in New Issue