First attempt at day 16
This commit is contained in:
		
							
								
								
									
										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 day14;
 | 
			
		||||
pub mod day15;
 | 
			
		||||
pub mod day16;
 | 
			
		||||
pub mod day2;
 | 
			
		||||
pub mod day3;
 | 
			
		||||
pub mod day4;
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user