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