add interactive solver #4

Merged
cscherrNT merged 29 commits from feat/interactive-solver into devel 2024-07-25 15:19:54 +02:00
3 changed files with 77 additions and 4 deletions
Showing only changes of commit 42323410f5 - Show all commits

View File

@ -44,21 +44,31 @@ struct Cli {
enum ReplCommand {
/// Let the user input the response to the last guess
///
/// Format:
Response { encoded: String },
/// Let the user input a word and the response for that word
///
/// Evaluation Format:
///
/// 'x' means wrong character
///
/// 'p' means present character
///
/// 'c' means correct character
Response { encoded: String },
/// Let the user input a word and the response for that word
///
/// Example:
///
/// 'xxxcc' means the first 3 chars are wrong but the second 2 chars are correct
///
/// 'xppxc' means the first character is wrong, the next two characters are present, the last
/// is correct
Guess {
your_guess: String,
evalutation: Evaluation,
},
/// Let the solver make a guess
Solve,
/// Show the current state of the game
Show,
/// Leave the Repl
Exit,
}
@ -114,11 +124,24 @@ fn help_guess_interactive(cli: Cli) -> anyhow::Result<()> {
// only None if the repl has not stepped yet
match repl.command().to_owned().unwrap() {
ReplCommand::Exit => break,
ReplCommand::Show => {
println!("{}", game);
}
ReplCommand::Solve => {
let best_guess = solver.guess_for(&game);
println!("best guess: {best_guess}");
}
ReplCommand::Guess {
your_guess,
evalutation,
} => {
println!("{}", game.guess(your_guess, Some(evalutation))?)
let guess = game.guess(your_guess, Some(evalutation));
if guess.is_err() {
eprintln!("{}", style(guess.unwrap_err()).red().bold());
continue;
}
println!("{}", guess.unwrap());
debug!("current gamestate: {game:#?}");
}
_ => todo!(),
}

View File

@ -1,6 +1,11 @@
use std::convert::Infallible;
use std::fmt::Display;
use std::str::FromStr;
use libpt::cli::console::{style, StyledObject};
use crate::wlist::word::Word;
use super::response::Status;
pub type EvaluationUnit = (char, Status);
@ -10,6 +15,23 @@ pub struct Evaluation {
inner: Vec<EvaluationUnit>,
}
impl Evaluation {
pub(crate) fn colorized_display(&self, guess: &Word) -> Vec<StyledObject<String>> {
assert_eq!(guess.len(), self.inner.len());
let mut buf = Vec::new();
for (i, e) in self.inner.iter().enumerate() {
let mut c = style(guess.chars().nth(i).unwrap().to_string());
if e.1 == Status::Matched {
c = c.green();
} else if e.1 == Status::Exists {
c = c.yellow();
}
buf.push(c);
}
buf
}
}
impl IntoIterator for Evaluation {
type Item = EvaluationUnit;
type IntoIter = std::vec::IntoIter<Self::Item>;

View File

@ -1,9 +1,11 @@
use core::panic;
use std::fmt::Display;
use crate::error::*;
use crate::wlist::word::{ManyWordsRef, Word, WordData};
use crate::wlist::WordList;
use libpt::cli::console::StyledObject;
use libpt::log::{debug, trace};
pub mod response;
@ -13,6 +15,7 @@ pub mod evaluation;
pub mod summary;
use self::evaluation::Evaluation;
use self::response::Status;
#[derive(Debug, Clone, PartialEq)]
@ -271,3 +274,28 @@ impl<'wl, WL: WordList> GameBuilder<'wl, WL> {
self
}
}
impl<'wl, WL: WordList> Display for Game<'wl, WL> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
// TODO: make this actually useful
// TODO: make this actually fancy
write!(
f,
"turn:\t\t{}\nsolution:\t{:?}\nguesses:\t",
self.step(),
self.solution(),
)?;
for s in self
.responses()
.iter()
.map(|v| v.evaluation().to_owned().colorized_display(v.guess()))
{
write!(f, "\"")?;
for si in s {
write!(f, "{si}")?;
}
write!(f, "\", ")?;
}
Ok(())
}
}