This commit is contained in:
kageru 2023-12-12 16:39:07 +01:00
parent eb5e58d4a1
commit ff7cfe5768

View File

@ -30,80 +30,69 @@ fn part1(lines: &Parsed) -> usize {
.iter() .iter()
.map(|(springs, expected)| { .map(|(springs, expected)| {
let mut cache = FnvHashMap::default(); let mut cache = FnvHashMap::default();
valid_combinations_recursive(&springs, &expected, 0, 0, 0, 0, false, &mut cache) valid_combinations(&springs, &expected, CallState::default(), &mut cache)
}) })
.sum() .sum()
} }
fn valid_combinations_recursive( #[derive(Debug, PartialEq, Eq, Hash, Default, Clone, Copy)]
springs: &[Spring], struct CallState {
constraints: &[usize], index: usize,
index: usize, broken_count: usize,
broken_count: usize, current_constraint: usize,
current_constraint: usize, broken_streak: usize,
broken_streak: usize,
just_completed_streak: bool, just_completed_streak: bool,
cache: &mut FnvHashMap<(usize, usize, usize, usize, bool), usize>, }
) -> usize {
if let Some(&cached) = cache.get(&(index, broken_count, current_constraint, broken_streak, just_completed_streak)) { impl CallState {
return cached; fn new(index: usize, broken_count: usize, current_constraint: usize, broken_streak: usize, just_completed_streak: bool) -> Self {
} Self { index, broken_count, current_constraint, broken_streak, just_completed_streak }
if index == springs.len() {
let valid = broken_count == constraints.iter().sum::<usize>()
&& current_constraint == constraints.len()
&& (broken_streak == 0 || just_completed_streak);
return valid as usize;
} }
let valid = match springs[index] { fn operational(self) -> Self {
Spring::Operational => { CallState::new(self.index + 1, self.broken_count, self.current_constraint, 0, false)
valid_combinations_recursive(springs, constraints, index + 1, broken_count, current_constraint, 0, false, cache) }
} }
Spring::Broken => {
if current_constraint < constraints.len() fn add_broken(springs: &[Spring], constraints: &[usize], state: CallState, cache: &mut FnvHashMap<CallState, usize>) -> Option<usize> {
&& broken_count < constraints.iter().take(current_constraint + 1).sum() (state.current_constraint < constraints.len()
&& !just_completed_streak && state.broken_count < constraints.iter().take(state.current_constraint + 1).sum()
{ && !state.just_completed_streak)
let just_completed_streak = broken_streak + 1 == constraints[current_constraint]; .then(|| {
valid_combinations_recursive( let just_completed_streak = state.broken_streak + 1 == constraints[state.current_constraint];
springs, valid_combinations(
constraints, springs,
index + 1, constraints,
broken_count + 1, CallState::new(
current_constraint + just_completed_streak as usize, state.index + 1,
broken_streak + 1, state.broken_count + 1,
state.current_constraint + just_completed_streak as usize,
state.broken_streak + 1,
just_completed_streak, just_completed_streak,
cache, ),
) cache,
} else { )
0 })
} }
}
fn valid_combinations(springs: &[Spring], constraints: &[usize], state: CallState, cache: &mut FnvHashMap<CallState, usize>) -> usize {
if let Some(&cached) = cache.get(&state) {
return cached;
}
if state.index == springs.len() {
return (state.broken_count == constraints.iter().sum::<usize>() && state.current_constraint == constraints.len()) as _;
}
let valid = match springs[state.index] {
Spring::Operational => valid_combinations(springs, constraints, state.operational(), cache),
Spring::Broken => add_broken(springs, constraints, state, cache).unwrap_or(0),
Spring::Unknown => { Spring::Unknown => {
let operational = let operational = valid_combinations(springs, constraints, state.operational(), cache);
valid_combinations_recursive(springs, constraints, index + 1, broken_count, current_constraint, 0, false, cache); let broken = add_broken(springs, constraints, state, cache).unwrap_or(0);
let broken = if current_constraint < constraints.len()
&& broken_count < constraints.iter().take(current_constraint + 1).sum()
&& !just_completed_streak
{
let just_completed_streak = broken_streak + 1 == constraints[current_constraint];
valid_combinations_recursive(
springs,
constraints,
index + 1,
broken_count + 1,
current_constraint + just_completed_streak as usize,
broken_streak + 1,
just_completed_streak,
cache,
)
} else {
0
};
operational + broken operational + broken
} }
}; };
cache.insert((index, broken_count, current_constraint, broken_streak, just_completed_streak), valid); cache.insert(state, valid);
valid valid
} }
@ -111,6 +100,7 @@ fn part2(lines: &Parsed) -> usize {
lines lines
.iter() .iter()
.map(|(springs, expected)| { .map(|(springs, expected)| {
// this seems cursed. is there a join() for iterators?
let springs = springs.to_vec(); let springs = springs.to_vec();
let new_spring_length = springs.len() * 5 + 4; let new_spring_length = springs.len() * 5 + 4;
( (
@ -120,7 +110,7 @@ fn part2(lines: &Parsed) -> usize {
}) })
.map(|(springs, expected)| { .map(|(springs, expected)| {
let mut cache = FnvHashMap::default(); let mut cache = FnvHashMap::default();
valid_combinations_recursive(&springs, &expected, 0, 0, 0, 0, false, &mut cache) valid_combinations(&springs, &expected, CallState::default(), &mut cache)
}) })
.sum() .sum()
} }