day 16 done
This commit is contained in:
8
Cargo.lock
generated
8
Cargo.lock
generated
@@ -54,6 +54,14 @@ dependencies = [
|
||||
"nom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "day16"
|
||||
version = "2022.0.0"
|
||||
dependencies = [
|
||||
"itertools",
|
||||
"nom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "day2"
|
||||
version = "2022.0.0"
|
||||
|
||||
@@ -15,6 +15,7 @@ members = [
|
||||
"day13",
|
||||
"day14",
|
||||
"day15",
|
||||
"day16",
|
||||
]
|
||||
resolver = "2"
|
||||
|
||||
|
||||
12
day16/Cargo.toml
Normal file
12
day16/Cargo.toml
Normal file
@@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "day16"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
nom.workspace = true
|
||||
itertools.workspace = true
|
||||
57
day16/input.txt
Normal file
57
day16/input.txt
Normal file
@@ -0,0 +1,57 @@
|
||||
Valve RU has flow rate=0; tunnels lead to valves YH, ID
|
||||
Valve QK has flow rate=24; tunnels lead to valves PQ, PP
|
||||
Valve RP has flow rate=11; tunnels lead to valves RM, BA, RI, EM
|
||||
Valve BX has flow rate=0; tunnels lead to valves ZX, VK
|
||||
Valve JL has flow rate=0; tunnels lead to valves ID, LC
|
||||
Valve DC has flow rate=25; tunnel leads to valve ST
|
||||
Valve HX has flow rate=0; tunnels lead to valves DH, FE
|
||||
Valve KJ has flow rate=0; tunnels lead to valves ZK, XN
|
||||
Valve EM has flow rate=0; tunnels lead to valves AW, RP
|
||||
Valve XN has flow rate=7; tunnels lead to valves LH, KJ, KU, AO
|
||||
Valve DH has flow rate=9; tunnels lead to valves SY, CC, QL, LH, HX
|
||||
Valve LH has flow rate=0; tunnels lead to valves XN, DH
|
||||
Valve PP has flow rate=0; tunnels lead to valves QK, TA
|
||||
Valve AO has flow rate=0; tunnels lead to valves AA, XN
|
||||
Valve SY has flow rate=0; tunnels lead to valves DH, AA
|
||||
Valve MZ has flow rate=0; tunnels lead to valves JT, PF
|
||||
Valve AA has flow rate=0; tunnels lead to valves JN, UN, WG, SY, AO
|
||||
Valve RM has flow rate=0; tunnels lead to valves XL, RP
|
||||
Valve BA has flow rate=0; tunnels lead to valves RP, YP
|
||||
Valve AD has flow rate=12; tunnels lead to valves LK, ZX, AW
|
||||
Valve ZN has flow rate=0; tunnels lead to valves EQ, HL
|
||||
Valve EX has flow rate=18; tunnel leads to valve RB
|
||||
Valve CR has flow rate=0; tunnels lead to valves TA, ST
|
||||
Valve WG has flow rate=0; tunnels lead to valves AA, TA
|
||||
Valve UN has flow rate=0; tunnels lead to valves WK, AA
|
||||
Valve VE has flow rate=0; tunnels lead to valves JA, KW
|
||||
Valve JA has flow rate=19; tunnels lead to valves PQ, VE
|
||||
Valve AW has flow rate=0; tunnels lead to valves AD, EM
|
||||
Valve XL has flow rate=0; tunnels lead to valves RM, PF
|
||||
Valve OD has flow rate=0; tunnels lead to valves VK, RI
|
||||
Valve FE has flow rate=0; tunnels lead to valves JT, HX
|
||||
Valve PQ has flow rate=0; tunnels lead to valves JA, QK
|
||||
Valve RB has flow rate=0; tunnels lead to valves CC, EX
|
||||
Valve JT has flow rate=3; tunnels lead to valves RF, MZ, ZK, FE, DD
|
||||
Valve YP has flow rate=0; tunnels lead to valves ID, BA
|
||||
Valve ID has flow rate=14; tunnels lead to valves JL, RU, YP
|
||||
Valve YH has flow rate=0; tunnels lead to valves RU, VK
|
||||
Valve TA has flow rate=21; tunnels lead to valves WG, KU, PP, RF, CR
|
||||
Valve LK has flow rate=0; tunnels lead to valves PF, AD
|
||||
Valve DD has flow rate=0; tunnels lead to valves JN, JT
|
||||
Valve HL has flow rate=0; tunnels lead to valves ZN, DW
|
||||
Valve VK has flow rate=22; tunnels lead to valves OD, KW, BX, YH
|
||||
Valve RF has flow rate=0; tunnels lead to valves JT, TA
|
||||
Valve CC has flow rate=0; tunnels lead to valves RB, DH
|
||||
Valve KW has flow rate=0; tunnels lead to valves VE, VK
|
||||
Valve PF has flow rate=10; tunnels lead to valves WK, MZ, QL, XL, LK
|
||||
Valve ZX has flow rate=0; tunnels lead to valves AD, BX
|
||||
Valve JN has flow rate=0; tunnels lead to valves DD, AA
|
||||
Valve ST has flow rate=0; tunnels lead to valves CR, DC
|
||||
Valve WK has flow rate=0; tunnels lead to valves PF, UN
|
||||
Valve DW has flow rate=13; tunnels lead to valves LC, HL
|
||||
Valve ZK has flow rate=0; tunnels lead to valves KJ, JT
|
||||
Valve QL has flow rate=0; tunnels lead to valves DH, PF
|
||||
Valve RI has flow rate=0; tunnels lead to valves OD, RP
|
||||
Valve EQ has flow rate=23; tunnel leads to valve ZN
|
||||
Valve LC has flow rate=0; tunnels lead to valves JL, DW
|
||||
Valve KU has flow rate=0; tunnels lead to valves XN, TA
|
||||
247
day16/src/main.rs
Normal file
247
day16/src/main.rs
Normal file
@@ -0,0 +1,247 @@
|
||||
use std::{
|
||||
collections::{HashMap, VecDeque},
|
||||
fs,
|
||||
};
|
||||
|
||||
use itertools::Itertools;
|
||||
use nom::{
|
||||
branch::alt,
|
||||
bytes::complete::tag,
|
||||
character::complete::{self, newline},
|
||||
error::Error,
|
||||
multi::{separated_list0, separated_list1},
|
||||
sequence::preceded,
|
||||
Parser,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
struct Valve {
|
||||
pub label: String,
|
||||
pub release: usize,
|
||||
pub connected_to: Vec<String>,
|
||||
}
|
||||
impl<'a> Parser<&'a str, Self, Error<&'a str>> for Valve {
|
||||
fn parse(&mut self, input: &'a str) -> nom::IResult<&'a str, Self, Error<&'a str>> {
|
||||
let (input, label) =
|
||||
preceded(tag("Valve "), complete::alpha1.map(|s: &str| s.to_owned()))(input)?;
|
||||
let (input, release) =
|
||||
preceded(tag(" has flow rate="), complete::u128.map(|s| s as usize))(input)?;
|
||||
let (input, connected_to) = preceded(
|
||||
alt((
|
||||
tag("; tunnels lead to valves "),
|
||||
tag("; tunnel leads to valve "),
|
||||
)),
|
||||
separated_list1(tag(", "), complete::alpha1.map(|s: &str| s.to_owned())),
|
||||
)(input)?;
|
||||
self.label = label;
|
||||
self.release = release;
|
||||
self.connected_to = connected_to.clone();
|
||||
Ok((input, self.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
//* nom parser to take string input and turn it in to a hashmaps of valves */
|
||||
fn parse_input(input: &str) -> nom::IResult<&str, HashMap<String, Valve>> {
|
||||
let (input, valves) = separated_list0(newline, Valve::default())(input)?;
|
||||
Ok((
|
||||
input,
|
||||
valves
|
||||
.iter()
|
||||
.map(|x| (x.label.clone(), x.clone()))
|
||||
.collect(),
|
||||
))
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord)]
|
||||
struct SimpleValve {
|
||||
pub _label: String,
|
||||
pub release: usize,
|
||||
pub connected_to: Vec<(usize, usize)>,
|
||||
}
|
||||
|
||||
//* takes a map of valves represented by strings and removes the unnexasrray steps and will create a complete graph of the connected */
|
||||
fn convert_to_distance(board: &HashMap<String, Valve>) -> Vec<SimpleValve> {
|
||||
let care_about = board
|
||||
.iter()
|
||||
.filter(|(pos, valve)| valve.release != 0 || *pos == "AA")
|
||||
.collect::<Vec<_>>();
|
||||
let mut distances: HashMap<(&str, &str), usize> = HashMap::new();
|
||||
//get the distance for each possible connection
|
||||
care_about.iter().for_each(|(from_pos, from_valve)| {
|
||||
let mut queue = VecDeque::from([(0_usize, *from_valve)]);
|
||||
while let Some((dist, check)) = queue.pop_front() {
|
||||
let dist = dist + 1;
|
||||
for v in check.connected_to.iter().filter_map(|i| board.get(i)) {
|
||||
if let Some(d) = distances.get(&(from_pos, &v.label)) {
|
||||
if dist >= *d {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
distances.insert((from_pos, &v.label), dist);
|
||||
|
||||
queue.push_back((dist, v));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
//makeing distances immutable
|
||||
let distances = distances;
|
||||
let holder = care_about
|
||||
.iter()
|
||||
.sorted_by(|(a, _), (b, _)| (*a).cmp(*b))
|
||||
.map(|(a, _)| (*a).clone())
|
||||
.enumerate()
|
||||
.map(|(i, a)| (a, i))
|
||||
.collect::<HashMap<_, _>>();
|
||||
care_about
|
||||
.iter()
|
||||
.map(|(pos, valve)| {
|
||||
let connected_to = care_about
|
||||
.iter()
|
||||
.filter(|(key, _)| "AA" != *key && **pos != **key)
|
||||
.map(|(to_pos, _)| {
|
||||
(
|
||||
*holder.get(*to_pos).unwrap(),
|
||||
*distances.get(&(pos, *to_pos)).unwrap(),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
(
|
||||
(*pos).clone(),
|
||||
SimpleValve {
|
||||
_label: valve.label.clone(),
|
||||
release: valve.release,
|
||||
connected_to,
|
||||
},
|
||||
)
|
||||
})
|
||||
.sorted_by(|(a, _), (b, _)| a.cmp(b))
|
||||
.map(|(_, a)| a)
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
struct StructKey(
|
||||
/*step*/ usize,
|
||||
/*valve*/ usize,
|
||||
/*pathmask*/ Pathmask,
|
||||
);
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
struct Pathmask {
|
||||
pub mask: usize,
|
||||
}
|
||||
impl From<usize> for Pathmask {
|
||||
fn from(mask: usize) -> Self {
|
||||
Self { mask }
|
||||
}
|
||||
}
|
||||
impl From<Pathmask> for usize {
|
||||
fn from(mask: Pathmask) -> Self {
|
||||
mask.mask
|
||||
}
|
||||
}
|
||||
|
||||
fn recurse(
|
||||
board: &Vec<SimpleValve>,
|
||||
vavle_index: usize,
|
||||
path: Pathmask,
|
||||
steps: usize,
|
||||
score: usize,
|
||||
cache: &mut HashMap<StructKey, usize>,
|
||||
) -> usize {
|
||||
//has this been cached?
|
||||
if let Some(sc) = cache.get(&StructKey(steps, vavle_index, path)) {
|
||||
println!("choo");
|
||||
return *sc;
|
||||
}
|
||||
let valve = board.get(vavle_index).unwrap();
|
||||
let new_path =
|
||||
Pathmask::from(std::convert::Into::<usize>::into(path) | (1_usize << vavle_index));
|
||||
let Some(v) = valve
|
||||
.connected_to
|
||||
.iter()
|
||||
.filter(|x| std::convert::Into::<usize>::into(path) & (1 << x.0) == 0 && steps > x.1 + 1)
|
||||
.map(|(next, new_dist)| {
|
||||
let new_steps = steps - 1 - new_dist;
|
||||
let score = score + board.get(*next).unwrap().release * new_steps;
|
||||
recurse(board, *next, new_path, new_steps, score, cache)
|
||||
})
|
||||
.fold(None, |acc: Option<usize>, new_spot: usize| {
|
||||
if acc.is_none() || new_spot > acc.unwrap() {
|
||||
Some(new_spot)
|
||||
} else {
|
||||
acc
|
||||
}
|
||||
})
|
||||
else {
|
||||
return score;
|
||||
};
|
||||
cache.insert(StructKey(steps, score, path), v);
|
||||
v
|
||||
}
|
||||
|
||||
fn part1(input: &str) -> String {
|
||||
//explore graph
|
||||
let board = convert_to_distance(&parse_input(input).unwrap().1);
|
||||
let cache = &mut HashMap::new();
|
||||
let r = recurse(&board, 0, 0.into(), 30, 0, cache);
|
||||
r.to_string()
|
||||
}
|
||||
|
||||
fn part2(input: &str) -> String {
|
||||
let board = convert_to_distance(&parse_input(input).unwrap().1);
|
||||
let cache = &mut HashMap::new();
|
||||
let top_mask: usize = (1 << board.len()) - 1;
|
||||
(0..=((top_mask + 1) / 2))
|
||||
.map(|i| {
|
||||
recurse(&board, 0, Pathmask::from(i), 26, 0, cache)
|
||||
+ recurse(&board, 0, Pathmask::from(top_mask ^ i), 26, 0, cache)
|
||||
})
|
||||
.max()
|
||||
.unwrap()
|
||||
.to_string()
|
||||
}
|
||||
//if open don't close
|
||||
fn main() {
|
||||
let file = fs::read_to_string("input.txt").unwrap();
|
||||
println!("Part 1: {}", part1(&file));
|
||||
println!("Part 2: {}", part2(&file));
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
const INPUT: &str = "Valve AA has flow rate=0; tunnels lead to valves DD, II, BB
|
||||
Valve BB has flow rate=13; tunnels lead to valves CC, AA
|
||||
Valve CC has flow rate=2; tunnels lead to valves DD, BB
|
||||
Valve DD has flow rate=20; tunnels lead to valves CC, AA, EE
|
||||
Valve EE has flow rate=3; tunnels lead to valves FF, DD
|
||||
Valve FF has flow rate=0; tunnels lead to valves EE, GG
|
||||
Valve GG has flow rate=0; tunnels lead to valves FF, HH
|
||||
Valve HH has flow rate=22; tunnel leads to valve GG
|
||||
Valve II has flow rate=0; tunnels lead to valves AA, JJ
|
||||
Valve JJ has flow rate=21; tunnel leads to valve II";
|
||||
|
||||
#[test]
|
||||
fn part1_works() {
|
||||
assert_eq!(part1(INPUT), "1651")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn part2_works() {
|
||||
assert_eq!(part2(INPUT), "1707")
|
||||
}
|
||||
#[test]
|
||||
fn testssss() {
|
||||
let board = convert_to_distance(&parse_input(INPUT).unwrap().1);
|
||||
let top_mask: usize = (1 << board.len()) - 1;
|
||||
(0..=((top_mask + 1) / 2)).for_each(|i| {
|
||||
let other = i ^ top_mask;
|
||||
let test = i | other;
|
||||
|
||||
assert_eq!(test, top_mask, "{i}");
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user