[TASK] Day 16 part 1

This commit is contained in:
2022-12-19 15:26:39 +01:00
parent 8345fa1c24
commit 25c4b6617b

View File

@@ -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<Valve>,
initial_valve_idx: usize,
valve_names: Vec<String>
}
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<ValveState, BestOptionResult>) -> Option<BestOptionResult> {
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<ValveStep> = None;
let mut best_step_result_option: Option<BestOptionResult> = 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<usize>
}
#[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<ValveStep>,
flow_per_step: Vec<u32>,
total_flow: u32
}
#[derive(Debug, Clone, Copy)]
enum ValveStep {
OpenValve(usize),
MoveTo(usize)
}