1 unstable release

0.1.0 Jan 12, 2022

#957 in Science

MIT/Apache

26KB
535 lines

wordle-solvers

A Wordle solver

License: MIT OR Apache-2.0


lib.rs:

Building blocks to create a Wordle solver.

This crates provides the type Wordle, which is an Automaton. It accepts only states that satisfy all constraints that are provided from the game.

The expected usage pattern is to search for some accepted state and refine the automaton based on the feedback that the game provides.

Example

use fst::{IntoStreamer, Set, Streamer};
use worlde_automaton::WordleBuilder;

// // Build an FST from a word list - we use some random words
let set = fst::Set::from_iter(&["crush", "morty", "party", "solid"]).unwrap();

// Create an empty Wordle
let wordle = WordleBuilder::new().build();

// Search with the current Wordle
let mut stream = set.search_with_state(wordle.clone()).into_stream();

// Select a guess from the stream
// In the example we take the first one
let (guess, guess_state) = stream.next().unwrap();
// The guess is an Option<SolveState> where a None represents an invalid match
// The fst crate should take care to not return invalid matches, so it should be safe to unwrap
let guess_state = guess_state.unwrap();

// In the first round, the guess is the first word, since all are valid
assert_eq!(guess, b"crush");

// Present the guess to the game and gather feedback
let mut next = WordleBuilder::from(wordle, guess_state);

// Let's say the correct word is 'party'

// The first letter 'c' is not in the word at all, it can _never_ be a part of the solution
next.never(guess[0]);

// The second letter 'r' is in the word, but in the _wrong position_
// The position is 0-based, corresponding to the byte index
next.wrong_pos(1, guess[1]);

// None of the following letters are part of the solution, we can eliminate them in bulk
next.never_all(&guess[2..]);


// let's try the next round
let wordle = next.build();

let mut stream = set.search_with_state(wordle.clone()).into_stream();
let (guess, guess_state) = stream.next().unwrap();
let guess_state = guess_state.unwrap();

// the next valid guess is 'morty' as 'crush' is eliminated, as is 'solid'
assert_eq!(guess, b"morty");

// Present the guess to the game for feedback
let mut next = WordleBuilder::from(wordle, guess_state);
// 'm' and 'o' are not in 'party'
next.never_all(&guess[..2]);

// The remaining letters are all in their correct position
next.correct_pos(2, guess[2]);
next.correct_pos(3, guess[3]);
next.correct_pos(4, guess[4]);

// Let's try the final round
let wordle = next.build();
let mut stream = set.search_with_state(wordle.clone()).into_stream();
let (guess, guess_state) = stream.next().unwrap();
let guess_state = guess_state.unwrap();

// Only 'party' remains as a candidate that fulfills all requirements
assert_eq!(guess, b"party");

// after asking the game, we can verify that we have arrived at a solution
let mut solution = WordleBuilder::from(wordle, guess_state);
solution.correct_pos(0, guess[0]);
solution.correct_pos(1, guess[1]);

// We don't need to add all the remaining characters, as they are already known to be correct
let solution = solution.build();
assert!(solution.is_solved());
assert_eq!(solution.decode_str(), String::from("party"));

Dependencies

~1.5MB