feat: day 16 (too long to count)
This commit is contained in:
parent
eeeec06e3d
commit
a788ddaa44
3 changed files with 326 additions and 0 deletions
52
.aoc-cache/16.txt
Normal file
52
.aoc-cache/16.txt
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
Valve JZ has flow rate=0; tunnels lead to valves IR, LY
|
||||||
|
Valve KD has flow rate=0; tunnels lead to valves NJ, ZS
|
||||||
|
Valve VW has flow rate=0; tunnels lead to valves IT, VH
|
||||||
|
Valve HS has flow rate=0; tunnels lead to valves OC, PN
|
||||||
|
Valve EU has flow rate=19; tunnel leads to valve GQ
|
||||||
|
Valve XF has flow rate=0; tunnels lead to valves WL, QD
|
||||||
|
Valve DD has flow rate=8; tunnels lead to valves GQ, YY, JV, SK
|
||||||
|
Valve TA has flow rate=0; tunnels lead to valves NJ, VJ
|
||||||
|
Valve IR has flow rate=9; tunnels lead to valves JZ, WI, VJ, GC, WG
|
||||||
|
Valve SS has flow rate=17; tunnels lead to valves SI, IZ, RK, WI
|
||||||
|
Valve SG has flow rate=0; tunnels lead to valves NV, NJ
|
||||||
|
Valve IT has flow rate=0; tunnels lead to valves LL, VW
|
||||||
|
Valve CP has flow rate=24; tunnels lead to valves HN, ZK, EJ
|
||||||
|
Valve SK has flow rate=0; tunnels lead to valves LL, DD
|
||||||
|
Valve IS has flow rate=0; tunnels lead to valves AA, LL
|
||||||
|
Valve HN has flow rate=0; tunnels lead to valves FF, CP
|
||||||
|
Valve VH has flow rate=10; tunnels lead to valves QO, VW, RV, PN
|
||||||
|
Valve JV has flow rate=0; tunnels lead to valves DD, RK
|
||||||
|
Valve ZS has flow rate=0; tunnels lead to valves KD, LL
|
||||||
|
Valve UC has flow rate=25; tunnels lead to valves JD, IV
|
||||||
|
Valve WI has flow rate=0; tunnels lead to valves SS, IR
|
||||||
|
Valve UR has flow rate=0; tunnels lead to valves QD, LY
|
||||||
|
Valve GC has flow rate=0; tunnels lead to valves AA, IR
|
||||||
|
Valve YY has flow rate=0; tunnels lead to valves DD, AA
|
||||||
|
Valve IV has flow rate=0; tunnels lead to valves ZK, UC
|
||||||
|
Valve BM has flow rate=0; tunnels lead to valves SA, WL
|
||||||
|
Valve JD has flow rate=0; tunnels lead to valves IZ, UC
|
||||||
|
Valve WL has flow rate=12; tunnels lead to valves EF, BM, EJ, XF
|
||||||
|
Valve AA has flow rate=0; tunnels lead to valves NV, YY, GC, IS, QO
|
||||||
|
Valve WG has flow rate=0; tunnels lead to valves LL, IR
|
||||||
|
Valve GQ has flow rate=0; tunnels lead to valves EU, DD
|
||||||
|
Valve SI has flow rate=0; tunnels lead to valves SS, NJ
|
||||||
|
Valve KH has flow rate=13; tunnels lead to valves SA, ON
|
||||||
|
Valve PC has flow rate=22; tunnel leads to valve ON
|
||||||
|
Valve QD has flow rate=14; tunnels lead to valves XF, UR
|
||||||
|
Valve IZ has flow rate=0; tunnels lead to valves SS, JD
|
||||||
|
Valve QO has flow rate=0; tunnels lead to valves AA, VH
|
||||||
|
Valve SA has flow rate=0; tunnels lead to valves BM, KH
|
||||||
|
Valve NV has flow rate=0; tunnels lead to valves AA, SG
|
||||||
|
Valve ZK has flow rate=0; tunnels lead to valves CP, IV
|
||||||
|
Valve ON has flow rate=0; tunnels lead to valves PC, KH
|
||||||
|
Valve PN has flow rate=0; tunnels lead to valves HS, VH
|
||||||
|
Valve RV has flow rate=0; tunnels lead to valves NJ, VH
|
||||||
|
Valve RK has flow rate=0; tunnels lead to valves SS, JV
|
||||||
|
Valve OC has flow rate=18; tunnel leads to valve HS
|
||||||
|
Valve EF has flow rate=0; tunnels lead to valves LY, WL
|
||||||
|
Valve VJ has flow rate=0; tunnels lead to valves TA, IR
|
||||||
|
Valve LL has flow rate=5; tunnels lead to valves ZS, IT, SK, IS, WG
|
||||||
|
Valve FF has flow rate=0; tunnels lead to valves HN, LY
|
||||||
|
Valve LY has flow rate=21; tunnels lead to valves EF, FF, UR, JZ
|
||||||
|
Valve EJ has flow rate=0; tunnels lead to valves WL, CP
|
||||||
|
Valve NJ has flow rate=6; tunnels lead to valves RV, KD, SG, SI, TA
|
264
src/bin/day_16.rs
Normal file
264
src/bin/day_16.rs
Normal file
|
@ -0,0 +1,264 @@
|
||||||
|
use std::collections::{HashMap, HashSet};
|
||||||
|
|
||||||
|
use aoc_2022::prelude::*;
|
||||||
|
|
||||||
|
use lru_cache::LruCache;
|
||||||
|
use regex::Regex;
|
||||||
|
|
||||||
|
type Input = HashMap<String, Valve>;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Valve {
|
||||||
|
flow_rate: usize,
|
||||||
|
tunnels: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse(s: &str) -> Result<Input> {
|
||||||
|
let re = Regex::new("Valve ([A-Z]{2}) has flow rate=([0-9]+)")?;
|
||||||
|
|
||||||
|
let mut valves = HashMap::new();
|
||||||
|
|
||||||
|
for line in s.lines() {
|
||||||
|
let (a, b) = line.split_once("; ").unwrap();
|
||||||
|
let captures = re.captures(a).unwrap();
|
||||||
|
let name = captures.get(1).unwrap().as_str().to_owned();
|
||||||
|
let flow_rate = captures.get(2).unwrap().as_str().parse()?;
|
||||||
|
|
||||||
|
let tunnels = b
|
||||||
|
.trim_start_matches("tunnel leads to valve")
|
||||||
|
.trim_start_matches("tunnels lead to valves")
|
||||||
|
.trim_start()
|
||||||
|
.split(", ")
|
||||||
|
.map(|s| s.to_owned())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
valves.insert(name, Valve { flow_rate, tunnels });
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(valves)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc(day = 16, parse = parse, test_cases = ["day_16.txt"])]
|
||||||
|
fn day_16(input: Input) -> Result<()> {
|
||||||
|
// Part 1
|
||||||
|
let (_path, greatest) = explore(
|
||||||
|
&input,
|
||||||
|
29,
|
||||||
|
"AA",
|
||||||
|
&HashSet::new(),
|
||||||
|
"AA".to_owned(),
|
||||||
|
&mut HashMap::new(),
|
||||||
|
);
|
||||||
|
println!("Part one: {greatest}");
|
||||||
|
// println!("{path}");
|
||||||
|
|
||||||
|
// Part 2
|
||||||
|
let greatest = explore_with_elephant(
|
||||||
|
&input,
|
||||||
|
25,
|
||||||
|
"AA",
|
||||||
|
"AA",
|
||||||
|
&HashSet::new(),
|
||||||
|
&mut LruCache::new(4_000_000),
|
||||||
|
);
|
||||||
|
println!("Part two: {greatest}");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
enum Action<'a> {
|
||||||
|
Open,
|
||||||
|
MoveTo(&'a str),
|
||||||
|
DoLiterallyNothingForAMinute,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sum_flow(input: &Input, open: &HashSet<String>) -> usize {
|
||||||
|
open.iter().map(|n| input[n].flow_rate).sum()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn encode_open(open: &HashSet<String>) -> String {
|
||||||
|
let mut result = String::new();
|
||||||
|
let mut open_vec = open.iter().collect::<Vec<_>>();
|
||||||
|
open_vec.sort();
|
||||||
|
for n in open_vec {
|
||||||
|
result.push_str(n);
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
fn explore(
|
||||||
|
input: &Input,
|
||||||
|
depth: usize,
|
||||||
|
node: &str,
|
||||||
|
open: &HashSet<String>,
|
||||||
|
path: String,
|
||||||
|
cache: &mut HashMap<(usize, String, String), (String, usize)>,
|
||||||
|
) -> (String, usize) {
|
||||||
|
let pressure_this_round = sum_flow(input, &open);
|
||||||
|
|
||||||
|
let cache_key = (depth, node.to_owned(), encode_open(open));
|
||||||
|
if let Some(result) = cache.get(&cache_key) {
|
||||||
|
return (result.0.clone(), pressure_this_round + result.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if depth == 0 {
|
||||||
|
return (path, pressure_this_round);
|
||||||
|
}
|
||||||
|
|
||||||
|
let current = &input[node];
|
||||||
|
let mut actions = current
|
||||||
|
.tunnels
|
||||||
|
.iter()
|
||||||
|
.map(|tunnel| Action::MoveTo(&*tunnel))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
actions.push(Action::DoLiterallyNothingForAMinute);
|
||||||
|
|
||||||
|
if current.flow_rate != 0 && !open.contains(node) {
|
||||||
|
actions.insert(0, Action::Open);
|
||||||
|
}
|
||||||
|
|
||||||
|
let result = actions
|
||||||
|
.iter()
|
||||||
|
.map(|action| match action {
|
||||||
|
Action::Open => {
|
||||||
|
let mut open = open.clone();
|
||||||
|
open.insert(node.to_owned());
|
||||||
|
explore(
|
||||||
|
input,
|
||||||
|
depth - 1,
|
||||||
|
node,
|
||||||
|
&open,
|
||||||
|
format!("{path}\n*open* ({pressure_this_round})"),
|
||||||
|
cache,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Action::MoveTo(n) => explore(
|
||||||
|
input,
|
||||||
|
depth - 1,
|
||||||
|
*n,
|
||||||
|
open,
|
||||||
|
format!("{path}\nmove to {n} ({pressure_this_round})"),
|
||||||
|
cache,
|
||||||
|
),
|
||||||
|
Action::DoLiterallyNothingForAMinute => explore(
|
||||||
|
input,
|
||||||
|
depth - 1,
|
||||||
|
node,
|
||||||
|
open,
|
||||||
|
format!("{path}\n*wait* ({pressure_this_round})"),
|
||||||
|
cache,
|
||||||
|
),
|
||||||
|
})
|
||||||
|
.max_by_key(|r| r.1)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
cache.insert(cache_key, result.clone());
|
||||||
|
|
||||||
|
(result.0, result.1 + pressure_this_round)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn explore_with_elephant(
|
||||||
|
input: &Input,
|
||||||
|
depth: usize,
|
||||||
|
my_node: &str,
|
||||||
|
elephant_node: &str,
|
||||||
|
open: &HashSet<String>,
|
||||||
|
cache: &mut LruCache<(usize, String, String, String), usize>,
|
||||||
|
) -> usize {
|
||||||
|
let pressure_this_round = sum_flow(input, &open);
|
||||||
|
|
||||||
|
let cache_key = (
|
||||||
|
depth,
|
||||||
|
my_node.to_owned(),
|
||||||
|
elephant_node.to_owned(),
|
||||||
|
encode_open(open),
|
||||||
|
);
|
||||||
|
if let Some(result) = cache.get_mut(&cache_key) {
|
||||||
|
return pressure_this_round + *result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if depth == 0 {
|
||||||
|
return pressure_this_round;
|
||||||
|
}
|
||||||
|
|
||||||
|
let my_actions = {
|
||||||
|
let current = &input[my_node];
|
||||||
|
let mut my_actions = current
|
||||||
|
.tunnels
|
||||||
|
.iter()
|
||||||
|
.map(|tunnel| Action::MoveTo(&*tunnel))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
my_actions.push(Action::DoLiterallyNothingForAMinute);
|
||||||
|
|
||||||
|
if current.flow_rate != 0 && !open.contains(my_node) {
|
||||||
|
my_actions.insert(0, Action::Open);
|
||||||
|
}
|
||||||
|
|
||||||
|
my_actions
|
||||||
|
};
|
||||||
|
|
||||||
|
let elephant_actions = {
|
||||||
|
let current = &input[elephant_node];
|
||||||
|
let mut elephant_actions = current
|
||||||
|
.tunnels
|
||||||
|
.iter()
|
||||||
|
.map(|tunnel| Action::MoveTo(&*tunnel))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
elephant_actions.push(Action::DoLiterallyNothingForAMinute);
|
||||||
|
|
||||||
|
if current.flow_rate != 0 && !open.contains(elephant_node) {
|
||||||
|
elephant_actions.insert(0, Action::Open);
|
||||||
|
}
|
||||||
|
|
||||||
|
elephant_actions
|
||||||
|
};
|
||||||
|
|
||||||
|
let paired_actions = my_actions
|
||||||
|
.into_iter()
|
||||||
|
.flat_map(|action| {
|
||||||
|
elephant_actions
|
||||||
|
.iter()
|
||||||
|
.map(Clone::clone)
|
||||||
|
.map(move |a| (action.clone(), a))
|
||||||
|
})
|
||||||
|
.filter(|(a, b)| match (a, b) {
|
||||||
|
(Action::Open, Action::Open) => false,
|
||||||
|
_ => true,
|
||||||
|
});
|
||||||
|
|
||||||
|
let result = paired_actions
|
||||||
|
.map(|(my_action, elephant_action)| {
|
||||||
|
let (open, my_node) = match my_action {
|
||||||
|
Action::Open => {
|
||||||
|
let mut open = open.clone();
|
||||||
|
open.insert(my_node.to_owned());
|
||||||
|
(open, my_node.to_owned())
|
||||||
|
}
|
||||||
|
Action::MoveTo(node) => (open.clone(), node.to_owned()),
|
||||||
|
Action::DoLiterallyNothingForAMinute => (open.clone(), my_node.to_owned()),
|
||||||
|
};
|
||||||
|
|
||||||
|
let (open, elephant_node) = match elephant_action {
|
||||||
|
Action::Open => {
|
||||||
|
let mut open = open.clone();
|
||||||
|
open.insert(elephant_node.to_owned());
|
||||||
|
(open, elephant_node.to_owned())
|
||||||
|
}
|
||||||
|
Action::MoveTo(node) => (open.clone(), node.to_owned()),
|
||||||
|
Action::DoLiterallyNothingForAMinute => (open.clone(), elephant_node.to_owned()),
|
||||||
|
};
|
||||||
|
|
||||||
|
explore_with_elephant(input, depth - 1, &my_node, &elephant_node, &open, cache)
|
||||||
|
})
|
||||||
|
.max()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
if depth < 10 || depth > 20 {
|
||||||
|
cache.insert(cache_key, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
result + pressure_this_round
|
||||||
|
}
|
10
test_cases/day_16.txt
Normal file
10
test_cases/day_16.txt
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
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
|
Loading…
Reference in a new issue