diff --git a/src/main.rs b/src/main.rs index c8a029a..c6996fc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,9 @@ use std::{ - collections::HashMap, + cmp, + collections::{HashMap, hash_map::Entry}, env, io::{self, BufRead, BufReader}, + process, }; use crate::letterset::LetterSet; @@ -9,7 +11,7 @@ use crate::letterset::LetterSet; mod letterset; fn main() { - let dictionary: Vec<_> = BufReader::new(io::stdin().lock()) + let mut dictionary: Vec<_> = BufReader::new(io::stdin().lock()) .lines() .map(|line| { let word = line.unwrap(); @@ -18,24 +20,46 @@ fn main() { }) .collect(); - println!( - "{:#?}", - solve( - &dictionary, - env::args().nth(1).map_or(LetterSet::FULL, LetterSet::from) - ) - ); + 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(" ")); } -fn solve(dictionary: &[(String, LetterSet)], target: LetterSet) -> Option<(u16, Vec)> { +fn solve(dictionary: &mut [(String, LetterSet)], target: LetterSet) -> Option<(u16, Vec)> { let mut bests = HashMap::new(); bests.insert(LetterSet::EMPTY, (0, Vec::new())); + // 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())) + }); + + 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, 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( @@ -49,16 +73,17 @@ fn score( let Some((word, ls, score)) = dictionary .iter() - .filter(|(_, ls)| !remaining.intersection(*ls).is_empty()) + .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(score( - dictionary, - bests, - remaining.difference(*ls), - )), + (u16::try_from(word.len()).unwrap()).saturating_add(rest_score), ) }) .min_by_key(|(_, _, score)| *score)