Primer commit
This commit is contained in:
1
src/lib.rs
Normal file
1
src/lib.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub mod words;
|
||||
467
src/main.rs
Normal file
467
src/main.rs
Normal file
@@ -0,0 +1,467 @@
|
||||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
io::{Write, stdin, stdout},
|
||||
};
|
||||
|
||||
use wordle_solver::words::WORDS;
|
||||
|
||||
const PRIMES: [u128; 54] = [
|
||||
2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97,
|
||||
101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193,
|
||||
197, 199, 211, 223, 227, 229, 233, 239, 241, 251,
|
||||
];
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
|
||||
enum LetterState {
|
||||
#[default]
|
||||
Gray,
|
||||
Yellow,
|
||||
Green,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, PartialEq, Eq, Clone, Copy, Hash)]
|
||||
struct Result([LetterState; 5]);
|
||||
|
||||
fn main() {
|
||||
let all_words: Vec<&str> = HashSet::from(WORDS).iter().copied().collect();
|
||||
let mut current_words = all_words.clone();
|
||||
|
||||
let mut hmap = HashMap::new();
|
||||
|
||||
loop {
|
||||
let old_length = current_words.len();
|
||||
println!("Palabras restantes: {}", old_length);
|
||||
let mut word = String::from(" ");
|
||||
while !all_words.contains(&word.trim()) {
|
||||
word = "".to_string();
|
||||
print!("Palabra: ");
|
||||
stdout().flush().unwrap();
|
||||
stdin().read_line(&mut word).unwrap();
|
||||
}
|
||||
let mut res_string = String::default();
|
||||
print!("Resultado: ");
|
||||
stdout().flush().unwrap();
|
||||
stdin().read_line(&mut res_string).unwrap();
|
||||
let res = res_from_string(res_string);
|
||||
get_remaining_words(&word, &res, &mut current_words);
|
||||
let new_length = current_words.len();
|
||||
let eliminadas = old_length - new_length;
|
||||
println!(
|
||||
" Palabras eliminadas: {} ({:.2}%)",
|
||||
eliminadas,
|
||||
eliminadas as f32 * 100_f32 / old_length as f32
|
||||
);
|
||||
|
||||
if current_words.len() == 1 {
|
||||
println!("{}", current_words[0]);
|
||||
break;
|
||||
}
|
||||
|
||||
let l = all_words.len();
|
||||
|
||||
let mut words_score = all_words
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, w)| {
|
||||
print!("\r{:.2}%", 100_f32 * i as f32 / l as f32);
|
||||
stdout().flush().unwrap();
|
||||
(*w, get_score(w, ¤t_words, &mut hmap))
|
||||
})
|
||||
.collect::<Vec<(&str, f32)>>();
|
||||
print!("\r");
|
||||
stdout().flush().unwrap();
|
||||
|
||||
words_score.sort_by(|a, b| {
|
||||
if (a.1 - 0.5).abs() > (b.1 - 0.5).abs() {
|
||||
std::cmp::Ordering::Greater
|
||||
} else {
|
||||
std::cmp::Ordering::Less
|
||||
}
|
||||
});
|
||||
|
||||
for (w, s) in words_score.iter().take(5) {
|
||||
println!(" {}: {}", w, s);
|
||||
}
|
||||
|
||||
if current_words.len() < 6 {
|
||||
println!(" Palabras posibles: ");
|
||||
for w in current_words.clone() {
|
||||
println!(" {}", w);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn res_from_string(s: String) -> Result {
|
||||
let mut r = Result::default();
|
||||
for (i, l) in r.0.iter_mut().enumerate() {
|
||||
match s.chars().nth(i).unwrap() {
|
||||
'g' => *l = LetterState::Gray,
|
||||
'v' => *l = LetterState::Green,
|
||||
'a' => *l = LetterState::Yellow,
|
||||
_ => panic!("Bad letter {i}: {:?}", s.chars().nth(i).unwrap()),
|
||||
}
|
||||
}
|
||||
r
|
||||
}
|
||||
|
||||
fn get_score(word: &str, words: &[&str], hmap: &mut HashMap<u128, Result>) -> f32 {
|
||||
let mut possible = 0_f32;
|
||||
let l = words.len();
|
||||
let other_words = Vec::from(words);
|
||||
|
||||
let mut score_hmap: HashMap<(&str, Result), f32> = HashMap::new();
|
||||
|
||||
for real_word in other_words.clone() {
|
||||
// let hmap = HashMap::new();
|
||||
let res = get_result(word, real_word, hmap);
|
||||
if let Some(score) = score_hmap.get(&(word, res)) {
|
||||
possible += score;
|
||||
} else {
|
||||
let c = get_number_rem_words(word, &res, &other_words);
|
||||
score_hmap.insert((word, res), c as f32 / l as f32);
|
||||
possible += c as f32 / l as f32;
|
||||
}
|
||||
}
|
||||
|
||||
possible / l as f32
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_remaining_words(word: &str, result: &Result, words: &mut Vec<&str>) {
|
||||
*words = words
|
||||
.iter()
|
||||
.copied()
|
||||
.filter(|&s| is_possible(word, result, s) && *s != word[..5])
|
||||
.collect();
|
||||
}
|
||||
|
||||
fn get_number_rem_words(word: &str, result: &Result, words: &[&str]) -> usize {
|
||||
let mut count = 0;
|
||||
|
||||
for possible_word in words {
|
||||
if is_possible(word, result, possible_word) && *possible_word != word {
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
count
|
||||
}
|
||||
|
||||
fn get_result(guess: &str, real_acc: &str, hmap: &mut HashMap<u128, Result>) -> Result {
|
||||
let hash = hash_words(guess, real_acc);
|
||||
if let Some(r) = hmap.get(&hash) {
|
||||
return *r;
|
||||
}
|
||||
let real = real_acc;
|
||||
assert!(
|
||||
guess.chars().count() == 5,
|
||||
"Guessed word length is not 5: {:?} length: {}",
|
||||
guess,
|
||||
guess.chars().count()
|
||||
);
|
||||
assert!(
|
||||
real.chars().count() == 5,
|
||||
"Real word length is not 5: {:?} length: {}",
|
||||
real.chars(),
|
||||
real.chars().count()
|
||||
);
|
||||
let mut res = [LetterState::Gray; 5];
|
||||
|
||||
let mut count_g: HashMap<char, u8> = HashMap::new();
|
||||
let mut count_r: HashMap<char, u8> = HashMap::new();
|
||||
for c in guess.chars() {
|
||||
*count_g.entry(c).or_insert(0) += 1;
|
||||
}
|
||||
for c in real.chars() {
|
||||
*count_r.entry(c).or_insert(0) += 1;
|
||||
}
|
||||
|
||||
for (i, letter) in guess
|
||||
.chars()
|
||||
.collect::<Vec<char>>()
|
||||
.iter()
|
||||
.enumerate()
|
||||
.rev()
|
||||
{
|
||||
let l = guess.chars().nth(i).unwrap();
|
||||
*count_g.entry(guess.chars().nth(i).unwrap()).or_insert(0) -= 1;
|
||||
if real.chars().nth(i).unwrap() == *letter {
|
||||
res[i] = LetterState::Green;
|
||||
*count_r.entry(l).or_insert(0) -= 1;
|
||||
} else if real.contains(*letter) && count_g.get(&l).unwrap() < count_r.get(&l).unwrap() {
|
||||
res[i] = LetterState::Yellow;
|
||||
}
|
||||
}
|
||||
let r = Result(res);
|
||||
hmap.insert(hash, r);
|
||||
r
|
||||
}
|
||||
|
||||
fn is_possible(guessed: &str, result: &Result, to_check: &str) -> bool {
|
||||
let mut count_g: HashMap<char, u8> = HashMap::new();
|
||||
let mut count_r: HashMap<char, u8> = HashMap::new();
|
||||
for c in guessed.chars() {
|
||||
*count_g.entry(c).or_insert(0) += 1;
|
||||
}
|
||||
for c in to_check.chars() {
|
||||
*count_r.entry(c).or_insert(0) += 1;
|
||||
}
|
||||
|
||||
for i in (0..5).rev() {
|
||||
let letter = guessed.chars().nth(i).unwrap();
|
||||
*count_g.entry(letter).or_insert(0) -= 1;
|
||||
match result.0[i] {
|
||||
LetterState::Gray => {
|
||||
if to_check.contains(letter)
|
||||
&& count_g.get(&letter).unwrap() < count_r.get(&letter).unwrap()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
LetterState::Yellow => {
|
||||
if !to_check.contains(letter) || to_check.chars().nth(i).unwrap() == letter {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
LetterState::Green => {
|
||||
*count_r.entry(letter).or_insert(0) -= 1;
|
||||
if to_check.chars().nth(i).unwrap() != letter {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
fn hash_words(word: &str, real: &str) -> u128 {
|
||||
let mut acc = 1;
|
||||
for i in 0..5 {
|
||||
let mut v = word.chars().nth(i).unwrap().to_ascii_lowercase() as usize - 97;
|
||||
if v > 26 {
|
||||
v = 26;
|
||||
}
|
||||
|
||||
acc *= PRIMES[v];
|
||||
}
|
||||
for i in 0..5 {
|
||||
let mut v = real.chars().nth(i).unwrap().to_ascii_lowercase() as usize - 70;
|
||||
if v > 53 {
|
||||
v = 53;
|
||||
}
|
||||
acc *= PRIMES[v];
|
||||
}
|
||||
acc
|
||||
}
|
||||
|
||||
/* --------------- TESTS --------------- */
|
||||
|
||||
#[test]
|
||||
fn test_get_result_aureo_manga() {
|
||||
let mut hmap = HashMap::new();
|
||||
assert_eq!(
|
||||
get_result(&String::from("aureo"), "manga", &mut hmap),
|
||||
Result([
|
||||
LetterState::Yellow,
|
||||
LetterState::Gray,
|
||||
LetterState::Gray,
|
||||
LetterState::Gray,
|
||||
LetterState::Gray
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_result_aabbb_manga() {
|
||||
let mut hmap = HashMap::new();
|
||||
assert_eq!(
|
||||
get_result(&String::from("aabbb"), "manga", &mut hmap),
|
||||
Result([
|
||||
LetterState::Yellow,
|
||||
LetterState::Green,
|
||||
LetterState::Gray,
|
||||
LetterState::Gray,
|
||||
LetterState::Gray
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_result_ababb_manga() {
|
||||
let mut hmap = HashMap::new();
|
||||
assert_eq!(
|
||||
get_result(&String::from("ababb"), "manga", &mut hmap),
|
||||
Result([
|
||||
LetterState::Yellow,
|
||||
LetterState::Gray,
|
||||
LetterState::Yellow,
|
||||
LetterState::Gray,
|
||||
LetterState::Gray
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_result_abaab_manga() {
|
||||
let mut hmap = HashMap::new();
|
||||
assert_eq!(
|
||||
get_result(&String::from("abaab"), "manga", &mut hmap),
|
||||
Result([
|
||||
LetterState::Yellow,
|
||||
LetterState::Gray,
|
||||
LetterState::Yellow,
|
||||
LetterState::Gray,
|
||||
LetterState::Gray
|
||||
])
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn test_get_result_hucha_mucha() {
|
||||
let mut hmap = HashMap::new();
|
||||
assert_eq!(
|
||||
get_result(&String::from("hucha"), "mucha", &mut hmap),
|
||||
Result([
|
||||
LetterState::Gray,
|
||||
LetterState::Green,
|
||||
LetterState::Green,
|
||||
LetterState::Green,
|
||||
LetterState::Green
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_result_gafea_salto() {
|
||||
let mut hmap = HashMap::new();
|
||||
assert_eq!(
|
||||
get_result(&String::from("gafea"), "salto", &mut hmap),
|
||||
Result([
|
||||
LetterState::Gray,
|
||||
LetterState::Green,
|
||||
LetterState::Gray,
|
||||
LetterState::Gray,
|
||||
LetterState::Gray
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_result_botox_salto() {
|
||||
let mut hmap = HashMap::new();
|
||||
assert_eq!(
|
||||
get_result(&String::from("botox"), "salto", &mut hmap),
|
||||
Result([
|
||||
LetterState::Gray,
|
||||
LetterState::Yellow,
|
||||
LetterState::Yellow,
|
||||
LetterState::Gray,
|
||||
LetterState::Gray
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_result_ulula_valor() {
|
||||
let mut hmap = HashMap::new();
|
||||
assert_eq!(
|
||||
get_result(&String::from("ulula"), "valor", &mut hmap),
|
||||
Result([
|
||||
LetterState::Gray,
|
||||
LetterState::Yellow,
|
||||
LetterState::Gray,
|
||||
LetterState::Gray,
|
||||
LetterState::Yellow
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_possible_hucha_mucha() {
|
||||
assert!(is_possible(
|
||||
"hucha",
|
||||
&Result([
|
||||
LetterState::Gray,
|
||||
LetterState::Green,
|
||||
LetterState::Green,
|
||||
LetterState::Green,
|
||||
LetterState::Green
|
||||
]),
|
||||
"mucha"
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_possible_aureo_bureo() {
|
||||
assert!(is_possible(
|
||||
"aureo",
|
||||
&Result([
|
||||
LetterState::Gray,
|
||||
LetterState::Green,
|
||||
LetterState::Green,
|
||||
LetterState::Green,
|
||||
LetterState::Green
|
||||
]),
|
||||
"bureo"
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_possible_izaba_zureo() {
|
||||
assert!(!is_possible(
|
||||
"izaba",
|
||||
&Result([
|
||||
LetterState::Gray,
|
||||
LetterState::Gray,
|
||||
LetterState::Gray,
|
||||
LetterState::Gray,
|
||||
LetterState::Gray
|
||||
]),
|
||||
"zureo"
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_possible_gafea_salto() {
|
||||
assert!(is_possible(
|
||||
"gafea",
|
||||
&Result([
|
||||
LetterState::Gray,
|
||||
LetterState::Green,
|
||||
LetterState::Gray,
|
||||
LetterState::Gray,
|
||||
LetterState::Gray
|
||||
]),
|
||||
"salto"
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_possible_botox_salto() {
|
||||
assert!(is_possible(
|
||||
"botox",
|
||||
&Result([
|
||||
LetterState::Gray,
|
||||
LetterState::Yellow,
|
||||
LetterState::Yellow,
|
||||
LetterState::Gray,
|
||||
LetterState::Gray
|
||||
]),
|
||||
"salto"
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_possible_ulula_valor() {
|
||||
assert!(is_possible(
|
||||
"ulula",
|
||||
&Result([
|
||||
LetterState::Gray,
|
||||
LetterState::Yellow,
|
||||
LetterState::Gray,
|
||||
LetterState::Gray,
|
||||
LetterState::Yellow
|
||||
]),
|
||||
"valor"
|
||||
));
|
||||
}
|
||||
1525
src/words.rs
Normal file
1525
src/words.rs
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user