From 25c4b6617b38fabd06b0485bae3751d070c9f5f9 Mon Sep 17 00:00:00 2001 From: Bas Dado Date: Mon, 19 Dec 2022 15:26:39 +0100 Subject: [PATCH] [TASK] Day 16 part 1 --- src/day16.rs | 138 ++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 114 insertions(+), 24 deletions(-) diff --git a/src/day16.rs b/src/day16.rs index 3260c9d..9c374c5 100644 --- a/src/day16.rs +++ b/src/day16.rs @@ -1,3 +1,4 @@ +use std::collections::HashMap; use crate::day_solver::DaySolver; use super::util; @@ -5,13 +6,14 @@ use super::util; pub struct Day16 { valves: Vec, initial_valve_idx: usize, + valve_names: Vec } impl Day16 { pub fn create() -> Self { - let lines = util::read_file("input/day16_example.txt"); - // let lines = util::read_file("input/day16.txt"); + // let lines = util::read_file("input/day16_example.txt"); + let lines = util::read_file("input/day16.txt"); // We first map all the valve names to indexes for performance reasons: let valve_names = lines.iter() @@ -30,10 +32,15 @@ impl Day16 { id: i, flow_rate, connections } }).collect(), - initial_valve_idx: valve_names.iter().position(|n| n == "AA").unwrap() + initial_valve_idx: valve_names.iter().position(|n| n == "AA").unwrap(), + valve_names } } + fn set_open(valve_idx: &usize, cur_open_valves_mask: &u64) -> u64 { + cur_open_valves_mask.clone() | (1u64 << valve_idx) + } + fn is_open(valve_idx: &usize, open_valves_mask: &u64) -> bool { open_valves_mask & (1u64 << valve_idx) != 0 } @@ -42,28 +49,64 @@ impl Day16 { !Self::is_open(valve_idx, open_valves_mask) } - fn set_open(valve_idx: &usize, cur_open_valves_mask: &u64) -> u64 { - cur_open_valves_mask.clone() | (1u64 << valve_idx) - } - - fn find_best_option(&self, cur_valve_idx: &usize, open_valves_mask: u64, cur_flow_rate: u32, minutes_left: u32) -> u32 { - - if minutes_left == 0 { return 0; } - - let cur_valve = &self.valves.get(*cur_valve_idx).unwrap(); - let mut next_step_options = Vec::new(); - if Self::is_closed(&cur_valve_idx, &open_valves_mask) && cur_valve.flow_rate > 0 { - // One option is to open the valve at the current position - next_step_options.push( - self.find_best_option(cur_valve_idx, Self::set_open(&cur_valve_idx, &open_valves_mask), cur_flow_rate + cur_valve.flow_rate, minutes_left - 1) - ); - } else { - // We try and move to an adjacent valve, see if we can be more useful there - for connection in &cur_valve.connections { - next_step_options.push(self.find_best_option(connection, open_valves_mask, cur_flow_rate, minutes_left - 1)); + fn calc_flow_rate(&self, open_valves_mask: &u64) -> u32 { + let mut res = 0; + for i in 0..self.valves.len() { + if Self::is_open(&i, open_valves_mask) { + res += self.valves[i].flow_rate } } - return cur_flow_rate + next_step_options.iter().max().unwrap(); + 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 } + + if let Some(res) = cache.get(&valve_state) { + return Some(res.clone()); + } + + 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 { + // 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); + best_step_option = Some(ValveStep::OpenValve(valve_state.my_position)); + } + // We try and move to an adjacent valve, see if we can be more useful there + for connection in cur_valve.connections.iter().rev() { + let next_step_result = self.find_best_option(ValveState::new(connection.to_owned(), valve_state.open_valves_mask, valve_state.minutes_left - 1), cur_flow_rate, cache); + if best_step_result_option.is_none() || next_step_result.as_ref().unwrap().total_flow > best_step_result_option.as_ref().unwrap().total_flow { + best_step_result_option = next_step_result; + best_step_option = Some(ValveStep::MoveTo(connection.to_owned())); + } + } + + let Some(best_step) = best_step_option else { panic!("Couldn't find a good step?") }; + + let mut flow_per_step = Vec::with_capacity(valve_state.minutes_left as usize); + flow_per_step.push(cur_flow_rate); + + let mut steps = Vec::with_capacity(valve_state.minutes_left as usize); + steps.push(best_step); + + let mut best_next_flow = 0; + if let Some(best_step_result) = best_step_result_option { + flow_per_step.extend(best_step_result.flow_per_step.iter()); + steps.extend(best_step_result.steps.iter()); + best_next_flow = best_step_result.total_flow; + } + + let step_result = BestOptionResult { + total_flow: best_next_flow + cur_flow_rate, + flow_per_step, + steps, + }; + cache.insert(valve_state, step_result.clone()); + Some(step_result) } } @@ -71,7 +114,28 @@ impl DaySolver for Day16 { fn solve_part1(&mut self) -> String { - self.find_best_option(&self.initial_valve_idx, 0, 0, 10).to_string() + let mut cache= HashMap::new(); + // let example_flows = [0, 0, 0, 20, 20, 20, 33, 33, 33, 33, 54, 54, 54, 54, 54, 54, 54, 54, 76, 76, 76, 76, 79, 79, 79, 81, 81, 81, 81, 81, 81]; + // let mut prev_best: u32 = 0; + // let mut example_running_total = 0; + // for i in 1..31u32 { + // let Some(best) = self.find_best_option(ValveState::new(self.initial_valve_idx, 0, i), 0, &mut cache) else { panic!("Not best!")}; + // let cur_example_flow = example_flows[i as usize]; + // example_running_total += cur_example_flow; + // println!("Best in {} minutes is {}, so I guess the flow is {}, while example flow is {} (example running total {})", i, best.total_flow, best.total_flow - prev_best, cur_example_flow, example_running_total); + // prev_best = best.total_flow; + // } + + 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]); + } + best.total_flow.to_string() } fn solve_part2(&mut self) -> String { @@ -84,4 +148,30 @@ struct Valve { id: usize, flow_rate: u32, connections: Vec +} + +#[derive(Debug, Clone, Hash, Eq, PartialEq)] +struct ValveState { + my_position: usize, + open_valves_mask: u64, + minutes_left: u32 +} + +impl ValveState { + fn new(cur_valve_idx: usize, open_valves_mask: u64, minutes_left: u32) -> Self{ + ValveState { my_position: cur_valve_idx, open_valves_mask, minutes_left } + } +} + +#[derive(Debug, Clone)] +struct BestOptionResult { + steps: Vec, + flow_per_step: Vec, + total_flow: u32 +} + +#[derive(Debug, Clone, Copy)] +enum ValveStep { + OpenValve(usize), + MoveTo(usize) } \ No newline at end of file