feat: refine wasm interfacing
- allow loading wordlist from internal data - no unwraps in wasm branches
This commit is contained in:
@@ -13,7 +13,7 @@ pub fn do_simulation(valid_words: WordList, target_words: WordList, opener: Opti
|
||||
if let Some(first_guess) = opener {
|
||||
match game.guess(&first_guess) {
|
||||
Ok(pat) => {
|
||||
solver.apply_guess(pat);
|
||||
solver.apply_guess(pat).unwrap();
|
||||
println!("{}", solver.tabular_format());
|
||||
}
|
||||
Err(n) => {
|
||||
@@ -24,12 +24,18 @@ pub fn do_simulation(valid_words: WordList, target_words: WordList, opener: Opti
|
||||
}
|
||||
|
||||
loop {
|
||||
let best_word = solver.evaluate_all_words().first().unwrap().word.clone();
|
||||
let best_word = solver
|
||||
.evaluate_all_words()
|
||||
.unwrap()
|
||||
.first()
|
||||
.unwrap()
|
||||
.word
|
||||
.clone();
|
||||
println!("{}", solver.best_word_format(5));
|
||||
|
||||
match game.guess(&best_word) {
|
||||
Ok(pat) => {
|
||||
solver.apply_guess(pat);
|
||||
solver.apply_guess(pat).unwrap();
|
||||
println!("{}", solver.tabular_format());
|
||||
}
|
||||
Err(n) => {
|
||||
|
||||
+1
-1
@@ -36,7 +36,7 @@ impl Pattern {
|
||||
))
|
||||
} else {
|
||||
Ok(Pattern {
|
||||
chars: word.chars().collect(),
|
||||
chars: word.chars().map(|c| c.to_ascii_uppercase()).collect(),
|
||||
stats,
|
||||
})
|
||||
}
|
||||
|
||||
+11
-9
@@ -53,13 +53,13 @@ impl Solver {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn apply_guess(&mut self, guess: Pattern) {
|
||||
pub fn apply_guess(&mut self, guess: Pattern) -> Result<(), String> {
|
||||
let new_possible_solutions = self.possible_solutions.apply_guess(&guess);
|
||||
let information_gained = self.possible_solutions.equal_likelieness_entropy()
|
||||
- new_possible_solutions.equal_likelieness_entropy();
|
||||
|
||||
if new_possible_solutions.len() == 0 {
|
||||
panic!("Solution space is empty");
|
||||
return Err("Solution space is empty".to_string());
|
||||
}
|
||||
|
||||
self.possible_solutions = new_possible_solutions;
|
||||
@@ -68,6 +68,8 @@ impl Solver {
|
||||
pattern: guess,
|
||||
information_gained,
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn estimate_guesses(&self, expected_information_gained: f64) -> f64 {
|
||||
@@ -78,10 +80,10 @@ impl Solver {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn evaluate_word(&self, word: &str) -> WordStats {
|
||||
pub fn evaluate_word(&self, word: &str) -> Result<WordStats, String> {
|
||||
let solution_probability = self.possible_solutions.word_probability(word);
|
||||
|
||||
let expected_information_gained = self.possible_solutions.entropy_if_guessed(word);
|
||||
let expected_information_gained = self.possible_solutions.entropy_if_guessed(word)?;
|
||||
|
||||
let score = (self.guesses.len() + 1) as f64;
|
||||
|
||||
@@ -89,29 +91,29 @@ impl Solver {
|
||||
+ (1.0 - solution_probability)
|
||||
* (score + self.estimate_guesses(expected_information_gained));
|
||||
|
||||
WordStats {
|
||||
Ok(WordStats {
|
||||
word: word.to_string(),
|
||||
solution_probability,
|
||||
expected_information_gained,
|
||||
expected_score_after_guess,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn evaluate_all_words(&mut self) -> Vec<WordStats> {
|
||||
pub fn evaluate_all_words(&mut self) -> Result<Vec<WordStats>, String> {
|
||||
self.best_words = self
|
||||
.valid_words
|
||||
.words()
|
||||
.par_bridge()
|
||||
.progress_count(self.valid_words.len() as u64)
|
||||
.map(|w| self.evaluate_word(w))
|
||||
.collect();
|
||||
.collect::<Result<_, String>>()?;
|
||||
|
||||
self.best_words.sort_by(|a, b| {
|
||||
a.expected_score_after_guess
|
||||
.total_cmp(&b.expected_score_after_guess)
|
||||
});
|
||||
|
||||
self.best_words.clone()
|
||||
Ok(self.best_words.clone())
|
||||
}
|
||||
|
||||
pub fn best_word_format(&self, n: usize) -> String {
|
||||
|
||||
+48
-13
@@ -1,25 +1,60 @@
|
||||
use std::io;
|
||||
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
use crate::{solver, word_list::WordList};
|
||||
use crate::{
|
||||
pattern::{CharStatus, Pattern},
|
||||
solver,
|
||||
word_list::WordList,
|
||||
};
|
||||
|
||||
#[wasm_bindgen]
|
||||
extern "C" {
|
||||
fn alert(s: &str);
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn greet() {
|
||||
alert("Hello, wasm-on-web!");
|
||||
}
|
||||
static DEFAULT_VALID_WORDS: &str = include_str!("../../valid-words.txt");
|
||||
static DEFAULT_ANSWERS: &str = include_str!("../../answers.txt");
|
||||
|
||||
#[wasm_bindgen]
|
||||
struct Solver(solver::Solver);
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub enum Color {
|
||||
GREY,
|
||||
YELLOW,
|
||||
GREEN,
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
extern "C" {
|
||||
pub fn alert(msg: &str);
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[wasm_bindgen]
|
||||
impl Solver {
|
||||
pub fn new() -> Solver {
|
||||
let wl = WordList::from(["test", "yooo"].iter());
|
||||
Solver(solver::Solver::from_single_word_list(wl))
|
||||
pub fn new() -> Result<Solver, String> {
|
||||
let valid_words =
|
||||
WordList::try_from(io::BufReader::new(io::Cursor::new(DEFAULT_VALID_WORDS)))?;
|
||||
let answers = WordList::try_from(io::BufReader::new(io::Cursor::new(DEFAULT_ANSWERS)))?;
|
||||
|
||||
Ok(Solver(solver::Solver::new(valid_words, answers)))
|
||||
}
|
||||
|
||||
pub fn guess(&mut self, word: &str, colors: Vec<Color>) -> Result<(), String> {
|
||||
let pat = Pattern::try_new(
|
||||
word,
|
||||
colors
|
||||
.iter()
|
||||
.map(|c| match c {
|
||||
Color::GREY => CharStatus::GREY,
|
||||
Color::YELLOW => CharStatus::YELLOW,
|
||||
Color::GREEN => CharStatus::GREEN,
|
||||
})
|
||||
.collect(),
|
||||
)?;
|
||||
|
||||
alert(&format!("{:?}", pat));
|
||||
|
||||
self.0.apply_guess(pat)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn stats(&self) -> String {
|
||||
|
||||
+20
-24
@@ -2,7 +2,7 @@ use rand::{rng, seq::IteratorRandom};
|
||||
use std::{
|
||||
collections::{hash_set, HashSet},
|
||||
fs::{self},
|
||||
io::{self, BufRead},
|
||||
io::{self, BufRead, Read},
|
||||
path::Path,
|
||||
};
|
||||
|
||||
@@ -16,27 +16,12 @@ pub struct WordList {
|
||||
words: HashSet<String>,
|
||||
}
|
||||
|
||||
impl<I, S> From<I> for WordList
|
||||
where
|
||||
I: Iterator<Item = S>,
|
||||
S: AsRef<str>,
|
||||
{
|
||||
fn from(value: I) -> Self {
|
||||
WordList {
|
||||
words: value.into_iter().map(|s| s.as_ref().to_owned()).collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl WordList {
|
||||
pub fn len(&self) -> usize {
|
||||
self.words.len()
|
||||
}
|
||||
|
||||
pub fn from_file(file: &Path) -> Result<WordList, String> {
|
||||
let file = fs::File::open(file).map_err(|e| e.to_string())?;
|
||||
impl<R: Read> TryFrom<io::BufReader<R>> for WordList {
|
||||
type Error = String;
|
||||
fn try_from(value: io::BufReader<R>) -> Result<WordList, String> {
|
||||
let mut len = None;
|
||||
let words: Result<HashSet<String>, String> = io::BufReader::new(file)
|
||||
|
||||
let words: Result<HashSet<String>, String> = value
|
||||
.lines()
|
||||
.map_while(Result::ok)
|
||||
.map(|s| s.trim_ascii().to_ascii_uppercase())
|
||||
@@ -63,6 +48,17 @@ impl WordList {
|
||||
|
||||
Ok(WordList { words: words? })
|
||||
}
|
||||
}
|
||||
|
||||
impl WordList {
|
||||
pub fn len(&self) -> usize {
|
||||
self.words.len()
|
||||
}
|
||||
|
||||
pub fn from_file(file: &Path) -> Result<WordList, String> {
|
||||
let file = fs::File::open(file).map_err(|e| e.to_string())?;
|
||||
WordList::try_from(io::BufReader::new(file))
|
||||
}
|
||||
|
||||
pub fn apply_guess(&self, pat: &Pattern) -> WordList {
|
||||
WordList {
|
||||
@@ -82,13 +78,13 @@ impl WordList {
|
||||
.count()
|
||||
}
|
||||
|
||||
pub fn entropy_if_guessed(&self, word: &str) -> f64 {
|
||||
pub fn entropy_if_guessed(&self, word: &str) -> Result<f64, String> {
|
||||
let mut e = 0.0;
|
||||
|
||||
let mut observed_patterns = HashSet::new();
|
||||
|
||||
for w in self.words.iter() {
|
||||
let pat = Pattern::from_guess(w, word).unwrap();
|
||||
let pat = Pattern::from_guess(w, word)?;
|
||||
if observed_patterns.contains(&pat) {
|
||||
continue;
|
||||
}
|
||||
@@ -100,7 +96,7 @@ impl WordList {
|
||||
}
|
||||
}
|
||||
|
||||
e
|
||||
Ok(e)
|
||||
}
|
||||
|
||||
pub fn word_probability(&self, word: &str) -> f64 {
|
||||
|
||||
Reference in New Issue
Block a user