diff --git a/.run/run-day-16.run.xml b/.run/run-day-16.run.xml new file mode 100644 index 0000000..74145d1 --- /dev/null +++ b/.run/run-day-16.run.xml @@ -0,0 +1,19 @@ + + + + \ No newline at end of file diff --git a/src/day16.rs b/src/day16.rs index 9c374c5..f823466 100644 --- a/src/day16.rs +++ b/src/day16.rs @@ -1,4 +1,5 @@ use std::collections::HashMap; +use crate::day16::ValveStep::{MoveTo, OpenValve}; use crate::day_solver::DaySolver; use super::util; @@ -59,6 +60,104 @@ impl Day16 { res } + fn should_valve_be_opened(&self, valve_idx: &usize, open_valves_mask: &u64) -> bool { + if Self::is_open(valve_idx, open_valves_mask) { false } + else if let Some(valve) = self.valves.get(*valve_idx) { + valve.flow_rate > 0 + } else { false } + } + + fn find_best_option_pt2(&self, valve_state: ValveStatePt2, cur_flow_rate: u32, cache: &mut HashMap) -> BestOptionPt2 { + + if valve_state.minutes_left == 0 { + return BestOptionPt2 { + // my_step: ValveStep::None, + // elephant_step: ValveStep::None, + total_flow: 0, + // cur_flow: cur_flow_rate + } + } + + if let Some(res) = cache.get(&valve_state) { + return res.to_owned(); + } + + let mut my_options = Vec::new(); + let mut elephant_options = Vec::new(); + + if self.should_valve_be_opened(&valve_state.my_position, &valve_state.open_valves_mask) { + // One option is to open the valve at the current position + my_options.push(OpenValve(valve_state.my_position)); + } + + if valve_state.my_position != valve_state.elephant_position && self.should_valve_be_opened(&valve_state.elephant_position, &valve_state.open_valves_mask) { + elephant_options.push(OpenValve(valve_state.elephant_position)); + } + + // We try and move to an adjacent valve, see if we can be more useful there + let my_valve = &self.valves[valve_state.my_position]; + for connection in my_valve.connections.iter() { + my_options.push(MoveTo(connection.to_owned())) + } + + let elephant_valve = &self.valves[valve_state.elephant_position]; + for connection in elephant_valve.connections.iter() { + elephant_options.push(MoveTo(connection.to_owned())) + } + + // Now we try all combinations of my and elephants options: + let mut best: u32 = 0; + let mut my_best_step = None; + let mut elephant_best_step = None; + let next_minutes_left = valve_state.minutes_left - 1; + for my_option in my_options { + + let mut my_next_position = valve_state.my_position; + let mut next_valve_mask_for_me = valve_state.open_valves_mask; + let mut next_flow_rate_for_me = cur_flow_rate; + + match my_option { + MoveTo(idx) => my_next_position = idx, + OpenValve(idx) => { + next_valve_mask_for_me = Self::set_open(&idx, &next_valve_mask_for_me); + next_flow_rate_for_me += self.valves[idx].flow_rate + } + _ => {} + } + for elephant_option in &elephant_options { + + let mut next_valve_mask = next_valve_mask_for_me; + let mut next_flow_rate = next_flow_rate_for_me; + let mut elephant_next_position = valve_state.elephant_position; + + match elephant_option { + MoveTo(idx) => elephant_next_position = *idx, + OpenValve(idx) => { + next_valve_mask = Self::set_open(&idx, &next_valve_mask); + next_flow_rate += self.valves[*idx].flow_rate + } + _ => {} + } + + let res = self.find_best_option_pt2(ValveStatePt2::new(my_next_position, elephant_next_position, next_valve_mask, next_minutes_left), next_flow_rate, cache); + if best == 0 || res.total_flow > best { + best = res.total_flow; + my_best_step = Some(my_option); + elephant_best_step = Some(elephant_option.to_owned()); + } + } + } + + let res = BestOptionPt2 { + // my_step: my_best_step.unwrap(), + // elephant_step: elephant_best_step.unwrap(), + // cur_flow: cur_flow_rate, + total_flow: best + cur_flow_rate + }; + cache.insert(valve_state, res.to_owned()); + res + } + fn find_best_option(&self, valve_state: ValveState, cur_flow_rate: u32, cache: &mut HashMap) -> Option { if valve_state.minutes_left == 0 { return None } @@ -70,7 +169,7 @@ impl Day16 { let cur_valve = &self.valves.get(valve_state.my_position).unwrap(); let mut best_step_option: Option = None; let mut best_step_result_option: Option = None; - if Self::is_closed(&valve_state.my_position, &valve_state.open_valves_mask) && cur_valve.flow_rate > 0 { + if self.should_valve_be_opened(&valve_state.my_position, &valve_state.open_valves_mask) { // One option is to open the valve at the current position let new_open_valves_mask = Self::set_open(&valve_state.my_position, &valve_state.open_valves_mask); best_step_result_option = self.find_best_option(ValveState::new(valve_state.my_position, new_open_valves_mask, valve_state.minutes_left - 1), cur_flow_rate + cur_valve.flow_rate, cache); @@ -127,19 +226,23 @@ impl DaySolver for Day16 { // } let best = self.find_best_option(ValveState::new(self.initial_valve_idx, 0, 30), 0, &mut cache).unwrap(); - for i in 0..best.steps.len() { - println!("Step {}", i + 1); - match best.steps[i] { - ValveStep::OpenValve(v) => println!("Opening valve {}", self.valve_names[v]), - ValveStep::MoveTo(v ) => println!("Moving to valve {}", self.valve_names[v]) - } - println!("The flow is now {}\n", best.flow_per_step[i]); - } + // for i in 0..best.steps.len() { + // println!("Step {}", i + 1); + // match best.steps[i] { + // ValveStep::OpenValve(v) => println!("Opening valve {}", self.valve_names[v]), + // ValveStep::MoveTo(v ) => println!("Moving to valve {}", self.valve_names[v]) + // } + // println!("The flow is now {}\n", best.flow_per_step[i]); + // } best.total_flow.to_string() } fn solve_part2(&mut self) -> String { - return 0.to_string(); + + let mut cache= HashMap::new(); + let best = self.find_best_option_pt2(ValveStatePt2::new(self.initial_valve_idx, self.initial_valve_idx, 0, 26), 0, &mut cache); + + return best.total_flow.to_string(); } } @@ -163,6 +266,28 @@ impl ValveState { } } +#[derive(Debug, Clone, Hash, Eq, PartialEq)] +struct ValveStatePt2 { + my_position: usize, + elephant_position: usize, + open_valves_mask: u64, + minutes_left: u32 +} + +#[derive(Debug, Clone, Copy)] +struct BestOptionPt2 { + // my_step: ValveStep, + // elephant_step: ValveStep, + total_flow: u32, + // cur_flow: u32 +} + +impl ValveStatePt2 { + fn new(my_position: usize, elephant_position: usize, open_valves_mask: u64, minutes_left: u32) -> Self{ + ValveStatePt2 { my_position, elephant_position, open_valves_mask, minutes_left } + } +} + #[derive(Debug, Clone)] struct BestOptionResult { steps: Vec, @@ -170,8 +295,9 @@ struct BestOptionResult { total_flow: u32 } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq)] enum ValveStep { OpenValve(usize), - MoveTo(usize) + MoveTo(usize), + None } \ No newline at end of file