From 46d409fa922d2454490d911ce697341b544b733f Mon Sep 17 00:00:00 2001 From: jazzpi Date: Tue, 20 Dec 2022 00:44:56 +0100 Subject: [PATCH] First attempt at day 16 --- src/bin/d16p1.rs | 58 ++++++++++++++ src/day16.rs | 199 +++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + 3 files changed, 258 insertions(+) create mode 100644 src/bin/d16p1.rs create mode 100644 src/day16.rs diff --git a/src/bin/d16p1.rs b/src/bin/d16p1.rs new file mode 100644 index 0000000..a5d72ba --- /dev/null +++ b/src/bin/d16p1.rs @@ -0,0 +1,58 @@ +use std::{ + sync::{ + atomic::{AtomicUsize, Ordering}, + Arc, Mutex, + }, + thread, +}; + +use aoc22::{day16, util}; + +pub fn main() { + let valves = day16::parse_valves(&util::parse_input()); + let dists = day16::calc_dists(&valves); + let state = day16::State::new(Arc::new(valves), Arc::new(dists)); + + let possible_states = Arc::new(Mutex::new(Vec::new())); + possible_states.lock().unwrap().push(state); + + let lower_bound = Arc::new(AtomicUsize::new(0)); + + let mut handles = Vec::new(); + + for _ in 0..16 { + let s = possible_states.clone(); + let l = lower_bound.clone(); + handles.push(thread::spawn(move || check_states(s, l))); + } + + for handle in handles { + handle.join().unwrap(); + } + + println!( + "Most pressure released is {}", + lower_bound.load(Ordering::Relaxed) + ); +} + +pub fn check_states(possible_states: Arc>>, lower_bound: Arc) { + loop { + let state = { possible_states.lock().unwrap().pop() }; + if state.is_none() { + break; + } + let state = state.unwrap(); + if state.finished() { + let score = state.lower_bound(); + lower_bound.fetch_max(score, Ordering::Relaxed); + } else { + let x = state.possible_actions(); + for action in x { + if action.upper_bound() > lower_bound.load(Ordering::Relaxed) { + possible_states.lock().unwrap().push(action); + } + } + } + } +} diff --git a/src/day16.rs b/src/day16.rs new file mode 100644 index 0000000..b13bf3f --- /dev/null +++ b/src/day16.rs @@ -0,0 +1,199 @@ +use std::{ + cmp::Ordering, + collections::{HashMap, HashSet, VecDeque}, + sync::Arc, +}; + +use itertools::Itertools; +use regex::Regex; + +const TOTAL_TIME: usize = 30; + +#[derive(Debug)] +pub struct Valve { + pub name: String, + pub flow_rate: usize, + pub tunnels: Vec, +} + +pub fn parse_valves(input: &String) -> HashMap { + let mut valves = HashMap::new(); + + let re = + Regex::new(r"^Valve (\w+) has flow rate=(\d+); tunnels? leads? to valves? (.*)$").unwrap(); + for line in input.lines() { + let captures = re + .captures(line) + .expect(&format!("No captures for {}", line)); + let name = captures.get(1).unwrap().as_str().to_owned(); + let flow_rate = captures.get(2).unwrap().as_str().parse().unwrap(); + let tunnels = captures + .get(3) + .unwrap() + .as_str() + .split(", ") + .map(|s| s.to_owned()) + .collect(); + let valve = Valve { + name: name.clone(), + flow_rate, + tunnels, + }; + valves.insert(name, valve); + } + + valves +} + +pub fn calc_dists(valves: &HashMap) -> HashMap<(String, String), usize> { + let mut result = HashMap::new(); + let dists_orig: HashMap = valves.keys().map(|name| (name.clone(), 0)).collect(); + + for name in valves.keys() { + let mut dists = dists_orig.clone(); + let mut next = VecDeque::new(); + next.push_back(name); + while let Some(n) = next.pop_front() { + let d_next = dists[n] + 1; + for tunnel in &valves[n].tunnels { + let d_tunnel = dists.get_mut(tunnel).unwrap(); + if *d_tunnel > d_next { + *d_tunnel = d_next; + next.push_back(tunnel); + } + } + } + + for n in valves.keys() { + result.insert((name.clone(), n.clone()), dists[n]); + } + } + + result +} + +#[derive(Debug, Clone)] +pub struct State { + pub valves: Arc>, + pub dists: Arc>, + pub opened: HashMap, + pub interesting: HashSet, + pub location: String, + pub flow_rate: usize, + pub time_remaining: usize, + pub pressure_released: usize, +} + +impl State { + pub fn new( + valves: Arc>, + dists: Arc>, + ) -> State { + let opened = valves.iter().map(|(k, _)| (k.clone(), false)).collect(); + + let interesting = valves + .iter() + .filter_map(|(name, valve)| { + if valve.flow_rate > 0 { + Some(name.clone()) + } else { + None + } + }) + .collect(); + + State { + valves, + dists, + opened, + interesting, + location: "AA".to_owned(), + flow_rate: 0, + time_remaining: TOTAL_TIME, + pressure_released: 0, + } + } + + pub fn finished(&self) -> bool { + self.time_remaining == 0 || self.interesting.is_empty() + } + + pub fn lower_bound(&self) -> usize { + self.pressure_released + self.flow_rate * self.time_remaining + } + + pub fn upper_bound(&self) -> usize { + let additional_flow: isize = self + .opened + .iter() + .filter_map(|(k, opened)| { + if *opened { + None + } else { + let dist = self.dists[&(self.location.clone(), k.clone())]; + if dist + 1 > self.time_remaining { + None + } else { + let flow = self.valves[k].flow_rate; + Some(-((flow * (self.time_remaining - dist - 1)) as isize)) + } + } + }) + // .sorted() + // .rev() + // .take(self.time_remaining) + .sum(); + let additional_flow = (-additional_flow) as usize; + + self.lower_bound() + additional_flow + } + + pub fn possible_actions(&self) -> Vec { + assert!(!self.finished()); + + let mut result = Vec::new(); + + let loc = &self.location; + + if !self.opened[loc] { + let mut open = self.clone(); + *open.opened.get_mut(loc).unwrap() = true; + // First increase pressure released, then the flow rate + open.pressure_released += open.flow_rate; + open.flow_rate += self.valves[loc].flow_rate; + open.time_remaining -= 1; + open.interesting.remove(loc); + result.push(open); + } + + for tunnel in &self.valves[loc].tunnels { + let mut moved = self.clone(); + moved.location = tunnel.clone(); + moved.pressure_released += moved.flow_rate; + moved.time_remaining -= 1; + result.push(moved); + } + + result + } +} + +impl Ord for State { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.upper_bound().cmp(&other.upper_bound()) + } +} + +impl PartialOrd for State { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl PartialEq for State { + fn eq(&self, other: &Self) -> bool { + self.cmp(other) == Ordering::Equal + } +} + +impl Eq for State {} diff --git a/src/lib.rs b/src/lib.rs index 0a502f3..f75f5f3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,6 +5,7 @@ pub mod day12; pub mod day13; pub mod day14; pub mod day15; +pub mod day16; pub mod day2; pub mod day3; pub mod day4;