From 369149d98d7999ee838784b15b22e2eb0e0f4fe6 Mon Sep 17 00:00:00 2001 From: ThetaDev Date: Thu, 15 Dec 2022 14:43:14 +0100 Subject: [PATCH 1/4] wip --- example/day15.txt | 14 +++++ input/day15.txt | 38 +++++++++++++ src/day15.rs | 132 ++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 2 + 4 files changed, 186 insertions(+) create mode 100644 example/day15.txt create mode 100644 input/day15.txt create mode 100644 src/day15.rs diff --git a/example/day15.txt b/example/day15.txt new file mode 100644 index 0000000..a612424 --- /dev/null +++ b/example/day15.txt @@ -0,0 +1,14 @@ +Sensor at x=2, y=18: closest beacon is at x=-2, y=15 +Sensor at x=9, y=16: closest beacon is at x=10, y=16 +Sensor at x=13, y=2: closest beacon is at x=15, y=3 +Sensor at x=12, y=14: closest beacon is at x=10, y=16 +Sensor at x=10, y=20: closest beacon is at x=10, y=16 +Sensor at x=14, y=17: closest beacon is at x=10, y=16 +Sensor at x=8, y=7: closest beacon is at x=2, y=10 +Sensor at x=2, y=0: closest beacon is at x=2, y=10 +Sensor at x=0, y=11: closest beacon is at x=2, y=10 +Sensor at x=20, y=14: closest beacon is at x=25, y=17 +Sensor at x=17, y=20: closest beacon is at x=21, y=22 +Sensor at x=16, y=7: closest beacon is at x=15, y=3 +Sensor at x=14, y=3: closest beacon is at x=15, y=3 +Sensor at x=20, y=1: closest beacon is at x=15, y=3 diff --git a/input/day15.txt b/input/day15.txt new file mode 100644 index 0000000..fb23c15 --- /dev/null +++ b/input/day15.txt @@ -0,0 +1,38 @@ +Sensor at x=2793338, y=1910659: closest beacon is at x=2504930, y=2301197 +Sensor at x=2887961, y=129550: closest beacon is at x=2745008, y=-872454 +Sensor at x=3887055, y=2785942: closest beacon is at x=4322327, y=2605441 +Sensor at x=3957399, y=2164042: closest beacon is at x=3651713, y=1889668 +Sensor at x=1268095, y=1265989: closest beacon is at x=1144814, y=2000000 +Sensor at x=2093967, y=2103436: closest beacon is at x=2504930, y=2301197 +Sensor at x=2980126, y=1348046: closest beacon is at x=3651713, y=1889668 +Sensor at x=508134, y=3998686: closest beacon is at x=1123963, y=4608563 +Sensor at x=2982740, y=3604350: closest beacon is at x=2756683, y=3240616 +Sensor at x=2372671, y=3929034: closest beacon is at x=2756683, y=3240616 +Sensor at x=437628, y=1124644: closest beacon is at x=570063, y=959065 +Sensor at x=3271179, y=3268845: closest beacon is at x=3444757, y=3373782 +Sensor at x=1899932, y=730465: closest beacon is at x=570063, y=959065 +Sensor at x=1390358, y=3881569: closest beacon is at x=1123963, y=4608563 +Sensor at x=554365, y=989190: closest beacon is at x=570063, y=959065 +Sensor at x=2225893, y=2703661: closest beacon is at x=2504930, y=2301197 +Sensor at x=3755905, y=1346206: closest beacon is at x=3651713, y=1889668 +Sensor at x=3967103, y=3930797: closest beacon is at x=3444757, y=3373782 +Sensor at x=3534099, y=2371166: closest beacon is at x=3651713, y=1889668 +Sensor at x=3420789, y=1720583: closest beacon is at x=3651713, y=1889668 +Sensor at x=2222479, y=3278186: closest beacon is at x=2756683, y=3240616 +Sensor at x=100457, y=871319: closest beacon is at x=570063, y=959065 +Sensor at x=1330699, y=2091946: closest beacon is at x=1144814, y=2000000 +Sensor at x=598586, y=99571: closest beacon is at x=570063, y=959065 +Sensor at x=3436099, y=3392932: closest beacon is at x=3444757, y=3373782 +Sensor at x=3338431, y=3346334: closest beacon is at x=3444757, y=3373782 +Sensor at x=3892283, y=688090: closest beacon is at x=3651713, y=1889668 +Sensor at x=1485577, y=1929020: closest beacon is at x=1144814, y=2000000 +Sensor at x=2991003, y=2951060: closest beacon is at x=2756683, y=3240616 +Sensor at x=2855486, y=2533468: closest beacon is at x=2504930, y=2301197 +Sensor at x=750865, y=1619637: closest beacon is at x=1144814, y=2000000 +Sensor at x=3378101, y=3402212: closest beacon is at x=3444757, y=3373782 +Sensor at x=3515528, y=2950404: closest beacon is at x=3444757, y=3373782 +Sensor at x=163133, y=2640553: closest beacon is at x=-1016402, y=3057364 +Sensor at x=1765550, y=3021994: closest beacon is at x=2756683, y=3240616 +Sensor at x=534625, y=1056421: closest beacon is at x=570063, y=959065 +Sensor at x=3418549, y=3380980: closest beacon is at x=3444757, y=3373782 +Sensor at x=29, y=389033: closest beacon is at x=570063, y=959065 diff --git a/src/day15.rs b/src/day15.rs new file mode 100644 index 0000000..90c6d02 --- /dev/null +++ b/src/day15.rs @@ -0,0 +1,132 @@ +//! Day 15: Beacon Exclusion Zone + +use std::collections::HashSet; + +use once_cell::sync::Lazy; +use regex::Regex; + +type Coord = (i32, i32); + +fn parse_input(input: &[String]) -> Vec<(Coord, Coord)> { + static LINE_REGEX: Lazy = Lazy::new(|| { + Regex::new("Sensor at x=(-?\\d+), y=(-?\\d+): closest beacon is at x=(-?\\d+), y=(-?\\d+)") + .unwrap() + }); + + input + .iter() + .map(|line| { + let cap = LINE_REGEX.captures(line).unwrap(); + ( + ( + cap.get(1).unwrap().as_str().parse().unwrap(), + cap.get(2).unwrap().as_str().parse().unwrap(), + ), + ( + cap.get(3).unwrap().as_str().parse().unwrap(), + cap.get(4).unwrap().as_str().parse().unwrap(), + ), + ) + }) + .collect() +} + +fn mdist(p1: Coord, p2: Coord) -> i32 { + (p1.0.abs_diff(p2.0) + p1.1.abs_diff(p2.1)) as i32 +} + +fn beacon_exclude(pos: Coord, closest: Coord, excluded: &mut HashSet, row: i32) { + let dist = mdist(pos, closest); + + for x in pos.0 - dist..pos.0 + dist { + let point = (x, row); + if point != closest && mdist(pos, point) <= dist { + excluded.insert(point); + } + } +} + +fn beacon_exclude_section(pos: Coord, closest: Coord, excluded: &mut HashSet, limit: i32) { + let dist = mdist(pos, closest); + + for y in 0..=limit { + for x in 0..=limit { + let point = (x, y); + if mdist(pos, point) <= dist { + excluded.insert(point); + } + } + } +} + +fn _part1(input: Vec, row: i32) -> String { + let sensors = parse_input(&input); + let mut excluded = HashSet::new(); + + sensors + .iter() + .for_each(|(pos, closest)| beacon_exclude(*pos, *closest, &mut excluded, row)); + + excluded.len().to_string() +} + +fn _part2(input: Vec, limit: i32) -> String { + let sensors = parse_input(&input); + let mut excluded = HashSet::new(); + + sensors + .iter() + .enumerate() + .for_each(|(i, (pos, closest))| { + println!("bc {}", i); + beacon_exclude_section(*pos, *closest, &mut excluded, limit); + }); + + for y in 0..limit { + for x in 0..limit { + let point = (x, y); + if !excluded.contains(&point) { + dbg!(&point); + let f = point.0 * 4000000 + point.1; + return f.to_string(); + } + } + } + + panic!("not found") +} + +pub fn part1(input: Vec) -> String { + _part1(input, 2000000) +} + +pub fn part2(input: Vec) -> String { + _part2(input, 4000000) +} + +#[cfg(test)] +mod tests { + use super::*; + + const DAY: u8 = 15; + + #[test] + fn t_example1() { + assert_eq!(_part1(crate::read_example(DAY), 10), "26"); + } + + #[test] + fn t_example2() { + assert_eq!(_part2(crate::read_example(DAY), 20), "56000011"); + } + + #[test] + fn t_part1() { + assert_eq!(part1(crate::read_input(DAY)), ""); + } + + #[test] + fn t_part2() { + assert_eq!(part2(crate::read_input(DAY)), ""); + } +} diff --git a/src/main.rs b/src/main.rs index 4b34779..3f47343 100644 --- a/src/main.rs +++ b/src/main.rs @@ -22,6 +22,7 @@ mod day11; mod day12; mod day13; mod day14; +mod day15; pub(crate) fn read_input(day: u8) -> Vec { read_input_file(path!("input" / format!("day{}.txt", day))) @@ -77,6 +78,7 @@ days! { 12, day12, 13, day13, 14, day14, + 15, day15, } fn main() { From 4951abb7f2b0c7276519d60a1087ce22e26f6770 Mon Sep 17 00:00:00 2001 From: ThetaDev Date: Thu, 15 Dec 2022 14:43:14 +0100 Subject: [PATCH 2/4] day 15 --- example/day15.txt | 14 ++++ input/day15.txt | 38 +++++++++++ src/day15.rs | 162 ++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 2 + 4 files changed, 216 insertions(+) create mode 100644 example/day15.txt create mode 100644 input/day15.txt create mode 100644 src/day15.rs diff --git a/example/day15.txt b/example/day15.txt new file mode 100644 index 0000000..a612424 --- /dev/null +++ b/example/day15.txt @@ -0,0 +1,14 @@ +Sensor at x=2, y=18: closest beacon is at x=-2, y=15 +Sensor at x=9, y=16: closest beacon is at x=10, y=16 +Sensor at x=13, y=2: closest beacon is at x=15, y=3 +Sensor at x=12, y=14: closest beacon is at x=10, y=16 +Sensor at x=10, y=20: closest beacon is at x=10, y=16 +Sensor at x=14, y=17: closest beacon is at x=10, y=16 +Sensor at x=8, y=7: closest beacon is at x=2, y=10 +Sensor at x=2, y=0: closest beacon is at x=2, y=10 +Sensor at x=0, y=11: closest beacon is at x=2, y=10 +Sensor at x=20, y=14: closest beacon is at x=25, y=17 +Sensor at x=17, y=20: closest beacon is at x=21, y=22 +Sensor at x=16, y=7: closest beacon is at x=15, y=3 +Sensor at x=14, y=3: closest beacon is at x=15, y=3 +Sensor at x=20, y=1: closest beacon is at x=15, y=3 diff --git a/input/day15.txt b/input/day15.txt new file mode 100644 index 0000000..fb23c15 --- /dev/null +++ b/input/day15.txt @@ -0,0 +1,38 @@ +Sensor at x=2793338, y=1910659: closest beacon is at x=2504930, y=2301197 +Sensor at x=2887961, y=129550: closest beacon is at x=2745008, y=-872454 +Sensor at x=3887055, y=2785942: closest beacon is at x=4322327, y=2605441 +Sensor at x=3957399, y=2164042: closest beacon is at x=3651713, y=1889668 +Sensor at x=1268095, y=1265989: closest beacon is at x=1144814, y=2000000 +Sensor at x=2093967, y=2103436: closest beacon is at x=2504930, y=2301197 +Sensor at x=2980126, y=1348046: closest beacon is at x=3651713, y=1889668 +Sensor at x=508134, y=3998686: closest beacon is at x=1123963, y=4608563 +Sensor at x=2982740, y=3604350: closest beacon is at x=2756683, y=3240616 +Sensor at x=2372671, y=3929034: closest beacon is at x=2756683, y=3240616 +Sensor at x=437628, y=1124644: closest beacon is at x=570063, y=959065 +Sensor at x=3271179, y=3268845: closest beacon is at x=3444757, y=3373782 +Sensor at x=1899932, y=730465: closest beacon is at x=570063, y=959065 +Sensor at x=1390358, y=3881569: closest beacon is at x=1123963, y=4608563 +Sensor at x=554365, y=989190: closest beacon is at x=570063, y=959065 +Sensor at x=2225893, y=2703661: closest beacon is at x=2504930, y=2301197 +Sensor at x=3755905, y=1346206: closest beacon is at x=3651713, y=1889668 +Sensor at x=3967103, y=3930797: closest beacon is at x=3444757, y=3373782 +Sensor at x=3534099, y=2371166: closest beacon is at x=3651713, y=1889668 +Sensor at x=3420789, y=1720583: closest beacon is at x=3651713, y=1889668 +Sensor at x=2222479, y=3278186: closest beacon is at x=2756683, y=3240616 +Sensor at x=100457, y=871319: closest beacon is at x=570063, y=959065 +Sensor at x=1330699, y=2091946: closest beacon is at x=1144814, y=2000000 +Sensor at x=598586, y=99571: closest beacon is at x=570063, y=959065 +Sensor at x=3436099, y=3392932: closest beacon is at x=3444757, y=3373782 +Sensor at x=3338431, y=3346334: closest beacon is at x=3444757, y=3373782 +Sensor at x=3892283, y=688090: closest beacon is at x=3651713, y=1889668 +Sensor at x=1485577, y=1929020: closest beacon is at x=1144814, y=2000000 +Sensor at x=2991003, y=2951060: closest beacon is at x=2756683, y=3240616 +Sensor at x=2855486, y=2533468: closest beacon is at x=2504930, y=2301197 +Sensor at x=750865, y=1619637: closest beacon is at x=1144814, y=2000000 +Sensor at x=3378101, y=3402212: closest beacon is at x=3444757, y=3373782 +Sensor at x=3515528, y=2950404: closest beacon is at x=3444757, y=3373782 +Sensor at x=163133, y=2640553: closest beacon is at x=-1016402, y=3057364 +Sensor at x=1765550, y=3021994: closest beacon is at x=2756683, y=3240616 +Sensor at x=534625, y=1056421: closest beacon is at x=570063, y=959065 +Sensor at x=3418549, y=3380980: closest beacon is at x=3444757, y=3373782 +Sensor at x=29, y=389033: closest beacon is at x=570063, y=959065 diff --git a/src/day15.rs b/src/day15.rs new file mode 100644 index 0000000..8e5403a --- /dev/null +++ b/src/day15.rs @@ -0,0 +1,162 @@ +//! Day 15: Beacon Exclusion Zone + +use itertools::Itertools; +use once_cell::sync::Lazy; +use regex::Regex; + +type Coord = (i32, i32); + +struct Sensor { + pos: Coord, + beacon: Coord, + radius: i32, +} + +impl Sensor { + fn new(pos: Coord, beacon: Coord) -> Self { + Self { + pos, + beacon, + radius: mdist(pos, beacon), + } + } +} + +fn parse_input(input: &[String]) -> Vec { + static LINE_REGEX: Lazy = Lazy::new(|| { + Regex::new("Sensor at x=(-?\\d+), y=(-?\\d+): closest beacon is at x=(-?\\d+), y=(-?\\d+)") + .unwrap() + }); + + input + .iter() + .map(|line| { + let cap = LINE_REGEX.captures(line).unwrap(); + + Sensor::new( + ( + cap.get(1).unwrap().as_str().parse().unwrap(), + cap.get(2).unwrap().as_str().parse().unwrap(), + ), + ( + cap.get(3).unwrap().as_str().parse().unwrap(), + cap.get(4).unwrap().as_str().parse().unwrap(), + ), + ) + }) + .collect() +} + +fn mdist(p1: Coord, p2: Coord) -> i32 { + (p1.0.abs_diff(p2.0) + p1.1.abs_diff(p2.1)) as i32 +} + +fn line_possible_positions(sensors: &[Sensor], y: i32, min: i32, max: i32) -> Vec { + let mut x = min; + let mut res = Vec::new(); + + while x <= max { + let mut jumped = false; + for sensor in sensors { + let dist = mdist((x, y), sensor.pos); + if dist <= sensor.radius { + let jump = sensor.radius - dist + 2 * x.abs_diff(sensor.pos.0) as i32 + 1; + x += jump; + jumped = true; + break; + } + } + + if !jumped { + res.push((x, y)); + x += 1; + } + } + res +} + +fn _part1(input: Vec, row: i32) -> String { + let sensors = parse_input(&input); + + let x_minmax = sensors + .iter() + .flat_map(|s| { + let ycorrect = (s.pos.1.abs_diff(row) as i32).min(s.radius); + [s.pos.0 - s.radius + ycorrect, s.pos.0 + s.radius - ycorrect] + }) + .minmax(); + let (min, max) = match x_minmax { + itertools::MinMaxResult::NoElements => panic!("no sensors"), + itertools::MinMaxResult::OneElement(x) => (x, x), + itertools::MinMaxResult::MinMax(min, max) => (min, max), + }; + + let n_possible = line_possible_positions(&sensors, row, min, max).len(); + let beacons_in_row = sensors + .iter() + .filter_map(|s| { + if s.beacon.1 == row { + Some(s.beacon) + } else { + None + } + }) + .unique() + .count(); + + let res = max - min + 1 - n_possible as i32 - beacons_in_row as i32; + res.to_string() +} + +fn _part2(input: Vec, limit: i32) -> String { + let sensors = parse_input(&input); + + for y in 0..limit { + let positions = line_possible_positions(&sensors, y, 0, limit); + if let Some(pos) = positions.first() { + if positions.len() > 1 { + panic!("more than 1 distress beacon found") + } + + let freq = pos.0 as i64 * 4_000_000 + pos.1 as i64; + return freq.to_string(); + } + } + + panic!("distress beacon not found") +} + +pub fn part1(input: Vec) -> String { + _part1(input, 2_000_000) +} + +pub fn part2(input: Vec) -> String { + _part2(input, 4_000_000) +} + +#[cfg(test)] +mod tests { + use super::*; + + const DAY: u8 = 15; + + #[test] + fn t_example1() { + assert_eq!(_part1(crate::read_example(DAY), 10), "26"); + } + + #[test] + fn t_example2() { + assert_eq!(_part2(crate::read_example(DAY), 20), "56000011"); + } + + #[test] + fn t_part1() { + assert_eq!(part1(crate::read_input(DAY)), "5166077"); + } + + #[test] + fn t_part2() { + assert_eq!(part2(crate::read_input(DAY)), "13071206703981"); + } +} diff --git a/src/main.rs b/src/main.rs index 4b34779..3f47343 100644 --- a/src/main.rs +++ b/src/main.rs @@ -22,6 +22,7 @@ mod day11; mod day12; mod day13; mod day14; +mod day15; pub(crate) fn read_input(day: u8) -> Vec { read_input_file(path!("input" / format!("day{}.txt", day))) @@ -77,6 +78,7 @@ days! { 12, day12, 13, day13, 14, day14, + 15, day15, } fn main() { From b3768bc2c2b6d7a44f02638d7d8c0eaedd361cd9 Mon Sep 17 00:00:00 2001 From: ThetaDev Date: Sat, 17 Dec 2022 15:47:11 +0100 Subject: [PATCH 3/4] day 16 --- example/day16.txt | 10 ++ input/day16.txt | 50 ++++++++++ src/day16.rs | 250 ++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 2 + 4 files changed, 312 insertions(+) create mode 100644 example/day16.txt create mode 100644 input/day16.txt create mode 100644 src/day16.rs diff --git a/example/day16.txt b/example/day16.txt new file mode 100644 index 0000000..9f30acc --- /dev/null +++ b/example/day16.txt @@ -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 diff --git a/input/day16.txt b/input/day16.txt new file mode 100644 index 0000000..f4a9984 --- /dev/null +++ b/input/day16.txt @@ -0,0 +1,50 @@ +Valve DJ has flow rate=0; tunnels lead to valves ZH, AA +Valve LP has flow rate=0; tunnels lead to valves AA, EE +Valve GT has flow rate=0; tunnels lead to valves FJ, AW +Valve RO has flow rate=5; tunnels lead to valves NO, FD, QV, BV +Valve PS has flow rate=0; tunnels lead to valves FY, UV +Valve QV has flow rate=0; tunnels lead to valves EB, RO +Valve MV has flow rate=0; tunnels lead to valves FL, EB +Valve RN has flow rate=0; tunnels lead to valves AW, LQ +Valve HF has flow rate=0; tunnels lead to valves QN, HW +Valve PY has flow rate=19; tunnel leads to valve SN +Valve AT has flow rate=0; tunnels lead to valves YQ, UY +Valve UY has flow rate=3; tunnels lead to valves KV, ID, AT, PB, PG +Valve YI has flow rate=0; tunnels lead to valves FL, FD +Valve EB has flow rate=8; tunnels lead to valves MV, GQ, QV +Valve ID has flow rate=0; tunnels lead to valves NO, UY +Valve FY has flow rate=15; tunnels lead to valves LQ, PS +Valve GQ has flow rate=0; tunnels lead to valves EB, KM +Valve HW has flow rate=0; tunnels lead to valves FJ, HF +Valve CQ has flow rate=17; tunnels lead to valves KM, GO +Valve AW has flow rate=20; tunnels lead to valves RN, GT, WH, MX +Valve BV has flow rate=0; tunnels lead to valves RO, ZH +Valve PB has flow rate=0; tunnels lead to valves UY, AA +Valve MX has flow rate=0; tunnels lead to valves AW, YG +Valve DE has flow rate=4; tunnels lead to valves MM, PZ, PG, DS, EP +Valve AA has flow rate=0; tunnels lead to valves EP, PB, LP, JT, DJ +Valve QN has flow rate=23; tunnels lead to valves SN, HF +Valve GO has flow rate=0; tunnels lead to valves CQ, MK +Valve PZ has flow rate=0; tunnels lead to valves IJ, DE +Valve PG has flow rate=0; tunnels lead to valves UY, DE +Valve FL has flow rate=18; tunnels lead to valves MV, YI +Valve DS has flow rate=0; tunnels lead to valves DE, ZH +Valve ZH has flow rate=11; tunnels lead to valves YQ, BV, DJ, DS, SB +Valve KV has flow rate=0; tunnels lead to valves UY, IJ +Valve UV has flow rate=9; tunnels lead to valves MM, PS, YG +Valve WH has flow rate=0; tunnels lead to valves JT, AW +Valve FD has flow rate=0; tunnels lead to valves YI, RO +Valve FJ has flow rate=24; tunnels lead to valves HW, GT +Valve JT has flow rate=0; tunnels lead to valves AA, WH +Valve SN has flow rate=0; tunnels lead to valves PY, QN +Valve KM has flow rate=0; tunnels lead to valves GQ, CQ +Valve LQ has flow rate=0; tunnels lead to valves RN, FY +Valve NO has flow rate=0; tunnels lead to valves ID, RO +Valve SB has flow rate=0; tunnels lead to valves ZH, IJ +Valve MK has flow rate=25; tunnel leads to valve GO +Valve YG has flow rate=0; tunnels lead to valves MX, UV +Valve IJ has flow rate=16; tunnels lead to valves EE, KV, PZ, SB +Valve EP has flow rate=0; tunnels lead to valves AA, DE +Valve MM has flow rate=0; tunnels lead to valves UV, DE +Valve YQ has flow rate=0; tunnels lead to valves AT, ZH +Valve EE has flow rate=0; tunnels lead to valves LP, IJ diff --git a/src/day16.rs b/src/day16.rs new file mode 100644 index 0000000..f141b50 --- /dev/null +++ b/src/day16.rs @@ -0,0 +1,250 @@ +//! Day 16: Proboscidea Volcanium + +use std::collections::HashMap; + +use once_cell::sync::Lazy; +use regex::Regex; + +#[derive(Debug, Clone)] +struct Valve { + name: String, + flow: u16, + connections: Vec, +} + +#[derive(Debug, Clone, Hash, PartialEq, Eq)] +struct CacheKey { + open: u64, + valve: u64, + remaining: u8, + pressure: u64, + do_open: bool, +} + +fn parse_input(input: &[String]) -> Vec { + static LINE_REGEX: Lazy = Lazy::new(|| { + Regex::new("Valve (\\w{2}) has flow rate=(\\d+); tunnels? leads? to valves? ([\\w, ]+)") + .unwrap() + }); + + let ttable = input + .iter() + .enumerate() + .map(|(i, line)| { + let cap = LINE_REGEX + .captures(line) + .unwrap_or_else(|| panic!("could not parse line: {}", line)); + (cap.get(1).unwrap().as_str().to_string(), i as u16) + }) + .collect::>(); + + input + .iter() + .map(|line| { + let cap = LINE_REGEX + .captures(line) + .unwrap_or_else(|| panic!("could not parse line: {}", line)); + Valve { + name: cap.get(1).unwrap().as_str().to_owned(), + flow: cap.get(2).unwrap().as_str().parse().unwrap(), + connections: cap + .get(3) + .unwrap() + .as_str() + .split(", ") + .map(|n| *ttable.get(n).unwrap()) + .collect(), + } + }) + .collect() +} + +fn dfs( + cache: &mut HashMap<(u64, u16, u16), u16>, + valves: &[Valve], + current: usize, + opened: u64, + rate: u16, + steps: u16, +) -> u16 { + if steps == 0 { + return 0; + } + + if let Some(&released) = cache.get(&(opened, steps, current as u16)) { + return released; + } + + let mut released = 0; + + // we have 2 options: open the valve or go to any of the connections + + // Can we open the current valve and does it makes sense to do it ? + if opened & (1 << current) == 0 && valves[current].flow > 0 { + let next_opened = opened | 1 << current; + let next_rate = rate + valves[current].flow; + released = dfs(cache, valves, current, next_opened, next_rate, steps - 1); + } + + for next in valves[current].connections.iter().copied() { + let rel = dfs(cache, valves, next as usize, opened, rate, steps - 1); + released = released.max(rel); + } + + released += rate; + cache.insert((opened, steps, current as u16), released); + released +} + +fn dfs_elephant( + cache: &mut HashMap<(u64, u16, u16, u16), u16>, + valves: &[Valve], + me: usize, + elephant: usize, + opened: u64, + rate: u16, + steps: u16, +) -> u16 { + if steps == 0 { + return 0; + } + + if let Some(&released) = cache.get(&(opened, steps, me as u16, elephant as u16)) { + return released; + } + + // both we and the elephant have 2 options: open valve/go next + let me_can_open = opened & (1 << me) == 0 && valves[me].flow > 0; + let ele_can_open = me != elephant && opened & (1 << elephant) == 0 && valves[elephant].flow > 0; + + let mut released = 0; + + // I open valve, elephant moves + if me_can_open { + let next_opened = opened | 1 << me; + let next_rate = rate + valves[me].flow; + + for elephant_next in valves[elephant].connections.iter().copied() { + released = released.max(dfs_elephant( + cache, + valves, + me, + elephant_next as usize, + next_opened, + next_rate, + steps - 1, + )); + } + } + + // Elephant opens valve, I move + if ele_can_open { + let next_opened = opened | 1 << elephant; + let next_rate = rate + valves[elephant].flow; + + for me_next in valves[me].connections.iter().copied() { + released = released.max(dfs_elephant( + cache, + valves, + me_next as usize, + elephant, + next_opened, + next_rate, + steps - 1, + )); + } + } + + // Both open valves + if me_can_open && ele_can_open { + let next_opened = opened | 1 << me | 1 << elephant; + let next_rate = rate + valves[me].flow + valves[elephant].flow; + released = released.max(dfs_elephant( + cache, + valves, + me, + elephant, + next_opened, + next_rate, + steps - 1, + )); + } + + // Both move + if !me_can_open || !ele_can_open { + for me_next in valves[me].connections.iter().copied() { + for elephant_next in valves[elephant].connections.iter().copied() { + released = released.max(dfs_elephant( + cache, + valves, + me_next as usize, + elephant_next as usize, + opened, + rate, + steps - 1, + )); + } + } + } + + released += rate; + cache.insert((opened, steps, me as u16, elephant as u16), released); + released +} + +pub fn part1(input: Vec) -> String { + let valves = parse_input(&input); + let mut cache = HashMap::new(); + + let valve = valves + .iter() + .enumerate() + .find(|(_, v)| v.name == "AA") + .unwrap() + .0; + let res = dfs(&mut cache, &valves, valve, 0, 0, 30); + + res.to_string() +} + +pub fn part2(input: Vec) -> String { + let valves = parse_input(&input); + let mut cache = HashMap::new(); + + let valve = valves + .iter() + .enumerate() + .find(|(_, v)| v.name == "AA") + .unwrap() + .0; + let res = dfs_elephant(&mut cache, &valves, valve, valve, 0, 0, 26); + + res.to_string() +} + +#[cfg(test)] +mod tests { + use super::*; + + const DAY: u8 = 16; + + #[test] + fn t_example1() { + assert_eq!(part1(crate::read_example(DAY)), "1651"); + } + + #[test] + fn t_example2() { + assert_eq!(part2(crate::read_example(DAY)), "1707"); + } + + #[test] + fn t_part1() { + assert_eq!(part1(crate::read_input(DAY)), "1906"); + } + + #[test] + fn t_part2() { + assert_eq!(part2(crate::read_input(DAY)), "2548"); + } +} diff --git a/src/main.rs b/src/main.rs index 3f47343..383014c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -23,6 +23,7 @@ mod day12; mod day13; mod day14; mod day15; +mod day16; pub(crate) fn read_input(day: u8) -> Vec { read_input_file(path!("input" / format!("day{}.txt", day))) @@ -79,6 +80,7 @@ days! { 13, day13, 14, day14, 15, day15, + 16, day16, } fn main() { From 2f7d33f7fd2741d160e9c9cad6154ec218594e6e Mon Sep 17 00:00:00 2001 From: ThetaDev Date: Sat, 17 Dec 2022 19:24:07 +0100 Subject: [PATCH 4/4] day 17 (incomplete) --- example/day17.txt | 1 + input/day17.txt | 1 + src/day17.rs | 143 ++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 2 + 4 files changed, 147 insertions(+) create mode 100644 example/day17.txt create mode 100644 input/day17.txt create mode 100644 src/day17.rs diff --git a/example/day17.txt b/example/day17.txt new file mode 100644 index 0000000..97a1aa1 --- /dev/null +++ b/example/day17.txt @@ -0,0 +1 @@ +>>><<><>><<<>><>>><<<>>><<<><<<>><>><<>> diff --git a/input/day17.txt b/input/day17.txt new file mode 100644 index 0000000..7d14bf6 --- /dev/null +++ b/input/day17.txt @@ -0,0 +1 @@ +><<><>><<<>><><<<>>>><<<>><<<><<<<>>>><<>>><><>>>><<<<>><<<<><<<<>>>><<>>><>>>><<<<>>>><<<<><<<>>><<<<>>><<<><<<>><<<>><<><>><<>><<<>><<<>>>><<<><<<><<<<><><<<<>>><<<>><<><<>><<<<><<><>>><<<><<<>>><<<><>>><<<<>><<<>><<<<>><<>><<<><>>><<<>>>><<<<>>>><<>><<>>>><>><<>>>><<<>>>><<<<><<<><<>>><<<<>>><<>>><<<<>><><<<>><<<>>><<><><<>><<<><<<<>>>><<<>>><<>><<<>>>><><>>><>>><<<>>><<<>>><<>><<<>><>>>><><<>>>><><>>>><<<>>><<<<><>>>><>><<<<>>>><<<>><<<><<><<<>><><>>>><<<<>><>>><<>><<>>><<<><<<>>>><<>>>><><<>>><>>>><<>>><><<><<<<>><<>>>><<>>>><>><<<<><>>>><>>><>>>><<<>><<<><<<<><<<>>><>>>><<<<>><>>>><<<<>>><><<<<><>>><<<<>><<><<<>>>><<<<>><<<><>>>><><<<<>>>><<<>>><<<><<<>>>><><<<<>>><<><<<<>>><>><<<>>>><>>><<><<>>>><>><<<>><>><<>><>>>><<<><>>><<<>>>><<<<>>>><<<<>>>><<<><<>><><>>><<>><>>>><<<<><<<<>>><<<<>>><>><>><>><<<>>>><><>>>><<><<<>>>><<<<>>>><<<>>>><<>><>>>><>><><<<>>>><<><<><<<<>>>><>>>><<<>><>><<<<><<>><>>>><<<<>>>><<<<>>><><<><>><<>>><<<>>><<<>><<>>><<<<>>><<>><<><<<<>>>><<<>>><>><>>><>>><<<<>><<<>>><>>>><<<><<<><<<>>><<<<>>>><<<>>><<<>>><<<>>><<<<>>>><<<>>><<<>>>><<>>><<<<><<<>><<<>>>><<<<>>>><>>><<<<>><<>><<<<><>><<>>>><<>>><<<>>>><<><>>><<>><>>>><<<<>>><<<<><<<<><>>>><>><<>><>>><<<>>>><<<<>>><>>>><><<<<>>><<<<><<<><<>><<<<>>><<<<>><<>>>><<<>>>><<<<>><<><<<><<<>>><<<<>>><>><<<<><<<><<>>>><<<><<<<>><<<><<<<>>>><<<<><<>>><<<<>>><<<>>>><><<<><>>>><<<><<<<>>><<>>>><<<<>>>><<>>><>><<<>><>>>><<<<>>>><<<<>>>><<>>>><<>>><<<><<><>>>><>>>><<>>><<<>>>><<<><<<<><>>><<<>>>><<<<>><<<>>><<>>>><<<<><<<>><<>>>><><<<>>><<<><<<>>>><<>>>><<<>>>><<<<>>>><>>>><<<>><>><<<>><>>>><<>>><>><><<<>>>><<<<>>>><>>><<<<><<<>><<>><<<<>>><<>><<>>><<<>>><><<>><<<<>><<>>>><<<<><<<>>><>><<<>>>><>><<<<>><<<>>>><<<<>><><<>><<<><<<>><<<>>>><<<<>><<<<>>><<<><<<<>>>><<<>><<<<><<<>>><<<><<>><<>>><<<<>><>>>><<><<<>>><<>>><<<>>>><<<><<>><><>>><>>><<<>><<<>>><<>>><<<<><><<<<>><<<<>>><<<>>><<<<>><<<><<>>>><>><<<>><<<<>><>>>><<<><<>>>><<<>>><>><<<<>>>><<<<>>>><<<>>>><<<><><<<<>>>><<<<><<<>>><<<>><<>>><<>>><>>>><<<>>><<<>>>><<>><<>><<>>><<<>>>><<><<<><<<<>><<<<><>><<<<>><>>>><<>><<<<>>><>>><>>><<<>>><<<<>>><<<>>><<<<>>><<<>>><><>><<<<>>>><>>>><><<<<><<>>><<<>>>><<<>>><<<<>>><<<>>><<>>><<<><<<<>><<>>><<<>>><<<<>>><>>>><<<>>><<<>>><>>>><<>>><<<<>>><<<>><<<><<<<><<>><<<><<<>><<<<>>>><><<<>>><<<<><><<<<>><<<<>><<<>><<<<>>>><<>><<<>>><><>><<<>>>><<<<><<<<>><<><<><<<<><<><<<><>><<>><>>><<<<>>><<<>>>><>>>><<><>>>><><<<<>>>><<<><>>><><<<<>><<>><<>>><<>>>><<>>><>>>><<<<>>><<>>>><<<><<<<>>><<><<>><<<>><>>>><<<>>>><<>>><<<><>><<<><<<<><<>><>><<>>><<<>><<<<>>><<<<><<<>>>><<<<>><<<>><<<<><>>><<<><<<<>>><>>>><<>><<<>>>><><<>>><<><<>>>><<>>><<<<>>>><<<<><<<<>><<>><>>><>>><<><<>>>><>>>><<<>><<<><<<<>>>><<>>>><><<<><<>><<>>>><<>>><<<<><<<<>>>><>>><<<<>><<<>><>><<<<>>>><>>>><<<>>>><<>>>><><<>>>><<<>>>><>><>>><<<><<<>>><>><<<<>><>><<<>>>><<<<>>><>>><>><<<><<<><<<>>><>>><<>>>><<<<>>><<<>><<<>><<>><<<<>>>><<>>>><<<><>>><<><<<<>>>><<<><<<<><<<>>>><<<><<><>>><<<>>>><>>>><<<<>>><><<<>>>><>><<><<<><<<>>><<>><<<<>>>><<<><<<<>>>><>>><<<>>><><<<<>><>>><<<><<<>>>><<><<<<>>><<<>>>><<<<>><<>>><><<><<>><>>>><<<<>>>><><><<<<>>>><<<>>>><>>><><<<<>><<<><<<<>>>><<>>><<<>>><<><>>><<<<>><<>>>><>>><<>>>><<<<>><<<>>>><<<><<>>>><<>>>><<>><<><>>>><<>>><<<<>>>><<<<>><<<<><<>>>><<<>>><<><<><<<><<>>>><><<><<>>>><<>>>><<<>>><<<>>><<>>><<>><><><<<>>><<>>>><<<>><<<>><<>>><<<>><>>>><<>>><<<>>><>>><<<<><<>>>><<>>><<>>>><<>><>>><<><<<><<<><<<<>><<<<>>>><<<<>>>><<<<>>>><<>><>>>><<<<>>>><<>>>><<<<>><<<<>><<><<<<>>><<<<>>><<<><>><>><<>><<><<>>><<>>>><<>><>>><>>><<>>>><<<><>>>><<>>><<>>><<<<>>><<<>>>><>>>><>>>><<>>>><<<>><><<<<>>>><<<<>>>><<><<<>>><<<<><<>><<>><><<<>>><>>>><<<>>>><<<<>><>>>><<>><<><<<<>><>><<<<>>><<><><><>><<>>><<<>>>><<<>>><<<>>>><<<>><<><<>><<<>>>><><>>>><<<<>>><<<<><>>>><>><<>>><<>>>><<<>><><<<<>><<<<>>><<<<>><>><>>><<<<><<<<>>>><<<<><<<>><<<>>>><>>>><<<><<<<><<<<><<>>><<>><<<<>><<<><<>>><<<>><>>><>>><><<><<<>><>><>>><<>>><<<<>><>>><<<><><<<<>><<>>>><<<>><<>><<<>>><<<><>>><<<>>><<<<><>>><<>><<<><<<<><<<<>><<>>><>><>><<><><<<>>><<>>><<>>>><<>>><<<><<<>><<<<>>><<<><<>>><<>>><>>><>>>><<><<<<>>>><>>>><>>><<<<>>><<<<>>>><<<<>><<<<>>>><>>>><<>>>><<>>>><<<>>><><<<>>>><><><<<>>><<>><>><<<<>><<>>><<<><<<>><<<><<<<>>>><<<>><<>><<>>>><>>>><<<>>>><<<<>><>>><>>>><<<<>>><<>>><<<>><<<<><<<>>><<<<>><>><<<>><<>>><<<<>>>><<<<>>>><>>>><<<<>><<>><<<>>><<<<><<>>><<<<>><<<<>>><>><><<<<>>><<<><<<>>>><<>><<>><>>><>>>><<<<><<<<><<>>><>>>><<>>>><><<>><>>>><><>>>><<><<<<><<>>>><<<<><<<<>>><<>>>><<>>>><<<<>>><<>><>>><>><<><>>>><<<>><<<>>><<<>>><>>>><<<><>>><<>>>><<>>>><<<<>>>><<<>>>><<<>><>>>><>>>><<>>>><<>>>><>>>><<>>>><<<<><>>><<<><><>>><<<<><><>><<>>><<<<><<<>>><<<><<><<><<<<><<><<>>><<<><><<>>>><<<<>>>><<<<>>>><<<>><<>><>><<>>>><<<><<<<><<<<>>>><>><<<<>>>><>>><<<<>><><<><<<<><<>>><><>>>><<<>>><<<>>><><<<<>>>><<<<>>>><<<<><>>>><<<>><<>><>><<<<>><<<<>>><<>>>><<>><<<>>><<>><>>>><<<>>>><<>>>><<>>>><><><<<<><<<>>>><<>><<<<>><<>>>><<<>>>><>>>><<<>><<>>><>>>><<<>>>><<><<>>>><<>><<<>>>><>>><<<<>>><<>>>><<><<>>>><<>>><>>>><<<><<><>>><<<<><<<<>><<<<>>><<<<><<><<>><<>>><<<<>>>><<>>><<<<>>><<<>><<<>>>><<><><<<<>><<<<>>><<>>><>>><<>>><>>><<<>>>><>>>><<>><<<<>>>><<>><<><<>>><<><<<<><><>>><<><<>>>><<<><<<<>>><<<<>>>><>><>>><>><<<<>><>>><<<<>>><><<><<<<>>>><>><<<>>>><<>><>>>><<>>>><<<>>>><<>>>><><<<>><<>>>><<<>>>><>>><<<<>>>><>>><<>><<><><<><>>><<>><<<>>>><<>><><<><<<>><<<>><<<<><<>><<>>><<<<><<<<><<<>>>><<<<><<<><<>>>><<<<><<<><<<<><>><<>>><<<>>>><>>><<<<>>><<>>><<<<>><<<<>><<<>><<><><<<<><<>>>><<<<>>>><<<<>><<<>><<<<>><<<>>>><<>>>><<>>>><<<>><<><<<>>>><>><<<>><<>>>><>><<<>>>><<<>><<<>>>><>><>><<<>><<<><<<><<<<>><>>>><<<<>>>><<<<><<<>>>><<<<>><>>>><<>>><<<>>><<><<<>>>><><<<>><<>>>><<<<>><>><<<<>>>><<>><<<<><<<>>>><>>>><<<><><><>>><<<>>><>>>><<>>>><>><>>><<<><<>>>><<<<>><>>>><<<<><<>>>><>>>><<<>>>><<<<>>><<<<>>>><<<<><<<<>><<<<>>><<<<>><><<><<><>>><<<<>>><<>><<>><<>><<<><<><<>>>><<>><>>><<><<>>>><<<>>><>>>><<>><<<>><>>><><<<>>><<<<><>><<<>>><<<<>>>><<><><<>>><<>><<>>>><<>><>>>><><<<<><>>><<<>>><<<>><<>>>><>>>><>>>><<<><>>>><<>>><<><<><<><<<<>>>><<<>>>><<<><>>>><<<>>>><<<>>><<<><<<<>>><>>>><<<<>>>><<>>>><>>><<>><><>><<<>><<<><<<<>>>><<<<>><<<>>>><>>>><<<><<<>><<>>><>><<>>>><<>>><<<<>>>><<>><<<<>>>><<<<><<>>><<<><<<<>>><<<<><<<<>>>><<<>><<<<><<<>>><<<>>><<<<><<<<><><<<<><>>><<<<>>><<<<>><<<>><<<<>>><<<<>>>><<>>>><>>><<<>><<>>>><>>><<<>><<<><<<>>>><>>><>><<><<<<>><<<<>>><<>>>><<<><<>><<<>>><<<>><<<<><<<<>><>><<<<><>><>>>><><<<<>>><><<<<>>>><><<<<>><>><<<>><<<<><<>><<<><<<><>><<<><>>>><<<><<<>><<<>>>><<>><<<><>><>><>><<>><<<<>><>>><>><<<>>>><>><<<<>>>><>><<<<>>>><<>>>><<>><<>>>><<><<<>><<><<<<>>><<<<><<>>><<>>>><<<<><<>><<<><<<>>><<<>>><<<>><<<<><<<>><>><<<<>>>><<<<>><<<>><><<<>>>><>><<<>>>><<>><<><<<<><<<<>><<<<><>><<<>>>><<>>><>><<<>>><<<<>>><>>><<>>>><>><<<<>><<<<>><><<<>><<>><>><<>>><<<>><<<<>>>><<<>>><<<>>><><<<><<>>><<<<>><<><<<<><<>>>><<<>>><<>><>><>>>><<<<>>><<><>>><>>><<<<>><<<<>>>><<<<>>>><<>>>><>>>><<<>><<<><<<>>>><><<><<<<><<<<>><>>><<<>>><><<>><<<<>><<<<>>>><>>>><<<<>>>><<<<><><>>><<<>><><<<>>>><>><<<>>>><<><<<>>><<<<><>>><<<<>>>><<<<>>><<<><<<>><<<><<>>>><<><<<<>>><<<<>>><><>>>><>>><<<>>>><<<<>>>><<>>><>>><<<><<<><>>><<>><<<<>>><>>>><<<>><<<><<<><<>>><<<<>>><<<>>>><>>><<<>>><<<<>>>><<>><<<<>>><<<>>><<<<>><<<<><<<<>><<<<><>>><<<><>>>><<<>>><<>>>><>>>><><<>>>><<>><<<<>>><<<<>>><<<>><<<>>><<<<>><<<<><<<>>>><<<><>>><<<><<<<><<>><<<>>><>>><<<<>>><<>><<>>><<<>>><<<><<<>><<<<>>>><<<>><<<<>><<<><<>>>><<<<><<<<>>><<<<><<>>>><<<><<<<><<<<>>><>>><<<<><<>><<<>><<<>><<>><<<><>><<>><>>>><><<<<><<<>>>><<<><<<>>>><>><<<><<>><>><<<<>>>><<<>>><>>><<<<><<>>><<>>><<<>>><<<>>><<>><<<<>><>>>><<<><<<<>><>>><<<<>><<<>><<<>>>><<<>>>><><<<>>><>>>><>><<>><<><><<<><>>><<>>>><>>><<<<>>><<<<>><<<><<<>><<>><><<<<>><<<<><>>><<<<>>>><>>>><>>>><<><<<>>>><<<<>><<<<><<<<>>><<<<>>>><<<><<>>><<><<<<>><<>>>><>><<<<><<><<<<>>>><<>>><<<<><<<>>><<<<><<><<<<>><>>><>><<<<>>><<>><<<><<><<>>><<<>>>><<<<>>>><><<<><<<<>>>><<<<>>><<<>>>><>><<<<>>>><<<<><>>><><<<>><>>>><<<<>><>><><<><<<><<<<>>>><>>>><>><<><>>><<<>>>><<<>><>>>><<<<>><<>>>><><<<>><<<>><<>>>><<>>><>><><<<<>><>><>>><>><<<>>>><<<<>>><<>>><<<<>>><<<>>><<>>>><<><<<<>>>><<<<><>><<>>>><>>><>><><<<<>><<>>><<<><<<<>>>><<><<>><<<>>><<><><<<>>><>>>><<<>><<<<>>>><<>>><<>>><<>><><<<<>><<>><<>>><<><<>><<>><<>>>><<<<>>><<><<>>><<<<><<<<>>><<<<>>><<<<>>>><<<><>><>><<<>>>><<<<>>>><<<>><<>>>><<<<>>><<<>>><<>>>><<><<<<><<<>><<<<>>><<<><><<<>>>><>>>><<>>>><>><>>><<<<>>><<<<>>>><>>><>>>><>><<<>>><<>>><><<<<>>>><><<<<><<<<>>>><<<>>>><<>>><<<<>>><>>><<>>><<>><<<>>><<<<>><<<<><<<>><<<>>><>>>><<<<><>>><>>><<<<>>>><<<<>>>><<<><<>><<<<>>><<<>>><<>>><<<<><>>><>>>><<<<>>><<>><<<><<<>>>><<<<><>>>><<<><>>>><<<<>><><<><<>>><>>>><>><<>>>><<<<>><<>>><<<><<<>>><<<<>><>>><>>><>>><<<>><<<>>><<>>>><<<>><<<<>>>><><<<><>>>><<>>>><<<>>>><<<<>>><<><>>>><<>>>><<>>><<<>>>><>>>><<><<<<>>><><<>>><<<>><<<<>>>><<>>>><<><<>>>><<<<>>><>><<>><<>>>><<<>>><<>>>><<>>><<>>><><<><>>><<>>><>>>><><>><<><<>><<><>><<<>><<>>><<<>><<>>>><><<><<<>>>><<<<>>><<><<>>>><<<>>>><>>>><<>>><<>><><><>>>><<<<>>><<<>>>><<<><<>>><>>>><<>>><<>><>>><>>><<<>>>><<>>>><<<>>><<<<><<>>><<>>><<<>>>><<>>>><<<><<<<>><<<>>>><<<>><<<>>><<<>>>><<>><<<><<<<>>>><<>><>><<<>><<>>>><<<>>>><>>>><>>><><<>>>><<<>><<<<>>>><<<<>><<>>><<<>><>>><<<<>><<>>>><><<<<>>>><><><<<<>><>>><<>>>><<<><<>><<<<>>><<<<>>><>>><<<<>><<<><>><<><<>>>><>>>><<<<><<>>>><<>>>><><<><<>>><<<>><>><<<>>><<<>>>><<<<>>><<<<>><>>><>><<<<><<>>><>><<>>>><<><<<<>>><<<<>>><<<>>><>>>><<>><<<<>>>><>><<<>>><><<>>><<<<>><>><><>>><<>>>><<<><<<>>>><<<>>>><><<<<>>><<<<><<>><<<<>>>><<<>>>><<<>>><<<>><<<>><<<>>><<<>>>><<<<>>>><>>>><<<>>><>>>><<>>><>>><>>><>><<<<><>>><>>><<>>><<<<>><><<>>><<>><<<<>><<<<><<>><<<<>><<>>>><<<<>><>>><<<<><<<><<<>>><>>>><<><<<>>>><<<>>><<>>><>>><<>><<>>><<<<>>>><<>>><<<>><<<>>><>>>><<<<>>>><<<<>>><<>>><<>>><<<>>><<<<><<<<>><<<<><<<<><><<<>>><<<>>><>>><><<>><<<<>>>><<<<>>><<>>><<<><>>><<<>>>><<>>><<>>><<<>>>><<>><<>>>><>>>><<<>>><<<><>>>><<<>><>><>>>><><<<>>>><<<<>>>><>>>><<><<<<><>>><>>><<<<>>><<>>>><<<<>><<>><<><<<<>>><>>><<<<><>>>><<<<>>>><<>><>>><<<>><><<>>>><<<><<<<>><<><<>>><>>><<<<>>>><<>><<<<>><<<<>><< diff --git a/src/day17.rs b/src/day17.rs new file mode 100644 index 0000000..0226ff8 --- /dev/null +++ b/src/day17.rs @@ -0,0 +1,143 @@ +//! Day 17: Pyroclastic Flow + +use std::collections::HashSet; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +struct Coord(i64, i64); + +impl Coord { + fn sx(self, dx: i64) -> Self { + Self(self.0 + dx, self.1) + } + + fn sy(self, dy: i64) -> Self { + Self(self.0, self.1 + dy) + } +} + +fn parse_input(input: &[String]) -> Vec { + let line = input.first().unwrap(); + line.chars() + .filter_map(|c| match c { + '>' => Some(1), + '<' => Some(-1), + _ => None, + }) + .collect() +} + +fn rock_coords(rock_i: usize, h: i64) -> Vec { + // bottom left coord + let coord = Coord(2, h + 3); + + match rock_i % 5 { + 0 => vec![coord, coord.sx(1), coord.sx(2), coord.sx(3)], + 1 => vec![ + coord.sx(1), + coord.sy(1), + coord.sx(1).sy(1), + coord.sx(2).sy(1), + coord.sx(1).sy(2), + ], + 2 => vec![ + coord, + coord.sx(1), + coord.sx(2), + coord.sx(2).sy(1), + coord.sx(2).sy(2), + ], + 3 => vec![coord, coord.sy(1), coord.sy(2), coord.sy(3)], + 4 => vec![coord, coord.sx(1), coord.sy(1), coord.sx(1).sy(1)], + _ => panic!(), + } +} + +// returns new height +fn drop_rock( + rock_i: usize, + grid: &mut HashSet, + h: i64, + shifts: &[i64], + si: &mut usize, +) -> i64 { + let mut coords = rock_coords(rock_i, h); + let mut coords_shifted; + + loop { + let shift = shifts[*si % shifts.len()]; + *si += 1; + coords_shifted = coords.iter().map(|c| c.sx(shift)).collect::>(); + + // check shift + if coords_shifted + .iter() + .any(|c| c.0 < 0 || c.0 > 6 || grid.contains(c)) + { + coords_shifted = coords; + } + + let coords_dropped = coords_shifted.iter().map(|c| c.sy(-1)).collect::>(); + if coords_dropped.iter().any(|c| c.1 < 0 || grid.contains(c)) { + grid.extend(coords_shifted.iter()); + return h.max(coords_shifted.iter().map(|c| c.1).max().unwrap_or_default() + 1); + } + coords_shifted = coords_dropped; + coords = coords_shifted.clone(); + } +} + +fn drop_rocks(shifts: &[i64], n: usize) -> String { + let mut h = 0; + let mut grid: HashSet = HashSet::new(); + let mut si = 0; + + for i in 0..n { + h = drop_rock(i, &mut grid, h, shifts, &mut si); + + if i > 0 && i % 1_000_000 == 0 { + println!("prog: {}M", i / 1_000_000); + grid.retain(|c| c.1 < h - 5); + } + } + + h.to_string() +} + +pub fn part1(input: Vec) -> String { + let shifts = parse_input(&input); + drop_rocks(&shifts, 2022) +} + +pub fn part2(_input: Vec) -> String { + // 10092 + // let shifts = parse_input(&input); + // drop_rocks(&shifts, 1_000_000_000_000) + String::new() +} + +#[cfg(test)] +mod tests { + use super::*; + + const DAY: u8 = 17; + + #[test] + fn t_example1() { + assert_eq!(part1(crate::read_example(DAY)), "3068"); + } + + #[test] + fn t_example2() { + assert_eq!(part2(crate::read_example(DAY)), ""); + } + + #[test] + fn t_part1() { + assert_eq!(part1(crate::read_input(DAY)), "3173"); + } + + #[test] + fn t_part2() { + assert_eq!(part2(crate::read_input(DAY)), ""); + } +} diff --git a/src/main.rs b/src/main.rs index 383014c..4786057 100644 --- a/src/main.rs +++ b/src/main.rs @@ -24,6 +24,7 @@ mod day13; mod day14; mod day15; mod day16; +mod day17; pub(crate) fn read_input(day: u8) -> Vec { read_input_file(path!("input" / format!("day{}.txt", day))) @@ -81,6 +82,7 @@ days! { 14, day14, 15, day15, 16, day16, + 17, day17, } fn main() {