From d3f89edddfa012f64b1bc60f1347986681aadf99 Mon Sep 17 00:00:00 2001 From: mehbark Date: Tue, 7 Jul 2026 20:24:00 -0400 Subject: [PATCH] doesn't work --- src/main.rs | 129 +++++++++++++++++++++------------------------------- 1 file changed, 53 insertions(+), 76 deletions(-) diff --git a/src/main.rs b/src/main.rs index c6996fc..e01bce7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,6 @@ use std::{ - cmp, - collections::{HashMap, hash_map::Entry}, + array, + collections::HashSet, env, io::{self, BufRead, BufReader}, process, @@ -22,85 +22,62 @@ fn main() { let target = env::args().nth(1).map_or(LetterSet::FULL, LetterSet::from); println!("letter count: {}", target.len()); - let Some((len, path)) = solve(&mut dictionary, target) else { - println!("failed :("); - process::exit(1); - }; - println!("{len}: {}", path.join(" ")); + let (len, path) = solve(&mut dictionary, target); + println!("{len}: {}", path.into_iter().collect::>().join(" ")); } -fn solve(dictionary: &mut [(String, LetterSet)], target: LetterSet) -> Option<(u16, Vec)> { - let mut bests = HashMap::new(); - bests.insert(LetterSet::EMPTY, (0, Vec::new())); +const MAX_SOLUTION_LENGTH: u8 = 50; - // WORDS WITH DUPES MUST BE AT THE END! - dictionary.sort_by_key(|(word, ls)| { - let dupes = word.len() - usize::from(ls.len()); - (dupes, cmp::Reverse(ls.len())) - }); +fn solve(dictionary: &mut [(String, LetterSet)], target: LetterSet) -> (u8, HashSet) { + let mut bests = (0..=LetterSet::FULL.to_index()) + .map(|_| (u8::MAX, HashSet::new())) + .collect::>(); + println!("allocated!"); + bests[LetterSet::FULL.to_index()] = (0, HashSet::new()); - for (word, ls) in dictionary.iter() { - let len = u16::try_from(word.len()).unwrap(); - match bests.entry(*ls) { - Entry::Occupied(mut occupied) => { - if occupied.get().0 > len { - occupied.insert((len, vec![word.clone()])); - } - } - Entry::Vacant(vacant) => { - vacant.insert((len, vec![word.clone()])); - } + score(dictionary, &mut bests, LetterSet::EMPTY, 0); + + bests[target.negation().to_index()].clone() +} + +// TODO: perhaps bottom up? (pick or no pick) (real potential!) (not obvious how to do path) +// the problem is that i'm not *improving* scores, i use the best one immediately +// 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)], + done_letters: LetterSet, + length: u8, +) -> u8 { + assert!(length <= MAX_SOLUTION_LENGTH); + + let known = bests[done_letters.to_index()].0; + if known < u8::MAX { + return known; + } + + for (i, (word, ls)) in dictionary + .iter() + .enumerate() + .filter(|(_, (_, ls))| !ls.difference(done_letters).is_empty()) + { + let keep_letters = done_letters.union(*ls); + let keep_length = length.saturating_add(u8::try_from(word.len()).unwrap()); + + if keep_length > MAX_SOLUTION_LENGTH { + continue; + } + + let keep = score(dictionary, bests, keep_letters, keep_length); + + let existing = bests[done_letters.to_index()].0; + if keep < existing { + bests[done_letters.to_index()].0 = keep; + bests[done_letters.to_index()].1.insert(word.clone()); } } - score(dictionary, &mut bests, target); - - bests.get(&target).cloned() -} - -// TODO: perhaps bottom up? (pick or no pick) (real potential!) -// the problem is that i'm not *improving* scores, i use the best one immediately -// length doesn't include spaces. i think that's okay -// assumes that `bests[LetterSet::EMPTY] == (0, Vec::new())` -fn score( - dictionary: &[(String, LetterSet)], - bests: &mut HashMap)>, - remaining: LetterSet, -) -> u16 { - if let Some(min) = bests.get(&remaining) { - return min.0; - } - - let Some((word, ls, score)) = dictionary - .iter() - .filter(|(_, ls)| { - ls.intersection(remaining.negation()).is_empty() - || !remaining.intersection(*ls).is_empty() - //&& ls.intersection(remaining.negation()).len() <= 1 - }) - .map(|(word, ls)| { - let rest_score = score(dictionary, bests, remaining.difference(*ls)); - ( - word, - ls, - (u16::try_from(word.len()).unwrap()).saturating_add(rest_score), - ) - }) - .min_by_key(|(_, _, score)| *score) - else { - return u16::MAX; - }; - - if score == u16::MAX { - return score; - } - - let remaining_after_word = remaining.difference(*ls); - let (_score, path) = bests - .get(&remaining_after_word) - .expect("we already inserted this"); - let mut path = path.clone(); - path.push(word.clone()); - bests.insert(remaining, (score, path)); - score + bests[done_letters.to_index()].0 }