diff --git a/src/day12.rs b/src/day12.rs new file mode 100644 index 0000000..504f14d --- /dev/null +++ b/src/day12.rs @@ -0,0 +1,155 @@ +use aoc_runner_derive::{aoc, aoc_generator}; +use itertools::Itertools; +use std::collections::HashMap; +use std::slice; + +#[aoc_generator(day12)] +fn parse(input: &str) -> Vec<(Vec, Vec)> { + let input: Vec<(Vec, Vec)> = input.split('\n') + .map(|line| { + line.split(' ') + .collect_tuple() + .unwrap() + }).map(|(springs, groups)| { + let springs: Vec = springs.chars() + .map(|char| { + char.to_condition() + }).collect(); + + let groups: Vec<_> = groups.split(',') + .map(|num| num.parse::().expect("Failed to parse group len")) + .collect(); + (springs, groups) + }).collect(); + input +} + +fn possible_count_cache(springs: &[Condition], groups: &[usize], cache: &mut HashMap<(usize, usize), usize>) -> usize { + if let Some(count) = cache.get(&(springs.len(), groups.len())) { + return *count; + } + + let mut count = 0; + + if groups.is_empty() { + count = if springs.contains(&Condition::Bad) { 0 } else { 1 }; + cache.insert((springs.len(), groups.len()), count); + + return count; + } + + for i in 0..springs.len() { + if springs[0..i].contains(&Condition::Bad) || i + groups[0] > springs.len() { + break; + } + + if springs[i..i + groups[0]].contains(&Condition::Good) { + continue; + } + + if groups.len() == 1 { + if i + groups[0] == springs.len() { + count += 1; + break; + } else { + count += possible_count_cache(&springs[i + groups[0]..], &[], cache); + continue; + } + } else if i + groups[0] + 1 > springs.len() { + break; + } else if springs[i + groups[0]] == Condition::Bad { + continue; + } + + count += possible_count_cache( + &springs[i + groups[0] + 1..], + &groups[1..], + cache, + ); + } + + cache.insert((springs.len(), groups.len()), count); + + count +} + +fn possible_count(springs: &[Condition], groups: &[usize]) -> usize { + possible_count_cache(springs, groups, &mut HashMap::new()) +} + +#[aoc(day12, part1)] +fn part1(input: &Vec<(Vec, Vec)>) -> usize { + input.iter() + .map(|(springs, groups)| possible_count(springs, groups)) + .sum() +} + +#[aoc(day12, part2)] +fn part2(input: &Vec<(Vec, Vec)>) -> usize { + input.iter() + .map(|(springs, groups)| possible_count( + &[ + &springs[..], + slice::from_ref(&Condition::WhoKnows), + &springs[..], + slice::from_ref(&Condition::WhoKnows), + &springs[..], + slice::from_ref(&Condition::WhoKnows), + &springs[..], + slice::from_ref(&Condition::WhoKnows), + &springs[..], + ].concat(), + &[ + &groups[..], + &groups[..], + &groups[..], + &groups[..], + &groups[..], + ].concat())) + .sum() +} + +#[derive(Debug, PartialEq, Clone)] +enum Condition { + Good, + Bad, + WhoKnows +} + +trait ConditionConvertable { + fn to_condition(self) -> Condition; +} + +impl ConditionConvertable for char { + fn to_condition(self) -> Condition { + match self { + '.' => Condition::Good, + '#' => Condition::Bad, + '?' => Condition::WhoKnows, + _ => panic!("Invalid spring char AHHH") + } + } +} + + +#[cfg(test)] +mod tests { + use super::*; + + const EX: &str = r"???.### 1,1,3 +.??..??...?##. 1,1,3 +?#?#?#?#?#?#?#? 1,3,1,6 +????.#...#... 4,1,1 +????.######..#####. 1,6,5 +?###???????? 3,2,1"; + + #[test] + fn part1_example() { + assert_eq!(part1(&parse(EX)), 21); + } + + #[test] + fn part2_example() { + assert_eq!(part2(&parse(EX)), 525152); + } +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 2122863..b4a08a1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,4 @@ +mod day12; mod day4; mod day2; mod day1;