From 37ffabc867fec4d61a6ce2b3e016ae5b223b0b49 Mon Sep 17 00:00:00 2001 From: mehbark Date: Tue, 7 Jul 2026 22:13:14 -0400 Subject: [PATCH] dramatically reduce memory usage ACQUEST ADZ ALKY AMPING ARF AVOW BAH JEUX --- src/main.rs | 43 +++++++++++++++++++++++++++---------------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/src/main.rs b/src/main.rs index 2584425..e6eb34f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,4 @@ use std::{ - array, - collections::HashSet, env, io::{self, BufRead, BufReader}, process, @@ -22,25 +20,39 @@ fn main() { let target = env::args().nth(1).map_or(LetterSet::FULL, LetterSet::from); println!("letter count: {}", target.len()); - let (len, path) = solve(&mut dictionary, target); + let Some((len, path)) = solve(&mut dictionary, target) else { + println!("failed :("); + process::exit(1); + }; println!("{len}: {}", path.into_iter().collect::>().join(" ")); } const MAX_SOLUTION_LENGTH: u8 = 200; -fn solve(dictionary: &mut [(String, LetterSet)], target: LetterSet) -> (u8, HashSet) { +fn solve(dictionary: &mut [(String, LetterSet)], target: LetterSet) -> Option<(u8, Vec<&str>)> { let mut bests = (0..=LetterSet::FULL.to_index()) - .map(|_| (u8::MAX, HashSet::new())) + .map(|_| (u8::MAX, None)) .collect::>(); println!("allocated!"); - bests[LetterSet::FULL.to_index()] = (0, HashSet::new()); + bests[LetterSet::FULL.to_index()] = (0, None); dictionary.sort_by_key(|(word, ls)| word.len() - usize::from(ls.len())); println!("sorted!"); score(dictionary, &mut bests, target.negation(), 0); - bests[target.negation().to_index()].clone() + let (len, start) = bests[target.negation().to_index()]; + let start = start?; + + let mut path = vec![start]; + let mut done = LetterSet::from(start); + + while let (_, Some(word)) = bests[done.to_index()] { + path.push(word); + done = done.union(LetterSet::from(word)); + } + + Some((len, path)) } // TODO: perhaps bottom up? (pick or no pick) (real potential!) (not obvious how to do path) @@ -48,9 +60,9 @@ fn solve(dictionary: &mut [(String, LetterSet)], target: LetterSet) -> (u8, Hash // length doesn't include spaces. i think that's okay // bests is sorta inverted; the keys are completed letters // assumes that `bests[LetterSet::FULL] == (0, Vec::new())` -fn score( - dictionary: &[(String, LetterSet)], - bests: &mut [(u8, HashSet)], +fn score<'d>( + dictionary: &'d [(String, LetterSet)], + bests: &mut [(u8, Option<&'d str>)], done_letters: LetterSet, length: u8, ) -> u8 { @@ -63,7 +75,7 @@ fn score( return known; } - let Some((word, ls, score)) = dictionary + let Some((word, score)) = dictionary .iter() .filter(|(word, ls)| { (!ls.difference(done_letters).is_empty()) @@ -73,16 +85,15 @@ fn score( let word_len = u8::try_from(word.len()).unwrap(); let new_length = word_len.saturating_add(length); let rest_score = score(dictionary, bests, done_letters.union(*ls), new_length); - (word, ls, word_len.saturating_add(rest_score)) + (word, word_len.saturating_add(rest_score)) }) - .take(2000) - .min_by_key(|(_, _, score)| *score) + .take(1000) + .min_by_key(|(_, score)| *score) else { return u8::MAX; }; bests[done_letters.to_index()].0 = score; - bests[done_letters.to_index()].1 = bests[done_letters.union(*ls).to_index()].1.clone(); - bests[done_letters.to_index()].1.insert(word.clone()); + bests[done_letters.to_index()].1 = Some(word); score }