diff --git a/2022/src/common.rs b/2022/src/common.rs index c9eebf8..53156a2 100644 --- a/2022/src/common.rs +++ b/2022/src/common.rs @@ -2,6 +2,7 @@ use std::cmp::Ordering; use std::ops::Add; +use std::ops::Div; use std::ops::Index; use std::ops::IndexMut; use std::ops::Sub; @@ -202,6 +203,14 @@ impl Sub for Vec2 { } } +impl Div for Vec2 { + type Output = Self; + + fn div(self, rhs: i32) -> Self::Output { + Self(self.0.map(|v| v / rhs)) + } +} + impl Index for Vec2 { type Output = i32; diff --git a/2022/src/day15.rs b/2022/src/day15.rs index 4cd5310..3149342 100644 --- a/2022/src/day15.rs +++ b/2022/src/day15.rs @@ -18,6 +18,22 @@ use crate::common::Vec2; struct Beacon { pos: Vec2, closest: Vec2, + range: i32, +} + +impl Beacon { + pub fn can_contain_unseen(&self, lower: Vec2, upper: Vec2) -> bool { + let corners = [ + lower, + upper, + Vec2([lower[0], upper[1]]), + Vec2([upper[0], lower[1]]), + ]; + + corners + .into_iter() + .any(|c| (c - self.pos).l1() > self.range) + } } fn parse_pos(input: &[u8]) -> IResult<&[u8], Vec2> { @@ -34,7 +50,11 @@ fn parse_beacons(input: &[u8]) -> IResult<&[u8], Vec> { preceded(tag("Sensor at "), parse_pos), preceded(tag(": closest beacon is at "), parse_pos), ), - |(pos, closest)| Beacon { pos, closest }, + |(pos, closest)| Beacon { + pos, + closest, + range: (pos - closest).l1(), + }, ); many0(terminated(parse_beacon, newline))(input) @@ -51,8 +71,7 @@ fn part1_generic(input: &[u8]) -> Result { let mut on_line = AHashSet::new(); for beacon in beacons { - let distance = (beacon.closest - beacon.pos).l1(); - let horizontal_distance = distance - (beacon.pos[1] - Y).abs(); + let horizontal_distance = beacon.range - (beacon.pos[1] - Y).abs(); if horizontal_distance >= 0 { not_blocking_yet.push(Reverse(( @@ -92,8 +111,53 @@ pub fn part1(input: &[u8]) -> Result { part1_generic::<2000000>(input) } -pub fn part2(_input: &[u8]) -> Result { - anyhow::bail!("not implemented") +fn part2_generic(input: &[u8]) -> Result { + let beacons = parse_input(input, parse_beacons)?; + + fn find_unseen(beacons: &[Beacon]) -> Result { + let mut todo = vec![(Vec2([0, 0]), Vec2([MAX, MAX]))]; + + while let Some((lower, upper)) = todo.pop() { + if lower == upper { + if beacons + .iter() + .all(|beacon| (beacon.pos - lower).l1() > beacon.range) + { + return Ok(lower); + } + } else { + let mid = (lower + upper) / 2; + + let quads = [ + (lower, mid), + (Vec2([lower[0], mid[1] + 1]), Vec2([mid[0], upper[1]])), + (Vec2([mid[0] + 1, lower[1]]), Vec2([upper[0], mid[1]])), + (Vec2([mid[0] + 1, mid[1] + 1]), upper), + ]; + + for (lower_new, upper_new) in quads { + if lower_new[0] > upper_new[0] || lower_new[1] > upper_new[1] { + // Empty quad in quad tree, skip + } else if beacons + .iter() + .all(|beacon| beacon.can_contain_unseen(lower_new, upper_new)) + { + todo.push((lower_new, upper_new)); + } + } + } + } + + anyhow::bail!("Did not find position") + } + + let Vec2([x, y]) = find_unseen::(&beacons)?; + + Ok((i64::from(x) * 4000000 + i64::from(y)).to_string()) +} + +pub fn part2(input: &[u8]) -> Result { + part2_generic::<4000000>(input) } #[cfg(test)] @@ -106,4 +170,9 @@ mod tests { fn sample_part1() { assert_eq!(part1_generic::<10>(SAMPLE).unwrap(), "26"); } + + #[test] + fn sample_part2() { + assert_eq!(part2_generic::<20>(SAMPLE).unwrap(), "56000011"); + } }