advent-of-code/2022/day15/rust/src/main.rs

101 lines
2.9 KiB
Rust

#![warn(clippy::pedantic)]
use std::io::stdin;
use aoc::{vec2::Vec2, SectionRange, UpToTwo};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct Sensor {
position: Vec2,
closest_beacon: Vec2,
}
impl Sensor {
fn beacon_distance(self) -> u32 {
self.closest_beacon.manhattan_dist(self.position)
}
}
fn main() {
let sensors: Vec<_> = stdin()
.lines()
.map(|line| {
let line = line.unwrap();
let (sensor, beacon) = line.split_once(": ").unwrap();
let point = |s: &str| {
let (x, y) = s.split_once(", ").unwrap();
let x = x.strip_prefix("x=").unwrap().parse().unwrap();
let y = y.strip_prefix("y=").unwrap().parse().unwrap();
(x, y).into()
};
let sensor = sensor.strip_prefix("Sensor at ").unwrap();
let beacon = beacon.strip_prefix("closest beacon is at ").unwrap();
Sensor {
position: point(sensor),
closest_beacon: point(beacon),
}
})
.collect();
for sensor in &sensors {
let dist = sensor.beacon_distance();
let candidates_x = sensor
.position
.on_x_with_manhattan_dist(sensor.closest_beacon.x, dist)
.unwrap();
let candidates_y = sensor
.position
.on_y_with_manhattan_dist(sensor.closest_beacon.y, dist)
.unwrap();
let candidates = [
candidates_x.0,
candidates_x.1,
candidates_y.0,
candidates_y.1,
];
assert!(candidates.contains(&sensor.closest_beacon));
}
let mut ranges: Vec<_> = sensors
.iter()
.flat_map(|sensor| {
let y = 2_000_000;
let Some((left, right)) = sensor
.position
.on_y_with_manhattan_dist(y, sensor.beacon_distance())
else {
return UpToTwo::Zero;
};
let range = SectionRange::try_new(left.x, right.x).unwrap();
if sensor.closest_beacon.y == y {
let beacon_pos = sensor.closest_beacon.y;
let beacon_pos = SectionRange::try_new(beacon_pos, beacon_pos).unwrap();
range - beacon_pos
} else {
UpToTwo::One(range)
}
})
.collect();
ranges.sort_unstable();
let mut covered = ranges.into_iter();
let mut count = 0;
let mut current_range = covered.next().unwrap();
for range in covered {
match current_range | range {
UpToTwo::Zero => unreachable!(),
UpToTwo::One(new) => current_range = new,
UpToTwo::Two(_old, new) => {
count += current_range.len().get();
current_range = new;
}
}
}
count += current_range.len().get();
println!("{:?}", count);
}