First attempt at day 16
This commit is contained in:
parent
0d30968d3e
commit
46d409fa92
58
src/bin/d16p1.rs
Normal file
58
src/bin/d16p1.rs
Normal file
@ -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<Mutex<Vec<day16::State>>>, lower_bound: Arc<AtomicUsize>) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
199
src/day16.rs
Normal file
199
src/day16.rs
Normal file
@ -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<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_valves(input: &String) -> HashMap<String, Valve> {
|
||||||
|
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<String, Valve>) -> HashMap<(String, String), usize> {
|
||||||
|
let mut result = HashMap::new();
|
||||||
|
let dists_orig: HashMap<String, usize> = 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<HashMap<String, Valve>>,
|
||||||
|
pub dists: Arc<HashMap<(String, String), usize>>,
|
||||||
|
pub opened: HashMap<String, bool>,
|
||||||
|
pub interesting: HashSet<String>,
|
||||||
|
pub location: String,
|
||||||
|
pub flow_rate: usize,
|
||||||
|
pub time_remaining: usize,
|
||||||
|
pub pressure_released: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl State {
|
||||||
|
pub fn new(
|
||||||
|
valves: Arc<HashMap<String, Valve>>,
|
||||||
|
dists: Arc<HashMap<(String, String), usize>>,
|
||||||
|
) -> 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<State> {
|
||||||
|
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<std::cmp::Ordering> {
|
||||||
|
Some(self.cmp(other))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for State {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.cmp(other) == Ordering::Equal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for State {}
|
@ -5,6 +5,7 @@ pub mod day12;
|
|||||||
pub mod day13;
|
pub mod day13;
|
||||||
pub mod day14;
|
pub mod day14;
|
||||||
pub mod day15;
|
pub mod day15;
|
||||||
|
pub mod day16;
|
||||||
pub mod day2;
|
pub mod day2;
|
||||||
pub mod day3;
|
pub mod day3;
|
||||||
pub mod day4;
|
pub mod day4;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user