add "Explore game tree"
This commit is contained in:
parent
2f09571673
commit
26135d641f
4 changed files with 105 additions and 35 deletions
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -55,6 +55,7 @@ export function Game(props: GameProps) {
|
|||
);
|
||||
const tableRef = useRef<HTMLTableElement>(null);
|
||||
const game = useMemo(() => wasm.Game.new(COMMON_WORDS, puzzle.history), []);
|
||||
const [gameTree, setGameTree] = useState<GuessResult[][]>();
|
||||
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,6 +265,7 @@ export function Game(props: GameProps) {
|
|||
/>
|
||||
<p>
|
||||
{gameState === GameState.Won && (
|
||||
<div class="win-buttons">
|
||||
<button
|
||||
onClick={() => {
|
||||
const emoji = props.colorBlind
|
||||
|
@ -286,8 +288,33 @@ export function Game(props: GameProps) {
|
|||
>
|
||||
Share results
|
||||
</button>
|
||||
<button onClick={() => setGameTree(game.explore_game_tree())}>
|
||||
Explore game tree
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</p>
|
||||
{gameTree && (
|
||||
<div className="game-tree">
|
||||
<p>Here are all the ways the game could've responded:</p>
|
||||
{gameTree?.map((option) =>
|
||||
<div className="game-tree-option">
|
||||
{option.map((guessResult, i) => {
|
||||
const cluedLetters = clue(guessResult);
|
||||
return (
|
||||
<Row
|
||||
key={i}
|
||||
wordLength={WORD_LENGTH}
|
||||
rowState={RowState.LockedIn}
|
||||
cluedLetters={cluedLetters}
|
||||
isPartOfPuzzle={false}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<p>
|
||||
Today's puzzle (#{puzzle.seed}) has {puzzle.solutions} path{puzzle.solutions === 1 ? '' : 's'} to a win.
|
||||
</p>
|
||||
|
|
|
@ -47,13 +47,11 @@ impl<const N: usize> AdversarialEvaluator<N> {
|
|||
pub fn possible_word(&self) -> Option<&Word<N>> {
|
||||
self.possible_words.first()
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Evaluator<N> for AdversarialEvaluator<N> {
|
||||
fn evaluate(&self, word: Word<N>) -> Matches<N> {
|
||||
pub fn best_secrets(&self, word: Word<N>) -> Vec<Word<N>> {
|
||||
let mut evaluated_matches: HashSet<Matches<N>> = HashSet::new();
|
||||
let mut best_secret: Option<Word<N>> = None;
|
||||
let mut best_possible_words_count: Option<usize> = None;
|
||||
let mut best_secrets: Vec<Word<N>> = Vec::new();
|
||||
let mut best_possible_words_count: usize = 0;
|
||||
let mut resulting_constraints = constraints::Total::new();
|
||||
|
||||
for possible_secret in &self.possible_words {
|
||||
|
@ -72,13 +70,24 @@ impl<const N: usize> Evaluator<N> for AdversarialEvaluator<N> {
|
|||
.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<const N: usize> Evaluator<N> for AdversarialEvaluator<N> {
|
||||
fn evaluate(&self, word: Word<N>) -> Matches<N> {
|
||||
let best_secrets = self.best_secrets(word);
|
||||
return guess(&word, best_secrets.first().unwrap());
|
||||
}
|
||||
|
||||
fn push(&mut self, result: GuessResult<N>) {
|
||||
|
|
Loading…
Reference in a new issue