Skip to content

Instantly share code, notes, and snippets.

@ysimonson
Last active January 9, 2022 18:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ysimonson/01a1dee41b1b5990c30568fd25d79c84 to your computer and use it in GitHub Desktop.
Save ysimonson/01a1dee41b1b5990c30568fd25d79c84 to your computer and use it in GitHub Desktop.
Rust wordle solver
[package]
name = "wordle_solver"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
rand = "0.8.4"
//! Rust wordle solver. Download the dictionary to words_alpha.txt from here:
//! https://raw.githubusercontent.com/dwyl/english-words/master/words_alpha.txt
use std::error::Error;
use std::fs::File;
use std::io::{BufRead, BufReader, stdin};
use rand::Rng;
#[derive(Debug, Eq, PartialEq)]
enum RoundInput {
PerfectMatch,
Match,
Miss
}
fn read_words() -> Result<Vec<String>, Box<dyn Error>> {
let file = File::open("words_alpha.txt")?;
let reader = BufReader::new(file);
let mut words = Vec::new();
for line in reader.lines() {
let word = line?;
if word.len() == 5 {
words.push(word);
}
}
Ok(words)
}
fn read_input() -> Result<Option<Vec<RoundInput>>, Box<dyn Error>> {
loop {
let mut buffer = String::with_capacity(7);
stdin().lock().read_line(&mut buffer)?;
buffer = buffer.trim_end().to_string();
if buffer == "X" {
return Ok(None);
}
if buffer.len() != 5 {
println!("invalid input, try again");
continue;
}
let input = buffer.chars().map(|c| {
match c {
'G' => RoundInput::PerfectMatch,
'Y' => RoundInput::Match,
_ => RoundInput::Miss,
}
}).collect::<Vec<RoundInput>>();
return Ok(Some(input));
}
}
fn main() -> Result<(), Box<dyn Error>> {
let mut words = read_words()?;
println!("Ready to play. Enter each round as a separate line; _ for no match, G for green, Y for yellow.");
println!("e.g. if the input guess is 'COVEY', 'G__Y_' would mean that the word starts with C, has an E in it, and does not have O, V or Y in it.");
println!("Alternatively, just enter 'X' if the word wasn't found in the wordle dictionary.");
for i in 1.. {
println!("=> round #{}, {} candidate words", i, words.len());
if words.len() == 0 {
println!("=> no words left");
break;
}
let guess_idx = rand::thread_rng().gen_range(0usize..words.len());
let guess = words.remove(guess_idx);
println!("=> guess: {}", guess);
if let Some(input) = read_input()? {
println!("=> response: {:?}", input);
if input.iter().all(|round_input| *round_input == RoundInput::PerfectMatch) {
println!("=> done!");
break;
}
for (j, round_input) in input.into_iter().enumerate() {
let guess_char = guess.chars().nth(j).unwrap();
let guess_char_count = guess.chars().filter(|c| *c == guess_char).count();
words = words.into_iter().filter(|word| {
match round_input {
RoundInput::PerfectMatch => word.chars().nth(j).unwrap() == guess_char,
RoundInput::Match => word.chars().nth(j).unwrap() != guess_char && word.contains(guess_char),
RoundInput::Miss if guess_char_count == 1 => !word.contains(guess_char),
RoundInput::Miss => word.chars().nth(j).unwrap() != guess_char,
}
}).collect();
}
}
}
Ok(())
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment