diff --git a/2022/src/bin/day15.rs b/2022/src/bin/day15.rs index 5550fe8..9f8b25f 100644 --- a/2022/src/bin/day15.rs +++ b/2022/src/bin/day15.rs @@ -2,10 +2,13 @@ use std::collections::HashSet; use regex::Regex; -const INPUT: &str = include_str!("../../input/day15.txt"); +// const INPUT: (&str, i32, i32) = (include_str!("../../input/day15-ex.txt"), 10, 20); +const INPUT: (&str, i32, i32) = (include_str!("../../input/day15.txt"), 2_000_000, 4_000_000); fn main() { - println!("{}", part1(INPUT, 2_000_000)); + let (input, y, max) = INPUT; + println!("Part 1: {}", part1(input, y)); + println!("Part 2: {}", part2(input, max)); } #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] @@ -50,8 +53,8 @@ fn part1(input: &str, yy: i32) -> usize { .cloned() .filter(|(s, b)| { let d_cover = s.pos.distance(b.pos); - let d_line = s.pos.distance(Pos::new(s.pos.x, yy)); - d_line <= d_cover + let dy = s.pos.distance(Pos::new(s.pos.x, yy)); + dy <= d_cover }) .collect(); @@ -99,3 +102,113 @@ fn parse_line(line: &str) -> (Sensor, Beacon) { }, ) } + +#[derive(Debug, Copy, Clone)] +struct Range { + start: i32, + end: i32, +} + +impl Range { + fn new(start: i32, end: i32) -> Self { + Self { start, end } + } + + fn union(&self, other: &Self) -> Option { + let adjacent = self.end + 1 >= other.start; + if adjacent { + use std::cmp::{max, min}; + let start = min(self.start, other.start); + let end = max(self.end, other.end); + Some(Self { start, end }) + } else { + None + } + } + + fn merge_left(mut rs: Vec) -> Self { + rs.sort_by_key(|r| r.start); + + let mut merged = rs[0]; + + for r in rs[1..].iter() { + if let Some(new) = merged.union(r) { + merged = new; + } else { + break; + } + } + merged + } + + fn len(&self) -> i32 { + 1 + self.end - self.start + } +} + +#[derive(Debug, Clone)] +struct Coverage { + ranges: Vec, + max: i32, +} + +impl Coverage { + fn new(max: i32) -> Self { + Self { + ranges: vec![], + max, + } + } + + fn insert(&mut self, r: Range) { + // Clip! + let r = Range { + start: r.start.max(0), + end: r.end.min(self.max), + }; + + if r.start <= r.end { + self.ranges.push(r); + } + } + + fn defrag(&self) -> Option { + let merged = Range::merge_left(self.ranges.clone()); + if merged.len() == self.max + 1 { + None + } else { + Some(merged.end + 1) + } + } +} + +fn part2(input: &str, max: i32) -> i64 { + let pairs: Vec<(Sensor, Beacon)> = aoc::lines(input).map(parse_line).collect(); + + for yy in 0..=max { + let mut coverage = Coverage::new(max); + + for (s, b) in &pairs { + let d_cover = s.pos.distance(b.pos); + + let projected = Pos::new(s.pos.x, yy); + let dy = s.pos.distance(projected); + let dx = d_cover - dy; + + if dx < 0 { + continue; + } + + let x = s.pos.x; + let r = Range::new(x - dx, x + dx); + + coverage.insert(r); + } + + if let Some(xx) = coverage.defrag() { + return 4_000_000 * (xx as i64) + (yy as i64); + } + } + + panic!("No solution? Must be the input! :P") +}