diff --git a/src/solutions/day_10.rs b/src/solutions/day_10.rs index f9aaa4c..7ea3286 100644 --- a/src/solutions/day_10.rs +++ b/src/solutions/day_10.rs @@ -90,6 +90,7 @@ impl Debug for Button { struct Machine { required_state: Lights, buttons: ArrayVec, + light_button_map: LightToButtonMap, #[allow(unused)] joltage_reqs: Vec, } @@ -103,7 +104,9 @@ impl Machine { let buttons = tokens .peeking_take_while(|t| t.starts_with('(')) .map(Button::parse) - .collect(); + .collect::>(); + + let light_button_map = LightToButtonMap::from_buttons(required_state.0.len(), &buttons); let joltage_reqs = tokens .next() @@ -119,25 +122,162 @@ impl Machine { Self { required_state, buttons, + light_button_map, joltage_reqs, } } } impl Machine { - fn shortest_sequence(&self) -> usize { - for button_set in (0..self.buttons.len()).flat_map(|k| self.buttons.iter().combinations(k)) + fn smallest_button_set(&self) -> usize { + let state = AlgState::new(self.required_state.0.len(), &self.buttons); + self.smallest_button_set_from_light(0, state).unwrap() + } + + fn smallest_button_set_from_light(&self, light: usize, state: AlgState) -> Option { + let matches_required_state = state.lights.0[light] == self.required_state.0[light]; + let possible_buttons = self.valid_buttons_for_light(light, &state.buttons.0); + + let mut smallest_found = None; + + let set_sizes = if matches_required_state { + (0..=possible_buttons.len()).step_by(2) + } else { + (1..=possible_buttons.len()).step_by(2) + }; + for pressed_button_set in + set_sizes.flat_map(|k| possible_buttons.iter().copied().combinations(k)) { - let mut lights = Lights::new(self.required_state.0.len()); - for button in &button_set { - lights.push_button(button); + // pressing more buttons won't do anything + if smallest_found.is_some_and(|smallest_found| { + state.num_pressed + pressed_button_set.len() >= smallest_found + }) { + break; + } + + let mut new_state = state.clone(); + + for button_index in pressed_button_set.iter().copied() { + let button = &self.buttons[button_index]; + new_state.press_button(button_index, button); } - if lights == self.required_state { - return button_set.len(); + + if let Some(count) = new_state.is_solution(&self.required_state) { + if count == 1 { + return Some(1); + } + + smallest_found = smallest_found.map(|s| s.min(count)).or(Some(count)); + + continue; + } + + if light >= self.required_state.0.len() { + continue; + } + + new_state.num_pressed += pressed_button_set.len(); + for excluded_button_index in possible_buttons + .iter() + .filter(|b| !pressed_button_set.contains(b)) + { + new_state.buttons.0[*excluded_button_index] = ButtonState::Unpressed; + } + + // check the next light with the new state + if let Some(count) = self.smallest_button_set_from_light(light + 1, new_state) { + smallest_found = smallest_found.map(|s| s.min(count)).or(Some(count)); } } - panic!("no solution found"); + smallest_found + } + + fn valid_buttons_for_light(&self, light: usize, button_states: &[ButtonState]) -> Vec { + self.light_button_map + .buttons_for_light(light) + .iter() + .copied() + .filter(|b| button_states[*b] == ButtonState::Unknown) + .collect() + } +} + +#[derive(Clone, Debug)] +struct AlgState { + lights: Lights, + buttons: ButtonList, + num_pressed: usize, +} + +impl AlgState { + fn new(light_count: usize, buttons: &[Button]) -> Self { + let lights = Lights::new(light_count); + let buttons = ButtonList::new(buttons.len()); + + Self { + lights, + buttons, + num_pressed: 0, + } + } + + fn press_button(&mut self, idx: usize, button: &Button) { + self.lights.push_button(button); + self.buttons.0[idx] = ButtonState::Pressed; + } + + fn is_solution(&self, required_state: &Lights) -> Option { + if self.lights == *required_state { + Some( + self.buttons + .0 + .iter() + .filter(|b| **b == ButtonState::Pressed) + .count(), + ) + } else { + None + } + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)] +enum ButtonState { + Pressed, + Unpressed, + #[default] + Unknown, +} + +#[derive(Clone, Debug)] +struct ButtonList(ArrayVec); + +impl ButtonList { + fn new(button_count: usize) -> Self { + let mut state = ArrayVec::new(); + state.extend(std::iter::repeat_n(ButtonState::default(), button_count)); + Self(state) + } +} + +#[derive(Clone, Debug)] +struct LightToButtonMap(ArrayVec, MAX_LIGHTS>); + +impl LightToButtonMap { + fn from_buttons(light_count: usize, buttons: &[Button]) -> Self { + let mut map = ArrayVec::new(); + map.extend(std::iter::repeat_n(ArrayVec::default(), light_count)); + + for (i, b) in buttons.iter().enumerate() { + map[b.0[0]].push(i); + } + + Self(map) + } + + fn buttons_for_light(&self, light: usize) -> &[usize] { + &self.0[light] } } @@ -155,7 +295,7 @@ impl Solution for Day10 { fn part1(&self) -> String { self.machines .iter() - .map(Machine::shortest_sequence) + .map(Machine::smallest_button_set) .sum::() .to_string() }