diff --git a/src/bench/builtin.rs b/src/bench/builtin.rs new file mode 100644 index 0000000..a343c69 --- /dev/null +++ b/src/bench/builtin.rs @@ -0,0 +1,31 @@ +use crate::game::{Game, GameBuilder}; +use crate::solve::Solver; +use crate::wlist::WordList; + +use super::Benchmark; + +#[derive(Debug, Clone)] +pub struct BuiltinBenchmark<'wl, WL: WordList, SL: Solver<'wl, WL>> { + wordlist: &'wl WL, + solver: SL, + builder: GameBuilder<'wl, WL>, +} + +impl<'wl, WL: WordList, SL: Solver<'wl, WL>> Benchmark<'wl, WL, SL> + for BuiltinBenchmark<'wl, WL, SL> +{ + fn build(wordlist: &'wl WL, solver: SL) -> crate::error::WResult { + let builder: GameBuilder<_> = Game::builder(wordlist); + Ok(Self { + wordlist, + solver, + builder, + }) + } + fn solver(&self) -> &SL { + &self.solver + } + fn builder(&'wl self) -> &'wl crate::game::GameBuilder<'wl, WL> { + &self.builder + } +} diff --git a/src/bench/mod.rs b/src/bench/mod.rs index 1e72ee8..a4bcf5d 100644 --- a/src/bench/mod.rs +++ b/src/bench/mod.rs @@ -3,31 +3,49 @@ use std::fmt::Debug; use libpt::log::debug; use crate::error::WResult; +use crate::game::response::GuessResponse; +use crate::game::{Game, GameBuilder}; +use crate::solve::Solver; use crate::wlist::WordList; -pub trait Benchmark<'wl, WL: WordList>: Clone + Sized + Debug { - fn build(wordlist: &'wl WL) -> WResult; - fn play(&self) -> WResult; - fn bench(&self, n: usize) -> WResult<(usize, f64)> { - // PERF: it would be better to make this multithreaded - let mut absolute: usize = 0; - let part = n / 20; +pub mod report; +use report::*; +#[cfg(feature = "builtin")] +pub mod builtin; + +pub trait Benchmark<'wl, WL, SL>: Clone + Sized + Debug +where + WL: WordList, + WL: 'wl, + SL: Solver<'wl, WL>, +{ + fn build(wordlist: &'wl WL, solver: SL) -> WResult; + fn builder(&'wl self) -> &'wl GameBuilder<'wl, WL>; + fn make_game(&'wl self) -> WResult> { + Ok(self.builder().build()?) + } + fn solver(&'wl self) -> &'wl SL; + fn play(&'wl self) -> WResult { + // FIXME: wth why does the lifetime break here? + let mut game: Game<'wl, WL> = self.make_game()?; + + todo!() + } + fn bench(&'wl self, n: usize) -> WResult { + // PERF: it would be better to make this multithreaded + let part = n / 20; + let mut report = Report::new(); let start = std::time::Instant::now(); for i in 0..n { // TODO: limit execution time for the following, perhaps async - absolute += self.play()?; + report.add(self.play()?); if i % part == part - 1 { - debug!( - "{} / {n}\t ratio: {} \t elapsed: {:?}", - i + 1, - absolute as f64 / n as f64, - start.elapsed() - ); + debug!("{}", report); } } - Ok((absolute, absolute as f64 / n as f64)) + Ok(report) } } diff --git a/src/bench/report.rs b/src/bench/report.rs new file mode 100644 index 0000000..fac9715 --- /dev/null +++ b/src/bench/report.rs @@ -0,0 +1,45 @@ +use std::fmt::Display; + +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +use crate::game::response::GuessResponse; + +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct Report { + data: Vec, +} + +impl Report { + pub fn new() -> Self { + Self { data: Vec::new() } + } + pub fn add(&mut self, data: GuessResponse) { + self.data.push(data) + } + + pub fn n(&self) -> usize { + self.data.len() + } + + pub fn win_ratio(&self) -> usize { + todo!() + } + + pub fn avg_score(&self) -> usize { + todo!() + } +} + +impl Display for Report { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "n: {}, win_ratio: {}, avg_score: {}", + self.n(), + self.win_ratio(), + self.avg_score() + ) + } +} diff --git a/src/bin/bench/cli.rs b/src/bin/bench/cli.rs index bec7407..f5286d4 100644 --- a/src/bin/bench/cli.rs +++ b/src/bin/bench/cli.rs @@ -47,25 +47,9 @@ fn main() -> anyhow::Result<()> { .max_steps(cli.max_steps) .precompute(cli.precompute); let solver = cli.solver.to_solver(&wl); - let mut game = builder.build()?; + let bench = cli.solver.to_solver(&wl); - debug!("{game:#?}"); - - let mut response: GuessResponse; - let mut _guess: Word; - loop { - response = solver.make_a_move(&mut game)?; - println!("{}. guess: {response}", game.step() - 1); - - 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()); - } + todo!(); Ok(()) } diff --git a/src/game/mod.rs b/src/game/mod.rs index 0094129..ea26110 100644 --- a/src/game/mod.rs +++ b/src/game/mod.rs @@ -203,7 +203,7 @@ impl<'wl, WL: WordList> GameBuilder<'wl, WL> { } /// build a [`Game`] with the stored configuration - pub fn build(&self) -> GameResult> { + pub fn build(&'wl self) -> GameResult> { debug!("{:#?}", self); let game: Game = Game::build(self.length, self.precompute, self.max_steps, self.wordlist)?; diff --git a/src/game/response.rs b/src/game/response.rs index dda8c61..2fca898 100644 --- a/src/game/response.rs +++ b/src/game/response.rs @@ -3,13 +3,22 @@ use crate::wlist::WordList; use anyhow::Ok; use colored::{ColoredString, Colorize}; use libpt::log::debug; +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; use std::fmt::Display; use super::Game; +#[derive(Debug, Clone, PartialEq, Copy, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct AtomicEvaluation { + char: char, + status: Status, +} pub type Evaluation = Vec<(char, Status)>; #[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct GuessResponse { guess: Word, evaluation: Evaluation, @@ -20,6 +29,7 @@ pub struct GuessResponse { } #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum Status { None = 0, Exists = 1, @@ -70,6 +80,14 @@ impl GuessResponse { pub fn guess(&self) -> &Word { &self.guess } + + pub fn step(&self) -> usize { + self.step + } + + pub fn max_steps(&self) -> usize { + self.max_steps + } } impl Display for GuessResponse {