feat: day 16 (too long to count)

This commit is contained in:
Ashhhleyyy 2022-12-18 21:28:17 +00:00
parent eeeec06e3d
commit a788ddaa44
Signed by: ash
GPG key ID: 83B789081A0878FB
3 changed files with 326 additions and 0 deletions

52
.aoc-cache/16.txt Normal file
View 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
View 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
View 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