diff --git a/web-optimle/src/lib.rs b/web-optimle/src/lib.rs
index 5365776..ccb4fe2 100644
--- a/web-optimle/src/lib.rs
+++ b/web-optimle/src/lib.rs
@@ -124,6 +124,32 @@ impl Game {
.collect()
}
+ pub fn explore_game_tree(&self) -> JsValue {
+ let mut adversary = evaluator::AdversarialEvaluator::new(&self.dictionary);
+ for result in &self.initial_history {
+ adversary.push(guess::GuessResult {
+ word: word!(result.word),
+ matches: matches_to_wordlelike!(result.matches),
+ });
+ }
+ let first_word = &self.current_attempt.first().unwrap().word;
+ let secrets = adversary.best_secrets(word![first_word]);
+ let mut game_tree = Vec::new();
+ for secret in secrets {
+ game_tree.push(vec![
+ GuessResult {
+ word: first_word.clone(),
+ matches: matches_from_wordlelike![guess::guess(word![first_word], &secret)],
+ },
+ GuessResult {
+ word: secret.iter().collect(),
+ matches: vec![2,2,2,2,2],
+ }
+ ]);
+ }
+ serde_wasm_bindgen::to_value(&game_tree).unwrap()
+ }
+
pub fn reset(&mut self) {
self.current_attempt = Vec::new();
}
diff --git a/web-optimle/www/src/App.css b/web-optimle/www/src/App.css
index feb2de3..edf8bd5 100644
--- a/web-optimle/www/src/App.css
+++ b/web-optimle/www/src/App.css
@@ -294,3 +294,11 @@ a:active {
.App-container.color-blind .App-about b.yellow-bg {
background-color: #85c0f9;
}
+
+.win-buttons button {
+ margin: 0 0.5em;
+}
+
+.game-tree-option {
+ margin-bottom: 0.5em;
+}
\ No newline at end of file
diff --git a/web-optimle/www/src/Game.tsx b/web-optimle/www/src/Game.tsx
index b3b9c15..9b82637 100644
--- a/web-optimle/www/src/Game.tsx
+++ b/web-optimle/www/src/Game.tsx
@@ -55,6 +55,7 @@ export function Game(props: GameProps) {
);
const tableRef = useRef(null);
const game = useMemo(() => wasm.Game.new(COMMON_WORDS, puzzle.history), []);
+ const [gameTree, setGameTree] = useState();
const reset = useCallback(() => {
game.reset();
setGuesses([...puzzle.history]);
@@ -78,6 +79,9 @@ export function Game(props: GameProps) {
...puzzle.history,
...storedTodayStats.winningGuesses,
]);
+ storedTodayStats.winningGuesses.forEach(
+ (result) => game.guess(result.word)
+ );
}
}
}
@@ -151,9 +155,6 @@ export function Game(props: GameProps) {
setHint("Not a valid word");
return;
}
- for (const g of guesses) {
- const c = clue(g);
- }
const matches: number[] = game.guess(currentGuess);
const result: GuessResult = {
word: currentGuess,
@@ -264,30 +265,56 @@ export function Game(props: GameProps) {
/>
{gameState === GameState.Won && (
-
+
+
+
+
)}
+ {gameTree && (
+
+
Here are all the ways the game could've responded:
+ {gameTree?.map((option) =>
+
+ {option.map((guessResult, i) => {
+ const cluedLetters = clue(guessResult);
+ return (
+
+ );
+ })}
+
+ )}
+
+ )}
Today's puzzle (#{puzzle.seed}) has {puzzle.solutions} path{puzzle.solutions === 1 ? '' : 's'} to a win.
diff --git a/wordlelike/src/evaluator.rs b/wordlelike/src/evaluator.rs
index 85ca399..d632aa6 100644
--- a/wordlelike/src/evaluator.rs
+++ b/wordlelike/src/evaluator.rs
@@ -47,13 +47,11 @@ impl AdversarialEvaluator {
pub fn possible_word(&self) -> Option<&Word> {
self.possible_words.first()
}
-}
-impl Evaluator for AdversarialEvaluator {
- fn evaluate(&self, word: Word) -> Matches {
+ pub fn best_secrets(&self, word: Word) -> Vec> {
let mut evaluated_matches: HashSet> = HashSet::new();
- let mut best_secret: Option> = None;
- let mut best_possible_words_count: Option = None;
+ let mut best_secrets: Vec> = Vec::new();
+ let mut best_possible_words_count: usize = 0;
let mut resulting_constraints = constraints::Total::new();
for possible_secret in &self.possible_words {
@@ -71,14 +69,25 @@ impl Evaluator for AdversarialEvaluator {
.filter(|&word| resulting_constraints.check(*word))
.count()
};
-
- if best_possible_words_count.is_none() || possible_words_count > best_possible_words_count.unwrap() {
- best_possible_words_count = Some(possible_words_count);
- best_secret = Some(*possible_secret);
+
+ if possible_words_count > best_possible_words_count {
+ best_possible_words_count = possible_words_count;
+ best_secrets.clear();
+ }
+
+ if possible_words_count == best_possible_words_count {
+ best_secrets.push(*possible_secret);
}
}
- return guess(&word, &best_secret.unwrap());
+ best_secrets
+ }
+}
+
+impl Evaluator for AdversarialEvaluator {
+ fn evaluate(&self, word: Word) -> Matches {
+ let best_secrets = self.best_secrets(word);
+ return guess(&word, best_secrets.first().unwrap());
}
fn push(&mut self, result: GuessResult) {