From c43808fbfc080e8cc6a571c141a950543974a9bd Mon Sep 17 00:00:00 2001 From: Dreaded_X Date: Thu, 15 Dec 2022 22:30:23 +0100 Subject: [PATCH] 2022 - Day 15 --- 2022/input/15/input | 33 ++++++ 2022/input/15/test-1 | 14 +++ 2022/src/bin/day15.rs | 229 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 276 insertions(+) create mode 100644 2022/input/15/input create mode 100644 2022/input/15/test-1 create mode 100644 2022/src/bin/day15.rs diff --git a/2022/input/15/input b/2022/input/15/input new file mode 100644 index 0000000..26816fd --- /dev/null +++ b/2022/input/15/input @@ -0,0 +1,33 @@ +Sensor at x=407069, y=1770807: closest beacon is at x=105942, y=2000000 +Sensor at x=2968955, y=2961853: closest beacon is at x=2700669, y=3091664 +Sensor at x=3069788, y=2289672: closest beacon is at x=3072064, y=2287523 +Sensor at x=2206, y=1896380: closest beacon is at x=105942, y=2000000 +Sensor at x=3010408, y=2580417: closest beacon is at x=2966207, y=2275132 +Sensor at x=2511130, y=2230361: closest beacon is at x=2966207, y=2275132 +Sensor at x=65435, y=2285654: closest beacon is at x=105942, y=2000000 +Sensor at x=2811709, y=3379959: closest beacon is at x=2801189, y=3200444 +Sensor at x=168413, y=3989039: closest beacon is at x=-631655, y=3592291 +Sensor at x=165506, y=2154294: closest beacon is at x=105942, y=2000000 +Sensor at x=2720578, y=3116882: closest beacon is at x=2700669, y=3091664 +Sensor at x=786521, y=1485720: closest beacon is at x=105942, y=2000000 +Sensor at x=82364, y=2011850: closest beacon is at x=105942, y=2000000 +Sensor at x=2764729, y=3156203: closest beacon is at x=2801189, y=3200444 +Sensor at x=1795379, y=1766882: closest beacon is at x=1616322, y=907350 +Sensor at x=2708986, y=3105910: closest beacon is at x=2700669, y=3091664 +Sensor at x=579597, y=439: closest beacon is at x=1616322, y=907350 +Sensor at x=2671201, y=2736834: closest beacon is at x=2700669, y=3091664 +Sensor at x=3901, y=2089464: closest beacon is at x=105942, y=2000000 +Sensor at x=144449, y=813212: closest beacon is at x=105942, y=2000000 +Sensor at x=3619265, y=3169784: closest beacon is at x=2801189, y=3200444 +Sensor at x=2239333, y=3878605: closest beacon is at x=2801189, y=3200444 +Sensor at x=2220630, y=2493371: closest beacon is at x=2966207, y=2275132 +Sensor at x=1148022, y=403837: closest beacon is at x=1616322, y=907350 +Sensor at x=996105, y=3077490: closest beacon is at x=2700669, y=3091664 +Sensor at x=3763069, y=3875159: closest beacon is at x=2801189, y=3200444 +Sensor at x=3994575, y=2268273: closest beacon is at x=3072064, y=2287523 +Sensor at x=3025257, y=2244500: closest beacon is at x=2966207, y=2275132 +Sensor at x=2721366, y=1657084: closest beacon is at x=2966207, y=2275132 +Sensor at x=3783491, y=1332930: closest beacon is at x=3072064, y=2287523 +Sensor at x=52706, y=2020407: closest beacon is at x=105942, y=2000000 +Sensor at x=2543090, y=47584: closest beacon is at x=3450858, y=-772833 +Sensor at x=3499766, y=2477193: closest beacon is at x=3072064, y=2287523 diff --git a/2022/input/15/test-1 b/2022/input/15/test-1 new file mode 100644 index 0000000..a612424 --- /dev/null +++ b/2022/input/15/test-1 @@ -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/2022/src/bin/day15.rs b/2022/src/bin/day15.rs new file mode 100644 index 0000000..6e1d7c4 --- /dev/null +++ b/2022/src/bin/day15.rs @@ -0,0 +1,229 @@ +#![feature(test)] +use std::{str::FromStr, cmp::{min, max}}; + +use anyhow::Result; +use aoc::Solver; + +// -- Runners -- +fn main() -> Result<()> { + Day::solve() +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn part1_test1() -> Result<()> { + Day::test(Day::part1, "test-1", 26) + } + #[test] + fn part1_solution() -> Result<()> { + Day::test(Day::part1, "input", 4883971) + } + #[test] + fn part2_test1() -> Result<()> { + Day::test(Day::part2, "test-1", 56000011) + } + #[test] + fn part2_solutin() -> Result<()> { + Day::test(Day::part2, "input", 12691026767556) + } + + // Benchmarks + extern crate test; + #[bench] + #[ignore] + fn part1_bench(b: &mut test::Bencher) { + Day::benchmark(Day::part1, b) + } + #[bench] + #[ignore] + fn part2_bench(b: &mut test::Bencher) { + Day::benchmark(Day::part2, b) + } +} + +#[derive(Debug)] +struct Position { + x: isize, + y: isize, +} + +#[derive(Debug)] +struct Sensor { + position: Position, + beacon: Position, +} + +impl Sensor { + fn distance(&self) -> isize { + return (self.position.x - self.beacon.x).abs() + (self.position.y - self.beacon.y).abs(); + } +} + +impl FromStr for Sensor { + type Err = anyhow::Error; + + fn from_str(s: &str) -> Result { + let mut split = s.split(" ").map(|entry| { + entry.chars().filter(|c| c.is_digit(10) || *c == '-').collect::() + }).filter(|entry| !entry.is_empty()) + .map(|num| num.parse().unwrap()); + + // Sensor + let x = split.next().unwrap(); + let y = split.next().unwrap(); + let position = Position{ x, y }; + + // Beacon + let x = split.next().unwrap(); + let y = split.next().unwrap(); + let beacon = Position{ x, y }; + + Ok(Self { position, beacon }) + } +} + +#[derive(Debug, Copy, Clone)] +struct Range { + start: isize, + end: isize, +} + +impl Range { + fn len(&self) -> isize { + self.end - self.start + } +} + +fn merge_step(ranges: &mut Vec) { + let mut merged = vec![false; ranges.len()]; + let mut counter = 0; + + for idx in 0..ranges.len() { + if merged[idx] { + continue; + } + + let mut r = ranges[idx]; + for (jdx, other) in ranges.iter().enumerate().skip(idx+1) { + if ranges[idx].end < other.start || other.end < ranges[idx].start { + continue; + } + + merged[idx] = true; + merged[jdx] = true; + + r.start = min(r.start, other.start); + r.end = max(r.end, other.end); + } + + ranges[counter] = r; + counter += 1; + } + + ranges.truncate(counter); +} + +fn merge(ranges: &mut Vec) { + loop { + let old_len = ranges.len(); + merge_step(ranges); + + if old_len == ranges.len() { + break; + } + } +} + +// @TODO Implement this without doing a bunch of memory allocation +fn get_ranges(sensors: &Vec, y_level: isize, exclude_beacons: bool) -> Vec { + let mut ranges = Vec::new(); + for sensor in sensors { + let offset = sensor.distance() - (sensor.position.y - y_level).abs(); + if offset < 0 { + continue; + } + + let mut start = sensor.position.x - offset; + let mut end = sensor.position.x + offset + 1; + + if exclude_beacons && sensor.beacon.y == y_level && sensor.beacon.x >= start && sensor.beacon.x < end { + // If there is a beacon in the range we actually have to create two different + // ranges that exclude the beacon + if sensor.beacon.x != start && sensor.beacon.x+1 != end { + ranges.push(Range { start, end: sensor.beacon.x }); + start = sensor.beacon.x + 1; + } else if sensor.beacon.x == start { + start += 1; + } else if sensor.beacon.x+1 == end { + end -= 1; + } + } + + if end > start { + ranges.push(Range{start,end}); + } + } + + merge(&mut ranges); + + return ranges; +} + +// -- Solution -- +pub struct Day; +impl aoc::Solver for Day { + type Output1 = isize; + type Output2 = isize; + + fn day() -> u8 { + 15 + } + + fn part1(input: &str) -> Self::Output1 { + let sensors = input + .lines() + .flat_map(Sensor::from_str) + .collect::>(); + + let y_level; + if input.lines().count() > 14 { + y_level = 2000000; + } else { + y_level = 10; + } + + get_ranges(&sensors, y_level, true) + .iter() + // .inspect(|range| println!("{range:?}")) + .map(Range::len) + .sum() + } + + fn part2(input: &str) -> Self::Output2 { + let sensors = input + .lines() + .flat_map(Sensor::from_str) + .collect::>(); + let max; + if input.lines().count() > 14 { + max = 4000000; + } else { + max = 20; + } + + for y_level in 0..(max+1) { + let ranges = get_ranges(&sensors, y_level, false); + + if ranges.len() > 1 { + let x = ranges[0].end; + + return x * 4000000 + y_level; + } + } + + return -1; + } +}